]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: improve locking on openbsd
authorJoel Sing <jsing@google.com>
Sat, 8 Oct 2011 13:56:13 +0000 (00:56 +1100)
committerJoel Sing <jsing@google.com>
Sat, 8 Oct 2011 13:56:13 +0000 (00:56 +1100)
Implement a locking model based on the current linux model - a
tri-state mutex with active spinning, passive spinning and sleeping.

R=golang-dev, dvyukov, rsc
CC=golang-dev
https://golang.org/cl/4974043

src/pkg/runtime/openbsd/386/sys.s
src/pkg/runtime/openbsd/amd64/defs.h
src/pkg/runtime/openbsd/amd64/sys.s
src/pkg/runtime/openbsd/thread.c

index c399c91050f49a860870fcffa15bf900cbb11c21..d2df51827648d46ea67dec396724cc6e14e34a04 100644 (file)
@@ -269,6 +269,16 @@ TEXT runtime·osyield(SB),7,$-4
        INT     $0x80
        RET
 
+TEXT runtime·thrsleep(SB),7,$-4
+       MOVL    $300, AX                // sys_thrsleep
+       INT     $0x80
+       RET
+
+TEXT runtime·thrwakeup(SB),7,$-4
+       MOVL    $301, AX                // sys_thrwakeup
+       INT     $0x80
+       RET
+
 TEXT runtime·sysctl(SB),7,$28
        LEAL    arg0+0(FP), SI
        LEAL    4(SP), DI
index 4eb5cd20565788659eb385f8a41c806bbc19a17d..968f22d586066caa31754a15819f5d5c758cdb7e 100644 (file)
@@ -114,6 +114,8 @@ struct Itimerval {
 
 typedef void sfxsave64;
 
+typedef void usavefpu;
+
 typedef struct Sigcontext Sigcontext;
 struct Sigcontext {
        int64 sc_rdi;
index b64868f31416267967d7bab19ad7692fcb401f70..29d74a1200d7236c9d18b1d8ce16b4f525aa1734 100644 (file)
@@ -62,19 +62,19 @@ TEXT runtime·osyield(SB),7,$0
        SYSCALL
        RET
 
-TEXT runtime·sys_thrsleep(SB),7,$0
-       MOVQ 8(SP), DI
-       MOVL 16(SP), SI
-       MOVQ 24(SP), DX
-       MOVQ 32(SP), R10
-       MOVL $300, AX
+TEXT runtime·thrsleep(SB),7,$0
+       MOVQ    8(SP), DI               // arg 1 - ident
+       MOVL    16(SP), SI              // arg 2 - clock_id
+       MOVQ    24(SP), DX              // arg 3 - tp
+       MOVQ    32(SP), R10             // arg 4 - lock
+       MOVL    $300, AX                // sys_thrsleep
        SYSCALL
        RET
 
-TEXT runtime·sys_thrwakeup(SB),7,$0
-       MOVQ 8(SP), DI
-       MOVL 16(SP), SI
-       MOVL $301, AX
+TEXT runtime·thrwakeup(SB),7,$0
+       MOVQ    8(SP), DI               // arg 1 - ident
+       MOVL    16(SP), SI              // arg 2 - n
+       MOVL    $301, AX                // sys_thrwakeup
        SYSCALL
        RET
 
index 2972a3cd41d571cb708bd23edf11081dd68c39cf..48e02b6a77d349e53e567f58beed9d3c3c6d1372 100644 (file)
@@ -6,15 +6,26 @@
 #include "os.h"
 #include "stack.h"
 
-extern SigTab runtime·sigtab[];
-
-extern int64 runtime·rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void));
-
 enum
 {
+       MUTEX_UNLOCKED = 0,
+       MUTEX_LOCKED = 1,
+       MUTEX_SLEEPING = 2,
+
+       ACTIVE_SPIN = 4,
+       ACTIVE_SPIN_CNT = 30,
+       PASSIVE_SPIN = 1,
+
+       ESRCH = 3,
        ENOTSUP = 91,
 };
 
