1*0Sigor@sysoev.ru 2*0Sigor@sysoev.ru /* 3*0Sigor@sysoev.ru * Copyright (C) Igor Sysoev 4*0Sigor@sysoev.ru * Copyright (C) NGINX, Inc. 5*0Sigor@sysoev.ru */ 6*0Sigor@sysoev.ru 7*0Sigor@sysoev.ru #include <nxt_main.h> 8*0Sigor@sysoev.ru 9*0Sigor@sysoev.ru 10*0Sigor@sysoev.ru #if (NXT_HAVE_SEM_TIMEDWAIT) 11*0Sigor@sysoev.ru 12*0Sigor@sysoev.ru /* 13*0Sigor@sysoev.ru * Linux POSIX semaphores use atomic/futex operations in since glibc 2.3. 14*0Sigor@sysoev.ru * 15*0Sigor@sysoev.ru * FreeBSD has two POSIX semaphore implementations. The first implementation 16*0Sigor@sysoev.ru * has been introduced in FreeBSD 5.0 but it has some drawbacks: 17*0Sigor@sysoev.ru * 1) it had a bug (http://bugs.freebsd.org/127545) fixed in FreeBSD 7.2; 18*0Sigor@sysoev.ru * 2) it does not use atomic operations and always calls ksem syscalls; 19*0Sigor@sysoev.ru * 3) a number of semaphores is just 30 by default and until FreeBSD 8.1 20*0Sigor@sysoev.ru * the number cannot be changed after boot time. 21*0Sigor@sysoev.ru * 22*0Sigor@sysoev.ru * The second implementation has been introduced in FreeBSD 6.1 in libthr 23*0Sigor@sysoev.ru * and uses atomic operations and umtx syscall. However, until FreeBSD 9.0 24*0Sigor@sysoev.ru * a choice of implementation depended on linking order of libthr and libc. 25*0Sigor@sysoev.ru * In FreeBSD 9.0 the umtx implementation has been moved to libc. 26*0Sigor@sysoev.ru * 27*0Sigor@sysoev.ru * Solaris have POSIX semaphores. 28*0Sigor@sysoev.ru * 29*0Sigor@sysoev.ru * MacOSX has limited POSIX semaphore implementation: 30*0Sigor@sysoev.ru * 1) sem_init() exists but returns ENOSYS; 31*0Sigor@sysoev.ru * 2) no sem_timedwait(). 32*0Sigor@sysoev.ru */ 33*0Sigor@sysoev.ru 34*0Sigor@sysoev.ru nxt_int_t 35*0Sigor@sysoev.ru nxt_sem_init(nxt_sem_t *sem, nxt_uint_t count) 36*0Sigor@sysoev.ru { 37*0Sigor@sysoev.ru if (sem_init(sem, 0, count) == 0) { 38*0Sigor@sysoev.ru nxt_thread_log_debug("sem_init(%p)", sem); 39*0Sigor@sysoev.ru return NXT_OK; 40*0Sigor@sysoev.ru } 41*0Sigor@sysoev.ru 42*0Sigor@sysoev.ru nxt_thread_log_alert("sem_init(%p) failed %E", sem, nxt_errno); 43*0Sigor@sysoev.ru return NXT_ERROR; 44*0Sigor@sysoev.ru } 45*0Sigor@sysoev.ru 46*0Sigor@sysoev.ru 47*0Sigor@sysoev.ru void 48*0Sigor@sysoev.ru nxt_sem_destroy(nxt_sem_t *sem) 49*0Sigor@sysoev.ru { 50*0Sigor@sysoev.ru if (sem_destroy(sem) == 0) { 51*0Sigor@sysoev.ru nxt_thread_log_debug("sem_destroy(%p)", sem); 52*0Sigor@sysoev.ru return; 53*0Sigor@sysoev.ru } 54*0Sigor@sysoev.ru 55*0Sigor@sysoev.ru nxt_thread_log_alert("sem_destroy(%p) failed %E", sem, nxt_errno); 56*0Sigor@sysoev.ru } 57*0Sigor@sysoev.ru 58*0Sigor@sysoev.ru 59*0Sigor@sysoev.ru nxt_int_t 60*0Sigor@sysoev.ru nxt_sem_post(nxt_sem_t *sem) 61*0Sigor@sysoev.ru { 62*0Sigor@sysoev.ru nxt_thread_log_debug("sem_post(%p)", sem); 63*0Sigor@sysoev.ru 64*0Sigor@sysoev.ru if (nxt_fast_path(sem_post(sem) == 0)) { 65*0Sigor@sysoev.ru return NXT_OK; 66*0Sigor@sysoev.ru } 67*0Sigor@sysoev.ru 68*0Sigor@sysoev.ru nxt_thread_log_alert("sem_post(%p) failed %E", sem, nxt_errno); 69*0Sigor@sysoev.ru 70*0Sigor@sysoev.ru return NXT_ERROR; 71*0Sigor@sysoev.ru } 72*0Sigor@sysoev.ru 73*0Sigor@sysoev.ru 74*0Sigor@sysoev.ru nxt_err_t 75*0Sigor@sysoev.ru nxt_sem_wait(nxt_sem_t *sem, nxt_nsec_t timeout) 76*0Sigor@sysoev.ru { 77*0Sigor@sysoev.ru int n; 78*0Sigor@sysoev.ru nxt_err_t err; 79*0Sigor@sysoev.ru nxt_nsec_t ns; 80*0Sigor@sysoev.ru nxt_thread_t *thr; 81*0Sigor@sysoev.ru nxt_realtime_t *now; 82*0Sigor@sysoev.ru struct timespec ts; 83*0Sigor@sysoev.ru 84*0Sigor@sysoev.ru thr = nxt_thread(); 85*0Sigor@sysoev.ru 86*0Sigor@sysoev.ru if (timeout == NXT_INFINITE_NSEC) { 87*0Sigor@sysoev.ru nxt_log_debug(thr->log, "sem_wait(%p) enter", sem); 88*0Sigor@sysoev.ru 89*0Sigor@sysoev.ru for ( ;; ) { 90*0Sigor@sysoev.ru n = sem_wait(sem); 91*0Sigor@sysoev.ru 92*0Sigor@sysoev.ru err = nxt_errno; 93*0Sigor@sysoev.ru 94*0Sigor@sysoev.ru nxt_thread_time_update(thr); 95*0Sigor@sysoev.ru 96*0Sigor@sysoev.ru if (nxt_fast_path(n == 0)) { 97*0Sigor@sysoev.ru nxt_thread_log_debug("sem_wait(%p) exit", sem); 98*0Sigor@sysoev.ru return 0; 99*0Sigor@sysoev.ru } 100*0Sigor@sysoev.ru 101*0Sigor@sysoev.ru switch (err) { 102*0Sigor@sysoev.ru 103*0Sigor@sysoev.ru case NXT_EINTR: 104*0Sigor@sysoev.ru nxt_log_error(NXT_LOG_INFO, thr->log, "sem_wait(%p) failed %E", 105*0Sigor@sysoev.ru sem, err); 106*0Sigor@sysoev.ru continue; 107*0Sigor@sysoev.ru 108*0Sigor@sysoev.ru default: 109*0Sigor@sysoev.ru nxt_log_alert(thr->log, "sem_wait(%p) failed %E", sem, err); 110*0Sigor@sysoev.ru return err; 111*0Sigor@sysoev.ru } 112*0Sigor@sysoev.ru } 113*0Sigor@sysoev.ru } 114*0Sigor@sysoev.ru 115*0Sigor@sysoev.ru #if (NXT_HAVE_SEM_TRYWAIT_FAST) 116*0Sigor@sysoev.ru 117*0Sigor@sysoev.ru nxt_log_debug(thr->log, "sem_trywait(%p) enter", sem); 118*0Sigor@sysoev.ru 119*0Sigor@sysoev.ru /* 120*0Sigor@sysoev.ru * Fast sem_trywait() using atomic operations may eliminate 121*0Sigor@sysoev.ru * timeout processing. 122*0Sigor@sysoev.ru */ 123*0Sigor@sysoev.ru 124*0Sigor@sysoev.ru if (nxt_fast_path(sem_trywait(sem) == 0)) { 125*0Sigor@sysoev.ru return 0; 126*0Sigor@sysoev.ru } 127*0Sigor@sysoev.ru 128*0Sigor@sysoev.ru #endif 129*0Sigor@sysoev.ru 130*0Sigor@sysoev.ru nxt_log_debug(thr->log, "sem_timedwait(%p, %N) enter", sem, timeout); 131*0Sigor@sysoev.ru 132*0Sigor@sysoev.ru now = nxt_thread_realtime(thr); 133*0Sigor@sysoev.ru ns = now->nsec + timeout; 134*0Sigor@sysoev.ru ts.tv_sec = now->sec + ns / 1000000000; 135*0Sigor@sysoev.ru ts.tv_nsec = ns % 1000000000; 136*0Sigor@sysoev.ru 137*0Sigor@sysoev.ru for ( ;; ) { 138*0Sigor@sysoev.ru n = sem_timedwait(sem, &ts); 139*0Sigor@sysoev.ru 140*0Sigor@sysoev.ru err = nxt_errno; 141*0Sigor@sysoev.ru 142*0Sigor@sysoev.ru nxt_thread_time_update(thr); 143*0Sigor@sysoev.ru 144*0Sigor@sysoev.ru if (nxt_fast_path(n == 0)) { 145*0Sigor@sysoev.ru nxt_thread_log_debug("sem_timedwait(%p) exit", sem); 146*0Sigor@sysoev.ru return 0; 147*0Sigor@sysoev.ru } 148*0Sigor@sysoev.ru 149*0Sigor@sysoev.ru switch (err) { 150*0Sigor@sysoev.ru 151*0Sigor@sysoev.ru case NXT_ETIMEDOUT: 152*0Sigor@sysoev.ru nxt_log_debug(thr->log, "sem_timedwait(%p) exit: %d", sem, err); 153*0Sigor@sysoev.ru return err; 154*0Sigor@sysoev.ru 155*0Sigor@sysoev.ru case NXT_EINTR: 156*0Sigor@sysoev.ru nxt_log_error(NXT_LOG_INFO, thr->log, "sem_timedwait(%p) failed %E", 157*0Sigor@sysoev.ru sem, err); 158*0Sigor@sysoev.ru continue; 159*0Sigor@sysoev.ru 160*0Sigor@sysoev.ru default: 161*0Sigor@sysoev.ru nxt_log_alert(thr->log, "sem_timedwait(%p) failed %E", sem, err); 162*0Sigor@sysoev.ru return err; 163*0Sigor@sysoev.ru } 164*0Sigor@sysoev.ru } 165*0Sigor@sysoev.ru } 166*0Sigor@sysoev.ru 167*0Sigor@sysoev.ru #else 168*0Sigor@sysoev.ru 169*0Sigor@sysoev.ru /* Semaphore implementation using pthread conditional variable. */ 170*0Sigor@sysoev.ru 171*0Sigor@sysoev.ru nxt_int_t 172*0Sigor@sysoev.ru nxt_sem_init(nxt_sem_t *sem, nxt_uint_t count) 173*0Sigor@sysoev.ru { 174*0Sigor@sysoev.ru if (nxt_thread_mutex_create(&sem->mutex) == NXT_OK) { 175*0Sigor@sysoev.ru 176*0Sigor@sysoev.ru if (nxt_thread_cond_create(&sem->cond) == NXT_OK) { 177*0Sigor@sysoev.ru sem->count = count; 178*0Sigor@sysoev.ru return NXT_OK; 179*0Sigor@sysoev.ru } 180*0Sigor@sysoev.ru 181*0Sigor@sysoev.ru nxt_thread_mutex_destroy(&sem->mutex); 182*0Sigor@sysoev.ru } 183*0Sigor@sysoev.ru 184*0Sigor@sysoev.ru return NXT_ERROR; 185*0Sigor@sysoev.ru } 186*0Sigor@sysoev.ru 187*0Sigor@sysoev.ru 188*0Sigor@sysoev.ru void 189*0Sigor@sysoev.ru nxt_sem_destroy(nxt_sem_t *sem) 190*0Sigor@sysoev.ru { 191*0Sigor@sysoev.ru nxt_thread_cond_destroy(&sem->cond); 192*0Sigor@sysoev.ru nxt_thread_mutex_destroy(&sem->mutex); 193*0Sigor@sysoev.ru } 194*0Sigor@sysoev.ru 195*0Sigor@sysoev.ru 196*0Sigor@sysoev.ru nxt_int_t 197*0Sigor@sysoev.ru nxt_sem_post(nxt_sem_t *sem) 198*0Sigor@sysoev.ru { 199*0Sigor@sysoev.ru nxt_int_t ret; 200*0Sigor@sysoev.ru 201*0Sigor@sysoev.ru if (nxt_slow_path(nxt_thread_mutex_lock(&sem->mutex) != NXT_OK)) { 202*0Sigor@sysoev.ru return NXT_ERROR; 203*0Sigor@sysoev.ru } 204*0Sigor@sysoev.ru 205*0Sigor@sysoev.ru ret = nxt_thread_cond_signal(&sem->cond); 206*0Sigor@sysoev.ru 207*0Sigor@sysoev.ru sem->count++; 208*0Sigor@sysoev.ru 209*0Sigor@sysoev.ru /* NXT_ERROR overrides NXT_OK. */ 210*0Sigor@sysoev.ru 211*0Sigor@sysoev.ru return (nxt_thread_mutex_unlock(&sem->mutex) | ret); 212*0Sigor@sysoev.ru } 213*0Sigor@sysoev.ru 214*0Sigor@sysoev.ru 215*0Sigor@sysoev.ru nxt_err_t 216*0Sigor@sysoev.ru nxt_sem_wait(nxt_sem_t *sem, nxt_nsec_t timeout) 217*0Sigor@sysoev.ru { 218*0Sigor@sysoev.ru nxt_err_t err; 219*0Sigor@sysoev.ru 220*0Sigor@sysoev.ru err = 0; 221*0Sigor@sysoev.ru 222*0Sigor@sysoev.ru if (nxt_slow_path(nxt_thread_mutex_lock(&sem->mutex) != NXT_OK)) { 223*0Sigor@sysoev.ru return NXT_ERROR; 224*0Sigor@sysoev.ru } 225*0Sigor@sysoev.ru 226*0Sigor@sysoev.ru while (sem->count == 0) { 227*0Sigor@sysoev.ru 228*0Sigor@sysoev.ru err = nxt_thread_cond_wait(&sem->cond, &sem->mutex, timeout); 229*0Sigor@sysoev.ru 230*0Sigor@sysoev.ru if (err != 0) { 231*0Sigor@sysoev.ru goto error; 232*0Sigor@sysoev.ru } 233*0Sigor@sysoev.ru } 234*0Sigor@sysoev.ru 235*0Sigor@sysoev.ru sem->count--; 236*0Sigor@sysoev.ru 237*0Sigor@sysoev.ru error: 238*0Sigor@sysoev.ru 239*0Sigor@sysoev.ru /* NXT_ERROR overrides NXT_OK and NXT_ETIMEDOUT. */ 240*0Sigor@sysoev.ru 241*0Sigor@sysoev.ru return (nxt_thread_mutex_unlock(&sem->mutex) | err); 242*0Sigor@sysoev.ru } 243*0Sigor@sysoev.ru 244*0Sigor@sysoev.ru #endif 245