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