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