package poll
import (
"internal/itoa"
"internal/syscall/unix"
"io"
"sync/atomic"
"syscall"
)
type FD struct {
fdmu fdMutex
Sysfd int
SysFile
pd pollDesc
csema uint32
isBlocking uint32
IsStream bool
ZeroReadIsEOF bool
isFile bool
}
func (fd *FD ) Init (net string , pollable bool ) error {
fd .SysFile .init ()
if net == "file" {
fd .isFile = true
}
if !pollable {
fd .isBlocking = 1
return nil
}
err := fd .pd .init (fd )
if err != nil {
fd .isBlocking = 1
}
return err
}
func (fd *FD ) destroy () error {
fd .pd .close ()
err := fd .SysFile .destroy (fd .Sysfd )
fd .Sysfd = -1
runtime_Semrelease (&fd .csema )
return err
}
func (fd *FD ) Close () error {
if !fd .fdmu .increfAndClose () {
return errClosing (fd .isFile )
}
fd .pd .evict ()
err := fd .decref ()
if fd .isBlocking == 0 {
runtime_Semacquire (&fd .csema )
}
return err
}
func (fd *FD ) SetBlocking () error {
if err := fd .incref (); err != nil {
return err
}
defer fd .decref ()
atomic .StoreUint32 (&fd .isBlocking , 1 )
return syscall .SetNonblock (fd .Sysfd , false )
}
const maxRW = 1 << 30
func (fd *FD ) Read (p []byte ) (int , error ) {
if err := fd .readLock (); err != nil {
return 0 , err
}
defer fd .readUnlock ()
if len (p ) == 0 {
return 0 , nil
}
if err := fd .pd .prepareRead (fd .isFile ); err != nil {
return 0 , err
}
if fd .IsStream && len (p ) > maxRW {
p = p [:maxRW ]
}
for {
n , err := ignoringEINTRIO (syscall .Read , fd .Sysfd , p )
if err != nil {
n = 0
if err == syscall .EAGAIN && fd .pd .pollable () {
if err = fd .pd .waitRead (fd .isFile ); err == nil {
continue
}
}
}
err = fd .eofError (n , err )
return n , err
}
}
func (fd *FD ) Pread (p []byte , off int64 ) (int , error ) {
if err := fd .incref (); err != nil {
return 0 , err
}
if fd .IsStream && len (p ) > maxRW {
p = p [:maxRW ]
}
var (
n int
err error
)
for {
n , err = syscall .Pread (fd .Sysfd , p , off )
if err != syscall .EINTR {
break
}
}
if err != nil {
n = 0
}
fd .decref ()
err = fd .eofError (n , err )
return n , err
}
func (fd *FD ) ReadFrom (p []byte ) (int , syscall .Sockaddr , error ) {
if err := fd .readLock (); err != nil {
return 0 , nil , err
}
defer fd .readUnlock ()
if err := fd .pd .prepareRead (fd .isFile ); err != nil {
return 0 , nil , err
}
for {
n , sa , err := syscall .Recvfrom (fd .Sysfd , p , 0 )
if err != nil {
if err == syscall .EINTR {
continue
}
n = 0
if err == syscall .EAGAIN && fd .pd .pollable () {
if err = fd .pd .waitRead (fd .isFile ); err == nil {
continue
}
}
}
err = fd .eofError (n , err )
return n , sa , err
}
}
func (fd *FD ) ReadFromInet4 (p []byte , from *syscall .SockaddrInet4 ) (int , error ) {
if err := fd .readLock (); err != nil {
return 0 , err
}
defer fd .readUnlock ()
if err := fd .pd .prepareRead (fd .isFile ); err != nil {
return 0 , err
}
for {
n , err := unix .RecvfromInet4 (fd .Sysfd , p , 0 , from )
if err != nil {
if err == syscall .EINTR {
continue
}
n = 0
if err == syscall .EAGAIN && fd .pd .pollable () {
if err = fd .pd .waitRead (fd .isFile ); err == nil {
continue
}
}
}
err = fd .eofError (n , err )
return n , err
}
}
func (fd *FD ) ReadFromInet6 (p []byte , from *syscall .SockaddrInet6 ) (int , error ) {
if err := fd .readLock (); err != nil {
return 0 , err
}
defer fd .readUnlock ()
if err := fd .pd .prepareRead (fd .isFile ); err != nil {
return 0 , err
}
for {
n , err := unix .RecvfromInet6 (fd .Sysfd , p , 0 , from )
if err != nil {
if err == syscall .EINTR {
continue
}
n = 0
if err == syscall .EAGAIN && fd .pd .pollable () {
if err = fd .pd .waitRead (fd .isFile ); err == nil {
continue
}
}
}
err = fd .eofError (n , err )
return n , err
}
}
func (fd *FD ) ReadMsg (p []byte , oob []byte , flags int ) (int , int , int , syscall .Sockaddr , error ) {
if err := fd .readLock (); err != nil {
return 0 , 0 , 0 , nil , err
}
defer fd .readUnlock ()
if err := fd .pd .prepareRead (fd .isFile ); err != nil {
return 0 , 0 , 0 , nil , err
}
for {
n , oobn , sysflags , sa , err := syscall .Recvmsg (fd .Sysfd , p , oob , flags )
if err != nil {
if err == syscall .EINTR {
continue
}
if err == syscall .EAGAIN && fd .pd .pollable () {
if err = fd .pd .waitRead (fd .isFile ); err == nil {
continue
}
}
}
err = fd .eofError (n , err )
return n , oobn , sysflags , sa , err
}
}
func (fd *FD ) ReadMsgInet4 (p []byte , oob []byte , flags int , sa4 *syscall .SockaddrInet4 ) (int , int , int , error ) {
if err := fd .readLock (); err != nil {
return 0 , 0 , 0 , err
}
defer fd .readUnlock ()
if err := fd .pd .prepareRead (fd .isFile ); err != nil {
return 0 , 0 , 0 , err
}
for {
n , oobn , sysflags , err := unix .RecvmsgInet4 (fd .Sysfd , p , oob , flags , sa4 )
if err != nil {
if err == syscall .EINTR {
continue
}
if err == syscall .EAGAIN && fd .pd .pollable () {
if err = fd .pd .waitRead (fd .isFile ); err == nil {
continue
}
}
}
err = fd .eofError (n , err )
return n , oobn , sysflags , err
}
}
func (fd *FD ) ReadMsgInet6 (p []byte , oob []byte , flags int , sa6 *syscall .SockaddrInet6 ) (int , int , int , error ) {
if err := fd .readLock (); err != nil {
return 0 , 0 , 0 , err
}
defer fd .readUnlock ()
if err := fd .pd .prepareRead (fd .isFile ); err != nil {
return 0 , 0 , 0 , err
}
for {
n , oobn , sysflags , err := unix .RecvmsgInet6 (fd .Sysfd , p , oob , flags , sa6 )
if err != nil {
if err == syscall .EINTR {
continue
}
if err == syscall .EAGAIN && fd .pd .pollable () {
if err = fd .pd .waitRead (fd .isFile ); err == nil {
continue
}
}
}
err = fd .eofError (n , err )
return n , oobn , sysflags , err
}
}
func (fd *FD ) Write (p []byte ) (int , error ) {
if err := fd .writeLock (); err != nil {
return 0 , err
}
defer fd .writeUnlock ()
if err := fd .pd .prepareWrite (fd .isFile ); err != nil {
return 0 , err
}
var nn int
for {
max := len (p )
if fd .IsStream && max -nn > maxRW {
max = nn + maxRW
}
n , err := ignoringEINTRIO (syscall .Write , fd .Sysfd , p [nn :max ])
if n > 0 {
if n > max -nn {
panic ("invalid return from write: got " + itoa .Itoa (n ) + " from a write of " + itoa .Itoa (max -nn ))
}
nn += n
}
if nn == len (p ) {
return nn , err
}
if err == syscall .EAGAIN && fd .pd .pollable () {
if err = fd .pd .waitWrite (fd .isFile ); err == nil {
continue
}
}
if err != nil {
return nn , err
}
if n == 0 {
return nn , io .ErrUnexpectedEOF
}
}
}
func (fd *FD ) Pwrite (p []byte , off int64 ) (int , error ) {
if err := fd .incref (); err != nil {
return 0 , err
}
defer fd .decref ()
var nn int
for {
max := len (p )
if fd .IsStream && max -nn > maxRW {
max = nn + maxRW
}
n , err := syscall .Pwrite (fd .Sysfd , p [nn :max ], off +int64 (nn ))
if err == syscall .EINTR {
continue
}
if n > 0 {
nn += n
}
if nn == len (p ) {
return nn , err
}
if err != nil {
return nn , err
}
if n == 0 {
return nn , io .ErrUnexpectedEOF
}
}
}
func (fd *FD ) WriteToInet4 (p []byte , sa *syscall .SockaddrInet4 ) (int , error ) {
if err := fd .writeLock (); err != nil {
return 0 , err
}
defer fd .writeUnlock ()
if err := fd .pd .prepareWrite (fd .isFile ); err != nil {
return 0 , err
}
for {
err := unix .SendtoInet4 (fd .Sysfd , p , 0 , sa )
if err == syscall .EINTR {
continue
}
if err == syscall .EAGAIN && fd .pd .pollable () {
if err = fd .pd .waitWrite (fd .isFile ); err == nil {
continue
}
}
if err != nil {
return 0 , err
}
return len (p ), nil
}
}
func (fd *FD ) WriteToInet6 (p []byte , sa *syscall .SockaddrInet6 ) (int , error ) {
if err := fd .writeLock (); err != nil {
return 0 , err
}
defer fd .writeUnlock ()
if err := fd .pd .prepareWrite (fd .isFile ); err != nil {
return 0 , err
}
for {
err := unix .SendtoInet6 (fd .Sysfd , p , 0 , sa )
if err == syscall .EINTR {
continue
}
if err == syscall .EAGAIN && fd .pd .pollable () {
if err = fd .pd .waitWrite (fd .isFile ); err == nil {
continue
}
}
if err != nil {
return 0 , err
}
return len (p ), nil
}
}
func (fd *FD ) WriteTo (p []byte , sa syscall .Sockaddr ) (int , error ) {
if err := fd .writeLock (); err != nil {
return 0 , err
}
defer fd .writeUnlock ()
if err := fd .pd .prepareWrite (fd .isFile ); err != nil {
return 0 , err
}
for {
err := syscall .Sendto (fd .Sysfd , p , 0 , sa )
if err == syscall .EINTR {
continue
}
if err == syscall .EAGAIN && fd .pd .pollable () {
if err = fd .pd .waitWrite (fd .isFile ); err == nil {
continue
}
}
if err != nil {
return 0 , err
}
return len (p ), nil
}
}
func (fd *FD ) WriteMsg (p []byte , oob []byte , sa syscall .Sockaddr ) (int , int , error ) {
if err := fd .writeLock (); err != nil {
return 0 , 0 , err
}
defer fd .writeUnlock ()
if err := fd .pd .prepareWrite (fd .isFile ); err != nil {
return 0 , 0 , err
}
for {
n , err := syscall .SendmsgN (fd .Sysfd , p , oob , sa , 0 )
if err == syscall .EINTR {
continue
}
if err == syscall .EAGAIN && fd .pd .pollable () {
if err = fd .pd .waitWrite (fd .isFile ); err == nil {
continue
}
}
if err != nil {
return n , 0 , err
}
return n , len (oob ), err
}
}
func (fd *FD ) WriteMsgInet4 (p []byte , oob []byte , sa *syscall .SockaddrInet4 ) (int , int , error ) {
if err := fd .writeLock (); err != nil {
return 0 , 0 , err
}
defer fd .writeUnlock ()
if err := fd .pd .prepareWrite (fd .isFile ); err != nil {
return 0 , 0 , err
}
for {
n , err := unix .SendmsgNInet4 (fd .Sysfd , p , oob , sa , 0 )
if err == syscall .EINTR {
continue
}
if err == syscall .EAGAIN && fd .pd .pollable () {
if err = fd .pd .waitWrite (fd .isFile ); err == nil {
continue
}
}
if err != nil {
return n , 0 , err
}
return n , len (oob ), err
}
}
func (fd *FD ) WriteMsgInet6 (p []byte , oob []byte , sa *syscall .SockaddrInet6 ) (int , int , error ) {
if err := fd .writeLock (); err != nil {
return 0 , 0 , err
}
defer fd .writeUnlock ()
if err := fd .pd .prepareWrite (fd .isFile ); err != nil {
return 0 , 0 , err
}
for {
n , err := unix .SendmsgNInet6 (fd .Sysfd , p , oob , sa , 0 )
if err == syscall .EINTR {
continue
}
if err == syscall .EAGAIN && fd .pd .pollable () {
if err = fd .pd .waitWrite (fd .isFile ); err == nil {
continue
}
}
if err != nil {
return n , 0 , err
}
return n , len (oob ), err
}
}
func (fd *FD ) Accept () (int , syscall .Sockaddr , string , error ) {
if err := fd .readLock (); err != nil {
return -1 , nil , "" , err
}
defer fd .readUnlock ()
if err := fd .pd .prepareRead (fd .isFile ); err != nil {
return -1 , nil , "" , err
}
for {
s , rsa , errcall , err := accept (fd .Sysfd )
if err == nil {
return s , rsa , "" , err
}
switch err {
case syscall .EINTR :
continue
case syscall .EAGAIN :
if fd .pd .pollable () {
if err = fd .pd .waitRead (fd .isFile ); err == nil {
continue
}
}
case syscall .ECONNABORTED :
continue
}
return -1 , nil , errcall , err
}
}
func (fd *FD ) Fchmod (mode uint32 ) error {
if err := fd .incref (); err != nil {
return err
}
defer fd .decref ()
return ignoringEINTR (func () error {
return syscall .Fchmod (fd .Sysfd , mode )
})
}
func (fd *FD ) Fstat (s *syscall .Stat_t ) error {
if err := fd .incref (); err != nil {
return err
}
defer fd .decref ()
return ignoringEINTR (func () error {
return syscall .Fstat (fd .Sysfd , s )
})
}
var dupCloexecUnsupported atomic .Bool
func DupCloseOnExec (fd int ) (int , string , error ) {
if syscall .F_DUPFD_CLOEXEC != 0 && !dupCloexecUnsupported .Load () {
r0 , err := unix .Fcntl (fd , syscall .F_DUPFD_CLOEXEC , 0 )
if err == nil {
return r0 , "" , nil
}
switch err {
case syscall .EINVAL , syscall .ENOSYS :
dupCloexecUnsupported .Store (true )
default :
return -1 , "fcntl" , err
}
}
return dupCloseOnExecOld (fd )
}
func (fd *FD ) Dup () (int , string , error ) {
if err := fd .incref (); err != nil {
return -1 , "" , err
}
defer fd .decref ()
return DupCloseOnExec (fd .Sysfd )
}
func (fd *FD ) WaitWrite () error {
return fd .pd .waitWrite (fd .isFile )
}
func (fd *FD ) WriteOnce (p []byte ) (int , error ) {
if err := fd .writeLock (); err != nil {
return 0 , err
}
defer fd .writeUnlock ()
return ignoringEINTRIO (syscall .Write , fd .Sysfd , p )
}
func (fd *FD ) RawRead (f func (uintptr ) bool ) error {
if err := fd .readLock (); err != nil {
return err
}
defer fd .readUnlock ()
if err := fd .pd .prepareRead (fd .isFile ); err != nil {
return err
}
for {
if f (uintptr (fd .Sysfd )) {
return nil
}
if err := fd .pd .waitRead (fd .isFile ); err != nil {
return err
}
}
}
func (fd *FD ) RawWrite (f func (uintptr ) bool ) error {
if err := fd .writeLock (); err != nil {
return err
}
defer fd .writeUnlock ()
if err := fd .pd .prepareWrite (fd .isFile ); err != nil {
return err
}
for {
if f (uintptr (fd .Sysfd )) {
return nil
}
if err := fd .pd .waitWrite (fd .isFile ); err != nil {
return err
}
}
}
func ignoringEINTRIO(fn func (fd int , p []byte ) (int , error ), fd int , p []byte ) (int , error ) {
for {
n , err := fn (fd , p )
if err != syscall .EINTR {
return n , err
}
}
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .