| 47 | } |
| 48 | |
| 49 | static int bthread_sem_wait_impl(bthread_sem_t* sem, const struct timespec* abstime) { |
| 50 | bool queue_lifo = false; |
| 51 | bool first_wait = true; |
| 52 | size_t sampling_range = bvar::INVALID_SAMPLING_RANGE; |
| 53 | // -1: don't sample. |
| 54 | // 0: default value. |
| 55 | // > 0: Start time of sampling. |
| 56 | int64_t start_ns = 0; |
| 57 | auto whole = (butil::atomic<unsigned>*)sem->butex; |
| 58 | while (true) { |
| 59 | unsigned num = whole->load(butil::memory_order_relaxed); |
| 60 | if (num > 0) { |
| 61 | if (whole->compare_exchange_weak(num, num - 1, |
| 62 | butil::memory_order_acquire, |
| 63 | butil::memory_order_relaxed)) { |
| 64 | if (start_ns > 0) { |
| 65 | const int64_t end_ns = butil::cpuwide_time_ns(); |
| 66 | const bthread_contention_site_t csite{end_ns - start_ns, sampling_range}; |
| 67 | bthread::submit_contention(csite, end_ns); |
| 68 | } |
| 69 | |
| 70 | return 0; |
| 71 | } |
| 72 | } |
| 73 | // Don't sample when contention profiler is off. |
| 74 | if (NULL != bthread::g_cp && start_ns == 0 && sem->enable_csite && |
| 75 | !bvar::is_sampling_range_valid(sampling_range)) { |
| 76 | // Ask Collector if this (contended) sem waiting should be sampled. |
| 77 | sampling_range = bvar::is_collectable(&bthread::g_cp_sl); |
| 78 | start_ns = bvar::is_sampling_range_valid(sampling_range) ? |
| 79 | butil::cpuwide_time_ns() : -1; |
| 80 | } else { |
| 81 | start_ns = -1; |
| 82 | } |
| 83 | if (bthread::butex_wait(sem->butex, 0, abstime, queue_lifo) < 0 && |
| 84 | errno != EWOULDBLOCK && errno != EINTR) { |
| 85 | // A sema should ignore interruptions in general since |
| 86 | // user code is unlikely to check the return value. |
| 87 | if (ETIMEDOUT == errno && start_ns > 0) { |
| 88 | // Failed to lock due to ETIMEDOUT, submit the elapse directly. |
| 89 | const int64_t end_ns = butil::cpuwide_time_ns(); |
| 90 | const bthread_contention_site_t csite{end_ns - start_ns, sampling_range}; |
| 91 | bthread::submit_contention(csite, end_ns); |
| 92 | } |
| 93 | |
| 94 | return errno; |
| 95 | } |
| 96 | // Ignore EWOULDBLOCK and EINTR. |
| 97 | if (first_wait && 0 == errno) { |
| 98 | first_wait = false; |
| 99 | } |
| 100 | if (!first_wait) { |
| 101 | // Normally, bthreads are queued in FIFO order. But competing with new |
| 102 | // arriving bthreads over sema, a woken up bthread has good chances of |
| 103 | // losing. Because new arriving bthreads are already running on CPU and |
| 104 | // there can be lots of them. In such case, for fairness, to avoid |
| 105 | // starvation, it is queued at the head of the waiter queue. |
| 106 | queue_lifo = true; |
no test coverage detected