xref: /unit/src/nxt_thread.c (revision 4:76c63e9b6322)
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->data;
118 
119     if (link->engine != 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 
185     nxt_log_debug(thr->log, "thread exit");
186 
187     link = thr->link;
188     thr->link = NULL;
189 
190     if (link != NULL) {
191         /*
192          * link->handler is already set to an exit handler,
193          * and link->task is already set to engine->task.
194          * The link should be freed by the exit handler.
195          */
196         link->work.obj =  thr->handle;
197 
198         nxt_event_engine_post(link->engine, &link->work);
199     }
200 
201     nxt_thread_time_free(thr);
202 
203     pthread_exit(NULL);
204     nxt_unreachable();
205 }
206 
207 
208 void
209 nxt_thread_cancel(nxt_thread_handle_t handle)
210 {
211     nxt_err_t  err;
212 
213     nxt_thread_log_debug("thread cancel: %PH", handle);
214 
215     err = pthread_cancel(handle);
216 
217     if (err != 0) {
218         nxt_main_log_alert("pthread_cancel(%PH) failed %E", handle, err);
219     }
220 }
221 
222 
223 void
224 nxt_thread_wait(nxt_thread_handle_t handle)
225 {
226     nxt_err_t  err;
227 
228     nxt_thread_log_debug("thread wait: %PH", handle);
229 
230     err = pthread_join(handle, NULL);
231 
232     if (err != 0) {
233         nxt_main_log_alert("pthread_join(%PH) failed %E", handle, err);
234     }
235 }
236