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 * Linux supports pthread spinlocks since glibc 2.3. Spinlock is an
120Sigor@sysoev.ru * atomic integer with zero initial value. On i386/amd64 however the
130Sigor@sysoev.ru * initial value is one. Spinlock never yields control.
140Sigor@sysoev.ru *
150Sigor@sysoev.ru * FreeBSD 5.2 and Solaris 10 support pthread spinlocks. Spinlock is a
160Sigor@sysoev.ru * structure and uses mutex implementation so it must be initialized by
170Sigor@sysoev.ru * by pthread_spin_init() and destroyed by pthread_spin_destroy().
18*765Sigor@sysoev.ru *
19*765Sigor@sysoev.ru * MacOSX supported OSSpinLockLock(), it was deprecated in 10.12 (Sierra).
200Sigor@sysoev.ru * OSSpinLockLock() tries to acquire a lock atomically. If the lock is
210Sigor@sysoev.ru * busy, on SMP system it tests the lock 1000 times in a tight loop with
220Sigor@sysoev.ru * "pause" instruction. If the lock has been released, OSSpinLockLock()
230Sigor@sysoev.ru * tries to acquire it again. On failure it goes again in the tight loop.
240Sigor@sysoev.ru * If the lock has not been released during spinning in the loop or
250Sigor@sysoev.ru * on UP system, OSSpinLockLock() calls thread_switch() to run 1ms
260Sigor@sysoev.ru * with depressed (the lowest) priority.
270Sigor@sysoev.ru */
280Sigor@sysoev.ru
290Sigor@sysoev.ru
300Sigor@sysoev.ru /* It should be adjusted with the "spinlock_count" directive. */
310Sigor@sysoev.ru static nxt_uint_t nxt_spinlock_count = 1000;
320Sigor@sysoev.ru
330Sigor@sysoev.ru
340Sigor@sysoev.ru void
nxt_thread_spin_init(nxt_uint_t ncpu,nxt_uint_t count)350Sigor@sysoev.ru nxt_thread_spin_init(nxt_uint_t ncpu, nxt_uint_t count)
360Sigor@sysoev.ru {
370Sigor@sysoev.ru switch (ncpu) {
380Sigor@sysoev.ru
390Sigor@sysoev.ru case 0:
400Sigor@sysoev.ru /* Explicit spinlock count. */
410Sigor@sysoev.ru nxt_spinlock_count = count;
420Sigor@sysoev.ru break;
430Sigor@sysoev.ru
440Sigor@sysoev.ru case 1:
450Sigor@sysoev.ru /* Spinning is useless on UP. */
460Sigor@sysoev.ru nxt_spinlock_count = 0;
470Sigor@sysoev.ru break;
480Sigor@sysoev.ru
490Sigor@sysoev.ru default:
500Sigor@sysoev.ru /*
510Sigor@sysoev.ru * SMP.
520Sigor@sysoev.ru *
530Sigor@sysoev.ru * TODO: The count should be 10 on a virtualized system
540Sigor@sysoev.ru * since virtualized CPUs may share the same physical CPU.
550Sigor@sysoev.ru */
560Sigor@sysoev.ru nxt_spinlock_count = 1000;
570Sigor@sysoev.ru break;
580Sigor@sysoev.ru }
590Sigor@sysoev.ru }
600Sigor@sysoev.ru
610Sigor@sysoev.ru
620Sigor@sysoev.ru void
nxt_thread_spin_lock(nxt_thread_spinlock_t * lock)630Sigor@sysoev.ru nxt_thread_spin_lock(nxt_thread_spinlock_t *lock)
640Sigor@sysoev.ru {
650Sigor@sysoev.ru nxt_uint_t n;
660Sigor@sysoev.ru
670Sigor@sysoev.ru nxt_thread_log_debug("spin_lock(%p) enter", lock);
680Sigor@sysoev.ru
690Sigor@sysoev.ru for ( ;; ) {
700Sigor@sysoev.ru
710Sigor@sysoev.ru again:
720Sigor@sysoev.ru
730Sigor@sysoev.ru if (nxt_fast_path(nxt_atomic_try_lock(lock))) {
740Sigor@sysoev.ru return;
750Sigor@sysoev.ru }
760Sigor@sysoev.ru
770Sigor@sysoev.ru for (n = nxt_spinlock_count; n != 0; n--) {
780Sigor@sysoev.ru
790Sigor@sysoev.ru nxt_cpu_pause();
800Sigor@sysoev.ru
810Sigor@sysoev.ru if (*lock == 0) {
820Sigor@sysoev.ru goto again;
830Sigor@sysoev.ru }
840Sigor@sysoev.ru }
850Sigor@sysoev.ru
860Sigor@sysoev.ru nxt_thread_yield();
870Sigor@sysoev.ru }
880Sigor@sysoev.ru }
890Sigor@sysoev.ru
900Sigor@sysoev.ru
910Sigor@sysoev.ru nxt_bool_t
nxt_thread_spin_trylock(nxt_thread_spinlock_t * lock)920Sigor@sysoev.ru nxt_thread_spin_trylock(nxt_thread_spinlock_t *lock)
930Sigor@sysoev.ru {
940Sigor@sysoev.ru nxt_thread_log_debug("spin_trylock(%p) enter", lock);
950Sigor@sysoev.ru
960Sigor@sysoev.ru if (nxt_fast_path(nxt_atomic_try_lock(lock))) {
970Sigor@sysoev.ru return 1;
980Sigor@sysoev.ru }
990Sigor@sysoev.ru
1000Sigor@sysoev.ru nxt_thread_log_debug("spin_trylock(%p) failed", lock);
1010Sigor@sysoev.ru
1020Sigor@sysoev.ru return 0;
1030Sigor@sysoev.ru }
1040Sigor@sysoev.ru
1050Sigor@sysoev.ru
1060Sigor@sysoev.ru void
nxt_thread_spin_unlock(nxt_thread_spinlock_t * lock)1070Sigor@sysoev.ru nxt_thread_spin_unlock(nxt_thread_spinlock_t *lock)
1080Sigor@sysoev.ru {
1090Sigor@sysoev.ru nxt_atomic_release(lock);
1100Sigor@sysoev.ru
1110Sigor@sysoev.ru nxt_thread_log_debug("spin_unlock(%p) exit", lock);
1120Sigor@sysoev.ru }
113