github.com/hbdrawn/golang@v0.0.0-20141214014649-6b835209aba2/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
    10  // that manages lifetime of an fd and serializes access
    11  // to Read and Write 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  // Write operations must do RWLock(false)/RWUnlock(false).
    39  // Misc operations must do Incref/Decref. Misc operations include functions like
    40  // setsockopt and setDeadline. They need to use Incref/Decref to ensure that
    41  // they operate on the correct fd in presence of a concurrent Close call
    42  // (otherwise fd can be closed under their feet).
    43  // Close operation must do IncrefAndClose/Decref.
    44  
    45  // RWLock/Incref return whether fd is open.
    46  // RWUnlock/Decref return whether fd is closed and there are no remaining references.
    47  
    48  func (mu *fdMutex) Incref() bool {
    49  	for {
    50  		old := atomic.LoadUint64(&mu.state)
    51  		if old&mutexClosed != 0 {
    52  			return false
    53  		}
    54  		new := old + mutexRef
    55  		if new&mutexRefMask == 0 {
    56  			panic("net: inconsistent fdMutex")
    57  		}
    58  		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
    59  			return true
    60  		}
    61  	}
    62  }
    63  
    64  func (mu *fdMutex) IncrefAndClose() bool {
    65  	for {
    66  		old := atomic.LoadUint64(&mu.state)
    67  		if old&mutexClosed != 0 {
    68  			return false
    69  		}
    70  		// Mark as closed and acquire a reference.
    71  		new := (old | mutexClosed) + mutexRef
    72  		if new&mutexRefMask == 0 {
    73  			panic("net: inconsistent fdMutex")
    74  		}
    75  		// Remove all read and write waiters.
    76  		new &^= mutexRMask | mutexWMask
    77  		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
    78  			// Wake all read and write waiters,
    79  			// they will observe closed flag after wakeup.
    80  			for old&mutexRMask != 0 {
    81  				old -= mutexRWait
    82  				runtime_Semrelease(&mu.rsema)
    83  			}
    84  			for old&mutexWMask != 0 {
    85  				old -= mutexWWait
    86  				runtime_Semrelease(&mu.wsema)
    87  			}
    88  			return true
    89  		}
    90  	}
    91  }
    92  
    93  func (mu *fdMutex) Decref() bool {
    94  	for {
    95  		old := atomic.LoadUint64(&mu.state)
    96  		if old&mutexRefMask == 0 {
    97  			panic("net: inconsistent fdMutex")
    98  		}
    99  		new := old - mutexRef
   100  		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
   101  			return new&(mutexClosed|mutexRefMask) == mutexClosed
   102  		}
   103  	}
   104  }
   105  
   106  func (mu *fdMutex) RWLock(read bool) bool {
   107  	var mutexBit, mutexWait, mutexMask uint64
   108  	var mutexSema *uint32
   109  	if read {
   110  		mutexBit = mutexRLock
   111  		mutexWait = mutexRWait
   112  		mutexMask = mutexRMask
   113  		mutexSema = &mu.rsema
   114  	} else {
   115  		mutexBit = mutexWLock
   116  		mutexWait = mutexWWait
   117  		mutexMask = mutexWMask
   118  		mutexSema = &mu.wsema
   119  	}
   120  	for {
   121  		old := atomic.LoadUint64(&mu.state)
   122  		if old&mutexClosed != 0 {
   123  			return false
   124  		}
   125  		var new uint64
   126  		if old&mutexBit == 0 {
   127  			// Lock is free, acquire it.
   128  			new = (old | mutexBit) + mutexRef
   129  			if new&mutexRefMask == 0 {
   130  				panic("net: inconsistent fdMutex")
   131  			}
   132  		} else {
   133  			// Wait for lock.
   134  			new = old + mutexWait
   135  			if new&mutexMask == 0 {
   136  				panic("net: inconsistent fdMutex")
   137  			}
   138  		}
   139  		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
   140  			if old&mutexBit == 0 {
   141  				return true
   142  			}
   143  			runtime_Semacquire(mutexSema)
   144  			// The signaller has subtracted mutexWait.
   145  		}
   146  	}
   147  }
   148  
   149  func (mu *fdMutex) RWUnlock(read bool) bool {
   150  	var mutexBit, mutexWait, mutexMask uint64
   151  	var mutexSema *uint32
   152  	if read {
   153  		mutexBit = mutexRLock
   154  		mutexWait = mutexRWait
   155  		mutexMask = mutexRMask
   156  		mutexSema = &mu.rsema
   157  	} else {
   158  		mutexBit = mutexWLock
   159  		mutexWait = mutexWWait
   160  		mutexMask = mutexWMask
   161  		mutexSema = &mu.wsema
   162  	}
   163  	for {
   164  		old := atomic.LoadUint64(&mu.state)
   165  		if old&mutexBit == 0 || old&mutexRefMask == 0 {
   166  			panic("net: inconsistent fdMutex")
   167  		}
   168  		// Drop lock, drop reference and wake read waiter if present.
   169  		new := (old &^ mutexBit) - mutexRef
   170  		if old&mutexMask != 0 {
   171  			new -= mutexWait
   172  		}
   173  		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
   174  			if old&mutexMask != 0 {
   175  				runtime_Semrelease(mutexSema)
   176  			}
   177  			return new&(mutexClosed|mutexRefMask) == mutexClosed
   178  		}
   179  	}
   180  }
   181  
   182  // Implemented in runtime package.
   183  func runtime_Semacquire(sema *uint32)
   184  func runtime_Semrelease(sema *uint32)