/* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include static void *nxt_thread_trampoline(void *data); static void nxt_thread_time_cleanup(void *data); #if (NXT_HAVE_PTHREAD_SPECIFIC_DATA) static void nxt_thread_key_dtor(void *data); void nxt_thread_init_data(nxt_thread_specific_data_t tsd) { void *p; nxt_err_t err; pthread_key_t key; while ((nxt_atomic_int_t) tsd->key < 0) { /* * Atomic allocation of a key number. * -1 means an uninitialized key, * -2 is the initializing lock to assure the single value for the key. */ if (nxt_atomic_cmp_set(&tsd->key, -1, -2)) { err = pthread_key_create(&key, nxt_thread_key_dtor); if (err != 0) { nxt_main_log_emerg("pthread_key_create() failed %E", err); goto fail; } tsd->key = (nxt_atomic_t) key; nxt_main_log_debug("pthread_key_create(): %A", tsd->key); } } if (pthread_getspecific((pthread_key_t) tsd->key) != NULL) { return; } p = nxt_zalloc(tsd->size); if (p == NULL) { goto fail; } err = pthread_setspecific((pthread_key_t) tsd->key, p); if (err == 0) { return; } nxt_main_log_alert("pthread_setspecific(%A) failed %E", tsd->key, err); fail: pthread_exit(NULL); nxt_unreachable(); } static void nxt_thread_key_dtor(void *data) { nxt_main_log_debug("pthread key dtor: %p", data); nxt_free(data); } #endif nxt_int_t nxt_thread_create(nxt_thread_handle_t *handle, nxt_thread_link_t *link) { nxt_err_t err; err = pthread_create(handle, NULL, nxt_thread_trampoline, link); if (nxt_fast_path(err == 0)) { nxt_thread_log_debug("pthread_create(): %PH", *handle); return NXT_OK; } nxt_thread_log_alert("pthread_create() failed %E", err); nxt_free(link); return NXT_ERROR; } static void * nxt_thread_trampoline(void *data) { nxt_thread_t *thr; nxt_thread_link_t *link; nxt_thread_start_t start; link = data; thr = nxt_thread_init(); nxt_log_debug(thr->log, "thread trampoline: %PH", thr->handle); pthread_cleanup_push(nxt_thread_time_cleanup, thr); start = link->start; data = link->data; if (link->engine != NULL) { thr->link = link; } else { nxt_free(link); } start(data); /* * nxt_thread_time_cleanup() should be called only if a thread * would be canceled, so ignore it here because nxt_thread_exit() * calls nxt_thread_time_free() as well. */ pthread_cleanup_pop(0); nxt_thread_exit(thr); nxt_unreachable(); return NULL; } nxt_thread_t * nxt_thread_init(void) { nxt_thread_t *thr; nxt_thread_init_data(nxt_thread_context); thr = nxt_thread(); if (thr->log == NULL) { thr->log = &nxt_main_log; thr->handle = nxt_thread_handle(); /* * Threads are never preempted by asynchronous signals, since * the signals are processed synchronously by dedicated thread. */ thr->time.signal = -1; nxt_thread_time_update(thr); } return thr; } static void nxt_thread_time_cleanup(void *data) { nxt_thread_t *thr; thr = data; nxt_log_debug(thr->log, "thread time cleanup"); nxt_thread_time_free(thr); } void nxt_thread_exit(nxt_thread_t *thr) { nxt_log_debug(thr->log, "thread exit"); if (thr->link != NULL) { nxt_event_engine_post(thr->link->engine, thr->link->exit, (void *) (uintptr_t) thr->handle, NULL, &nxt_main_log); nxt_free(thr->link); thr->link = NULL; } nxt_thread_time_free(thr); pthread_exit(NULL); nxt_unreachable(); } void nxt_thread_cancel(nxt_thread_handle_t handle) { nxt_err_t err; nxt_thread_log_debug("thread cancel: %PH", handle); err = pthread_cancel(handle); if (err != 0) { nxt_main_log_alert("pthread_cancel(%PH) failed %E", handle, err); } } void nxt_thread_wait(nxt_thread_handle_t handle) { nxt_err_t err; nxt_thread_log_debug("thread wait: %PH", handle); err = pthread_join(handle, NULL); if (err != 0) { nxt_main_log_alert("pthread_join(%PH) failed %E", handle, err); } }