xref: /unit/src/nxt_semaphore.c (revision 0:a63ceefd6ab0)
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
nxt_sem_init(nxt_sem_t * sem,nxt_uint_t count)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
nxt_sem_destroy(nxt_sem_t * sem)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
nxt_sem_post(nxt_sem_t * sem)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
nxt_sem_wait(nxt_sem_t * sem,nxt_nsec_t timeout)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
nxt_sem_init(nxt_sem_t * sem,nxt_uint_t count)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
nxt_sem_destroy(nxt_sem_t * sem)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
nxt_sem_post(nxt_sem_t * sem)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
nxt_sem_wait(nxt_sem_t * sem,nxt_nsec_t timeout)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