xref: /unit/src/nxt_thread_mutex.c (revision 564:762f8c976ead)
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 /*
110Sigor@sysoev.ru  * All modern pthread mutex implementations try to acquire a lock atomically
120Sigor@sysoev.ru  * in userland before going to sleep in kernel.  Some spins on SMP systems
130Sigor@sysoev.ru  * before the sleeping.
140Sigor@sysoev.ru  *
150Sigor@sysoev.ru  * In Solaris since version 8 all mutex types spin before sleeping.
160Sigor@sysoev.ru  * The default spin count is 1000.  It can be overridden using
170Sigor@sysoev.ru  * _THREAD_ADAPTIVE_SPIN=100 environment variable.
180Sigor@sysoev.ru  *
190Sigor@sysoev.ru  * In MacOSX all mutex types spin to acquire a lock protecting a mutex's
200Sigor@sysoev.ru  * internals.  If the mutex is busy, thread calls Mach semaphore_wait().
210Sigor@sysoev.ru  *
220Sigor@sysoev.ru  *
230Sigor@sysoev.ru  * PTHREAD_MUTEX_NORMAL lacks deadlock detection and is the fastest
240Sigor@sysoev.ru  * mutex type.
250Sigor@sysoev.ru  *
260Sigor@sysoev.ru  *   Linux:    No spinning.  The internal name PTHREAD_MUTEX_TIMED_NP
270Sigor@sysoev.ru  *             remains from the times when pthread_mutex_timedlock() was
280Sigor@sysoev.ru  *             non-standard extension.  Alias name: PTHREAD_MUTEX_FAST_NP.
290Sigor@sysoev.ru  *   FreeBSD:  No spinning.
300Sigor@sysoev.ru  *
310Sigor@sysoev.ru  *
320Sigor@sysoev.ru  * PTHREAD_MUTEX_ERRORCHECK is usually as fast as PTHREAD_MUTEX_NORMAL
330Sigor@sysoev.ru  * yet has lightweight deadlock detection.
340Sigor@sysoev.ru  *
350Sigor@sysoev.ru  *   Linux:    No spinning.  The internal name: PTHREAD_MUTEX_ERRORCHECK_NP.
360Sigor@sysoev.ru  *   FreeBSD:  No spinning.
370Sigor@sysoev.ru  *
380Sigor@sysoev.ru  *
390Sigor@sysoev.ru  * PTHREAD_MUTEX_RECURSIVE allows recursive locking.
400Sigor@sysoev.ru  *
410Sigor@sysoev.ru  *   Linux:    No spinning.  The internal name: PTHREAD_MUTEX_RECURSIVE_NP.
420Sigor@sysoev.ru  *   FreeBSD:  No spinning.
430Sigor@sysoev.ru  *
440Sigor@sysoev.ru  *
450Sigor@sysoev.ru  * PTHREAD_MUTEX_ADAPTIVE_NP spins on SMP systems before sleeping.
460Sigor@sysoev.ru  *
470Sigor@sysoev.ru  *   Linux:    No deadlock detection.  Dynamically changes a spin count
480Sigor@sysoev.ru  *             for each mutex from 10 to 100 based on spin count taken
490Sigor@sysoev.ru  *             previously.
500Sigor@sysoev.ru  *
510Sigor@sysoev.ru  *   FreeBSD:  Deadlock detection.  The default spin count is 2000.
520Sigor@sysoev.ru  *             It can be overriden using LIBPTHREAD_SPINLOOPS environment
530Sigor@sysoev.ru  *             variable or by pthread_mutex_setspinloops_np().  If a lock
540Sigor@sysoev.ru  *             is still busy, sched_yield() can be called on both UP and
550Sigor@sysoev.ru  *             SMP systems.  The default yield loop count is zero, but it
560Sigor@sysoev.ru  *             can be set by LIBPTHREAD_YIELDLOOPS environment variable or
570Sigor@sysoev.ru  *             by pthread_mutex_setyieldloops_np().  sched_yield() moves
580Sigor@sysoev.ru  *             a thread to the end of CPU scheduler run queue and this is
590Sigor@sysoev.ru  *             cheaper than removing the thread from the queue and sleeping.
600Sigor@sysoev.ru  *
610Sigor@sysoev.ru  *   Solaris:  No PTHREAD_MUTEX_ADAPTIVE_NP .
620Sigor@sysoev.ru  *   MacOSX:   No PTHREAD_MUTEX_ADAPTIVE_NP.
630Sigor@sysoev.ru  *
640Sigor@sysoev.ru  *
650Sigor@sysoev.ru  * PTHREAD_MUTEX_ELISION_NP is a Linux extension to elide locks using
660Sigor@sysoev.ru  * Intel Restricted Transactional Memory.  It is the most suitable for
670Sigor@sysoev.ru  * rwlock pattern access because it allows simultaneous reads without lock.
680Sigor@sysoev.ru  * Supported since glibc 2.18.
690Sigor@sysoev.ru  *
700Sigor@sysoev.ru  *
710Sigor@sysoev.ru  * PTHREAD_MUTEX_DEFAULT is default mutex type.
720Sigor@sysoev.ru  *
730Sigor@sysoev.ru  *   Linux:    PTHREAD_MUTEX_NORMAL.
740Sigor@sysoev.ru  *   FreeBSD:  PTHREAD_MUTEX_ERRORCHECK.
750Sigor@sysoev.ru  *   Solaris:  PTHREAD_MUTEX_NORMAL.
760Sigor@sysoev.ru  *   MacOSX:   PTHREAD_MUTEX_NORMAL.
770Sigor@sysoev.ru  */
780Sigor@sysoev.ru 
790Sigor@sysoev.ru 
800Sigor@sysoev.ru nxt_int_t
nxt_thread_mutex_create(nxt_thread_mutex_t * mtx)810Sigor@sysoev.ru nxt_thread_mutex_create(nxt_thread_mutex_t *mtx)
820Sigor@sysoev.ru {
830Sigor@sysoev.ru     nxt_err_t            err;
840Sigor@sysoev.ru     pthread_mutexattr_t  attr;
850Sigor@sysoev.ru 
860Sigor@sysoev.ru     err = pthread_mutexattr_init(&attr);
870Sigor@sysoev.ru     if (err != 0) {
88*564Svbart@nginx.com         nxt_thread_log_alert("pthread_mutexattr_init() failed %E", err);
890Sigor@sysoev.ru         return NXT_ERROR;
900Sigor@sysoev.ru     }
910Sigor@sysoev.ru 
920Sigor@sysoev.ru     err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
930Sigor@sysoev.ru     if (err != 0) {
94*564Svbart@nginx.com         nxt_thread_log_alert("pthread_mutexattr_settype"
950Sigor@sysoev.ru                              "(PTHREAD_MUTEX_ERRORCHECK) failed %E", err);
960Sigor@sysoev.ru         return NXT_ERROR;
970Sigor@sysoev.ru     }
980Sigor@sysoev.ru 
990Sigor@sysoev.ru     err = pthread_mutex_init(mtx, &attr);
1000Sigor@sysoev.ru     if (err != 0) {
101*564Svbart@nginx.com         nxt_thread_log_alert("pthread_mutex_init() failed %E", err);
1020Sigor@sysoev.ru         return NXT_ERROR;
1030Sigor@sysoev.ru     }
1040Sigor@sysoev.ru 
1050Sigor@sysoev.ru     err = pthread_mutexattr_destroy(&attr);
1060Sigor@sysoev.ru     if (err != 0) {
1070Sigor@sysoev.ru         nxt_thread_log_alert("pthread_mutexattr_destroy() failed %E", err);
1080Sigor@sysoev.ru     }
1090Sigor@sysoev.ru 
1100Sigor@sysoev.ru     nxt_thread_log_debug("pthread_mutex_init(%p)", mtx);
1110Sigor@sysoev.ru 
1120Sigor@sysoev.ru     return NXT_OK;
1130Sigor@sysoev.ru }
1140Sigor@sysoev.ru 
1150Sigor@sysoev.ru 
1160Sigor@sysoev.ru void
nxt_thread_mutex_destroy(nxt_thread_mutex_t * mtx)1170Sigor@sysoev.ru nxt_thread_mutex_destroy(nxt_thread_mutex_t *mtx)
1180Sigor@sysoev.ru {
1190Sigor@sysoev.ru     nxt_err_t  err;
1200Sigor@sysoev.ru 
1210Sigor@sysoev.ru     err = pthread_mutex_destroy(mtx);
1220Sigor@sysoev.ru     if (nxt_slow_path(err != 0)) {
1230Sigor@sysoev.ru         nxt_thread_log_alert("pthread_mutex_destroy() failed %E", err);
1240Sigor@sysoev.ru     }
1250Sigor@sysoev.ru 
1260Sigor@sysoev.ru     nxt_thread_log_debug("pthread_mutex_destroy(%p)", mtx);
1270Sigor@sysoev.ru }
1280Sigor@sysoev.ru 
1290Sigor@sysoev.ru 
1300Sigor@sysoev.ru nxt_int_t
nxt_thread_mutex_lock(nxt_thread_mutex_t * mtx)1310Sigor@sysoev.ru nxt_thread_mutex_lock(nxt_thread_mutex_t *mtx)
1320Sigor@sysoev.ru {
1330Sigor@sysoev.ru     nxt_err_t  err;
1340Sigor@sysoev.ru 
1350Sigor@sysoev.ru     nxt_thread_log_debug("pthread_mutex_lock(%p) enter", mtx);
1360Sigor@sysoev.ru 
1370Sigor@sysoev.ru     err = pthread_mutex_lock(mtx);
1380Sigor@sysoev.ru     if (nxt_fast_path(err == 0)) {
1390Sigor@sysoev.ru         return NXT_OK;
1400Sigor@sysoev.ru     }
1410Sigor@sysoev.ru 
1420Sigor@sysoev.ru     nxt_thread_log_alert("pthread_mutex_lock() failed %E", err);
1430Sigor@sysoev.ru 
1440Sigor@sysoev.ru     return NXT_ERROR;
1450Sigor@sysoev.ru }
1460Sigor@sysoev.ru 
1470Sigor@sysoev.ru 
1480Sigor@sysoev.ru nxt_bool_t
nxt_thread_mutex_trylock(nxt_thread_mutex_t * mtx)1490Sigor@sysoev.ru nxt_thread_mutex_trylock(nxt_thread_mutex_t *mtx)
1500Sigor@sysoev.ru {
1510Sigor@sysoev.ru     nxt_err_t  err;
1520Sigor@sysoev.ru 
1530Sigor@sysoev.ru     nxt_thread_debug(thr);
1540Sigor@sysoev.ru 
1550Sigor@sysoev.ru     nxt_log_debug(thr->log, "pthread_mutex_trylock(%p) enter", mtx);
1560Sigor@sysoev.ru 
1570Sigor@sysoev.ru     err = pthread_mutex_trylock(mtx);
1580Sigor@sysoev.ru     if (nxt_fast_path(err == 0)) {
1590Sigor@sysoev.ru         return 1;
1600Sigor@sysoev.ru     }
1610Sigor@sysoev.ru 
1620Sigor@sysoev.ru     if (err == NXT_EBUSY) {
1630Sigor@sysoev.ru         nxt_log_debug(thr->log, "pthread_mutex_trylock(%p) failed", mtx);
1640Sigor@sysoev.ru 
1650Sigor@sysoev.ru     } else {
1660Sigor@sysoev.ru         nxt_thread_log_alert("pthread_mutex_trylock() failed %E", err);
1670Sigor@sysoev.ru     }
1680Sigor@sysoev.ru 
1690Sigor@sysoev.ru     return 0;
1700Sigor@sysoev.ru }
1710Sigor@sysoev.ru 
1720Sigor@sysoev.ru 
1730Sigor@sysoev.ru nxt_int_t
nxt_thread_mutex_unlock(nxt_thread_mutex_t * mtx)1740Sigor@sysoev.ru nxt_thread_mutex_unlock(nxt_thread_mutex_t *mtx)
1750Sigor@sysoev.ru {
1760Sigor@sysoev.ru     nxt_err_t     err;
1770Sigor@sysoev.ru     nxt_thread_t  *thr;
1780Sigor@sysoev.ru 
1790Sigor@sysoev.ru     err = pthread_mutex_unlock(mtx);
1800Sigor@sysoev.ru 
1810Sigor@sysoev.ru     thr = nxt_thread();
1820Sigor@sysoev.ru     nxt_thread_time_update(thr);
1830Sigor@sysoev.ru 
1840Sigor@sysoev.ru     if (nxt_fast_path(err == 0)) {
1850Sigor@sysoev.ru         nxt_log_debug(thr->log, "pthread_mutex_unlock(%p) exit", mtx);
1860Sigor@sysoev.ru         return NXT_OK;
1870Sigor@sysoev.ru     }
1880Sigor@sysoev.ru 
1890Sigor@sysoev.ru     nxt_log_alert(thr->log, "pthread_mutex_unlock() failed %E", err);
1900Sigor@sysoev.ru 
1910Sigor@sysoev.ru     return NXT_ERROR;
1920Sigor@sysoev.ru }
193