xref: /unit/src/nxt_thread.c (revision 737:8ca436f03869)
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