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
nxt_thread_init_data(nxt_thread_specific_data_t tsd)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
nxt_thread_key_dtor(void * data)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
nxt_thread_create(nxt_thread_handle_t * handle,nxt_thread_link_t * link)81 nxt_thread_create(nxt_thread_handle_t *handle, nxt_thread_link_t *link)
82 {
83 nxt_err_t err;
84
85 err = pthread_create(handle, NULL, nxt_thread_trampoline, link);
86
87 if (nxt_fast_path(err == 0)) {
88 nxt_thread_log_debug("pthread_create(): %PH", *handle);
89
90 return NXT_OK;
91 }
92
93 nxt_thread_log_alert("pthread_create() failed %E", err);
94
95 nxt_free(link);
96
97 return NXT_ERROR;
98 }
99
100
101 static void *
nxt_thread_trampoline(void * data)102 nxt_thread_trampoline(void *data)
103 {
104 nxt_thread_t *thr;
105 nxt_thread_link_t *link;
106 nxt_thread_start_t start;
107
108 link = data;
109
110 thr = nxt_thread_init();
111
112 nxt_log_debug(thr->log, "thread trampoline: %PH", thr->handle);
113
114 pthread_cleanup_push(nxt_thread_time_cleanup, thr);
115
116 start = link->start;
117 data = link->work.data;
118
119 if (link->work.handler != NULL) {
120 thr->link = link;
121
122 } else {
123 nxt_free(link);
124 }
125
126 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 pthread_cleanup_pop(0);
134
135 nxt_thread_exit(thr);
136 nxt_unreachable();
137 return NULL;
138 }
139
140
141 nxt_thread_t *
nxt_thread_init(void)142 nxt_thread_init(void)
143 {
144 nxt_thread_t *thr;
145
146 nxt_thread_init_data(nxt_thread_context);
147
148 thr = nxt_thread();
149
150 if (thr->log == NULL) {
151 thr->log = &nxt_main_log;
152 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 thr->time.signal = -1;
159
160 nxt_thread_time_update(thr);
161 }
162
163 nxt_random_init(&thr->random);
164
165 return thr;
166 }
167
168
169 static void
nxt_thread_time_cleanup(void * data)170 nxt_thread_time_cleanup(void *data)
171 {
172 nxt_thread_t *thr;
173
174 thr = data;
175
176 nxt_log_debug(thr->log, "thread time cleanup");
177
178 nxt_thread_time_free(thr);
179 }
180
181
182 void
nxt_thread_exit(nxt_thread_t * thr)183 nxt_thread_exit(nxt_thread_t *thr)
184 {
185 nxt_thread_link_t *link;
186 nxt_event_engine_t *engine;
187
188 nxt_log_debug(thr->log, "thread exit");
189
190 link = thr->link;
191 thr->link = NULL;
192
193 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 link->work.obj = (void *) (uintptr_t) thr->handle;
200 engine = nxt_container_of(link->work.task, nxt_event_engine_t, task);
201
202 nxt_event_engine_post(engine, &link->work);
203 }
204
205 nxt_thread_time_free(thr);
206
207 pthread_exit(NULL);
208 nxt_unreachable();
209 }
210
211
212 void
nxt_thread_cancel(nxt_thread_handle_t handle)213 nxt_thread_cancel(nxt_thread_handle_t handle)
214 {
215 nxt_err_t err;
216
217 nxt_thread_log_debug("thread cancel: %PH", handle);
218
219 err = pthread_cancel(handle);
220
221 if (err != 0) {
222 nxt_main_log_alert("pthread_cancel(%PH) failed %E", handle, err);
223 }
224 }
225
226
227 void
nxt_thread_wait(nxt_thread_handle_t handle)228 nxt_thread_wait(nxt_thread_handle_t handle)
229 {
230 nxt_err_t err;
231
232 nxt_thread_log_debug("thread wait: %PH", handle);
233
234 err = pthread_join(handle, NULL);
235
236 if (err != 0) {
237 nxt_main_log_alert("pthread_join(%PH) failed %E", handle, err);
238 }
239 }
240
241
242 nxt_tid_t
nxt_thread_tid(nxt_thread_t * thr)243 nxt_thread_tid(nxt_thread_t *thr)
244 {
245 #if (NXT_HAVE_THREAD_STORAGE_CLASS)
246
247 if (nxt_slow_path(thr->tid == 0)) {
248 thr->tid = nxt_thread_get_tid();
249 }
250
251 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 }
268