github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sync/mutex_unsafe.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file.
     5  
     6  // +build go1.13
     7  // +build !go1.18
     8  
     9  // When updating the build constraint (above), check that syncMutex matches the
    10  // standard library sync.Mutex definition.
    11  
    12  package sync
    13  
    14  import (
    15  	"sync"
    16  	"sync/atomic"
    17  	"unsafe"
    18  )
    19  
    20  // CrossGoroutineMutex is equivalent to Mutex, but it need not be unlocked by a
    21  // the same goroutine that locked the mutex.
    22  type CrossGoroutineMutex struct {
    23  	sync.Mutex
    24  }
    25  
    26  type syncMutex struct {
    27  	state int32
    28  	sema  uint32
    29  }
    30  
    31  func (m *CrossGoroutineMutex) state() *int32 {
    32  	return &(*syncMutex)(unsafe.Pointer(&m.Mutex)).state
    33  }
    34  
    35  // Lock locks the underlying Mutex.
    36  // +checklocksignore
    37  func (m *CrossGoroutineMutex) Lock() {
    38  	m.Mutex.Lock()
    39  }
    40  
    41  // Unlock unlocks the underlying Mutex.
    42  // +checklocksignore
    43  func (m *CrossGoroutineMutex) Unlock() {
    44  	m.Mutex.Unlock()
    45  }
    46  
    47  const (
    48  	mutexUnlocked = 0
    49  	mutexLocked   = 1
    50  )
    51  
    52  // TryLock tries to acquire the mutex. It returns true if it succeeds and false
    53  // otherwise. TryLock does not block.
    54  func (m *CrossGoroutineMutex) TryLock() bool {
    55  	if atomic.CompareAndSwapInt32(m.state(), mutexUnlocked, mutexLocked) {
    56  		if RaceEnabled {
    57  			RaceAcquire(unsafe.Pointer(&m.Mutex))
    58  		}
    59  		return true
    60  	}
    61  	return false
    62  }
    63  
    64  // Mutex is a mutual exclusion lock. The zero value for a Mutex is an unlocked
    65  // mutex.
    66  //
    67  // A Mutex must not be copied after first use.
    68  //
    69  // A Mutex must be unlocked by the same goroutine that locked it. This
    70  // invariant is enforced with the 'checklocks' build tag.
    71  type Mutex struct {
    72  	m CrossGoroutineMutex
    73  }
    74  
    75  // Lock locks m. If the lock is already in use, the calling goroutine blocks
    76  // until the mutex is available.
    77  // +checklocksignore
    78  func (m *Mutex) Lock() {
    79  	noteLock(unsafe.Pointer(m))
    80  	m.m.Lock()
    81  }
    82  
    83  // Unlock unlocks m.
    84  //
    85  // Preconditions:
    86  // * m is locked.
    87  // * m was locked by this goroutine.
    88  // +checklocksignore
    89  func (m *Mutex) Unlock() {
    90  	noteUnlock(unsafe.Pointer(m))
    91  	m.m.Unlock()
    92  }
    93  
    94  // TryLock tries to acquire the mutex. It returns true if it succeeds and false
    95  // otherwise. TryLock does not block.
    96  // +checklocksignore
    97  func (m *Mutex) TryLock() bool {
    98  	// Note lock first to enforce proper locking even if unsuccessful.
    99  	noteLock(unsafe.Pointer(m))
   100  	locked := m.m.TryLock()
   101  	if !locked {
   102  		noteUnlock(unsafe.Pointer(m))
   103  	}
   104  	return locked
   105  }