Coverage Report

Created: 2023-06-29 07:01

/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