]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: simplify NetBSD semaphores
authorMatthew Dempsky <mdempsky@google.com>
Mon, 23 Feb 2015 08:05:30 +0000 (17:05 +0900)
committerJoel Sing <jsing@google.com>
Wed, 25 Feb 2015 03:02:28 +0000 (03:02 +0000)
NetBSD's semaphore implementation is derived from OpenBSD's, but has
subsequently diverged due to cleanups that were only applied to the
latter (https://golang.org/cl/137960043, https://golang.org/cl/5563).
This CL applies analogous cleanups for NetBSD.

Notably, we can also remove the scary NetBSD deadlock warning.
NetBSD's manual pages document that lwp_unpark on a not-yet-parked LWP
will cause that LWP's next lwp_park system call to return immediately,
so there's no race hazard.

Change-Id: Ib06844c420d2496ac289748eba13eb4700bbbbb2
Reviewed-on: https://go-review.googlesource.com/5564
Reviewed-by: Dmitry Vyukov <dvyukov@google.com>
Reviewed-by: Joel Sing <jsing@google.com>
src/runtime/os1_netbsd.go

index 9b650abd48b70607f7383867846af6cd74ff5c66..f2e6ef682e28ca914a8cd33dae3618ffe75c7f15 100644 (file)
@@ -7,8 +7,8 @@ package runtime
 import "unsafe"
 
 const (
-       _ESRCH   = 3
-       _ENOTSUP = 91
+       _ESRCH     = 3
+       _ETIMEDOUT = 60
 
        // From NetBSD's <sys/time.h>
        _CLOCK_REALTIME  = 0
@@ -46,91 +46,40 @@ func semacreate() uintptr {
 func semasleep(ns int64) int32 {
        _g_ := getg()
 
-       // spin-mutex lock
-       for {
-               if xchg(&_g_.m.waitsemalock, 1) == 0 {
-                       break
-               }
-               osyield()
+       // Compute sleep deadline.
+       var tsp *timespec
+       if ns >= 0 {
+               var ts timespec
+               var nsec int32
+               ns += nanotime()
+               ts.set_sec(timediv(ns, 1000000000, &nsec))
+               ts.set_nsec(nsec)
+               tsp = &ts
        }
 
        for {
-               // lock held
-               if _g_.m.waitsemacount == 0 {
-                       // sleep until semaphore != 0 or timeout.
-                       // thrsleep unlocks m.waitsemalock.
-                       if ns < 0 {
-                               // TODO(jsing) - potential deadlock!
-                               //
-                               // There is a potential deadlock here since we
-                               // have to release the waitsemalock mutex
-                               // before we call lwp_park() to suspend the
-                               // thread. This allows another thread to
-                               // release the lock and call lwp_unpark()
-                               // before the thread is actually suspended.
-                               // If this occurs the current thread will end
-                               // up sleeping indefinitely. Unfortunately
-                               // the NetBSD kernel does not appear to provide
-                               // a mechanism for unlocking the userspace
-                               // mutex once the thread is actually parked.
-                               atomicstore(&_g_.m.waitsemalock, 0)
-                               lwp_park(nil, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil)
-                       } else {
-                               var ts timespec
-                               var nsec int32
-                               ns += nanotime()
-                               ts.set_sec(timediv(ns, 1000000000, &nsec))
-                               ts.set_nsec(nsec)
-                               // TODO(jsing) - potential deadlock!
-                               // See above for details.
-                               atomicstore(&_g_.m.waitsemalock, 0)
-                               lwp_park(&ts, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil)
-                       }
-                       // reacquire lock
-                       for {
-                               if xchg(&_g_.m.waitsemalock, 1) == 0 {
-                                       break
-                               }
-                               osyield()
+               v := atomicload(&_g_.m.waitsemacount)
+               if v > 0 {
+                       if cas(&_g_.m.waitsemacount, v, v-1) {
+                               return 0 // semaphore acquired
                        }
+                       continue
                }
 
-               // lock held (again)
-               if _g_.m.waitsemacount != 0 {
-                       // semaphore is available.
-                       _g_.m.waitsemacount--
-                       // spin-mutex unlock
-                       atomicstore(&_g_.m.waitsemalock, 0)
-                       return 0
-               }
-
-               // semaphore not available.
-               // if there is a timeout, stop now.
-               // otherwise keep trying.
-               if ns >= 0 {
-                       break
+               // Sleep until unparked by semawakeup or timeout.
+               ret := lwp_park(tsp, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil)
+               if ret == _ETIMEDOUT {
+                       return -1
                }
        }
-
-       // lock held but giving up
-       // spin-mutex unlock
-       atomicstore(&_g_.m.waitsemalock, 0)
-       return -1
 }
 
 //go:nosplit
 func semawakeup(mp *m) {
-       // spin-mutex lock
-       for {
-               if xchg(&mp.waitsemalock, 1) == 0 {
-                       break
-               }
-               osyield()
-       }
-
-       mp.waitsemacount++
-       // TODO(jsing) - potential deadlock, see semasleep() for details.
-       // Confirm that LWP is parked before unparking...
+       xadd(&mp.waitsemacount, 1)
+       // From NetBSD's _lwp_unpark(2) manual:
+       // "If the target LWP is not currently waiting, it will return
+       // immediately upon the next call to _lwp_park()."
        ret := lwp_unpark(int32(mp.procid), unsafe.Pointer(&mp.waitsemacount))
        if ret != 0 && ret != _ESRCH {
                // semawakeup can be called on signal stack.
@@ -138,9 +87,6 @@ func semawakeup(mp *m) {
                        print("thrwakeup addr=", &mp.waitsemacount, " sem=", mp.waitsemacount, " ret=", ret, "\n")
                })
        }
-
-       // spin-mutex unlock
-       atomicstore(&mp.waitsemalock, 0)
 }
 
 func newosproc(mp *m, stk unsafe.Pointer) {