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

3
我正在编写一个针对Go的Jupyter内核,在执行Go代码之前,我创建了一个名为side named pipe(syscall.Mkfifo)的机制,以允许发布html、图像等内容。
我的内核创建了此命名管道,然后在新的goroutine中打开它进行读取,并相应地轮询输入。现在,打开命名管道进行读取是一种阻塞操作 (它会等待直到另一侧打开)。
但有些程序不想使用此机制,也永远不会打开命名管道的另一侧。当发生这种情况时,我的goroutine将出现泄漏,并且它将永远在打开的命名管道上等待 -- 即使我将其删除。
代码看起来大致如下(省略了错误处理):
    ...
    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)
    }

doneChan 会在我启动的程序执行结束时关闭。

问题在于,即使 os.Remove(pipePath) 正确执行,如果另一端没有打开,os.Open(pipePath) 永远不会返回。

有没有一种方法可以强制中断 os.Open(pipePath),或者达到相同的效果的另一种方法?

提前感谢!!

2个回答

1

根据文档,您可以以非阻塞方式打开FIFO。因此,类似以下的代码应该可以工作:

    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()
    }

1
谢谢,这个方法也可以!有趣的是,os.OpenFile 没有记录这个选项(也没有定义 O_NONBLOCK)。在 fifo 的文档中仔细阅读后,他们确实提到了非阻塞打开读取将立即成功返回(我很惊讶,我以为它会立即失败),所以我们得到了一个 *os.File。但根据这篇博客文章,从这个文件读取将返回 EAGAIN 而不是阻塞。我必须试一下... - Jan

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) // 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)
    }

1
我认为你过度设计了你的解决方案:一旦你有了一个 *os.File,你可以在其上设置读取和/或写入超时时间,以便相关操作自然超时,不需要锁定。像 这样 的东西应该可以满足你的需求。 - kostix
此外,在非常受限制的系统中,"泄漏"一个单独的goroutine来处理输入通常不是一个问题,它只会在整个过程中处于空闲状态。当您无法强制关闭fd时,例如stdin时,这种情况很常见。 - JimB
@kostix:我认为你的建议不可行,因为你没有一个*os.File:记住它会在os.Open上阻塞,所以你还没有得到*os.File。没有办法关闭或设置任何截止日期。不过还是谢谢你的尝试 :) - Jan
@JimB 同意...但由于我正在实现一个Jupyter内核,这些执行将经常发生,我不想每次都泄漏一个goroutine。此外,出于原则和好奇心...应该有一种正确的方法来做到这一点而没有泄漏。这个解决方案并不“漂亮”,但目前看起来似乎已经起作用了。 感谢您的答案! - Jan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接