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