github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/runtime/sema.go (about)

     1  // Copyright 2009 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  // Semaphore implementation exposed to Go.
     6  // 暴露的Semaphore实现
     7  // Intended use is provide a sleep and wakeup
     8  // primitive that can be used in the contended case
     9  // of other synchronization primitives.
    10  // Thus it targets the same goal as Linux's futex,
    11  // but it has much simpler semantics.
    12  // 目地用处是提供一个睡眠和唤醒的操作, 其可以用于其他同步原语的原语
    13  // 因此, 其目地和linux的futex是一样的, 只是有者更简单的语义
    14  //
    15  // That is, don't think of these as semaphores.
    16  // Think of them as a way to implement sleep and wakeup
    17  // such that every sleep is paired with a single wakeup,
    18  // even if, due to races, the wakeup happens before the sleep.
    19  //
    20  // See Mullender and Cox, ``Semaphores in Plan 9,''
    21  // http://swtch.com/semaphore.pdf
    22  
    23  // 用到了树堆(treap=tree+heap)这种数据结构, 其结构相当于随机数据插入的二叉搜索树, 实现简单且能基本实现随机平衡结构, 这里用最小堆
    24  
    25  package runtime
    26  
    27  import (
    28  	"runtime/internal/atomic"
    29  	"runtime/internal/sys"
    30  	"unsafe"
    31  )
    32  
    33  // Asynchronous semaphore for sync.Mutex.
    34  
    35  // A semaRoot holds a balanced tree of sudog with distinct addresses (s.elem).
    36  // Each of those sudog may in turn point (through s.waitlink) to a list
    37  // of other sudogs waiting on the same address.
    38  // The operations on the inner lists of sudogs with the same address
    39  // are all O(1). The scanning of the top-level semaRoot list is O(log n),
    40  // where n is the number of distinct addresses with goroutines blocked
    41  // on them that hash to the given semaRoot.
    42  // See golang.org/issue/17953 for a program that worked badly
    43  // before we introduced the second level of list, and test/locklinear.go
    44  // for a test that exercises this.
    45  type semaRoot struct {
    46  	lock  mutex
    47  	treap *sudog // root of balanced tree of unique waiters.
    48  	nwait uint32 // Number of waiters. Read w/o the lock.
    49  }
    50  
    51  // Prime to not correlate with any user patterns.
    52  const semTabSize = 251
    53  
    54  var semtable [semTabSize]struct {
    55  	root semaRoot
    56  	pad  [sys.CacheLineSize - unsafe.Sizeof(semaRoot{})]byte
    57  }
    58  
    59  //go:linkname sync_runtime_Semacquire sync.runtime_Semacquire
    60  func sync_runtime_Semacquire(addr *uint32) {
    61  	semacquire1(addr, false, semaBlockProfile)
    62  }
    63  
    64  //go:linkname poll_runtime_Semacquire internal/poll.runtime_Semacquire
    65  func poll_runtime_Semacquire(addr *uint32) {
    66  	semacquire1(addr, false, semaBlockProfile)
    67  }
    68  
    69  //go:linkname sync_runtime_Semrelease sync.runtime_Semrelease
    70  func sync_runtime_Semrelease(addr *uint32, handoff bool) {
    71  	semrelease1(addr, handoff)
    72  }
    73  
    74  //go:linkname sync_runtime_SemacquireMutex sync.runtime_SemacquireMutex
    75  func sync_runtime_SemacquireMutex(addr *uint32, lifo bool) {
    76  	semacquire1(addr, lifo, semaBlockProfile|semaMutexProfile)
    77  }
    78  
    79  //go:linkname poll_runtime_Semrelease internal/poll.runtime_Semrelease
    80  func poll_runtime_Semrelease(addr *uint32) {
    81  	semrelease(addr)
    82  }
    83  
    84  func readyWithTime(s *sudog, traceskip int) {
    85  	if s.releasetime != 0 {
    86  		s.releasetime = cputicks()
    87  	}
    88  	goready(s.g, traceskip)
    89  }
    90  
    91  type semaProfileFlags int
    92  
    93  const (
    94  	semaBlockProfile semaProfileFlags = 1 << iota
    95  	semaMutexProfile
    96  )
    97  
    98  // Called from runtime.
    99  func semacquire(addr *uint32) {
   100  	semacquire1(addr, false, 0)
   101  }
   102  
   103  func semacquire1(addr *uint32, lifo bool, profile semaProfileFlags) {
   104  	gp := getg()
   105  	if gp != gp.m.curg {
   106  		throw("semacquire not on the G stack")
   107  	}
   108  
   109  	// Easy case.
   110  	if cansemacquire(addr) {
   111  		return
   112  	}
   113  
   114  	// Harder case:
   115  	//	increment waiter count
   116  	//	try cansemacquire one more time, return if succeeded
   117  	//	enqueue itself as a waiter
   118  	//	sleep
   119  	//	(waiter descriptor is dequeued by signaler)
   120  	s := acquireSudog()
   121  	root := semroot(addr)
   122  	t0 := int64(0)
   123  	s.releasetime = 0
   124  	s.acquiretime = 0
   125  	s.ticket = 0
   126  	if profile&semaBlockProfile != 0 && blockprofilerate > 0 {
   127  		t0 = cputicks()
   128  		s.releasetime = -1
   129  	}
   130  	if profile&semaMutexProfile != 0 && mutexprofilerate > 0 {
   131  		if t0 == 0 {
   132  			t0 = cputicks()
   133  		}
   134  		s.acquiretime = t0
   135  	}
   136  	for {
   137  		lock(&root.lock)
   138  		// Add ourselves to nwait to disable "easy case" in semrelease.
   139  		atomic.Xadd(&root.nwait, 1)
   140  		// Check cansemacquire to avoid missed wakeup.
   141  		if cansemacquire(addr) {
   142  			atomic.Xadd(&root.nwait, -1)
   143  			unlock(&root.lock)
   144  			break
   145  		}
   146  		// Any semrelease after the cansemacquire knows we're waiting
   147  		// (we set nwait above), so go to sleep.
   148  		root.queue(addr, s, lifo)
   149  		goparkunlock(&root.lock, "semacquire", traceEvGoBlockSync, 4)
   150  		if s.ticket != 0 || cansemacquire(addr) {
   151  			break
   152  		}
   153  	}
   154  	if s.releasetime > 0 {
   155  		blockevent(s.releasetime-t0, 3)
   156  	}
   157  	releaseSudog(s)
   158  }
   159  
   160  func semrelease(addr *uint32) {
   161  	semrelease1(addr, false)
   162  }
   163  
   164  func semrelease1(addr *uint32, handoff bool) {
   165  	root := semroot(addr)
   166  	atomic.Xadd(addr, 1)
   167  
   168  	// Easy case: no waiters?
   169  	// This check must happen after the xadd, to avoid a missed wakeup
   170  	// (see loop in semacquire).
   171  	if atomic.Load(&root.nwait) == 0 {
   172  		return
   173  	}
   174  
   175  	// Harder case: search for a waiter and wake it.
   176  	lock(&root.lock)
   177  	if atomic.Load(&root.nwait) == 0 {
   178  		// The count is already consumed by another goroutine,
   179  		// so no need to wake up another goroutine.
   180  		unlock(&root.lock)
   181  		return
   182  	}
   183  	s, t0 := root.dequeue(addr)
   184  	if s != nil {
   185  		atomic.Xadd(&root.nwait, -1)
   186  	}
   187  	unlock(&root.lock)
   188  	if s != nil { // May be slow, so unlock first
   189  		acquiretime := s.acquiretime
   190  		if acquiretime != 0 {
   191  			mutexevent(t0-acquiretime, 3)
   192  		}
   193  		if s.ticket != 0 {
   194  			throw("corrupted semaphore ticket")
   195  		}
   196  		if handoff && cansemacquire(addr) {
   197  			s.ticket = 1
   198  		}
   199  		readyWithTime(s, 5)
   200  	}
   201  }
   202  
   203  func semroot(addr *uint32) *semaRoot {
   204  	return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root
   205  }
   206  
   207  // addr指向一个值(信号量), 如果这个值>0则表示有资源, 直接cas -1即可
   208  // 如果为0则表示没有资源
   209  func cansemacquire(addr *uint32) bool {
   210  	for {
   211  		v := atomic.Load(addr)
   212  		if v == 0 {
   213  			return false
   214  		}
   215  		if atomic.Cas(addr, v, v-1) {
   216  			return true
   217  		}
   218  	}
   219  }
   220  
   221  // queue adds s to the blocked goroutines in semaRoot.
   222  // 二叉搜索树插入一个节点
   223  func (root *semaRoot) queue(addr *uint32, s *sudog, lifo bool) {
   224  	s.g = getg()
   225  	s.elem = unsafe.Pointer(addr)
   226  	s.next = nil
   227  	s.prev = nil
   228  
   229  	var last *sudog
   230  	pt := &root.treap
   231  	for t := *pt; t != nil; t = *pt {
   232  		if t.elem == unsafe.Pointer(addr) {
   233  			// Already have addr in list.
   234  			if lifo {
   235  				// Substitute s in t's place in treap.
   236  				*pt = s
   237  				s.ticket = t.ticket
   238  				s.acquiretime = t.acquiretime
   239  				s.parent = t.parent
   240  				s.prev = t.prev
   241  				s.next = t.next
   242  				if s.prev != nil {
   243  					s.prev.parent = s
   244  				}
   245  				if s.next != nil {
   246  					s.next.parent = s
   247  				}
   248  				// Add t first in s's wait list.
   249  				s.waitlink = t
   250  				s.waittail = t.waittail
   251  				if s.waittail == nil {
   252  					s.waittail = t
   253  				}
   254  				t.parent = nil
   255  				t.prev = nil
   256  				t.next = nil
   257  				t.waittail = nil
   258  			} else {
   259  				// Add s to end of t's wait list.
   260  				if t.waittail == nil {
   261  					t.waitlink = s
   262  				} else {
   263  					t.waittail.waitlink = s
   264  				}
   265  				t.waittail = s
   266  				s.waitlink = nil
   267  			}
   268  			return
   269  		}
   270  		last = t
   271  		if uintptr(unsafe.Pointer(addr)) < uintptr(t.elem) {
   272  			pt = &t.prev
   273  		} else {
   274  			pt = &t.next
   275  		}
   276  	}
   277  
   278  	// Add s as new leaf in tree of unique addrs.
   279  	// The balanced tree is a treap using ticket as the random heap priority.
   280  	// That is, it is a binary tree ordered according to the elem addresses,
   281  	// but then among the space of possible binary trees respecting those
   282  	// addresses, it is kept balanced on average by maintaining a heap ordering
   283  	// on the ticket: s.ticket <= both s.prev.ticket and s.next.ticket.
   284  	// https://en.wikipedia.org/wiki/Treap
   285  	// http://faculty.washington.edu/aragon/pubs/rst89.pdf
   286  	//
   287  	// s.ticket compared with zero in couple of places, therefore set lowest bit.
   288  	// It will not affect treap's quality noticeably.
   289  	s.ticket = fastrand() | 1
   290  	s.parent = last
   291  	*pt = s
   292  
   293  	// Rotate up into tree according to ticket (priority).
   294  	for s.parent != nil && s.parent.ticket > s.ticket {
   295  		if s.parent.prev == s {
   296  			root.rotateRight(s.parent)
   297  		} else {
   298  			if s.parent.next != s { // 用一些断言保证树没什么问题
   299  				panic("semaRoot queue")
   300  			}
   301  			root.rotateLeft(s.parent)
   302  		}
   303  	}
   304  }
   305  
   306  // dequeue searches for and finds the first goroutine
   307  // in semaRoot blocked on addr.
   308  // If the sudog was being profiled, dequeue returns the time
   309  // at which it was woken up as now. Otherwise now is 0.
   310  // 如果这个addr里有多个元素, 则只dequeue第一个
   311  func (root *semaRoot) dequeue(addr *uint32) (found *sudog, now int64) {
   312  	ps := &root.treap
   313  	s := *ps
   314  	for ; s != nil; s = *ps {
   315  		if s.elem == unsafe.Pointer(addr) {
   316  			goto Found
   317  		}
   318  		if uintptr(unsafe.Pointer(addr)) < uintptr(s.elem) {
   319  			ps = &s.prev
   320  		} else {
   321  			ps = &s.next
   322  		}
   323  	}
   324  	return nil, 0
   325  
   326  Found:
   327  	now = int64(0)
   328  	if s.acquiretime != 0 {
   329  		now = cputicks()
   330  	}
   331  	if t := s.waitlink; t != nil {
   332  		// 有多于一个元素, 则树不用修改, 改这个节点链表即可
   333  		// Substitute t, also waiting on addr, for s in root tree of unique addrs.
   334  		*ps = t
   335  		t.ticket = s.ticket
   336  		t.parent = s.parent
   337  		t.prev = s.prev
   338  		if t.prev != nil {
   339  			t.prev.parent = t
   340  		}
   341  		t.next = s.next
   342  		if t.next != nil {
   343  			t.next.parent = t
   344  		}
   345  		if t.waitlink != nil {
   346  			t.waittail = s.waittail
   347  		} else {
   348  			t.waittail = nil
   349  		}
   350  		t.acquiretime = now
   351  		s.waitlink = nil
   352  		s.waittail = nil
   353  	} else {
   354  		// Rotate s down to be leaf of tree for removal, respecting priorities.
   355  		for s.next != nil || s.prev != nil {
   356  			if s.next == nil || s.prev != nil && s.prev.ticket < s.next.ticket {
   357  				root.rotateRight(s)
   358  			} else {
   359  				root.rotateLeft(s)
   360  			}
   361  		}
   362  		// Remove s, now a leaf.
   363  		if s.parent != nil {
   364  			if s.parent.prev == s {
   365  				s.parent.prev = nil
   366  			} else {
   367  				s.parent.next = nil
   368  			}
   369  		} else {
   370  			root.treap = nil
   371  		}
   372  	}
   373  	s.parent = nil
   374  	s.elem = nil
   375  	s.next = nil
   376  	s.prev = nil
   377  	s.ticket = 0
   378  	return s, now
   379  }
   380  
   381  // rotateLeft rotates the tree rooted at node x.
   382  // turning (x a (y b c)) into (y (x a b) c).
   383  func (root *semaRoot) rotateLeft(x *sudog) {
   384  	// p -> (x a (y b c))
   385  	p := x.parent
   386  	a, y := x.prev, x.next
   387  	b, c := y.prev, y.next
   388  
   389  	y.prev = x
   390  	x.parent = y
   391  	y.next = c
   392  	if c != nil {
   393  		c.parent = y
   394  	}
   395  	x.prev = a
   396  	if a != nil {
   397  		a.parent = x
   398  	}
   399  	x.next = b
   400  	if b != nil {
   401  		b.parent = x
   402  	}
   403  
   404  	y.parent = p
   405  	if p == nil {
   406  		root.treap = y
   407  	} else if p.prev == x {
   408  		p.prev = y
   409  	} else {
   410  		if p.next != x {
   411  			throw("semaRoot rotateLeft")
   412  		}
   413  		p.next = y
   414  	}
   415  }
   416  
   417  // rotateRight rotates the tree rooted at node y.
   418  // turning (y (x a b) c) into (x a (y b c)).
   419  func (root *semaRoot) rotateRight(y *sudog) {
   420  	// p -> (y (x a b) c)
   421  	p := y.parent
   422  	x, c := y.prev, y.next
   423  	a, b := x.prev, x.next
   424  
   425  	x.prev = a
   426  	if a != nil {
   427  		a.parent = x
   428  	}
   429  	x.next = y
   430  	y.parent = x
   431  	y.prev = b
   432  	if b != nil {
   433  		b.parent = y
   434  	}
   435  	y.next = c
   436  	if c != nil {
   437  		c.parent = y
   438  	}
   439  
   440  	x.parent = p
   441  	if p == nil {
   442  		root.treap = x
   443  	} else if p.prev == y {
   444  		p.prev = x
   445  	} else {
   446  		if p.next != y {
   447  			throw("semaRoot rotateRight")
   448  		}
   449  		p.next = x
   450  	}
   451  }
   452  
   453  // notifyList is a ticket-based notification list used to implement sync.Cond.
   454  // 基于ticket的提醒队列, 用来实现sync.Cond
   455  //
   456  // It must be kept in sync with the sync package.
   457  type notifyList struct {
   458  	// wait is the ticket number of the next waiter. It is atomically
   459  	// incremented outside the lock.
   460  	// 下一个等待者的ticket number, 总是>=notify
   461  	wait uint32
   462  
   463  	// notify is the ticket number of the next waiter to be notified. It can
   464  	// be read outside the lock, but is only written to with lock held.
   465  	// 这是下一个会被notify的waiter的ticket number
   466  	//
   467  	// Both wait & notify can wrap around, and such cases will be correctly
   468  	// handled as long as their "unwrapped" difference is bounded by 2^31.
   469  	// For this not to be the case, we'd need to have 2^31+ goroutines
   470  	// blocked on the same condvar, which is currently not possible.
   471  	notify uint32
   472  
   473  	// List of parked waiters.
   474  	lock mutex
   475  	head *sudog
   476  	tail *sudog
   477  }
   478  
   479  // less checks if a < b, considering a & b running counts that may overflow the
   480  // 32-bit range, and that their "unwrapped" difference is always less than 2^31.
   481  func less(a, b uint32) bool {
   482  	return int32(a-b) < 0
   483  }
   484  
   485  // notifyListAdd adds the caller to a notify list such that it can receive
   486  // notifications. The caller must eventually call notifyListWait to wait for
   487  // such a notification, passing the returned ticket number.
   488  //go:linkname notifyListAdd sync.runtime_notifyListAdd
   489  func notifyListAdd(l *notifyList) uint32 {
   490  	// This may be called concurrently, for example, when called from
   491  	// sync.Cond.Wait while holding a RWMutex in read mode.
   492  	return atomic.Xadd(&l.wait, 1) - 1
   493  }
   494  
   495  // notifyListWait waits for a notification. If one has been sent since
   496  // notifyListAdd was called, it returns immediately. Otherwise, it blocks.
   497  //go:linkname notifyListWait sync.runtime_notifyListWait
   498  func notifyListWait(l *notifyList, t uint32) {
   499  	lock(&l.lock)
   500  
   501  	// Return right away if this ticket has already been notified.
   502  	// why not just t < l.notify
   503  	if less(t, l.notify) {
   504  		unlock(&l.lock)
   505  		return
   506  	}
   507  
   508  	// Enqueue itself.
   509  	s := acquireSudog()
   510  	s.g = getg()
   511  	s.ticket = t
   512  	s.releasetime = 0
   513  	t0 := int64(0)
   514  	if blockprofilerate > 0 {
   515  		t0 = cputicks()
   516  		s.releasetime = -1
   517  	}
   518  	if l.tail == nil {
   519  		l.head = s
   520  	} else {
   521  		l.tail.next = s
   522  	}
   523  	l.tail = s
   524  	goparkunlock(&l.lock, "semacquire", traceEvGoBlockCond, 3)
   525  	if t0 != 0 {
   526  		blockevent(s.releasetime-t0, 2)
   527  	}
   528  	releaseSudog(s)
   529  }
   530  
   531  // notifyListNotifyAll notifies all entries in the list.
   532  //go:linkname notifyListNotifyAll sync.runtime_notifyListNotifyAll
   533  func notifyListNotifyAll(l *notifyList) {
   534  	// Fast-path: if there are no new waiters since the last notification
   535  	// we don't need to acquire the lock.
   536  	if atomic.Load(&l.wait) == atomic.Load(&l.notify) {
   537  		return
   538  	}
   539  
   540  	// Pull the list out into a local variable, waiters will be readied
   541  	// outside the lock.
   542  	lock(&l.lock)
   543  	s := l.head
   544  	l.head = nil
   545  	l.tail = nil
   546  
   547  	// Update the next ticket to be notified. We can set it to the current
   548  	// value of wait because any previous waiters are already in the list
   549  	// or will notice that they have already been notified when trying to
   550  	// add themselves to the list.
   551  	atomic.Store(&l.notify, atomic.Load(&l.wait))
   552  	unlock(&l.lock)
   553  
   554  	// Go through the local list and ready all waiters.
   555  	for s != nil {
   556  		next := s.next
   557  		s.next = nil
   558  		readyWithTime(s, 4)
   559  		s = next
   560  	}
   561  }
   562  
   563  // notifyListNotifyOne notifies one entry in the list.
   564  //go:linkname notifyListNotifyOne sync.runtime_notifyListNotifyOne
   565  func notifyListNotifyOne(l *notifyList) {
   566  	// Fast-path: if there are no new waiters since the last notification
   567  	// we don't need to acquire the lock at all.
   568  	if atomic.Load(&l.wait) == atomic.Load(&l.notify) {
   569  		return
   570  	}
   571  
   572  	lock(&l.lock)
   573  
   574  	// Re-check under the lock if we need to do anything.
   575  	t := l.notify
   576  	if t == atomic.Load(&l.wait) {
   577  		unlock(&l.lock)
   578  		return
   579  	}
   580  
   581  	// Update the next notify ticket number.
   582  	atomic.Store(&l.notify, t+1)
   583  
   584  	// Try to find the g that needs to be notified.
   585  	// If it hasn't made it to the list yet we won't find it,
   586  	// but it won't park itself once it sees the new notify number.
   587  	//
   588  	// This scan looks linear but essentially always stops quickly.
   589  	// Because g's queue separately from taking numbers,
   590  	// there may be minor reorderings in the list, but we
   591  	// expect the g we're looking for to be near the front.
   592  	// The g has others in front of it on the list only to the
   593  	// extent that it lost the race, so the iteration will not
   594  	// be too long. This applies even when the g is missing:
   595  	// it hasn't yet gotten to sleep and has lost the race to
   596  	// the (few) other g's that we find on the list.
   597  	for p, s := (*sudog)(nil), l.head; s != nil; p, s = s, s.next {
   598  		if s.ticket == t {
   599  			n := s.next
   600  			if p != nil {
   601  				p.next = n
   602  			} else {
   603  				l.head = n
   604  			}
   605  			if n == nil {
   606  				l.tail = p
   607  			}
   608  			unlock(&l.lock)
   609  			s.next = nil
   610  			readyWithTime(s, 4)
   611  			return
   612  		}
   613  	}
   614  	unlock(&l.lock)
   615  }
   616  
   617  //go:linkname notifyListCheck sync.runtime_notifyListCheck
   618  func notifyListCheck(sz uintptr) {
   619  	if sz != unsafe.Sizeof(notifyList{}) {
   620  		print("runtime: bad notifyList size - sync=", sz, " runtime=", unsafe.Sizeof(notifyList{}), "\n")
   621  		throw("bad notifyList size")
   622  	}
   623  }
   624  
   625  //go:linkname sync_nanotime sync.runtime_nanotime
   626  func sync_nanotime() int64 {
   627  	return nanotime()
   628  }