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