Source file src/runtime/lock_futex.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 dragonfly || freebsd || linux
     6  
     7  package runtime
     8  
     9  import (
    10  	"internal/runtime/atomic"
    11  	"unsafe"
    12  )
    13  
    14  // We use the uintptr mutex.key and note.key as a uint32.
    15  //
    16  //go:nosplit
    17  func key32(p *uintptr) *uint32 {
    18  	return (*uint32)(unsafe.Pointer(p))
    19  }
    20  
    21  // One-time notifications.
    22  func noteclear(n *note) {
    23  	n.key = 0
    24  }
    25  
    26  func notewakeup(n *note) {
    27  	old := atomic.Xchg(key32(&n.key), 1)
    28  	if old != 0 {
    29  		print("notewakeup - double wakeup (", old, ")\n")
    30  		throw("notewakeup - double wakeup")
    31  	}
    32  	futexwakeup(key32(&n.key), 1)
    33  }
    34  
    35  func notesleep(n *note) {
    36  	gp := getg()
    37  	if gp != gp.m.g0 {
    38  		throw("notesleep not on g0")
    39  	}
    40  	ns := int64(-1)
    41  	if *cgo_yield != nil {
    42  		// Sleep for an arbitrary-but-moderate interval to poll libc interceptors.
    43  		ns = 10e6
    44  	}
    45  	for atomic.Load(key32(&n.key)) == 0 {
    46  		gp.m.blocked = true
    47  		futexsleep(key32(&n.key), 0, ns)
    48  		if *cgo_yield != nil {
    49  			asmcgocall(*cgo_yield, nil)
    50  		}
    51  		gp.m.blocked = false
    52  	}
    53  }
    54  
    55  // May run with m.p==nil if called from notetsleep, so write barriers
    56  // are not allowed.
    57  //
    58  //go:nosplit
    59  //go:nowritebarrier
    60  func notetsleep_internal(n *note, ns int64) bool {
    61  	gp := getg()
    62  
    63  	if ns < 0 {
    64  		if *cgo_yield != nil {
    65  			// Sleep for an arbitrary-but-moderate interval to poll libc interceptors.
    66  			ns = 10e6
    67  		}
    68  		for atomic.Load(key32(&n.key)) == 0 {
    69  			gp.m.blocked = true
    70  			futexsleep(key32(&n.key), 0, ns)
    71  			if *cgo_yield != nil {
    72  				asmcgocall(*cgo_yield, nil)
    73  			}
    74  			gp.m.blocked = false
    75  		}
    76  		return true
    77  	}
    78  
    79  	if atomic.Load(key32(&n.key)) != 0 {
    80  		return true
    81  	}
    82  
    83  	deadline := nanotime() + ns
    84  	for {
    85  		if *cgo_yield != nil && ns > 10e6 {
    86  			ns = 10e6
    87  		}
    88  		gp.m.blocked = true
    89  		futexsleep(key32(&n.key), 0, ns)
    90  		if *cgo_yield != nil {
    91  			asmcgocall(*cgo_yield, nil)
    92  		}
    93  		gp.m.blocked = false
    94  		if atomic.Load(key32(&n.key)) != 0 {
    95  			break
    96  		}
    97  		now := nanotime()
    98  		if now >= deadline {
    99  			break
   100  		}
   101  		ns = deadline - now
   102  	}
   103  	return atomic.Load(key32(&n.key)) != 0
   104  }
   105  
   106  func notetsleep(n *note, ns int64) bool {
   107  	gp := getg()
   108  	if gp != gp.m.g0 && gp.m.preemptoff != "" {
   109  		throw("notetsleep not on g0")
   110  	}
   111  
   112  	return notetsleep_internal(n, ns)
   113  }
   114  
   115  // same as runtimeĀ·notetsleep, but called on user g (not g0)
   116  // calls only nosplit functions between entersyscallblock/exitsyscall.
   117  func notetsleepg(n *note, ns int64) bool {
   118  	gp := getg()
   119  	if gp == gp.m.g0 {
   120  		throw("notetsleepg on g0")
   121  	}
   122  
   123  	entersyscallblock()
   124  	ok := notetsleep_internal(n, ns)
   125  	exitsyscall()
   126  	return ok
   127  }
   128  
   129  func beforeIdle(int64, int64) (*g, bool) {
   130  	return nil, false
   131  }
   132  
   133  func checkTimeouts() {}
   134  
   135  //go:nosplit
   136  func semacreate(mp *m) {}
   137  
   138  //go:nosplit
   139  func semasleep(ns int64) int32 {
   140  	mp := getg().m
   141  
   142  	for v := atomic.Xadd(&mp.waitsema, -1); ; v = atomic.Load(&mp.waitsema) {
   143  		if int32(v) >= 0 {
   144  			return 0
   145  		}
   146  		futexsleep(&mp.waitsema, v, ns)
   147  		if ns >= 0 {
   148  			if int32(v) >= 0 {
   149  				return 0
   150  			} else {
   151  				return -1
   152  			}
   153  		}
   154  	}
   155  }
   156  
   157  //go:nosplit
   158  func semawakeup(mp *m) {
   159  	v := atomic.Xadd(&mp.waitsema, 1)
   160  	if v == 0 {
   161  		futexwakeup(&mp.waitsema, 1)
   162  	}
   163  }
   164  

View as plain text