/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 | } |