/src/unit/src/nxt_semaphore.c
Line | Count | Source (jump to first uncovered line) |
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 |
35 | | nxt_sem_init(nxt_sem_t *sem, nxt_uint_t count) |
36 | 0 | { |
37 | 0 | if (sem_init(sem, 0, count) == 0) { |
38 | 0 | nxt_thread_log_debug("sem_init(%p)", sem); |
39 | 0 | return NXT_OK; |
40 | 0 | } |
41 | | |
42 | 0 | nxt_thread_log_alert("sem_init(%p) failed %E", sem, nxt_errno); |
43 | 0 | return NXT_ERROR; |
44 | 0 | } |
45 | | |
46 | | |
47 | | void |
48 | | nxt_sem_destroy(nxt_sem_t *sem) |
49 | 0 | { |
50 | 0 | if (sem_destroy(sem) == 0) { |
51 | 0 | nxt_thread_log_debug("sem_destroy(%p)", sem); |
52 | 0 | return; |
53 | 0 | } |
54 | | |
55 | 0 | nxt_thread_log_alert("sem_destroy(%p) failed %E", sem, nxt_errno); |
56 | 0 | } |
57 | | |
58 | | |
59 | | nxt_int_t |
60 | | nxt_sem_post(nxt_sem_t *sem) |
61 | 0 | { |
62 | 0 | nxt_thread_log_debug("sem_post(%p)", sem); |
63 | |
|
64 | 0 | if (nxt_fast_path(sem_post(sem) == 0)) { |
65 | 0 | return NXT_OK; |
66 | 0 | } |
67 | | |
68 | 0 | nxt_thread_log_alert("sem_post(%p) failed %E", sem, nxt_errno); |
69 | |
|
70 | 0 | return NXT_ERROR; |
71 | 0 | } |
72 | | |
73 | | |
74 | | nxt_err_t |
75 | | nxt_sem_wait(nxt_sem_t *sem, nxt_nsec_t timeout) |
76 | 0 | { |
77 | 0 | int n; |
78 | 0 | nxt_err_t err; |
79 | 0 | nxt_nsec_t ns; |
80 | 0 | nxt_thread_t *thr; |
81 | 0 | nxt_realtime_t *now; |
82 | 0 | struct timespec ts; |
83 | |
|
84 | 0 | thr = nxt_thread(); |
85 | |
|
86 | 0 | if (timeout == NXT_INFINITE_NSEC) { |
87 | 0 | nxt_log_debug(thr->log, "sem_wait(%p) enter", sem); |
88 | |
|
89 | 0 | for ( ;; ) { |
90 | 0 | n = sem_wait(sem); |
91 | |
|
92 | 0 | err = nxt_errno; |
93 | |
|
94 | 0 | nxt_thread_time_update(thr); |
95 | |
|
96 | 0 | if (nxt_fast_path(n == 0)) { |
97 | 0 | nxt_thread_log_debug("sem_wait(%p) exit", sem); |
98 | 0 | return 0; |
99 | 0 | } |
100 | | |
101 | 0 | switch (err) { |
102 | | |
103 | 0 | case NXT_EINTR: |
104 | 0 | nxt_log_error(NXT_LOG_INFO, thr->log, "sem_wait(%p) failed %E", |
105 | 0 | sem, err); |
106 | 0 | continue; |
107 | | |
108 | 0 | default: |
109 | 0 | nxt_log_alert(thr->log, "sem_wait(%p) failed %E", sem, err); |
110 | 0 | return err; |
111 | 0 | } |
112 | 0 | } |
113 | 0 | } |
114 | | |
115 | 0 | #if (NXT_HAVE_SEM_TRYWAIT_FAST) |
116 | | |
117 | 0 | 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 | 0 | if (nxt_fast_path(sem_trywait(sem) == 0)) { |
125 | 0 | return 0; |
126 | 0 | } |
127 | | |
128 | 0 | #endif |
129 | | |
130 | 0 | nxt_log_debug(thr->log, "sem_timedwait(%p, %N) enter", sem, timeout); |
131 | |
|
132 | 0 | now = nxt_thread_realtime(thr); |
133 | 0 | ns = now->nsec + timeout; |
134 | 0 | ts.tv_sec = now->sec + ns / 1000000000; |
135 | 0 | ts.tv_nsec = ns % 1000000000; |
136 | |
|
137 | 0 | for ( ;; ) { |
138 | 0 | n = sem_timedwait(sem, &ts); |
139 | |
|
140 | 0 | err = nxt_errno; |
141 | |
|
142 | 0 | nxt_thread_time_update(thr); |
143 | |
|
144 | 0 | if (nxt_fast_path(n == 0)) { |
145 | 0 | nxt_thread_log_debug("sem_timedwait(%p) exit", sem); |
146 | 0 | return 0; |
147 | 0 | } |
148 | | |
149 | 0 | switch (err) { |
150 | | |
151 | 0 | case NXT_ETIMEDOUT: |
152 | 0 | nxt_log_debug(thr->log, "sem_timedwait(%p) exit: %d", sem, err); |
153 | 0 | return err; |
154 | | |
155 | 0 | case NXT_EINTR: |
156 | 0 | nxt_log_error(NXT_LOG_INFO, thr->log, "sem_timedwait(%p) failed %E", |
157 | 0 | sem, err); |
158 | 0 | continue; |
159 | | |
160 | 0 | default: |
161 | 0 | nxt_log_alert(thr->log, "sem_timedwait(%p) failed %E", sem, err); |
162 | 0 | return err; |
163 | 0 | } |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | | #else |
168 | | |
169 | | /* Semaphore implementation using pthread conditional variable. */ |
170 | | |
171 | | nxt_int_t |
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 |
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 |
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 |
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 |