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

     1  // Copyright 2017 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  package runtime
     6  
     7  import (
     8  	"runtime/internal/atomic"
     9  )
    10  
    11  // This is a copy of sync/rwmutex.go rewritten to work in the runtime.
    12  
    13  // A rwmutex is a reader/writer mutual exclusion lock.
    14  // The lock can be held by an arbitrary number of readers or a single writer.
    15  // This is a variant of sync.RWMutex, for the runtime package.
    16  // Like mutex, rwmutex blocks the calling M.
    17  // It does not interact with the goroutine scheduler.
    18  // 不与go scheduler交流, 阻塞m
    19  type rwmutex struct {
    20  	rLock      mutex    // protects readers, readerPass, writer
    21  	readers    muintptr // list of pending readers
    22  	readerPass uint32   // number of pending readers to skip readers list
    23  
    24  	wLock  mutex    // serializes writers
    25  	writer muintptr // pending writer waiting for completing readers
    26  
    27  	readerCount uint32 // number of pending readers
    28  	readerWait  uint32 // number of departing readers
    29  }
    30  
    31  const rwmutexMaxReaders = 1 << 30
    32  
    33  // rlock locks rw for reading.
    34  func (rw *rwmutex) rlock() {
    35  	// The reader must not be allowed to lose its P or else other
    36  	// things blocking on the lock may consume all of the Ps and
    37  	// deadlock (issue #20903). Alternatively, we could drop the P
    38  	// while sleeping.
    39  	acquirem()
    40  	if int32(atomic.Xadd(&rw.readerCount, 1)) < 0 {
    41  		// A writer is pending. Park on the reader queue.
    42  		systemstack(func() {
    43  			lock(&rw.rLock)
    44  			if rw.readerPass > 0 {
    45  				// Writer finished.
    46  				rw.readerPass -= 1
    47  				unlock(&rw.rLock)
    48  			} else {
    49  				// Queue this reader to be woken by
    50  				// the writer.
    51  				m := getg().m
    52  				m.schedlink = rw.readers
    53  				rw.readers.set(m)
    54  				unlock(&rw.rLock)
    55  				notesleep(&m.park)
    56  				noteclear(&m.park)
    57  			}
    58  		})
    59  	}
    60  }
    61  
    62  // runlock undoes a single rlock call on rw.
    63  func (rw *rwmutex) runlock() {
    64  	if r := int32(atomic.Xadd(&rw.readerCount, -1)); r < 0 {
    65  		if r+1 == 0 || r+1 == -rwmutexMaxReaders {
    66  			throw("runlock of unlocked rwmutex")
    67  		}
    68  		// A writer is pending.
    69  		if atomic.Xadd(&rw.readerWait, -1) == 0 {
    70  			// The last reader unblocks the writer.
    71  			lock(&rw.rLock)
    72  			w := rw.writer.ptr()
    73  			if w != nil {
    74  				notewakeup(&w.park)
    75  			}
    76  			unlock(&rw.rLock)
    77  		}
    78  	}
    79  	releasem(getg().m)
    80  }
    81  
    82  // lock locks rw for writing.
    83  func (rw *rwmutex) lock() {
    84  	// Resolve competition with other writers and stick to our P.
    85  	lock(&rw.wLock)
    86  	m := getg().m
    87  	// Announce that there is a pending writer.
    88  	r := int32(atomic.Xadd(&rw.readerCount, -rwmutexMaxReaders)) + rwmutexMaxReaders
    89  	// Wait for any active readers to complete.
    90  	lock(&rw.rLock)
    91  	if r != 0 && atomic.Xadd(&rw.readerWait, r) != 0 {
    92  		// Wait for reader to wake us up.
    93  		systemstack(func() {
    94  			rw.writer.set(m)
    95  			unlock(&rw.rLock)
    96  			notesleep(&m.park)
    97  			noteclear(&m.park)
    98  		})
    99  	} else {
   100  		unlock(&rw.rLock)
   101  	}
   102  }
   103  
   104  // unlock unlocks rw for writing.
   105  func (rw *rwmutex) unlock() {
   106  	// Announce to readers that there is no active writer.
   107  	r := int32(atomic.Xadd(&rw.readerCount, rwmutexMaxReaders))
   108  	if r >= rwmutexMaxReaders {
   109  		throw("unlock of unlocked rwmutex")
   110  	}
   111  	// Unblock blocked readers.
   112  	lock(&rw.rLock)
   113  	for rw.readers.ptr() != nil {
   114  		reader := rw.readers.ptr()
   115  		rw.readers = reader.schedlink
   116  		reader.schedlink.set(nil)
   117  		notewakeup(&reader.park)
   118  		r -= 1
   119  	}
   120  	// If r > 0, there are pending readers that aren't on the
   121  	// queue. Tell them to skip waiting.
   122  	rw.readerPass += uint32(r)
   123  	unlock(&rw.rLock)
   124  	// Allow other writers to proceed.
   125  	unlock(&rw.wLock)
   126  }