Coverage Report

Created: 2025-10-10 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/unit/src/nxt_thread.c
Line
Count
Source
1
2
/*
3
 * Copyright (C) Igor Sysoev
4
 * Copyright (C) NGINX, Inc.
5
 */
6
7
#include <nxt_main.h>
8
9
10
static void *nxt_thread_trampoline(void *data);
11
static void nxt_thread_time_cleanup(void *data);
12
13
14
#if (NXT_HAVE_PTHREAD_SPECIFIC_DATA)
15
16
static void nxt_thread_key_dtor(void *data);
17
18
19
void
20
nxt_thread_init_data(nxt_thread_specific_data_t tsd)
21
{
22
    void           *p;
23
    nxt_err_t      err;
24
    pthread_key_t  key;
25
26
    while ((nxt_atomic_int_t) tsd->key < 0) {
27
        /*
28
         * Atomic allocation of a key number.
29
         * -1 means an uninitialized key,
30
         * -2 is the initializing lock to assure the single value for the key.
31
         */
32
        if (nxt_atomic_cmp_set(&tsd->key, -1, -2)) {
33
34
            err = pthread_key_create(&key, nxt_thread_key_dtor);
35
            if (err != 0) {
36
                nxt_main_log_alert("pthread_key_create() failed %E", err);
37
                goto fail;
38
            }
39
40
            tsd->key = (nxt_atomic_t) key;
41
42
            nxt_main_log_debug("pthread_key_create(): %A", tsd->key);
43
        }
44
    }
45
46
    if (pthread_getspecific((pthread_key_t) tsd->key) != NULL) {
47
        return;
48
    }
49
50
    p = nxt_zalloc(tsd->size);
51
    if (p == NULL) {
52
        goto fail;
53
    }
54
55
    err = pthread_setspecific((pthread_key_t) tsd->key, p);
56
    if (err == 0) {
57
        return;
58
    }
59
60
    nxt_main_log_alert("pthread_setspecific(%A) failed %E", tsd->key, err);
61
62
fail:
63
64
    pthread_exit(NULL);
65
    nxt_unreachable();
66
}
67
68
69
static void
70
nxt_thread_key_dtor(void *data)
71
{
72
    nxt_main_log_debug("pthread key dtor: %p", data);
73
74
    nxt_free(data);
75
}
76
77
#endif
78
79
80
nxt_int_t
81
nxt_thread_create(nxt_thread_handle_t *handle, nxt_thread_link_t *link)
82
0
{
83
0
    nxt_err_t  err;
84
85
0
    err = pthread_create(handle, NULL, nxt_thread_trampoline, link);
86
87
0
    if (nxt_fast_path(err == 0)) {
88
0
        nxt_thread_log_debug("pthread_create(): %PH", *handle);
89
90
0
        return NXT_OK;
91
0
    }
92
93
0
    nxt_thread_log_alert("pthread_create() failed %E", err);
94
95
0
    nxt_free(link);
96
97
0
    return NXT_ERROR;
98
0
}
99
100
101
static void *
102
nxt_thread_trampoline(void *data)
103
0
{
104
0
    nxt_thread_t        *thr;
105
0
    nxt_thread_link_t   *link;
106
0
    nxt_thread_start_t  start;
107
108
0
    link = data;
109
110
0
    thr = nxt_thread_init();
111
112
0
    nxt_log_debug(thr->log, "thread trampoline: %PH", thr->handle);
113
114
0
    pthread_cleanup_push(nxt_thread_time_cleanup, thr);
115
116
0
    start = link->start;
117
0
    data = link->work.data;
118
119
0
    if (link->work.handler != NULL) {
120
0
        thr->link = link;
121
122
0
    } else {
123
0
        nxt_free(link);
124
0
    }
125
126
0
    start(data);
127
128
    /*
129
     * nxt_thread_time_cleanup() should be called only if a thread
130
     * would be canceled, so ignore it here because nxt_thread_exit()
131
     * calls nxt_thread_time_free() as well.
132
     */
133
0
    pthread_cleanup_pop(0);
134
135
0
    nxt_thread_exit(thr);
136
0
    nxt_unreachable();
137
0
    return NULL;
138
0
}
139
140
141
nxt_thread_t *
142
nxt_thread_init(void)
143
0
{
144
0
    nxt_thread_t  *thr;
145
146
0
    nxt_thread_init_data(nxt_thread_context);
147
148
0
    thr = nxt_thread();
149
150
0
    if (thr->log == NULL) {
151
0
        thr->log = &nxt_main_log;
152
0
        thr->handle = nxt_thread_handle();
153
154
        /*
155
         * Threads are never preempted by asynchronous signals, since
156
         * the signals are processed synchronously by dedicated thread.
157
         */
158
0
        thr->time.signal = -1;
159
160
0
        nxt_thread_time_update(thr);
161
0
    }
162
163
0
    nxt_random_init(&thr->random);
164
165
0
    return thr;
166
0
}
167
168
169
static void
170
nxt_thread_time_cleanup(void *data)
171
0
{
172
0
    nxt_thread_t  *thr;
173
174
0
    thr = data;
175
176
0
    nxt_log_debug(thr->log, "thread time cleanup");
177
178
0
    nxt_thread_time_free(thr);
179
0
}
180
181
182
void
183
nxt_thread_exit(nxt_thread_t *thr)
184
0
{
185
0
    nxt_thread_link_t   *link;
186
0
    nxt_event_engine_t  *engine;
187
188
0
    nxt_log_debug(thr->log, "thread exit");
189
190
0
    link = thr->link;
191
0
    thr->link = NULL;
192
193
0
    if (link != NULL) {
194
        /*
195
         * link->work.handler is already set to an exit handler,
196
         * and link->work.task is already set to the correct engine->task.
197
         * The link should be freed by the exit handler.
198
         */
199
0
        link->work.obj = (void *) (uintptr_t) thr->handle;
200
0
        engine = nxt_container_of(link->work.task, nxt_event_engine_t, task);
201
202
0
        nxt_event_engine_post(engine, &link->work);
203
0
    }
204
205
0
    nxt_thread_time_free(thr);
206
207
0
    pthread_exit(NULL);
208
0
    nxt_unreachable();
209
0
}
210
211
212
void
213
nxt_thread_cancel(nxt_thread_handle_t handle)
214
0
{
215
0
    nxt_err_t  err;
216
217
0
    nxt_thread_log_debug("thread cancel: %PH", handle);
218
219
0
    err = pthread_cancel(handle);
220
221
0
    if (err != 0) {
222
0
        nxt_main_log_alert("pthread_cancel(%PH) failed %E", handle, err);
223
0
    }
224
0
}
225
226
227
void
228
nxt_thread_wait(nxt_thread_handle_t handle)
229
0
{
230
0
    nxt_err_t  err;
231
232
0
    nxt_thread_log_debug("thread wait: %PH", handle);
233
234
0
    err = pthread_join(handle, NULL);
235
236
0
    if (err != 0) {
237
0
        nxt_main_log_alert("pthread_join(%PH) failed %E", handle, err);
238
0
    }
239
0
}
240
241
242
nxt_tid_t
243
nxt_thread_tid(nxt_thread_t *thr)
244
0
{
245
0
#if (NXT_HAVE_THREAD_STORAGE_CLASS)
246
247
0
    if (nxt_slow_path(thr->tid == 0)) {
248
0
        thr->tid = nxt_thread_get_tid();
249
0
    }
250
251
0
    return thr->tid;
252
253
#else
254
255
    if (nxt_fast_path(thr != NULL)) {
256
257
        if (nxt_slow_path(thr->tid == 0)) {
258
            thr->tid = nxt_thread_get_tid();
259
        }
260
261
        return thr->tid;
262
    }
263
264
    return nxt_thread_get_tid();
265
266
#endif
267
0
}