Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net: document that File method is ONLY for passing to children #2458

Closed
gopherbot opened this issue Nov 13, 2011 · 15 comments

Comments

Projects
None yet
4 participants
@gopherbot
Copy link

commented Nov 13, 2011

by sebastien.paolacci:

Before filing a bug, please check whether it has been fixed since
the latest release: run "hg pull", "hg update default", rebuild, and
retry
what you did to
reproduce the problem.  Thanks.

What steps will reproduce the problem?
1. Ask for an os.File representation of a timeout-protected connection
2. Issue a timeout-able framework call (e.g con.{accept, read, write})
3. Wait.

What is the expected output?
Effective timeout: "resource temporarily unavailable".

What do you see instead?
Call will block forever.


Which compiler are you using (5g, 6g, 8g, gccgo)?
6g

Which operating system are you using?
linux/amd64

Which revision are you using?  (hg identify)
r60.3, tip.

Please provide any additional information below.
Discussed on golang-nuts
(http://groups.google.com/group/golang-nuts/browse_thread/thread/c88f08fa652497da#),
issue filled for the record.

package main 

import ( 
        "flag" 
        "net" 
        "fmt" 
        "syscall" 
) 

const TIMEOUT = 1e8 

func main() { 
        unblock := flag.Bool("unblock", false, "revert socket blocking mode") 
        flag.Parse() 

        addr, _ := net.ResolveTCPAddr("tcp", "12345") 
        l, err := net.ListenTCP("tcp", addr) 
        if err != nil { 
                panic(err) 
        } 
        if err := l.SetTimeout(TIMEOUT); err != nil { 
                panic(err) 
        } 

        f, _ := l.File() 
        fmt.Printf("observing '%v'\n", f) 

        if *unblock { 
                syscall.SetNonblock(f.Fd(), true) 
        } 

        fmt.Printf("should timeout in %v seconds...\n", float64(TIMEOUT)/1e9) 
        con, err := l.Accept() // Will hang forever if the '-unblock' flag is not set.
        fmt.Println(con, err) 
}

Attachments:

  1. timeout.patch (4183 bytes)
@gopherbot

This comment has been minimized.

Copy link
Author

commented Nov 13, 2011

Comment 1 by sebastien.paolacci:

Kind of duplicate of issue #1692 "SO_REUSEADDDR vs UDP vs timeouts": same root cause in
both cases.
@mikioh

This comment has been minimized.

Copy link
Contributor

commented Nov 14, 2011

@rsc

This comment has been minimized.

Copy link
Contributor

commented Nov 14, 2011

Comment 3:

The most common reason to ask for the os.File is to pass
it to exec for a new child.  Leaving it in non-blocking mode
makes it useless for that.
I'd rather just document that timeouts stop working if you
call this method.  Callers should be rare.
Russ
@gopherbot

This comment has been minimized.

Copy link
Author

commented Nov 14, 2011

Comment 4 by sebastien.paolacci:

You're right, passing the fd to a new child is exactly how I initially faced the need
but the situation as well (original listener wouldn't unblock from the accept call). I
would have suspected the Get/Setsockopt pair being an even more standard use case, like
in Mikio's SO_REUSEADDR, but you must have much more points in your stat than I have;).
I however feel puzzled by your statement about the uselessness of passing the fd in a
non-blocking mode. I'm doing that on a regular basis, by reverting the
SetNonblock(false), and never (yet) spotted any misbehavior. Did I miss some important
point here?
Thanks,
Sebastien
@rsc

This comment has been minimized.

Copy link
Contributor

commented Nov 14, 2011

Comment 5:

Passing a non-blocking fd only works if you are giving
the fd to a program that will do non-blocking I/O.
A common case is to accept the network connection
and then use that fd as stdin, stdout, and stderr when
running an ordinary program.  Those programs typically
do not expect non-blocking i/o on those fds and will
behave incorrectly.
It sounds like you are passing the fd to a program that
is going to use non-blocking i/o itself, so it is going to
put the fd back in non-blocking mode no matter what you
pass it.  The usual convention is that at program startup
a preopened fd is in blocking mode.
For example, here is a good way to kill your shell: run
a program that leaves the tty in non-blocking mode when
it exits:
package main
import "syscall"
func main() {
    syscall.SetNonblock(0, true)
}
@gopherbot

This comment has been minimized.

Copy link
Author

commented Nov 15, 2011

Comment 6 by sebastien.paolacci:

Yes, indeed harmful. I guess sanitizing god received fds, like any other input args, is
the only way to get that rigth in the general case. But that's slightly out of topic.
Let's hope that a better timeout api will emerge at a later time. Timeouts are always a
bit dirty but they're also useful.
Thanks,
Sebastien
@rsc

This comment has been minimized.

Copy link
Contributor

commented Nov 15, 2011

Comment 7:

I do hope for a better timeout API but I think that
calling File() will always disable timeouts.
It's implied by the way that Unix works.
@gopherbot

This comment has been minimized.

Copy link
Author

commented Nov 15, 2011

Comment 8 by sebastien.paolacci:

Not straightforward for some use cases, but I'm fine with that. Workarounds are known
and accessible, they however possibly need some more advertising.
As for the Get/Setsockopt syscall pair that need fds, extending the Set{KeepAlive,
Linger, NoDelay, xBuffer, xTimeout} family might just be enough in the end.
@rsc

This comment has been minimized.

Copy link
Contributor

commented Dec 9, 2011

Comment 9:

Labels changed: added priority-later, removed priority-medium.

@alberts

This comment has been minimized.

Copy link
Contributor

commented Dec 13, 2011

Comment 10:

As another data point, we did this:
file, err := c.File()
fd := file.Fd()
syscall.SetsockoptInet4Addr(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, addr)
which caused all kinds of interesting issues.
On Close the UDP socket we broke by calling File() would sometimes do one of the
following:
- return "address family not supported by protocol" from Read
- return "bad file descriptor" from Read
- return n = 0 and a nil error
Hopefully others might find this and avoid this problem.
@rsc

This comment has been minimized.

Copy link
Contributor

commented Jan 29, 2012

Comment 11:

Status changed to Accepted.

@rsc

This comment has been minimized.

Copy link
Contributor

commented Sep 12, 2012

Comment 12:

People think c.File().Fd() is a good way to fiddle with socket options. It is not. Make
that clear in docs and close this bug.
@rsc

This comment has been minimized.

Copy link
Contributor

commented Sep 12, 2012

Comment 13:

Labels changed: added go1.1.

@gopherbot

This comment has been minimized.

Copy link
Author

commented Dec 4, 2012

Comment 14 by rickarnoldjr:

Fixed by https://golang.org/cl/6869054
@rsc

This comment has been minimized.

Copy link
Contributor

commented Dec 6, 2012

Comment 15:

This issue was closed by revision 5416e6e.

Status changed to Fixed.

@gopherbot gopherbot added fixed labels Dec 6, 2012

@rsc rsc added this to the Go1.1 milestone Apr 14, 2015

@rsc rsc removed the go1.1 label Apr 14, 2015

@golang golang locked and limited conversation to collaborators Jun 24, 2016

This issue was closed.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
You can’t perform that action at this time.