Copyright 2013 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

package poll

import 
fdMutex is a specialized synchronization primitive that manages lifetime of an fd and serializes access to Read, Write and Close methods on FD.
fdMutex.state is organized as follows: 1 bit - whether FD is closed, if set all subsequent lock operations will fail. 1 bit - lock for read operations. 1 bit - lock for write operations. 20 bits - total number of references (read+write+misc). 20 bits - number of outstanding read waiters. 20 bits - number of outstanding write waiters.
const (
	mutexClosed  = 1 << 0
	mutexRLock   = 1 << 1
	mutexWLock   = 1 << 2
	mutexRef     = 1 << 3
	mutexRefMask = (1<<20 - 1) << 3
	mutexRWait   = 1 << 23
	mutexRMask   = (1<<20 - 1) << 23
	mutexWWait   = 1 << 43
	mutexWMask   = (1<<20 - 1) << 43
)

const overflowMsg = "too many concurrent operations on a single file or socket (max 1048575)"
Read operations must do rwlock(true)/rwunlock(true). Write operations must do rwlock(false)/rwunlock(false). Misc operations must do incref/decref. Misc operations include functions like setsockopt and setDeadline. They need to use incref/decref to ensure that they operate on the correct fd in presence of a concurrent close call (otherwise fd can be closed under their feet). Close operations must do increfAndClose/decref.
incref adds a reference to mu. It reports whether mu is available for reading or writing.
func ( *fdMutex) () bool {
	for {
		 := atomic.LoadUint64(&.state)
		if &mutexClosed != 0 {
			return false
		}
		 :=  + mutexRef
		if &mutexRefMask == 0 {
			panic(overflowMsg)
		}
		if atomic.CompareAndSwapUint64(&.state, , ) {
			return true
		}
	}
}
increfAndClose sets the state of mu to closed. It returns false if the file was already closed.
func ( *fdMutex) () bool {
	for {
		 := atomic.LoadUint64(&.state)
		if &mutexClosed != 0 {
			return false
Mark as closed and acquire a reference.
		 := ( | mutexClosed) + mutexRef
		if &mutexRefMask == 0 {
			panic(overflowMsg)
Remove all read and write waiters.
Wake all read and write waiters, they will observe closed flag after wakeup.
			for &mutexRMask != 0 {
				 -= mutexRWait
				runtime_Semrelease(&.rsema)
			}
			for &mutexWMask != 0 {
				 -= mutexWWait
				runtime_Semrelease(&.wsema)
			}
			return true
		}
	}
}
decref removes a reference from mu. It reports whether there is no remaining reference.
func ( *fdMutex) () bool {
	for {
		 := atomic.LoadUint64(&.state)
		if &mutexRefMask == 0 {
			panic("inconsistent poll.fdMutex")
		}
		 :=  - mutexRef
		if atomic.CompareAndSwapUint64(&.state, , ) {
			return &(mutexClosed|mutexRefMask) == mutexClosed
		}
	}
}
lock adds a reference to mu and locks mu. It reports whether mu is available for reading or writing.
func ( *fdMutex) ( bool) bool {
	var , ,  uint64
	var  *uint32
	if  {
		 = mutexRLock
		 = mutexRWait
		 = mutexRMask
		 = &.rsema
	} else {
		 = mutexWLock
		 = mutexWWait
		 = mutexWMask
		 = &.wsema
	}
	for {
		 := atomic.LoadUint64(&.state)
		if &mutexClosed != 0 {
			return false
		}
		var  uint64
Lock is free, acquire it.
			 = ( | ) + mutexRef
			if &mutexRefMask == 0 {
				panic(overflowMsg)
			}
Wait for lock.
			 =  + 
			if & == 0 {
				panic(overflowMsg)
			}
		}
		if atomic.CompareAndSwapUint64(&.state, , ) {
			if & == 0 {
				return true
			}
The signaller has subtracted mutexWait.
		}
	}
}
unlock removes a reference from mu and unlocks mu. It reports whether there is no remaining reference.
func ( *fdMutex) ( bool) bool {
	var , ,  uint64
	var  *uint32
	if  {
		 = mutexRLock
		 = mutexRWait
		 = mutexRMask
		 = &.rsema
	} else {
		 = mutexWLock
		 = mutexWWait
		 = mutexWMask
		 = &.wsema
	}
	for {
		 := atomic.LoadUint64(&.state)
		if & == 0 || &mutexRefMask == 0 {
			panic("inconsistent poll.fdMutex")
Drop lock, drop reference and wake read waiter if present.
		 := ( &^ ) - mutexRef
		if & != 0 {
			 -= 
		}
		if atomic.CompareAndSwapUint64(&.state, , ) {
			if & != 0 {
				runtime_Semrelease()
			}
			return &(mutexClosed|mutexRefMask) == mutexClosed
		}
	}
}
Implemented in runtime package.
incref adds a reference to fd. It returns an error when fd cannot be used.
func ( *FD) () error {
	if !.fdmu.incref() {
		return errClosing(.isFile)
	}
	return nil
}
decref removes a reference from fd. It also closes fd when the state of fd is set to closed and there is no remaining reference.
func ( *FD) () error {
	if .fdmu.decref() {
		return .destroy()
	}
	return nil
}
readLock adds a reference to fd and locks fd for reading. It returns an error when fd cannot be used for reading.
func ( *FD) () error {
	if !.fdmu.rwlock(true) {
		return errClosing(.isFile)
	}
	return nil
}
readUnlock removes a reference from fd and unlocks fd for reading. It also closes fd when the state of fd is set to closed and there is no remaining reference.
func ( *FD) () {
	if .fdmu.rwunlock(true) {
		.destroy()
	}
}
writeLock adds a reference to fd and locks fd for writing. It returns an error when fd cannot be used for writing.
func ( *FD) () error {
	if !.fdmu.rwlock(false) {
		return errClosing(.isFile)
	}
	return nil
}
writeUnlock removes a reference from fd and unlocks fd for writing. It also closes fd when the state of fd is set to closed and there is no remaining reference.
func ( *FD) () {
	if .fdmu.rwunlock(false) {
		.destroy()
	}