github.com/dannin/go@v0.0.0-20161031215817-d35dfd405eaa/src/net/fd_mutex.go (about)

     1  // Copyright 2013 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 net
     6  
     7  import "sync/atomic"
     8  
     9  // fdMutex is a specialized synchronization primitive that manages
    10  // lifetime of an fd and serializes access to Read, Write and Close
    11  // methods on netFD.
    12  type fdMutex struct {
    13  	state uint64
    14  	rsema uint32
    15  	wsema uint32
    16  }
    17  
    18  // fdMutex.state is organized as follows:
    19  // 1 bit - whether netFD is closed, if set all subsequent lock operations will fail.
    20  // 1 bit - lock for read operations.
    21  // 1 bit - lock for write operations.
    22  // 20 bits - total number of references (read+write+misc).
    23  // 20 bits - number of outstanding read waiters.
    24  // 20 bits - number of outstanding write waiters.
    25  const (
    26  	mutexClosed  = 1 << 0
    27  	mutexRLock   = 1 << 1
    28  	mutexWLock   = 1 << 2
    29  	mutexRef     = 1 << 3
    30  	mutexRefMask = (1<<20 - 1) << 3
    31  	mutexRWait   = 1 << 23
    32  	mutexRMask   = (1<<20 - 1) << 23
    33  	mutexWWait   = 1 << 43
    34  	mutexWMask   = (1<<20 - 1) << 43
    35  )
    36  
    37  // Read operations must do rwlock(true)/rwunlock(true).
    38  //
    39  // Write operations must do rwlock(false)/rwunlock(false).
    40  //
    41  // Misc operations must do incref/decref.
    42  // Misc operations include functions like setsockopt and setDeadline.
    43  // They need to use incref/decref to ensure that they operate on the
    44  // correct fd in presence of a concurrent close call (otherwise fd can
    45  // be closed under their feet).
    46  //
    47  // Close operations must do increfAndClose/decref.
    48  
    49  // incref adds a reference to mu.
    50  // It reports whether mu is available for reading or writing.
    51  func (mu *fdMutex) incref() bool {
    52  	for {
    53  		old := atomic.LoadUint64(&mu.state)
    54  		if old&mutexClosed != 0 {
    55  			return false
    56  		}
    57  		new := old + mutexRef
    58  		if new&mutexRefMask == 0 {
    59  			panic("net: inconsistent fdMutex")
    60  		}
    61  		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
    62  			return true
    63  		}
    64  	}
    65  }
    66  
    67  // increfAndClose sets the state of mu to closed.
    68  // It reports whether there is no remaining reference.
    69  func (mu *fdMutex) increfAndClose() bool {
    70  	for {
    71  		old := atomic.LoadUint64(&mu.state)
    72  		if old&mutexClosed != 0 {
    73  			return false
    74  		}
    75  		// Mark as closed and acquire a reference.
    76  		new := (old | mutexClosed) + mutexRef
    77  		if new&mutexRefMask == 0 {
    78  			panic("net: inconsistent fdMutex")
    79  		}
    80  		// Remove all read and write waiters.
    81  		new &^= mutexRMask | mutexWMask
    82  		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
    83  			// Wake all read and write waiters,
    84  			// they will observe closed flag after wakeup.
    85  			for old&mutexRMask != 0 {
    86  				old -= mutexRWait
    87  				runtime_Semrelease(&mu.rsema)
    88  			}
    89  			for old&mutexWMask != 0 {
    90  				old -= mutexWWait
    91  				runtime_Semrelease(&mu.wsema)
    92  			}
    93  			return true
    94  		}
    95  	}
    96  }
    97  
    98  // decref removes a reference from mu.
    99  // It reports whether there is no remaining reference.
   100  func (mu *fdMutex) decref() bool {
   101  	for {
   102  		old := atomic.LoadUint64(&mu.state)
   103  		if old&mutexRefMask == 0 {
   104  			panic("net: inconsistent fdMutex")
   105  		}
   106  		new := old - mutexRef
   107  		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
   108  			return new&(mutexClosed|mutexRefMask) == mutexClosed
   109  		}
   110  	}
   111  }
   112  
   113  // lock adds a reference to mu and locks mu.
   114  // It reports whether mu is available for reading or writing.
   115  func (mu *fdMutex) rwlock(read bool) bool {
   116  	var mutexBit, mutexWait, mutexMask uint64
   117  	var mutexSema *uint32
   118  	if read {
   119  		mutexBit = mutexRLock
   120  		mutexWait = mutexRWait
   121  		mutexMask = mutexRMask
   122  		mutexSema = &mu.rsema
   123  	} else {
   124  		mutexBit = mutexWLock
   125  		mutexWait = mutexWWait
   126  		mutexMask = mutexWMask
   127  		mutexSema = &mu.wsema
   128  	}
   129  	for {
   130  		old := atomic.LoadUint64(&mu.state)
   131  		if old&mutexClosed != 0 {
   132  			return false
   133  		}
   134  		var new uint64
   135  		if old&mutexBit == 0 {
   136  			// Lock is free, acquire it.
   137  			new = (old | mutexBit) + mutexRef
   138  			if new&mutexRefMask == 0 {
   139  				panic("net: inconsistent fdMutex")
   140  			}
   141  		} else {
   142  			// Wait for lock.
   143  			new = old + mutexWait
   144  			if new&mutexMask == 0 {
   145  				panic("net: inconsistent fdMutex")
   146  			}
   147  		}
   148  		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
   149  			if old&mutexBit == 0 {
   150  				return true
   151  			}
   152  			runtime_Semacquire(mutexSema)
   153  			// The signaller has subtracted mutexWait.
   154  		}
   155  	}
   156  }
   157  
   158  // unlock removes a reference from mu and unlocks mu.
   159  // It reports whether there is no remaining reference.
   160  func (mu *fdMutex) rwunlock(read bool) bool {
   161  	var mutexBit, mutexWait, mutexMask uint64
   162  	var mutexSema *uint32
   163  	if read {
   164  		mutexBit = mutexRLock
   165  		mutexWait = mutexRWait
   166  		mutexMask = mutexRMask
   167  		mutexSema = &mu.rsema
   168  	} else {
   169  		mutexBit = mutexWLock
   170  		mutexWait = mutexWWait
   171  		mutexMask = mutexWMask
   172  		mutexSema = &mu.wsema
   173  	}
   174  	for {
   175  		old := atomic.LoadUint64(&mu.state)
   176  		if old&mutexBit == 0 || old&mutexRefMask == 0 {
   177  			panic("net: inconsistent fdMutex")
   178  		}
   179  		// Drop lock, drop reference and wake read waiter if present.
   180  		new := (old &^ mutexBit) - mutexRef
   181  		if old&mutexMask != 0 {
   182  			new -= mutexWait
   183  		}
   184  		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
   185  			if old&mutexMask != 0 {
   186  				runtime_Semrelease(mutexSema)
   187  			}
   188  			return new&(mutexClosed|mutexRefMask) == mutexClosed
   189  		}
   190  	}
   191  }
   192  
   193  // Implemented in runtime package.
   194  func runtime_Semacquire(sema *uint32)
   195  func runtime_Semrelease(sema *uint32)
   196  
   197  // incref adds a reference to fd.
   198  // It returns an error when fd cannot be used.
   199  func (fd *netFD) incref() error {
   200  	if !fd.fdmu.incref() {
   201  		return errClosing
   202  	}
   203  	return nil
   204  }
   205  
   206  // decref removes a reference from fd.
   207  // It also closes fd when the state of fd is set to closed and there
   208  // is no remaining reference.
   209  func (fd *netFD) decref() {
   210  	if fd.fdmu.decref() {
   211  		fd.destroy()
   212  	}
   213  }
   214  
   215  // readLock adds a reference to fd and locks fd for reading.
   216  // It returns an error when fd cannot be used for reading.
   217  func (fd *netFD) readLock() error {
   218  	if !fd.fdmu.rwlock(true) {
   219  		return errClosing
   220  	}
   221  	return nil
   222  }
   223  
   224  // readUnlock removes a reference from fd and unlocks fd for reading.
   225  // It also closes fd when the state of fd is set to closed and there
   226  // is no remaining reference.
   227  func (fd *netFD) readUnlock() {
   228  	if fd.fdmu.rwunlock(true) {
   229  		fd.destroy()
   230  	}
   231  }
   232  
   233  // writeLock adds a reference to fd and locks fd for writing.
   234  // It returns an error when fd cannot be used for writing.
   235  func (fd *netFD) writeLock() error {
   236  	if !fd.fdmu.rwlock(false) {
   237  		return errClosing
   238  	}
   239  	return nil
   240  }
   241  
   242  // writeUnlock removes a reference from fd and unlocks fd for writing.
   243  // It also closes fd when the state of fd is set to closed and there
   244  // is no remaining reference.
   245  func (fd *netFD) writeUnlock() {
   246  	if fd.fdmu.rwunlock(false) {
   247  		fd.destroy()
   248  	}
   249  }