github.com/zhongdalu/gf@v1.0.0/g/os/gmutex/gmutex.go (about)

     1  // Copyright 2019 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/zhongdalu/gf.
     6  
     7  // Package gmutex implements graceful concurrent-safe mutex with more rich features.
     8  package gmutex
     9  
    10  import (
    11  	"math"
    12  	"runtime"
    13  
    14  	"github.com/zhongdalu/gf/g/container/gtype"
    15  )
    16  
    17  // The high level Mutex, which implements more rich features for mutex.
    18  type Mutex struct {
    19  	state   *gtype.Int32  // Indicates the state of mutex.
    20  	writer  *gtype.Int32  // Pending writer count.
    21  	reader  *gtype.Int32  // Pending reader count.
    22  	writing chan struct{} // Channel for writer blocking.
    23  	reading chan struct{} // Channel for reader blocking.
    24  }
    25  
    26  // New creates and returns a new mutex.
    27  func New() *Mutex {
    28  	return &Mutex{
    29  		state:   gtype.NewInt32(),
    30  		writer:  gtype.NewInt32(),
    31  		reader:  gtype.NewInt32(),
    32  		writing: make(chan struct{}, 1),
    33  		reading: make(chan struct{}, math.MaxInt32),
    34  	}
    35  }
    36  
    37  // Lock locks the mutex for writing purpose.
    38  // If the mutex is already locked by another goroutine for reading or writing,
    39  // it blocks until the lock is available.
    40  func (m *Mutex) Lock() {
    41  	for {
    42  		// Using CAS operation to get the writing lock atomically.
    43  		if m.state.Cas(0, -1) {
    44  			return
    45  		}
    46  		// It or else blocks to wait for the next chance.
    47  		m.writer.Add(1)
    48  		<-m.writing
    49  	}
    50  }
    51  
    52  // Unlock unlocks writing lock on the mutex.
    53  // It is safe to be called multiple times if there's any locks or not.
    54  func (m *Mutex) Unlock() {
    55  	if m.state.Cas(-1, 0) {
    56  		// Note that there might be more than one goroutines can enter this block.
    57  		var n int32
    58  		// Writing lock unlocks, then first check the blocked readers.
    59  		// If there're readers blocked, it unlocks them with preemption.
    60  		for {
    61  			if n = m.reader.Val(); n > 0 {
    62  				if m.reader.Cas(n, 0) {
    63  					for ; n > 0; n-- {
    64  						m.reading <- struct{}{}
    65  					}
    66  					break
    67  				} else {
    68  					runtime.Gosched()
    69  				}
    70  			} else {
    71  				break
    72  			}
    73  		}
    74  
    75  		// It then also kindly feeds the pending writers with one chance.
    76  		if n = m.writer.Val(); n > 0 {
    77  			if m.writer.Cas(n, n-1) {
    78  				m.writing <- struct{}{}
    79  			}
    80  		}
    81  	}
    82  }
    83  
    84  // TryLock tries locking the mutex for writing purpose.
    85  // It returns true immediately if success, or if there's a write/reading lock on the mutex,
    86  // it returns false immediately.
    87  func (m *Mutex) TryLock() bool {
    88  	if m.state.Cas(0, -1) {
    89  		return true
    90  	}
    91  	return false
    92  }
    93  
    94  // RLock locks mutex for reading purpose purpose.
    95  // If the mutex is already locked for writing,
    96  // it blocks until the lock is available.
    97  func (m *Mutex) RLock() {
    98  	var n int32
    99  	for {
   100  		if n = m.state.Val(); n >= 0 {
   101  			// If there's no writing lock currently, then do the reading lock checks.
   102  			if m.state.Cas(n, n+1) {
   103  				return
   104  			} else {
   105  				runtime.Gosched()
   106  			}
   107  		} else {
   108  			// It or else pends the reader.
   109  			m.reader.Add(1)
   110  			<-m.reading
   111  		}
   112  	}
   113  }
   114  
   115  // RUnlock unlocks the reading lock on the mutex.
   116  // It is safe to be called multiple times if there's any locks or not.
   117  func (m *Mutex) RUnlock() {
   118  	var n int32
   119  	for {
   120  		if n = m.state.Val(); n >= 1 {
   121  			if m.state.Cas(n, n-1) {
   122  				break
   123  			} else {
   124  				runtime.Gosched()
   125  			}
   126  		} else {
   127  			break
   128  		}
   129  	}
   130  	// Reading lock unlocks, it then only check the blocked writers.
   131  	// Note that it is not necessary to check the pending readers here.
   132  	// <n == 1> means the state of mutex comes down to zero.
   133  	if n == 1 {
   134  		if n = m.writer.Val(); n > 0 {
   135  			if m.writer.Cas(n, n-1) {
   136  				m.writing <- struct{}{}
   137  			}
   138  		}
   139  	}
   140  }
   141  
   142  // TryRLock tries locking the mutex for reading purpose.
   143  // It returns true immediately if success, or if there's a writing lock on the mutex,
   144  // it returns false immediately.
   145  func (m *Mutex) TryRLock() bool {
   146  	var n int32
   147  	for {
   148  		if n = m.state.Val(); n >= 0 {
   149  			if m.state.Cas(n, n+1) {
   150  				return true
   151  			} else {
   152  				runtime.Gosched()
   153  			}
   154  		} else {
   155  			return false
   156  		}
   157  	}
   158  }
   159  
   160  // IsLocked checks whether the mutex is locked with writing or reading lock.
   161  // Note that the result might be changed after it's called,
   162  // so it cannot be the criterion for atomic operations.
   163  func (m *Mutex) IsLocked() bool {
   164  	return m.state.Val() != 0
   165  }
   166  
   167  // IsWLocked checks whether the mutex is locked by writing lock.
   168  // Note that the result might be changed after it's called,
   169  // so it cannot be the criterion for atomic operations.
   170  func (m *Mutex) IsWLocked() bool {
   171  	return m.state.Val() < 0
   172  }
   173  
   174  // IsRLocked checks whether the mutex is locked by reading lock.
   175  // Note that the result might be changed after it's called,
   176  // so it cannot be the criterion for atomic operations.
   177  func (m *Mutex) IsRLocked() bool {
   178  	return m.state.Val() > 0
   179  }
   180  
   181  // LockFunc locks the mutex for writing with given callback function <f>.
   182  // If there's a write/reading lock the mutex, it will blocks until the lock is released.
   183  //
   184  // It releases the lock after <f> is executed.
   185  func (m *Mutex) LockFunc(f func()) {
   186  	m.Lock()
   187  	defer m.Unlock()
   188  	f()
   189  }
   190  
   191  // RLockFunc locks the mutex for reading with given callback function <f>.
   192  // If there's a writing lock the mutex, it will blocks until the lock is released.
   193  //
   194  // It releases the lock after <f> is executed.
   195  func (m *Mutex) RLockFunc(f func()) {
   196  	m.RLock()
   197  	defer m.RUnlock()
   198  	f()
   199  }
   200  
   201  // TryLockFunc tries locking the mutex for writing with given callback function <f>.
   202  // it returns true immediately if success, or if there's a write/reading lock on the mutex,
   203  // it returns false immediately.
   204  //
   205  // It releases the lock after <f> is executed.
   206  func (m *Mutex) TryLockFunc(f func()) (result bool) {
   207  	if m.TryLock() {
   208  		result = true
   209  		defer m.Unlock()
   210  		f()
   211  	}
   212  	return
   213  }
   214  
   215  // TryRLockFunc tries locking the mutex for reading with given callback function <f>.
   216  // It returns true immediately if success, or if there's a writing lock on the mutex,
   217  // it returns false immediately.
   218  //
   219  // It releases the lock after <f> is executed.
   220  func (m *Mutex) TryRLockFunc(f func()) (result bool) {
   221  	if m.TryRLock() {
   222  		result = true
   223  		defer m.RUnlock()
   224  		f()
   225  	}
   226  	return
   227  }