Home > OS >  How to interrupt a blocking `os.Open` call waiting on a Fifo in Go?
How to interrupt a blocking `os.Open` call waiting on a Fifo in Go?

Time:01-28

I'm writing a Jupyter kernel for Go, and before executing the Go code I create a side named pipe (syscall.Mkfifo) as a mechanism to allow one to publish html, images, etc.

My kernel creates the fifo, and then opens it for reading in a new goroutine and polls for input accordingly. Now, opening a fifo for reading is a blocking operation (it waits until someone opens on the other side).

But some programs are not interested in using this mechanism and will never open the other side of the fifo. When this happens my goroutine leaks, and it forever waits on the opening fifo -- even after I remove it.

The code looks more or less (less error handling) like this:

    ...
    syscall.Mkfifo(pipePath, 0600)
    go func() {
      pipeReader, _ := os.Open(pipePath)
      go poll(pipeReader) // Reads and process each entry until pipeReader is closed.
      <-doneChan
      pipeReader.Close()
    }
    go func() {
      <-doneChan
      os.Remove(pipePath)
    }

Where doneChan is closed when the program I start finishes executing.

And the issue is that os.Open(pipePath) never returns if the other end is never opened, even though the os.Remove(pipePath) is properly executed.

Is there a way to forcefully interrupt os.Open(pipePath), or another way to achieving the same thing ?

Thanks in advance!!

CodePudding user response:

Ugh, it took just a bit more coffee and thinking.

I forgot that I can open the other end of the pipe myself, if I know the program I executed didn't open it. And this unblocks the open for reading.

The revised code, in case anyone bumps into this:

    pipeOpenedForReading := false
    var muFifo sync.Mutex{}
    syscall.Mkfifo(pipePath, 0600)
    go func() {
      pipeReader, _ := os.Open(pipePath)
      muFifo.Lock()
      pipeOpenedForReading = true
      muFifo.Unlock()
      go poll(pipeReader) // Reads and process each entry until pipeReader is closed.
      <-doneChan
      pipeReader.Close()
    }
    go func() {
      <-doneChan
      muFifo.Lock()
      if !pipeOpenedForReading {
        // Open for writing, unblocking the os.Open for reading.
        f, err = os.OpenFile(pipePath, O_WRONLY, 0600)
        if err == nil {
          close(f)
        }
      }
      muFifo.Unlock
      os.Remove(pipePath)
    }

CodePudding user response:

You can open FIFO in non-blocking way, according to documentation. So something like this should work:

    go func() {
      pipeReader, _ := os.OpenFile(pipePath, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
      go poll(pipeReader) // Reads and process each entry until pipeReader is closed.
      <-doneChan
      pipeReader.Close()
    }
  •  Tags:  
  • go
  • Related