| 547 | }; |
| 548 | |
| 549 | void wait_for_butex(void* arg) { |
| 550 | auto args = static_cast<WaitForButexArgs*>(arg); |
| 551 | ButexBthreadWaiter* const bw = args->bw; |
| 552 | Butex* const b = bw->initial_butex; |
| 553 | // 1: waiter with timeout should have waiter_state == WAITER_STATE_READY |
| 554 | // before they're queued, otherwise the waiter is already timedout |
| 555 | // and removed by TimerThread, in which case we should stop queueing. |
| 556 | // |
| 557 | // Visibility of waiter_state: |
| 558 | // [bthread] [TimerThread] |
| 559 | // waiter_state = TIMED |
| 560 | // tt_lock { add task } |
| 561 | // tt_lock { get task } |
| 562 | // waiter_lock { waiter_state=TIMEDOUT } |
| 563 | // waiter_lock { use waiter_state } |
| 564 | // tt_lock represents TimerThread::_mutex. Visibility of waiter_state is |
| 565 | // sequenced by two locks, both threads are guaranteed to see the correct |
| 566 | // value. |
| 567 | { |
| 568 | BAIDU_SCOPED_LOCK(b->waiter_lock); |
| 569 | if (b->value.load(butil::memory_order_relaxed) != bw->expected_value) { |
| 570 | bw->waiter_state = WAITER_STATE_UNMATCHEDVALUE; |
| 571 | } else if (bw->waiter_state == WAITER_STATE_READY/*1*/ && |
| 572 | !bw->task_meta->interrupted) { |
| 573 | if (args->prepend) { |
| 574 | b->waiters.Prepend(bw); |
| 575 | } else { |
| 576 | b->waiters.Append(bw); |
| 577 | } |
| 578 | bw->container.store(b, butil::memory_order_relaxed); |
| 579 | #ifdef BRPC_BTHREAD_TRACER |
| 580 | bw->control->_task_tracer.set_status(TASK_STATUS_SUSPENDED, bw->task_meta); |
| 581 | #endif // BRPC_BTHREAD_TRACER |
| 582 | if (bw->abstime != NULL) { |
| 583 | bw->sleep_id = get_global_timer_thread()->schedule( |
| 584 | erase_from_butex_and_wakeup, bw, *bw->abstime); |
| 585 | if (!bw->sleep_id) { // TimerThread stopped. |
| 586 | errno = ESTOP; |
| 587 | erase_from_butex_and_wakeup(bw); |
| 588 | } |
| 589 | } |
| 590 | return; |
| 591 | } |
| 592 | } |
| 593 | |
| 594 | // b->container is NULL which makes erase_from_butex_and_wakeup() and |
| 595 | // TaskGroup::interrupt() no-op, there's no race between following code and |
| 596 | // the two functions. The on-stack ButexBthreadWaiter is safe to use and |
| 597 | // bw->waiter_state will not change again. |
| 598 | // unsleep_if_necessary(bw, get_global_timer_thread()); |
| 599 | tls_task_group->ready_to_run(bw->task_meta); |
| 600 | // FIXME: jump back to original thread is buggy. |
| 601 | |
| 602 | // // Value unmatched or waiter is already woken up by TimerThread, jump |
| 603 | // // back to original bthread. |
| 604 | // TaskGroup* g = tls_task_group; |
| 605 | // ReadyToRunArgs args = { g->current_tid(), false }; |
| 606 | // g->set_remained(TaskGroup::ready_to_run_in_worker, &args); |
nothing calls this directly
no test coverage detected