如何在Go语言中中断一个阻塞的`os.Open`调用,该调用正在等待一个Fifo?

huangapple go评论90阅读模式
英文:

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

问题

我正在为Go编写一个Jupyter内核,在执行Go代码之前,我创建了一个名为管道(syscall.Mkfifo)的边缘,作为允许发布HTML、图像等内容的机制。

我的内核创建了这个管道,然后在一个新的goroutine中打开它以进行读取,并相应地轮询输入。现在,打开一个管道进行读取是一个阻塞操作(它会等待直到另一端打开)。

但是有些程序不希望使用这种机制,它们永远不会打开管道的另一端。当这种情况发生时,我的goroutine泄漏了,并且它会永远等待打开的管道,即使我将其删除。

代码大致如下(省略了错误处理):

    ...
	syscall.Mkfifo(pipePath, 0600)
    go func() {
      pipeReader, _ := os.Open(pipePath)
      go poll(pipeReader) // 读取和处理每个条目,直到pipeReader关闭。
      <-doneChan
      pipeReader.Close()
    }
    go func() {
      <-doneChan
      os.Remove(pipePath)
    }

其中doneChan在我启动的程序执行完成时关闭。

问题是,如果另一端从未打开,os.Open(pipePath)永远不会返回,即使os.Remove(pipePath)被正确执行。

有没有一种方法可以强制中断os.Open(pipePath),或者以其他方式实现相同的效果?

提前感谢!

英文:

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.
      &lt;-doneChan
      pipeReader.Close()
    }
    go func() {
      &lt;-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!!

答案1

得分: 1

啊,只需要再喝一点咖啡并思考一下就好了。

我忘记了,如果我知道我执行的程序没有打开它,我可以自己打开管道的另一端。这样就解除了读取的阻塞。

修改后的代码,以防有人遇到这个问题:

    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) // 读取并处理每个条目,直到pipeReader关闭。
      <-doneChan
      pipeReader.Close()
    }
    go func() {
      <-doneChan
      muFifo.Lock()
      if !pipeOpenedForReading {
        // 打开写入,解除对读取的阻塞。
        f, err = os.OpenFile(pipePath, O_WRONLY, 0600)
        if err == nil {
          close(f)
        }
      }
      muFifo.Unlock()
      os.Remove(pipePath)
    }
英文:

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.
      &lt;-doneChan
      pipeReader.Close()
    }
    go func() {
      &lt;-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)
    }

答案2

得分: 1

你可以按照文档中的说明以非阻塞的方式打开FIFO。所以像这样的代码应该可以工作:

go func() {
  pipeReader, _ := os.OpenFile(pipePath, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
  go poll(pipeReader) // 读取并处理每个条目,直到pipeReader关闭。
  <-doneChan
  pipeReader.Close()
}
英文:

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.
      &lt;-doneChan
      pipeReader.Close()
    }

huangapple
  • 本文由 发表于 2023年1月27日 16:07:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/75255426.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定