github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/sync/mutex.go (about)

     1  package sync
     2  
     3  import (
     4  	"internal/task"
     5  	_ "unsafe"
     6  
     7  	"runtime/volatile"
     8  )
     9  
    10  type Mutex struct {
    11  	state   uint8 // Set to non-zero if locked.
    12  	blocked task.Stack
    13  }
    14  
    15  //go:linkname scheduleTask runtime.runqueuePushBack
    16  func scheduleTask(*task.Task)
    17  
    18  func (m *Mutex) Lock() {
    19  	if m.islocked() {
    20  		// Push self onto stack of blocked tasks, and wait to be resumed.
    21  		m.blocked.Push(task.Current())
    22  		task.Pause()
    23  		return
    24  	}
    25  
    26  	m.setlock(true)
    27  }
    28  
    29  func (m *Mutex) Unlock() {
    30  	if !m.islocked() {
    31  		panic("sync: unlock of unlocked Mutex")
    32  	}
    33  
    34  	// Wake up a blocked task, if applicable.
    35  	if t := m.blocked.Pop(); t != nil {
    36  		scheduleTask(t)
    37  	} else {
    38  		m.setlock(false)
    39  	}
    40  }
    41  
    42  // TryLock tries to lock m and reports whether it succeeded.
    43  //
    44  // Note that while correct uses of TryLock do exist, they are rare,
    45  // and use of TryLock is often a sign of a deeper problem
    46  // in a particular use of mutexes.
    47  func (m *Mutex) TryLock() bool {
    48  	if m.islocked() {
    49  		return false
    50  	}
    51  	m.Lock()
    52  	return true
    53  }
    54  
    55  func (m *Mutex) islocked() bool {
    56  	return volatile.LoadUint8(&m.state) != 0
    57  }
    58  
    59  func (m *Mutex) setlock(b bool) {
    60  	volatile.StoreUint8(&m.state, boolToU8(b))
    61  }
    62  
    63  func boolToU8(b bool) uint8 {
    64  	if b {
    65  		return 1
    66  	}
    67  	return 0
    68  }
    69  
    70  type RWMutex struct {
    71  	// waitingWriters are all of the tasks waiting for write locks.
    72  	waitingWriters task.Stack
    73  
    74  	// waitingReaders are all of the tasks waiting for a read lock.
    75  	waitingReaders task.Stack
    76  
    77  	// state is the current state of the RWMutex.
    78  	// Iff the mutex is completely unlocked, it contains rwMutexStateUnlocked (aka 0).
    79  	// Iff the mutex is write-locked, it contains rwMutexStateWLocked.
    80  	// While the mutex is read-locked, it contains the current number of readers.
    81  	state uint32
    82  }
    83  
    84  const (
    85  	rwMutexStateUnlocked = uint32(0)
    86  	rwMutexStateWLocked  = ^uint32(0)
    87  	rwMutexMaxReaders    = rwMutexStateWLocked - 1
    88  )
    89  
    90  func (rw *RWMutex) Lock() {
    91  	if rw.state == 0 {
    92  		// The mutex is completely unlocked.
    93  		// Lock without waiting.
    94  		rw.state = rwMutexStateWLocked
    95  		return
    96  	}
    97  
    98  	// Wait for the lock to be released.
    99  	rw.waitingWriters.Push(task.Current())
   100  	task.Pause()
   101  }
   102  
   103  func (rw *RWMutex) Unlock() {
   104  	switch rw.state {
   105  	case rwMutexStateWLocked:
   106  		// This is correct.
   107  
   108  	case rwMutexStateUnlocked:
   109  		// The mutex is already unlocked.
   110  		panic("sync: unlock of unlocked RWMutex")
   111  
   112  	default:
   113  		// The mutex is read-locked instead of write-locked.
   114  		panic("sync: write-unlock of read-locked RWMutex")
   115  	}
   116  
   117  	switch {
   118  	case rw.maybeUnblockReaders():
   119  		// Switched over to read mode.
   120  
   121  	case rw.maybeUnblockWriter():
   122  		// Transferred to another writer.
   123  
   124  	default:
   125  		// Nothing is waiting for the lock.
   126  		rw.state = rwMutexStateUnlocked
   127  	}
   128  }
   129  
   130  func (rw *RWMutex) RLock() {
   131  	if rw.state == rwMutexStateWLocked {
   132  		// Wait for the write lock to be released.
   133  		rw.waitingReaders.Push(task.Current())
   134  		task.Pause()
   135  		return
   136  	}
   137  
   138  	if rw.state == rwMutexMaxReaders {
   139  		panic("sync: too many readers on RWMutex")
   140  	}
   141  
   142  	// Increase the reader count.
   143  	rw.state++
   144  }
   145  
   146  func (rw *RWMutex) RUnlock() {
   147  	switch rw.state {
   148  	case rwMutexStateUnlocked:
   149  		// The mutex is already unlocked.
   150  		panic("sync: unlock of unlocked RWMutex")
   151  
   152  	case rwMutexStateWLocked:
   153  		// The mutex is write-locked instead of read-locked.
   154  		panic("sync: read-unlock of write-locked RWMutex")
   155  	}
   156  
   157  	rw.state--
   158  
   159  	if rw.state == rwMutexStateUnlocked {
   160  		// This was the last reader.
   161  		// Try to unblock a writer.
   162  		rw.maybeUnblockWriter()
   163  	}
   164  }
   165  
   166  func (rw *RWMutex) maybeUnblockReaders() bool {
   167  	var n uint32
   168  	for {
   169  		t := rw.waitingReaders.Pop()
   170  		if t == nil {
   171  			break
   172  		}
   173  
   174  		n++
   175  		scheduleTask(t)
   176  	}
   177  	if n == 0 {
   178  		return false
   179  	}
   180  
   181  	rw.state = n
   182  	return true
   183  }
   184  
   185  func (rw *RWMutex) maybeUnblockWriter() bool {
   186  	t := rw.waitingWriters.Pop()
   187  	if t == nil {
   188  		return false
   189  	}
   190  
   191  	rw.state = rwMutexStateWLocked
   192  	scheduleTask(t)
   193  
   194  	return true
   195  }
   196  
   197  type Locker interface {
   198  	Lock()
   199  	Unlock()
   200  }
   201  
   202  // RLocker returns a Locker interface that implements
   203  // the Lock and Unlock methods by calling rw.RLock and rw.RUnlock.
   204  func (rw *RWMutex) RLocker() Locker {
   205  	return (*rlocker)(rw)
   206  }
   207  
   208  type rlocker RWMutex
   209  
   210  func (r *rlocker) Lock()   { (*RWMutex)(r).RLock() }
   211  func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }