The interruption is "persistent" compared to the ones caused by signals, namely if a bthread is interrupted when it's not blocked, the interruption is still remembered and will be checked at next blocking. This designing choice simplifies the implementation and reduces notification loss caused by race conditions. TODO: bthreads created by BTHREAD_ATTR_PTHREAD blocking on bthread_usleep() can't be
| 1083 | // TODO: bthreads created by BTHREAD_ATTR_PTHREAD blocking on bthread_usleep() |
| 1084 | // can't be interrupted. |
| 1085 | int TaskGroup::interrupt(bthread_t tid, TaskControl* c, bthread_tag_t tag) { |
| 1086 | // Consume current_waiter in the TaskMeta, wake it up then set it back. |
| 1087 | ButexWaiter* w = NULL; |
| 1088 | uint64_t sleep_id = 0; |
| 1089 | int rc = interrupt_and_consume_waiters(tid, &w, &sleep_id); |
| 1090 | if (rc) { |
| 1091 | return rc; |
| 1092 | } |
| 1093 | // a bthread cannot wait on a butex and be sleepy at the same time. |
| 1094 | CHECK(!sleep_id || !w); |
| 1095 | if (w != NULL) { |
| 1096 | erase_from_butex_because_of_interruption(w); |
| 1097 | // If butex_wait() already wakes up before we set current_waiter back, |
| 1098 | // the function will spin until current_waiter becomes non-NULL. |
| 1099 | rc = set_butex_waiter(tid, w); |
| 1100 | if (rc) { |
| 1101 | LOG(FATAL) << "butex_wait should spin until setting back waiter"; |
| 1102 | return rc; |
| 1103 | } |
| 1104 | } else if (sleep_id != 0) { |
| 1105 | if (get_global_timer_thread()->unschedule(sleep_id) == 0) { |
| 1106 | TaskGroup* g = tls_task_group; |
| 1107 | TaskMeta* m = address_meta(tid); |
| 1108 | if (g) { |
| 1109 | g->ready_to_run(m); |
| 1110 | } else { |
| 1111 | if (!c) { |
| 1112 | return EINVAL; |
| 1113 | } |
| 1114 | c->choose_one_group(tag)->ready_to_run_remote(m); |
| 1115 | } |
| 1116 | } |
| 1117 | } |
| 1118 | return 0; |
| 1119 | } |
| 1120 | |
| 1121 | void TaskGroup::yield(TaskGroup** pg) { |
| 1122 | TaskGroup* g = *pg; |
nothing calls this directly
no test coverage detected