xref: /unit/src/nxt_spinlock.c (revision 765)
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
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
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
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
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