xref: /unit/src/nxt_thread.c (revision 53:0b10a73d4993)
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_emerg("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 {
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 *
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 *
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     return thr;
164 }
165 
166 
167 static void
168 nxt_thread_time_cleanup(void *data)
169 {
170     nxt_thread_t  *thr;
171 
172     thr = data;
173 
174     nxt_log_debug(thr->log, "thread time cleanup");
175 
176     nxt_thread_time_free(thr);
177 }
178 
179 
180 void
181 nxt_thread_exit(nxt_thread_t *thr)
182 {
183     nxt_thread_link_t   *link;
184     nxt_event_engine_t  *engine;
185 
186     nxt_log_debug(thr->log, "thread exit");
187 
188     link = thr->link;
189     thr->link = NULL;
190 
191     if (link != NULL) {
192         /*
193          * link->work.handler is already set to an exit handler,
194          * and link->work.task is already set to the correct engine->task.
195          * The link should be freed by the exit handler.
196          */
197         link->work.obj = (void *) (uintptr_t) thr->handle;
198         engine = nxt_container_of(link->work.task, nxt_event_engine_t, task);
199 
200         nxt_event_engine_post(engine, &link->work);
201     }
202 
203     nxt_thread_time_free(thr);
204 
205     pthread_exit(NULL);
206     nxt_unreachable();
207 }
208 
209 
210 void
211 nxt_thread_cancel(nxt_thread_handle_t handle)
212 {
213     nxt_err_t  err;
214 
215     nxt_thread_log_debug("thread cancel: %PH", handle);
216 
217     err = pthread_cancel(handle);
218 
219     if (err != 0) {
220         nxt_main_log_alert("pthread_cancel(%PH) failed %E", handle, err);
221     }
222 }
223 
224 
225 void
226 nxt_thread_wait(nxt_thread_handle_t handle)
227 {
228     nxt_err_t  err;
229 
230     nxt_thread_log_debug("thread wait: %PH", handle);
231 
232     err = pthread_join(handle, NULL);
233 
234     if (err != 0) {
235         nxt_main_log_alert("pthread_join(%PH) failed %E", handle, err);
236     }
237 }
238