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