Home  >  Article  >  Backend Development  >  GOLANG: Why don't SetDeadline/SetReadDeadline/SetWriteDeadline work for files when using os.File.Fd()?

GOLANG: Why don't SetDeadline/SetReadDeadline/SetWriteDeadline work for files when using os.File.Fd()?

PHPz
PHPzforward
2024-02-09 08:30:211068browse

GOLANG:为什么在使用 os.File.Fd() 时 SetDeadline/SetReadDeadline/SetWriteDeadline 对文件不起作用?

php editor Zimo will answer your question in this article about SetDeadline/SetReadDeadline/SetWriteDeadline not working on files when using os.File.Fd() in Golang . In Golang, these methods are used to set the deadline of files, but sometimes they may be invalid. Next, we'll explore possible causes and provide solutions to ensure these methods are working properly.

Question content

I use a combination of os.File.SetReadDeadline and os.File.ReadFull. But even with SetReadDeadline, the deadline I set is completely ignored and ReadFull blocks forever. why is that?

Additional information: I'm firing some IOCTLS to the file, so os.File.Fd() is needed to get the file descriptor.

Solution

tl; Doctor:

After using os.file.fd(), use syscall.setnonblock(fd.fd(), true)

on the file

This is due to read In golang unix:

func (fd *fd) read(p []byte) (int, error) {
    if err := fd.readlock(); err != nil {
        return 0, err
    }
    defer fd.readunlock()
    if len(p) == 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 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
    }
}

If the file is set to blocking mode, the first n, err := ignoringeintrio(syscall.read, fd.sysfd, p) will block forever. waitread Will only be executed if the file is opened in non-blocking mode. But I did open the file in non-blocking mode, so what happened?

Implementation of os.file.fd() Broken it:

func (f *File) Fd() uintptr {
    if f == nil {
        return ^(uintptr(0))
    }

    // If we put the file descriptor into nonblocking mode,
    // then set it to blocking mode before we return it,
    // because historically we have always returned a descriptor
    // opened in blocking mode. The File will continue to work,
    // but any blocking operation will tie up a thread.
    if f.nonblock {
        f.pfd.SetBlocking()
    }

    return uintptr(f.pfd.Sysfd)
}

fd() Always sets the file to blocking. Therefore, we must undo the operation before waiting for polling to read. therefore:

After using os.file.fd(), use syscall.setnonblock(fd.fd(), true)

on the file

The above is the detailed content of GOLANG: Why don't SetDeadline/SetReadDeadline/SetWriteDeadline work for files when using os.File.Fd()?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete