In addition to all of the other points that have been made by Ian and others I think it is important to reinforce another point. On UNIX like systems every function call that returns a file descriptor (e.g., `open()` and `socket()`) is expected to return the lowest unused file descriptor. If the second fd you pass to `dup2()` is not already open then there is an inherent race, resulting in random failures, of any use of `dup2()` without coordination with every other thread that might allocate a file descriptor. This has nothing to do with Go. It is inherent in the UNIX process model.
The `dup2()` function is typically used in two highly stylized ways. The first, and most common, is to open a file like object and alias it to stdin (fd 0), stdout (fd 1), or stderr (fd 2). Assuming those three file descriptors were open at the time of the `dup2()` it is guaranteed there will not be a race with other threads that might allocate a new fd. Even if the `dup2()` fails with EINTR. Retrying the `dup2()` in that instance might succeed but will probably fail since EINTR on `close()` typically only occurs when there is a persistent error. The other common use case is exemplified by POSIX 1003.2 shells such as ksh and bash. Here we're talking about processes with a single thread that might open a file like object but want to ensure the fd is not in a "reserved" range of file descriptors. Specifically, POSIX shells allow a script to explicitly redirect to a single digit (i.e., 0 <= fd < 10) file descriptor. So if the shell performs an operation that assigns a fd in that range it will need to move it outside of that range. A typical example is opening the interactive command history file. The fd from opening the command history file might be in the reserved range. So the shell uses `dup2()` to move it outside that range. The shell knows which file descriptors are in use and can safely use `dup2()` to reassign the fd. If you don't use `dup2()` in the stylized ways discussed above then you are guaranteed to have random failures of your code. What makes those failures particularly problematic is that they will occur exceedingly infrequently. You wrote: > Atomicity with close is the whole point of dup2. No, it is not. On Fri, Feb 28, 2020 at 8:26 PM Philip Boampong <pboampo...@gmail.com> wrote: > Thanks for the reply. > > > Note that dup2() can only fail with EINTR if the new fd is currently > open on a "slow" device and the implicit close() fails due to being > interrupted. > > I understand the condition may be rare, but I still want to know the > correct way to handle it. > > > In my experience it is usually an error if the new fd is currently in > use unless the new fd is 0, 1 or 2 . > > The Go programmer is not fully in charge of the FD namespace: > libraries and the runtime can create new FDs at any time. Therefore, > unless you are sure that newfd *is* in use and know exactly what it > is, you are probably looking for trouble. > > > If you expect the new fd to be in use at the time of the dup2() it is > usually better, from the viewpoint of clarity, to incur the cost of an > explicit close() call. > > That seems very dangerous, especially in Go. > If another goroutine opens a file between the close and the dup2, such > open will likely reuse newfd which is about to be replaced. Then the > second goroutine will have the wrong file and operate concurrently on > it. > Atomicity with close is the whole point of dup2. > > > So your question is really what to do if close() fails with EINTR and > the answer is retry. > > The man page [1] explicitly says that Linux close(2) should *never* be > retried, not even on EINTR. > As I mentioned, there are plans to change close to return EINPROGRESS, > or even no error at all, instead of EINTR [2]. > > [1] http://man7.org/linux/man-pages/man2/close.2.html > [2] https://lwn.net/Articles/576478/ > -- Kurtis Rader Caretaker of the exceptional canines Junior and Hank -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CABx2%3DD9uoR8WFU%2BF%2Bw%3Dmwr3KJHrT6wh%3DYPZykvF9TiUqd2d%2BUA%40mail.gmail.com.