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