// 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.

//go:build unix || (js && wasm) || wasip1

package poll

import (
	
	
	
	
)

// 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.
type FD struct {
	// Lock sysfd and serialize access to Read and Write methods.
	fdmu fdMutex

	// System file descriptor. Immutable until Close.
	Sysfd int

	// Platform dependent state of the file descriptor.
	SysFile

	// I/O poller.
	pd pollDesc

	// Semaphore signaled when file is closed.
	csema uint32

	// Non-zero if this file has been set to blocking mode.
	isBlocking uint32

	// Whether this is a streaming descriptor, as opposed to a
	// packet-based descriptor like a UDP socket. Immutable.
	IsStream bool

	// Whether a zero byte read indicates EOF. This is false for a
	// message based socket connection.
	ZeroReadIsEOF bool

	// Whether this is a file rather than a network socket.
	isFile bool
}

// 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 {
	.SysFile.init()

	// We don't actually care about the various network types.
	if  == "file" {
		.isFile = true
	}
	if ! {
		.isBlocking = 1
		return nil
	}
	 := .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()

	 := .SysFile.destroy(.Sysfd)

	.Sysfd = -1
	runtime_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() {
		return errClosing(.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)
	return syscall.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.
const maxRW = 1 << 30

// Read implements io.Reader.
func ( *FD) ( []byte) (int, error) {
	if  := .readLock();  != nil {
		return 0, 
	}
	defer .readUnlock()
	if len() == 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)
		return 0, nil
	}
	if  := .pd.prepareRead(.isFile);  != nil {
		return 0, 
	}
	if .IsStream && len() > maxRW {
		 = [:maxRW]
	}
	for {
		,  := ignoringEINTRIO(syscall.Read, .Sysfd, )
		if  != nil {
			 = 0
			if  == 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 {
		return 0, 
	}
	if .IsStream && len() > maxRW {
		 = [:maxRW]
	}
	var (
		   int
		 error
	)
	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 {
		return 0, nil, 
	}
	defer .readUnlock()
	if  := .pd.prepareRead(.isFile);  != nil {
		return 0, nil, 
	}
	for {
		, ,  := syscall.Recvfrom(.Sysfd, , 0)
		if  != nil {
			if  == syscall.EINTR {
				continue
			}
			 = 0
			if  == syscall.EAGAIN && .pd.pollable() {
				if  = .pd.waitRead(.isFile);  == nil {
					continue
				}
			}
		}
		 = .eofError(, )
		return , , 
	}
}

// ReadFromInet4 wraps the recvfrom network call for IPv4.
func ( *FD) ( []byte,  *syscall.SockaddrInet4) (int, error) {
	if  := .readLock();  != nil {
		return 0, 
	}
	defer .readUnlock()
	if  := .pd.prepareRead(.isFile);  != nil {
		return 0, 
	}
	for {
		,  := unix.RecvfromInet4(.Sysfd, , 0, )
		if  != nil {
			if  == syscall.EINTR {
				continue
			}
			 = 0
			if  == syscall.EAGAIN && .pd.pollable() {
				if  = .pd.waitRead(.isFile);  == nil {
					continue
				}
			}
		}
		 = .eofError(, )
		return , 
	}
}

// ReadFromInet6 wraps the recvfrom network call for IPv6.
func ( *FD) ( []byte,  *syscall.SockaddrInet6) (int, error) {
	if  := .readLock();  != nil {
		return 0, 
	}
	defer .readUnlock()
	if  := .pd.prepareRead(.isFile);  != nil {
		return 0, 
	}
	for {
		,  := unix.RecvfromInet6(.Sysfd, , 0, )
		if  != nil {
			if  == syscall.EINTR {
				continue
			}
			 = 0
			if  == 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, int, syscall.Sockaddr, error) {
	if  := .readLock();  != nil {
		return 0, 0, 0, nil, 
	}
	defer .readUnlock()
	if  := .pd.prepareRead(.isFile);  != nil {
		return 0, 0, 0, nil, 
	}
	for {
		, , , ,  := syscall.Recvmsg(.Sysfd, , , )
		if  != nil {
			if  == syscall.EINTR {
				continue
			}
			// TODO(dfc) should n and oobn be set to 0
			if  == syscall.EAGAIN && .pd.pollable() {
				if  = .pd.waitRead(.isFile);  == nil {
					continue
				}
			}
		}
		 = .eofError(, )
		return , , , , 
	}
}

// ReadMsgInet4 is ReadMsg, but specialized for syscall.SockaddrInet4.
func ( *FD) ( []byte,  []byte,  int,  *syscall.SockaddrInet4) (int, int, int, error) {
	if  := .readLock();  != nil {
		return 0, 0, 0, 
	}
	defer .readUnlock()
	if  := .pd.prepareRead(.isFile);  != nil {
		return 0, 0, 0, 
	}
	for {
		, , ,  := unix.RecvmsgInet4(.Sysfd, , , , )
		if  != nil {
			if  == syscall.EINTR {
				continue
			}
			// TODO(dfc) should n and oobn be set to 0
			if  == syscall.EAGAIN && .pd.pollable() {
				if  = .pd.waitRead(.isFile);  == nil {
					continue
				}
			}
		}
		 = .eofError(, )
		return , , , 
	}
}

// ReadMsgInet6 is ReadMsg, but specialized for syscall.SockaddrInet6.
func ( *FD) ( []byte,  []byte,  int,  *syscall.SockaddrInet6) (int, int, int, error) {
	if  := .readLock();  != nil {
		return 0, 0, 0, 
	}
	defer .readUnlock()
	if  := .pd.prepareRead(.isFile);  != nil {
		return 0, 0, 0, 
	}
	for {
		, , ,  := unix.RecvmsgInet6(.Sysfd, , , , )
		if  != nil {
			if  == syscall.EINTR {
				continue
			}
			// TODO(dfc) should n and oobn be set to 0
			if  == syscall.EAGAIN && .pd.pollable() {
				if  = .pd.waitRead(.isFile);  == nil {
					continue
				}
			}
		}
		 = .eofError(, )
		return , , , 
	}
}

// Write implements io.Writer.
func ( *FD) ( []byte) (int, error) {
	if  := .writeLock();  != nil {
		return 0, 
	}
	defer .writeUnlock()
	if  := .pd.prepareWrite(.isFile);  != nil {
		return 0, 
	}
	var  int
	for {
		 := 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 {
		return 0, 
	}
	defer .decref()
	var  int
	for {
		 := 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
		}
	}
}

// WriteToInet4 wraps the sendto network call for IPv4 addresses.
func ( *FD) ( []byte,  *syscall.SockaddrInet4) (int, error) {
	if  := .writeLock();  != nil {
		return 0, 
	}
	defer .writeUnlock()
	if  := .pd.prepareWrite(.isFile);  != nil {
		return 0, 
	}
	for {
		 := unix.SendtoInet4(.Sysfd, , 0, )
		if  == syscall.EINTR {
			continue
		}
		if  == syscall.EAGAIN && .pd.pollable() {
			if  = .pd.waitWrite(.isFile);  == nil {
				continue
			}
		}
		if  != nil {
			return 0, 
		}
		return len(), nil
	}
}

// WriteToInet6 wraps the sendto network call for IPv6 addresses.
func ( *FD) ( []byte,  *syscall.SockaddrInet6) (int, error) {
	if  := .writeLock();  != nil {
		return 0, 
	}
	defer .writeUnlock()
	if  := .pd.prepareWrite(.isFile);  != nil {
		return 0, 
	}
	for {
		 := unix.SendtoInet6(.Sysfd, , 0, )
		if  == syscall.EINTR {
			continue
		}
		if  == syscall.EAGAIN && .pd.pollable() {
			if  = .pd.waitWrite(.isFile);  == nil {
				continue
			}
		}
		if  != nil {
			return 0, 
		}
		return len(), nil
	}
}

// WriteTo wraps the sendto network call.
func ( *FD) ( []byte,  syscall.Sockaddr) (int, error) {
	if  := .writeLock();  != nil {
		return 0, 
	}
	defer .writeUnlock()
	if  := .pd.prepareWrite(.isFile);  != nil {
		return 0, 
	}
	for {
		 := syscall.Sendto(.Sysfd, , 0, )
		if  == syscall.EINTR {
			continue
		}
		if  == syscall.EAGAIN && .pd.pollable() {
			if  = .pd.waitWrite(.isFile);  == nil {
				continue
			}
		}
		if  != nil {
			return 0, 
		}
		return len(), nil
	}
}

// WriteMsg wraps the sendmsg network call.
func ( *FD) ( []byte,  []byte,  syscall.Sockaddr) (int, int, error) {
	if  := .writeLock();  != nil {
		return 0, 0, 
	}
	defer .writeUnlock()
	if  := .pd.prepareWrite(.isFile);  != nil {
		return 0, 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(), 
	}
}

// WriteMsgInet4 is WriteMsg specialized for syscall.SockaddrInet4.
func ( *FD) ( []byte,  []byte,  *syscall.SockaddrInet4) (int, int, error) {
	if  := .writeLock();  != nil {
		return 0, 0, 
	}
	defer .writeUnlock()
	if  := .pd.prepareWrite(.isFile);  != nil {
		return 0, 0, 
	}
	for {
		,  := unix.SendmsgNInet4(.Sysfd, , , , 0)
		if  == syscall.EINTR {
			continue
		}
		if  == syscall.EAGAIN && .pd.pollable() {
			if  = .pd.waitWrite(.isFile);  == nil {
				continue
			}
		}
		if  != nil {
			return , 0, 
		}
		return , len(), 
	}
}

// WriteMsgInet6 is WriteMsg specialized for syscall.SockaddrInet6.
func ( *FD) ( []byte,  []byte,  *syscall.SockaddrInet6) (int, int, error) {
	if  := .writeLock();  != nil {
		return 0, 0, 
	}
	defer .writeUnlock()
	if  := .pd.prepareWrite(.isFile);  != nil {
		return 0, 0, 
	}
	for {
		,  := unix.SendmsgNInet6(.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  {
		case syscall.EINTR:
			continue
		case syscall.EAGAIN:
			if .pd.pollable() {
				if  = .pd.waitRead(.isFile);  == nil {
					continue
				}
			}
		case syscall.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, , 
	}
}

// Fchmod wraps syscall.Fchmod.
func ( *FD) ( uint32) error {
	if  := .incref();  != nil {
		return 
	}
	defer .decref()
	return ignoringEINTR(func() error {
		return syscall.Fchmod(.Sysfd, )
	})
}

// Fstat wraps syscall.Fstat
func ( *FD) ( *syscall.Stat_t) error {
	if  := .incref();  != nil {
		return 
	}
	defer .decref()
	return ignoringEINTR(func() error {
		return syscall.Fstat(.Sysfd, )
	})
}

// dupCloexecUnsupported indicates whether F_DUPFD_CLOEXEC is supported by the kernel.
var dupCloexecUnsupported atomic.Bool

// DupCloseOnExec dups fd and marks it close-on-exec.
func ( int) (int, string, error) {
	if syscall.F_DUPFD_CLOEXEC != 0 && !dupCloexecUnsupported.Load() {
		,  := unix.Fcntl(, syscall.F_DUPFD_CLOEXEC, 0)
		if  == nil {
			return , "", nil
		}
		switch  {
		case syscall.EINVAL, syscall.ENOSYS:
			// Old kernel, or js/wasm (which returns
			// ENOSYS). Fall back to the portable way from
			// now on.
			dupCloexecUnsupported.Store(true)
		default:
			return -1, "fcntl", 
		}
	}
	return dupCloseOnExecOld()
}

// Dup duplicates the file descriptor.
func ( *FD) () (int, string, error) {
	if  := .incref();  != nil {
		return -1, "", 
	}
	defer .decref()
	return DupCloseOnExec(.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 {
		return 0, 
	}
	defer .writeUnlock()
	return ignoringEINTRIO(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)) {
			return nil
		}
		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)) {
			return nil
		}
		if  := .pd.waitWrite(.isFile);  != nil {
			return 
		}
	}
}

// ignoringEINTRIO is like ignoringEINTR, but just for IO calls.
func ignoringEINTRIO( func( int,  []byte) (int, error),  int,  []byte) (int, error) {
	for {
		,  := (, )
		if  != syscall.EINTR {
			return , 
		}
	}
}