xref: /unit/src/nxt_thread.c (revision 8)
10Sigor@sysoev.ru 
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru  */
60Sigor@sysoev.ru 
70Sigor@sysoev.ru #include <nxt_main.h>
80Sigor@sysoev.ru 
90Sigor@sysoev.ru 
100Sigor@sysoev.ru static void *nxt_thread_trampoline(void *data);
110Sigor@sysoev.ru static void nxt_thread_time_cleanup(void *data);
120Sigor@sysoev.ru 
130Sigor@sysoev.ru 
140Sigor@sysoev.ru #if (NXT_HAVE_PTHREAD_SPECIFIC_DATA)
150Sigor@sysoev.ru 
160Sigor@sysoev.ru static void nxt_thread_key_dtor(void *data);
170Sigor@sysoev.ru 
180Sigor@sysoev.ru 
190Sigor@sysoev.ru void
200Sigor@sysoev.ru nxt_thread_init_data(nxt_thread_specific_data_t tsd)
210Sigor@sysoev.ru {
220Sigor@sysoev.ru     void           *p;
230Sigor@sysoev.ru     nxt_err_t      err;
240Sigor@sysoev.ru     pthread_key_t  key;
250Sigor@sysoev.ru 
260Sigor@sysoev.ru     while ((nxt_atomic_int_t) tsd->key < 0) {
270Sigor@sysoev.ru         /*
280Sigor@sysoev.ru          * Atomic allocation of a key number.
290Sigor@sysoev.ru          * -1 means an uninitialized key,
300Sigor@sysoev.ru          * -2 is the initializing lock to assure the single value for the key.
310Sigor@sysoev.ru          */
320Sigor@sysoev.ru         if (nxt_atomic_cmp_set(&tsd->key, -1, -2)) {
330Sigor@sysoev.ru 
340Sigor@sysoev.ru             err = pthread_key_create(&key, nxt_thread_key_dtor);
350Sigor@sysoev.ru             if (err != 0) {
360Sigor@sysoev.ru                 nxt_main_log_emerg("pthread_key_create() failed %E", err);
370Sigor@sysoev.ru                 goto fail;
380Sigor@sysoev.ru             }
390Sigor@sysoev.ru 
400Sigor@sysoev.ru             tsd->key = (nxt_atomic_t) key;
410Sigor@sysoev.ru 
420Sigor@sysoev.ru             nxt_main_log_debug("pthread_key_create(): %A", tsd->key);
430Sigor@sysoev.ru         }
440Sigor@sysoev.ru     }
450Sigor@sysoev.ru 
460Sigor@sysoev.ru     if (pthread_getspecific((pthread_key_t) tsd->key) != NULL) {
470Sigor@sysoev.ru         return;
480Sigor@sysoev.ru     }
490Sigor@sysoev.ru 
500Sigor@sysoev.ru     p = nxt_zalloc(tsd->size);
510Sigor@sysoev.ru     if (p == NULL) {
520Sigor@sysoev.ru         goto fail;
530Sigor@sysoev.ru     }
540Sigor@sysoev.ru 
550Sigor@sysoev.ru     err = pthread_setspecific((pthread_key_t) tsd->key, p);
560Sigor@sysoev.ru     if (err == 0) {
570Sigor@sysoev.ru         return;
580Sigor@sysoev.ru     }
590Sigor@sysoev.ru 
600Sigor@sysoev.ru     nxt_main_log_alert("pthread_setspecific(%A) failed %E", tsd->key, err);
610Sigor@sysoev.ru 
620Sigor@sysoev.ru fail:
630Sigor@sysoev.ru 
640Sigor@sysoev.ru     pthread_exit(NULL);
650Sigor@sysoev.ru     nxt_unreachable();
660Sigor@sysoev.ru }
670Sigor@sysoev.ru 
680Sigor@sysoev.ru 
690Sigor@sysoev.ru static void
700Sigor@sysoev.ru nxt_thread_key_dtor(void *data)
710Sigor@sysoev.ru {
720Sigor@sysoev.ru     nxt_main_log_debug("pthread key dtor: %p", data);
730Sigor@sysoev.ru 
740Sigor@sysoev.ru     nxt_free(data);
750Sigor@sysoev.ru }
760Sigor@sysoev.ru 
770Sigor@sysoev.ru #endif
780Sigor@sysoev.ru 
790Sigor@sysoev.ru 
800Sigor@sysoev.ru nxt_int_t
810Sigor@sysoev.ru nxt_thread_create(nxt_thread_handle_t *handle, nxt_thread_link_t *link)
820Sigor@sysoev.ru {
830Sigor@sysoev.ru     nxt_err_t  err;
840Sigor@sysoev.ru 
850Sigor@sysoev.ru     err = pthread_create(handle, NULL, nxt_thread_trampoline, link);
860Sigor@sysoev.ru 
870Sigor@sysoev.ru     if (nxt_fast_path(err == 0)) {
880Sigor@sysoev.ru         nxt_thread_log_debug("pthread_create(): %PH", *handle);
890Sigor@sysoev.ru 
900Sigor@sysoev.ru         return NXT_OK;
910Sigor@sysoev.ru     }
920Sigor@sysoev.ru 
930Sigor@sysoev.ru     nxt_thread_log_alert("pthread_create() failed %E", err);
940Sigor@sysoev.ru 
950Sigor@sysoev.ru     nxt_free(link);
960Sigor@sysoev.ru 
970Sigor@sysoev.ru     return NXT_ERROR;
980Sigor@sysoev.ru }
990Sigor@sysoev.ru 
1000Sigor@sysoev.ru 
1010Sigor@sysoev.ru static void *
1020Sigor@sysoev.ru nxt_thread_trampoline(void *data)
1030Sigor@sysoev.ru {
1040Sigor@sysoev.ru     nxt_thread_t        *thr;
1050Sigor@sysoev.ru     nxt_thread_link_t   *link;
1060Sigor@sysoev.ru     nxt_thread_start_t  start;
1070Sigor@sysoev.ru 
1080Sigor@sysoev.ru     link = data;
1090Sigor@sysoev.ru 
1100Sigor@sysoev.ru     thr = nxt_thread_init();
1110Sigor@sysoev.ru 
1120Sigor@sysoev.ru     nxt_log_debug(thr->log, "thread trampoline: %PH", thr->handle);
1130Sigor@sysoev.ru 
1140Sigor@sysoev.ru     pthread_cleanup_push(nxt_thread_time_cleanup, thr);
1150Sigor@sysoev.ru 
1160Sigor@sysoev.ru     start = link->start;
1170Sigor@sysoev.ru     data = link->data;
1180Sigor@sysoev.ru 
1190Sigor@sysoev.ru     if (link->engine != NULL) {
1200Sigor@sysoev.ru         thr->link = link;
1210Sigor@sysoev.ru 
1220Sigor@sysoev.ru     } else {
1230Sigor@sysoev.ru         nxt_free(link);
1240Sigor@sysoev.ru     }
1250Sigor@sysoev.ru 
1260Sigor@sysoev.ru     start(data);
1270Sigor@sysoev.ru 
1280Sigor@sysoev.ru     /*
1290Sigor@sysoev.ru      * nxt_thread_time_cleanup() should be called only if a thread
1300Sigor@sysoev.ru      * would be canceled, so ignore it here because nxt_thread_exit()
1310Sigor@sysoev.ru      * calls nxt_thread_time_free() as well.
1320Sigor@sysoev.ru      */
1330Sigor@sysoev.ru     pthread_cleanup_pop(0);
1340Sigor@sysoev.ru 
1350Sigor@sysoev.ru     nxt_thread_exit(thr);
1360Sigor@sysoev.ru     nxt_unreachable();
1370Sigor@sysoev.ru     return NULL;
1380Sigor@sysoev.ru }
1390Sigor@sysoev.ru 
1400Sigor@sysoev.ru 
1410Sigor@sysoev.ru nxt_thread_t *
1420Sigor@sysoev.ru nxt_thread_init(void)
1430Sigor@sysoev.ru {
1440Sigor@sysoev.ru     nxt_thread_t  *thr;
1450Sigor@sysoev.ru 
1460Sigor@sysoev.ru     nxt_thread_init_data(nxt_thread_context);
1470Sigor@sysoev.ru 
1480Sigor@sysoev.ru     thr = nxt_thread();
1490Sigor@sysoev.ru 
1500Sigor@sysoev.ru     if (thr->log == NULL) {
1510Sigor@sysoev.ru         thr->log = &nxt_main_log;
1520Sigor@sysoev.ru         thr->handle = nxt_thread_handle();
1530Sigor@sysoev.ru 
1540Sigor@sysoev.ru         /*
1550Sigor@sysoev.ru          * Threads are never preempted by asynchronous signals, since
1560Sigor@sysoev.ru          * the signals are processed synchronously by dedicated thread.
1570Sigor@sysoev.ru          */
1580Sigor@sysoev.ru         thr->time.signal = -1;
1590Sigor@sysoev.ru 
1600Sigor@sysoev.ru         nxt_thread_time_update(thr);
1610Sigor@sysoev.ru     }
1620Sigor@sysoev.ru 
1630Sigor@sysoev.ru     return thr;
1640Sigor@sysoev.ru }
1650Sigor@sysoev.ru 
1660Sigor@sysoev.ru 
1670Sigor@sysoev.ru static void
1680Sigor@sysoev.ru nxt_thread_time_cleanup(void *data)
1690Sigor@sysoev.ru {
1700Sigor@sysoev.ru     nxt_thread_t  *thr;
1710Sigor@sysoev.ru 
1720Sigor@sysoev.ru     thr = data;
1730Sigor@sysoev.ru 
1740Sigor@sysoev.ru     nxt_log_debug(thr->log, "thread time cleanup");
1750Sigor@sysoev.ru 
1760Sigor@sysoev.ru     nxt_thread_time_free(thr);
1770Sigor@sysoev.ru }
1780Sigor@sysoev.ru 
1790Sigor@sysoev.ru 
1800Sigor@sysoev.ru void
1810Sigor@sysoev.ru nxt_thread_exit(nxt_thread_t *thr)
1820Sigor@sysoev.ru {
1834Sigor@sysoev.ru     nxt_thread_link_t  *link;
1844Sigor@sysoev.ru 
1850Sigor@sysoev.ru     nxt_log_debug(thr->log, "thread exit");
1860Sigor@sysoev.ru 
1874Sigor@sysoev.ru     link = thr->link;
1884Sigor@sysoev.ru     thr->link = NULL;
1890Sigor@sysoev.ru 
1904Sigor@sysoev.ru     if (link != NULL) {
1914Sigor@sysoev.ru         /*
1924Sigor@sysoev.ru          * link->handler is already set to an exit handler,
1934Sigor@sysoev.ru          * and link->task is already set to engine->task.
1944Sigor@sysoev.ru          * The link should be freed by the exit handler.
1954Sigor@sysoev.ru          */
196*8Sigor@sysoev.ru         link->work.obj = (void *) (uintptr_t) thr->handle;
1974Sigor@sysoev.ru 
1984Sigor@sysoev.ru         nxt_event_engine_post(link->engine, &link->work);
1990Sigor@sysoev.ru     }
2000Sigor@sysoev.ru 
2010Sigor@sysoev.ru     nxt_thread_time_free(thr);
2020Sigor@sysoev.ru 
2030Sigor@sysoev.ru     pthread_exit(NULL);
2040Sigor@sysoev.ru     nxt_unreachable();
2050Sigor@sysoev.ru }
2060Sigor@sysoev.ru 
2070Sigor@sysoev.ru 
2080Sigor@sysoev.ru void
2090Sigor@sysoev.ru nxt_thread_cancel(nxt_thread_handle_t handle)
2100Sigor@sysoev.ru {
2110Sigor@sysoev.ru     nxt_err_t  err;
2120Sigor@sysoev.ru 
2130Sigor@sysoev.ru     nxt_thread_log_debug("thread cancel: %PH", handle);
2140Sigor@sysoev.ru 
2150Sigor@sysoev.ru     err = pthread_cancel(handle);
2160Sigor@sysoev.ru 
2170Sigor@sysoev.ru     if (err != 0) {
2180Sigor@sysoev.ru         nxt_main_log_alert("pthread_cancel(%PH) failed %E", handle, err);
2190Sigor@sysoev.ru     }
2200Sigor@sysoev.ru }
2210Sigor@sysoev.ru 
2220Sigor@sysoev.ru 
2230Sigor@sysoev.ru void
2240Sigor@sysoev.ru nxt_thread_wait(nxt_thread_handle_t handle)
2250Sigor@sysoev.ru {
2260Sigor@sysoev.ru     nxt_err_t  err;
2270Sigor@sysoev.ru 
2280Sigor@sysoev.ru     nxt_thread_log_debug("thread wait: %PH", handle);
2290Sigor@sysoev.ru 
2300Sigor@sysoev.ru     err = pthread_join(handle, NULL);
2310Sigor@sysoev.ru 
2320Sigor@sysoev.ru     if (err != 0) {
2330Sigor@sysoev.ru         nxt_main_log_alert("pthread_join(%PH) failed %E", handle, err);
2340Sigor@sysoev.ru     }
2350Sigor@sysoev.ru }
236