Skip to content

os: add a function to construct *File with non-blocking file descriptor #22939

Closed
@crvv

Description

@crvv

Change

Add a new function to construct a *File with a file descriptor, set non blocking and put it into runtime poller. A naive but working diff:

--- a/src/os/file_unix.go
+++ b/src/os/file_unix.go
@@ -74,14 +74,17 @@ func (f *File) Fd() uintptr {
 // NewFile returns a new File with the given file descriptor and
 // name. The returned value will be nil if fd is not a valid file
 // descriptor.
 func NewFile(fd uintptr, name string) *File {
        return newFile(fd, name, kindNewFile)
 }
 
+func NewFileNonBlocking(fd uintptr, name string) *File {
+       return newFile(fd, name, kindOpenFile)
+}

Benefit

With this, I can make use of the runtime poller and have SetDeadline method on custom network interfaces.
An example (on Linux):

package main

import (
	"os"
	"syscall"
	"time"
	"unsafe"
)

const IFF_TUN = 0x0001

func main() {
	f, err := syscall.Open("/dev/net/tun", os.O_RDWR, 0)
	if err != nil { panic(err) }
	fd := uintptr(f)

	var req ifreq
	req.flags = IFF_TUN
	copy(req.name[:], "tun0")
	ioctl(fd, syscall.TUNSETIFF, uintptr(unsafe.Pointer(&req)))

	conn := os.NewFileNonBlocking(fd, "tun0")

	packet := make([]byte, 2000)
	for {
		err := conn.SetDeadline(time.Now().Add(time.Second * 2))
		if err != nil { panic(err) }
		n, err := conn.Read(packet)
		if err != nil {
			println(err.Error())
		} else {
			println("read a packet with length", n)
		}
	}
}

func ioctl(fd uintptr, request uintptr, ifreq uintptr) {
	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, request, ifreq)
	if errno != 0 { panic(errno) }
}

type ifreq struct {
	name  [16]byte
	flags uint16
}

Related issue

#15021
It looks like the main motivations of #15021 are runtime poller and SetDeadline.
Both of them can be solved by this very simple function.

Activity

added this to the Proposal milestone on Nov 30, 2017
ianlancetaylor

ianlancetaylor commented on Dec 1, 2017

@ianlancetaylor
Contributor

Perhaps a better approach would be to add a mechanism for the os package to use syscall.RawConn, much as we've recently added to the net package. That would let you use os.OpenFile and then call the ioctl you need.

crvv

crvv commented on Dec 1, 2017

@crvv
ContributorAuthor

That would let you use os.OpenFile and then call the ioctl you need.

I figured out that the tun fd must be put into epoll after the ioctl call, otherwise epoll doesn't work on it. But os.OpenFile will do the epoll things before ioctl.

Besides, the fd can also come from other syscalls like socket.

ianlancetaylor

ianlancetaylor commented on Dec 1, 2017

@ianlancetaylor
Contributor

If the fd comes from socket, then it ought to use functions in the net package.

Perhaps if os.NewFile sees that the descriptor is already in non-blocking mode, it should try adding it to the poller.

crvv

crvv commented on Dec 1, 2017

@crvv
ContributorAuthor

To achieve the goal (make use of runtime poller from outside the stdlib), the net package ways are #15021 and #10565. They are much more complex.
I just think it is a very simple solution that put a file descriptor into runtime poller through os package.

Perhaps if os.NewFile sees that the descriptor is already in non-blocking mode, it should try adding it to the poller.

I think this is a better solution.

rsc

rsc commented on Dec 4, 2017

@rsc
Contributor

We should fix this one way or another. Certainly returning EAGAIN from File.Read is not useful to clients, since they don't know when is again.

added
NeedsDecisionFeedback is required from experts, contributors, and/or the community before a change can be made.
on Dec 4, 2017
changed the title [-]proposal: os: add a function to construct *File with non-blocking file descriptor[/-] [+]os: add a function to construct *File with non-blocking file descriptor[/+] on Dec 4, 2017
rsc

rsc commented on Dec 4, 2017

@rsc
Contributor

Leaving for @ianlancetaylor to decide the exact fix.

odeke-em

odeke-em commented on Dec 17, 2017

@odeke-em
Member

@ianlancetaylor, I am going to move this out of the "Proposal" milestone and into Go1.11 since it was accepted. Please feel free to revert.

modified the milestones: Proposal, Go1.11 on Dec 17, 2017

5 remaining items

riobard

riobard commented on Mar 23, 2018

@riobard

Will calling syscall.SetNonblock correctly register the fd to runtime poller?

ianlancetaylor

ianlancetaylor commented on Mar 23, 2018

@ianlancetaylor
Contributor

No, but the case I'm concerned about with regard to Cl 100077 is people who want to use a non-blocking FD. Their code can work if they pass a blocking FD to os.NewFile and then call syscall.SetNonblock to switch the file into non-blocking mode.

kirbyzhou

kirbyzhou commented on Apr 13, 2018

@kirbyzhou

if Stdin and Stdout is Linux FIFO/Pipe, can I use any method above to avoid blocking deadlock?

ianlancetaylor

ianlancetaylor commented on Apr 13, 2018

@ianlancetaylor
Contributor

@kirbyzhou No, sorry, this does not apply to stdin/stdout, and those not going to be in non-blocking mode anyhow. In any case everything will work with stdin/stdout, you just won't be able to call SetDeadline.

kirbyzhou

kirbyzhou commented on Apr 16, 2018

@kirbyzhou

So if I want to do Graceful Exit with I/O blocked on Stdon/Stdout which is FIFO/PIPE, what can I do?

For example, the program read plain text from Stdin, and write compressed data to Stdout.
When ctrl-C / kill happens, I want the program to write a valid file end before exit.

ianlancetaylor

ianlancetaylor commented on Apr 16, 2018

@ianlancetaylor
Contributor

It's not clear to me what ^C has to do with non-blocking standard input. If you want to handling ^C, use signal.Notify from the os/signal package.

In Go it is always easy to start a goroutine to handle something else. You can always set a deadline by starting a new goroutine or by using time.AfterFunc. Being able to call SetDeadline improves efficiency significantly when using hundreds of file descriptors. For just standard input, efficiency is unimportant, and the language provides multiple mechanisms for handling deadlines.

locked and limited conversation to collaborators on Apr 16, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @riobard@rsc@ianlancetaylor@kirbyzhou@odeke-em

        Issue actions

          os: add a function to construct *File with non-blocking file descriptor · Issue #22939 · golang/go