+extern SigTab runtime·sigtab[];
+
+extern int64 runtime·rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void));
+extern int32 runtime·thrsleep(void *, void *, void*, void *);
+extern int32 runtime·thrwakeup(void *, int32);
+
 // From OpenBSD's <sys/sysctl.h>
 #define        CTL_HW  6
 #define        HW_NCPU 3
@@ -39,28 +50,86 @@ getncpu(void)
                return 1;
 }
 
-// Basic spinlocks using CAS. We can improve on these later.
+// Possible lock states are MUTEX_UNLOCKED, MUTEX_LOCKED and MUTEX_SLEEPING.
+// MUTEX_SLEEPING means that there is potentially at least one sleeping thread.
+// Note that there can be spinning threads during all states - they do not
+// affect the mutex's state.
 static void
 lock(Lock *l)
 {
+       uint32 i, v, wait, spin;
+       int32 ret;
+
+       // Speculative grab for lock.
+       v = runtime·xchg(&l->key, MUTEX_LOCKED);
+       if(v == MUTEX_UNLOCKED)
+               return;
+
+       // If we ever change the lock from MUTEX_SLEEPING to some other value,
+       // we must be careful to change it back to MUTEX_SLEEPING before
+       // returning, to ensure that the sleeping thread gets its wakeup call.
+       wait = v;
+
+       // No point spinning unless there are multiple processors.
+       spin = 0;
+       if(runtime·ncpu > 1)
+               spin = ACTIVE_SPIN;
+
        for(;;) {
-               if(runtime·cas(&l->key, 0, 1))
+               // Try for lock, spinning.
+               for(i = 0; i < spin; i++) {
+                       while(l->key == MUTEX_UNLOCKED)
+                               if(runtime·cas(&l->key, MUTEX_UNLOCKED, wait))
+                                       return;
+                       runtime·procyield(ACTIVE_SPIN_CNT);
+               }
+
+               // Try for lock, rescheduling.
+               for(i = 0; i < PASSIVE_SPIN; i++) {
+                       while(l->key == MUTEX_UNLOCKED)
+                               if(runtime·cas(&l->key, MUTEX_UNLOCKED, wait))
+                                       return;
+                       runtime·osyield();
+               }
+
+               // Grab a lock on sema and sleep - sema will be unlocked by
+               // thrsleep() and we'll get woken by another thread.
+               // Note that thrsleep unlocks on a _spinlock_lock_t which is
+               // an int on amd64, so we need to be careful here.
+               while (!runtime·cas(&l->sema, MUTEX_UNLOCKED, MUTEX_LOCKED))
+                       runtime·osyield();
+               v = runtime·xchg(&l->key, MUTEX_SLEEPING);
+               if(v == MUTEX_UNLOCKED) {
+                       l->sema = MUTEX_UNLOCKED;
                        return;
-               runtime·osyield();
+               }
+               wait = v;
+               ret = runtime·thrsleep(&l->key, 0, 0, &l->sema);
+               if (ret != 0) {
+                       runtime·printf("thrsleep addr=%p sema=%d ret=%d\n",
+                               &l->key, l->sema, ret);
+                       l->sema = MUTEX_UNLOCKED;
+               }
        }
 }
 
 static void
 unlock(Lock *l)
 {
-       uint32 v;
-
-       for (;;) {
-               v = l->key;
-               if((v&1) == 0)
-                       runtime·throw("unlock of unlocked lock");
-               if(runtime·cas(&l->key, v, 0))
-                       break;
+       uint32 v, ret;
+
+       while (!runtime·cas(&l->sema, MUTEX_UNLOCKED, MUTEX_LOCKED))
+               runtime·osyield();
+       v = runtime·xchg(&l->key, MUTEX_UNLOCKED);
+       l->sema = MUTEX_UNLOCKED;
+       if(v == MUTEX_UNLOCKED)
+               runtime·throw("unlock of unlocked lock");
+       if(v == MUTEX_SLEEPING) {
+               ret = runtime·thrwakeup(&l->key, 0);
+               if (ret != 0 && ret != ESRCH) {
+                       runtime·printf("thrwakeup addr=%p sem=%d ret=%d\n",
+                               &l->key, l->sema, ret);
+               }
        }
 }