// Copyright 2017 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.// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solarispackage pollimport ()// FD is a file descriptor. The net and os packages use this type as a// field of a larger type representing a network connection or OS file.typeFDstruct {// Lock sysfd and serialize access to Read and Write methods.fdmufdMutex// System file descriptor. Immutable until Close.Sysfdint// I/O poller.pdpollDesc// Writev cache.iovecs *[]syscall.Iovec// Semaphore signaled when file is closed.csemauint32// Non-zero if this file has been set to blocking mode.isBlockinguint32// Whether this is a streaming descriptor, as opposed to a // packet-based descriptor like a UDP socket. Immutable.IsStreambool// Whether a zero byte read indicates EOF. This is false for a // message based socket connection.ZeroReadIsEOFbool// Whether this is a file rather than a network socket.isFilebool}// Init initializes the FD. The Sysfd field should already be set.// This can be called multiple times on a single FD.// The net argument is a network name from the net package (e.g., "tcp"),// or "file".// Set pollable to true if fd should be managed by runtime netpoll.func ( *FD) ( string, bool) error {// We don't actually care about the various network types.if == "file" { .isFile = true }if ! { .isBlocking = 1returnnil } := .pd.init()if != nil {// If we could not initialize the runtime poller, // assume we are using blocking mode. .isBlocking = 1 }return}// Destroy closes the file descriptor. This is called when there are// no remaining references.func ( *FD) () error {// Poller may want to unregister fd in readiness notification mechanism, // so this must be executed before CloseFunc. .pd.close()// We don't use ignoringEINTR here because POSIX does not define // whether the descriptor is closed if close returns EINTR. // If the descriptor is indeed closed, using a loop would race // with some other goroutine opening a new descriptor. // (The Linux kernel guarantees that it is closed on an EINTR error.) := CloseFunc(.Sysfd) .Sysfd = -1runtime_Semrelease(&.csema)return}// Close closes the FD. The underlying file descriptor is closed by the// destroy method when there are no remaining references.func ( *FD) () error {if !.fdmu.increfAndClose() {returnerrClosing(.isFile) }// Unblock any I/O. Once it all unblocks and returns, // so that it cannot be referring to fd.sysfd anymore, // the final decref will close fd.sysfd. This should happen // fairly quickly, since all the I/O is non-blocking, and any // attempts to block in the pollDesc will return errClosing(fd.isFile). .pd.evict()// The call to decref will call destroy if there are no other // references. := .decref()// Wait until the descriptor is closed. If this was the only // reference, it is already closed. Only wait if the file has // not been set to blocking mode, as otherwise any current I/O // may be blocking, and that would block the Close. // No need for an atomic read of isBlocking, increfAndClose means // we have exclusive access to fd.if .isBlocking == 0 {runtime_Semacquire(&.csema) }return}// SetBlocking puts the file into blocking mode.func ( *FD) () error {if := .incref(); != nil {return }defer .decref()// Atomic store so that concurrent calls to SetBlocking // do not cause a race condition. isBlocking only ever goes // from 0 to 1 so there is no real race here.atomic.StoreUint32(&.isBlocking, 1)returnsyscall.SetNonblock(.Sysfd, false)}// Darwin and FreeBSD can't read or write 2GB+ files at a time,// even on 64-bit systems.// The same is true of socket implementations on many systems.// See golang.org/issue/7812 and golang.org/issue/16266.// Use 1GB instead of, say, 2GB-1, to keep subsequent reads aligned.constmaxRW = 1 << 30// Read implements io.Reader.func ( *FD) ( []byte) (int, error) {if := .readLock(); != nil {return0, }defer .readUnlock()iflen() == 0 {// If the caller wanted a zero byte read, return immediately // without trying (but after acquiring the readLock). // Otherwise syscall.Read returns 0, nil which looks like // io.EOF. // TODO(bradfitz): make it wait for readability? (Issue 15735)return0, nil }if := .pd.prepareRead(.isFile); != nil {return0, }if .IsStream && len() > maxRW { = [:maxRW] }for { , := ignoringEINTRIO(syscall.Read, .Sysfd, )if != nil { = 0if == syscall.EAGAIN && .pd.pollable() {if = .pd.waitRead(.isFile); == nil {continue } } } = .eofError(, )return , }}// Pread wraps the pread system call.func ( *FD) ( []byte, int64) (int, error) {// Call incref, not readLock, because since pread specifies the // offset it is independent from other reads. // Similarly, using the poller doesn't make sense for pread.if := .incref(); != nil {return0, }if .IsStream && len() > maxRW { = [:maxRW] }var (interror )for { , = syscall.Pread(.Sysfd, , )if != syscall.EINTR {break } }if != nil { = 0 } .decref() = .eofError(, )return , }// ReadFrom wraps the recvfrom network call.func ( *FD) ( []byte) (int, syscall.Sockaddr, error) {if := .readLock(); != nil {return0, nil, }defer .readUnlock()if := .pd.prepareRead(.isFile); != nil {return0, nil, }for { , , := syscall.Recvfrom(.Sysfd, , 0)if != nil {if == syscall.EINTR {continue } = 0if == syscall.EAGAIN && .pd.pollable() {if = .pd.waitRead(.isFile); == nil {continue } } } = .eofError(, )return , , }}// ReadMsg wraps the recvmsg network call.func ( *FD) ( []byte, []byte) (int, int, int, syscall.Sockaddr, error) {if := .readLock(); != nil {return0, 0, 0, nil, }defer .readUnlock()if := .pd.prepareRead(.isFile); != nil {return0, 0, 0, nil, }for { , , , , := syscall.Recvmsg(.Sysfd, , , 0)if != nil {if == syscall.EINTR {continue }// TODO(dfc) should n and oobn be set to 0if == syscall.EAGAIN && .pd.pollable() {if = .pd.waitRead(.isFile); == nil {continue } } } = .eofError(, )return , , , , }}// Write implements io.Writer.func ( *FD) ( []byte) (int, error) {if := .writeLock(); != nil {return0, }defer .writeUnlock()if := .pd.prepareWrite(.isFile); != nil {return0, }varintfor { := len()if .IsStream && - > maxRW { = + maxRW } , := ignoringEINTRIO(syscall.Write, .Sysfd, [:])if > 0 { += }if == len() {return , }if == syscall.EAGAIN && .pd.pollable() {if = .pd.waitWrite(.isFile); == nil {continue } }if != nil {return , }if == 0 {return , io.ErrUnexpectedEOF } }}// Pwrite wraps the pwrite system call.func ( *FD) ( []byte, int64) (int, error) {// Call incref, not writeLock, because since pwrite specifies the // offset it is independent from other writes. // Similarly, using the poller doesn't make sense for pwrite.if := .incref(); != nil {return0, }defer .decref()varintfor { := len()if .IsStream && - > maxRW { = + maxRW } , := syscall.Pwrite(.Sysfd, [:], +int64())if == syscall.EINTR {continue }if > 0 { += }if == len() {return , }if != nil {return , }if == 0 {return , io.ErrUnexpectedEOF } }}// WriteTo wraps the sendto network call.func ( *FD) ( []byte, syscall.Sockaddr) (int, error) {if := .writeLock(); != nil {return0, }defer .writeUnlock()if := .pd.prepareWrite(.isFile); != nil {return0, }for { := syscall.Sendto(.Sysfd, , 0, )if == syscall.EINTR {continue }if == syscall.EAGAIN && .pd.pollable() {if = .pd.waitWrite(.isFile); == nil {continue } }if != nil {return0, }returnlen(), nil }}// WriteMsg wraps the sendmsg network call.func ( *FD) ( []byte, []byte, syscall.Sockaddr) (int, int, error) {if := .writeLock(); != nil {return0, 0, }defer .writeUnlock()if := .pd.prepareWrite(.isFile); != nil {return0, 0, }for { , := syscall.SendmsgN(.Sysfd, , , , 0)if == syscall.EINTR {continue }if == syscall.EAGAIN && .pd.pollable() {if = .pd.waitWrite(.isFile); == nil {continue } }if != nil {return , 0, }return , len(), }}// Accept wraps the accept network call.func ( *FD) () (int, syscall.Sockaddr, string, error) {if := .readLock(); != nil {return -1, nil, "", }defer .readUnlock()if := .pd.prepareRead(.isFile); != nil {return -1, nil, "", }for { , , , := accept(.Sysfd)if == nil {return , , "", }switch {casesyscall.EINTR:continuecasesyscall.EAGAIN:if .pd.pollable() {if = .pd.waitRead(.isFile); == nil {continue } }casesyscall.ECONNABORTED:// This means that a socket on the listen // queue was closed before we Accept()ed it; // it's a silly error, so try again.continue }return -1, nil, , }}// Seek wraps syscall.Seek.func ( *FD) ( int64, int) (int64, error) {if := .incref(); != nil {return0, }defer .decref()returnsyscall.Seek(.Sysfd, , )}// ReadDirent wraps syscall.ReadDirent.// We treat this like an ordinary system call rather than a call// that tries to fill the buffer.func ( *FD) ( []byte) (int, error) {if := .incref(); != nil {return0, }defer .decref()for { , := ignoringEINTRIO(syscall.ReadDirent, .Sysfd, )if != nil { = 0if == syscall.EAGAIN && .pd.pollable() {if = .pd.waitRead(.isFile); == nil {continue } } }// Do not call eofError; caller does not expect to see io.EOF.return , }}// Fchmod wraps syscall.Fchmod.func ( *FD) ( uint32) error {if := .incref(); != nil {return }defer .decref()returnignoringEINTR(func() error {returnsyscall.Fchmod(.Sysfd, ) })}// Fchdir wraps syscall.Fchdir.func ( *FD) () error {if := .incref(); != nil {return }defer .decref()returnsyscall.Fchdir(.Sysfd)}// Fstat wraps syscall.Fstatfunc ( *FD) ( *syscall.Stat_t) error {if := .incref(); != nil {return }defer .decref()returnignoringEINTR(func() error {returnsyscall.Fstat(.Sysfd, ) })}// tryDupCloexec indicates whether F_DUPFD_CLOEXEC should be used.// If the kernel doesn't support it, this is set to 0.vartryDupCloexec = int32(1)// DupCloseOnExec dups fd and marks it close-on-exec.func ( int) (int, string, error) {ifsyscall.F_DUPFD_CLOEXEC != 0 && atomic.LoadInt32(&tryDupCloexec) == 1 { , := fcntl(, syscall.F_DUPFD_CLOEXEC, 0)if == nil {return , "", nil }switch .(syscall.Errno) {casesyscall.EINVAL, syscall.ENOSYS:// Old kernel, or js/wasm (which returns // ENOSYS). Fall back to the portable way from // now on.atomic.StoreInt32(&tryDupCloexec, 0)default:return -1, "fcntl", } }returndupCloseOnExecOld()}// dupCloseOnExecOld is the traditional way to dup an fd and// set its O_CLOEXEC bit, using two system calls.func ( int) (int, string, error) {syscall.ForkLock.RLock()defersyscall.ForkLock.RUnlock() , := syscall.Dup()if != nil {return -1, "dup", }syscall.CloseOnExec()return , "", nil}// Dup duplicates the file descriptor.func ( *FD) () (int, string, error) {if := .incref(); != nil {return -1, "", }defer .decref()returnDupCloseOnExec(.Sysfd)}// On Unix variants only, expose the IO event for the net code.// WaitWrite waits until data can be read from fd.func ( *FD) () error {return .pd.waitWrite(.isFile)}// WriteOnce is for testing only. It makes a single write call.func ( *FD) ( []byte) (int, error) {if := .writeLock(); != nil {return0, }defer .writeUnlock()returnignoringEINTRIO(syscall.Write, .Sysfd, )}// RawRead invokes the user-defined function f for a read operation.func ( *FD) ( func(uintptr) bool) error {if := .readLock(); != nil {return }defer .readUnlock()if := .pd.prepareRead(.isFile); != nil {return }for {if (uintptr(.Sysfd)) {returnnil }if := .pd.waitRead(.isFile); != nil {return } }}// RawWrite invokes the user-defined function f for a write operation.func ( *FD) ( func(uintptr) bool) error {if := .writeLock(); != nil {return }defer .writeUnlock()if := .pd.prepareWrite(.isFile); != nil {return }for {if (uintptr(.Sysfd)) {returnnil }if := .pd.waitWrite(.isFile); != nil {return } }}// ignoringEINTRIO is like ignoringEINTR, but just for IO calls.func ( func( int, []byte) (int, error), int, []byte) (int, error) {for { , := (, )if != syscall.EINTR {return , } }}
The pages are generated with Goldsv0.2.8-preview. (GOOS=darwin GOARCH=arm64)