| 1050 | const int MAX_SPIN_ITER = 4; |
| 1051 | |
| 1052 | inline int mutex_lock_contended_impl(bthread_mutex_t* __restrict m, |
| 1053 | const struct timespec* __restrict abstime) { |
| 1054 | BTHREAD_MUTEX_CHECK_OWNER; |
| 1055 | // When a bthread first contends for a lock, active spinning makes sense. |
| 1056 | // Spin only few times and only if local `rq' is empty. |
| 1057 | TaskGroup* g = BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group); |
| 1058 | if (BAIDU_UNLIKELY(NULL == g || g->rq_size() == 0)) { |
| 1059 | for (int i = 0; i < MAX_SPIN_ITER; ++i) { |
| 1060 | cpu_relax(); |
| 1061 | } |
| 1062 | } |
| 1063 | |
| 1064 | bool queue_lifo = false; |
| 1065 | bool first_wait = true; |
| 1066 | auto whole = (butil::atomic<unsigned>*)m->butex; |
| 1067 | while (whole->exchange(BTHREAD_MUTEX_CONTENDED) & BTHREAD_MUTEX_LOCKED) { |
| 1068 | if (bthread::butex_wait(whole, BTHREAD_MUTEX_CONTENDED, abstime, queue_lifo) < 0 && |
| 1069 | errno != EWOULDBLOCK && errno != EINTR/*note*/) { |
| 1070 | // A mutex lock should ignore interruptions in general since |
| 1071 | // user code is unlikely to check the return value. |
| 1072 | return errno; |
| 1073 | } |
| 1074 | // Ignore EWOULDBLOCK and EINTR. |
| 1075 | if (first_wait && 0 == errno) { |
| 1076 | first_wait = false; |
| 1077 | } |
| 1078 | if (!first_wait) { |
| 1079 | // Normally, bthreads are queued in FIFO order. But competing with new |
| 1080 | // arriving bthreads over the ownership of mutex, a woken up bthread |
| 1081 | // has good chances of losing. Because new arriving bthreads are already |
| 1082 | // running on CPU and there can be lots of them. In such case, for fairness, |
| 1083 | // to avoid starvation, it is queued at the head of the waiter queue. |
| 1084 | queue_lifo = true; |
| 1085 | } |
| 1086 | } |
| 1087 | BTHREAD_MUTEX_SET_OWNER; |
| 1088 | return 0; |
| 1089 | } |
| 1090 | |
| 1091 | #ifdef BTHREAD_USE_FAST_PTHREAD_MUTEX |
| 1092 | namespace internal { |
no test coverage detected