Source file src/runtime/lock_sema.go

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build aix || darwin || netbsd || openbsd || plan9 || solaris || windows
     6  
     7  package runtime
     8  
     9  import (
    10  	"internal/runtime/atomic"
    11  	"unsafe"
    12  )
    13  
    14  const (
    15  	locked uintptr = 1
    16  )
    17  
    18  // One-time notifications.
    19  func noteclear(n *note) {
    20  	n.key = 0
    21  }
    22  
    23  func notewakeup(n *note) {
    24  	var v uintptr
    25  	for {
    26  		v = atomic.Loaduintptr(&n.key)
    27  		if atomic.Casuintptr(&n.key, v, locked) {
    28  			break
    29  		}
    30  	}
    31  
    32  	// Successfully set waitm to locked.
    33  	// What was it before?
    34  	switch {
    35  	case v == 0:
    36  		// Nothing was waiting. Done.
    37  	case v == locked:
    38  		// Two notewakeups! Not allowed.
    39  		throw("notewakeup - double wakeup")
    40  	default:
    41  		// Must be the waiting m. Wake it up.
    42  		semawakeup((*m)(unsafe.Pointer(v)))
    43  	}
    44  }
    45  
    46  func notesleep(n *note) {
    47  	gp := getg()
    48  	if gp != gp.m.g0 {
    49  		throw("notesleep not on g0")
    50  	}
    51  	semacreate(gp.m)
    52  	if !atomic.Casuintptr(&n.key, 0, uintptr(unsafe.Pointer(gp.m))) {
    53  		// Must be locked (got wakeup).
    54  		if n.key != locked {
    55  			throw("notesleep - waitm out of sync")
    56  		}
    57  		return
    58  	}
    59  	// Queued. Sleep.
    60  	gp.m.blocked = true
    61  	if *cgo_yield == nil {
    62  		semasleep(-1)
    63  	} else {
    64  		// Sleep for an arbitrary-but-moderate interval to poll libc interceptors.
    65  		const ns = 10e6
    66  		for atomic.Loaduintptr(&n.key) == 0 {
    67  			semasleep(ns)
    68  			asmcgocall(*cgo_yield, nil)
    69  		}
    70  	}
    71  	gp.m.blocked = false
    72  }
    73  
    74  //go:nosplit
    75  func notetsleep_internal(n *note, ns int64, gp *g, deadline int64) bool {
    76  	// gp and deadline are logically local variables, but they are written
    77  	// as parameters so that the stack space they require is charged
    78  	// to the caller.
    79  	// This reduces the nosplit footprint of notetsleep_internal.
    80  	gp = getg()
    81  
    82  	// Register for wakeup on n->waitm.
    83  	if !atomic.Casuintptr(&n.key, 0, uintptr(unsafe.Pointer(gp.m))) {
    84  		// Must be locked (got wakeup).
    85  		if n.key != locked {
    86  			throw("notetsleep - waitm out of sync")
    87  		}
    88  		return true
    89  	}
    90  	if ns < 0 {
    91  		// Queued. Sleep.
    92  		gp.m.blocked = true
    93  		if *cgo_yield == nil {
    94  			semasleep(-1)
    95  		} else {
    96  			// Sleep in arbitrary-but-moderate intervals to poll libc interceptors.
    97  			const ns = 10e6
    98  			for semasleep(ns) < 0 {
    99  				asmcgocall(*cgo_yield, nil)
   100  			}
   101  		}
   102  		gp.m.blocked = false
   103  		return true
   104  	}
   105  
   106  	deadline = nanotime() + ns
   107  	for {
   108  		// Registered. Sleep.
   109  		gp.m.blocked = true
   110  		if *cgo_yield != nil && ns > 10e6 {
   111  			ns = 10e6
   112  		}
   113  		if semasleep(ns) >= 0 {
   114  			gp.m.blocked = false
   115  			// Acquired semaphore, semawakeup unregistered us.
   116  			// Done.
   117  			return true
   118  		}
   119  		if *cgo_yield != nil {
   120  			asmcgocall(*cgo_yield, nil)
   121  		}
   122  		gp.m.blocked = false
   123  		// Interrupted or timed out. Still registered. Semaphore not acquired.
   124  		ns = deadline - nanotime()
   125  		if ns <= 0 {
   126  			break
   127  		}
   128  		// Deadline hasn't arrived. Keep sleeping.
   129  	}
   130  
   131  	// Deadline arrived. Still registered. Semaphore not acquired.
   132  	// Want to give up and return, but have to unregister first,
   133  	// so that any notewakeup racing with the return does not
   134  	// try to grant us the semaphore when we don't expect it.
   135  	for {
   136  		v := atomic.Loaduintptr(&n.key)
   137  		switch v {
   138  		case uintptr(unsafe.Pointer(gp.m)):
   139  			// No wakeup yet; unregister if possible.
   140  			if atomic.Casuintptr(&n.key, v, 0) {
   141  				return false
   142  			}
   143  		case locked:
   144  			// Wakeup happened so semaphore is available.
   145  			// Grab it to avoid getting out of sync.
   146  			gp.m.blocked = true
   147  			if semasleep(-1) < 0 {
   148  				throw("runtime: unable to acquire - semaphore out of sync")
   149  			}
   150  			gp.m.blocked = false
   151  			return true
   152  		default:
   153  			throw("runtime: unexpected waitm - semaphore out of sync")
   154  		}
   155  	}
   156  }
   157  
   158  func notetsleep(n *note, ns int64) bool {
   159  	gp := getg()
   160  	if gp != gp.m.g0 {
   161  		throw("notetsleep not on g0")
   162  	}
   163  	semacreate(gp.m)
   164  	return notetsleep_internal(n, ns, nil, 0)
   165  }
   166  
   167  // same as runtimeĀ·notetsleep, but called on user g (not g0)
   168  // calls only nosplit functions between entersyscallblock/exitsyscall.
   169  func notetsleepg(n *note, ns int64) bool {
   170  	gp := getg()
   171  	if gp == gp.m.g0 {
   172  		throw("notetsleepg on g0")
   173  	}
   174  	semacreate(gp.m)
   175  	entersyscallblock()
   176  	ok := notetsleep_internal(n, ns, nil, 0)
   177  	exitsyscall()
   178  	return ok
   179  }
   180  
   181  func beforeIdle(int64, int64) (*g, bool) {
   182  	return nil, false
   183  }
   184  
   185  func checkTimeouts() {}
   186  

View as plain text