poll()c函数在windows上

时间:2009-11-04 05:27:27

标签: c mingw

我想知道我是否可以在MinGW开发链上使用poll()函数。我有CodeBlocks + MinGW。非常感谢。

3 个答案:

答案 0 :(得分:2)

最后,听说mingw不支持poll()或功能有限。

那是在2009年。

在2020年...受支持,但仍然存在缺陷,如Git 2.25.2(2020年3月)所示,其中对MinGW的poll()仿真进行了改进。

请参见commit 94f4d01Alexandr Miloslavskiy (SyntevoAlex)(2020年2月17日)。
(由Junio C Hamano -- gitster --commit 1ac37de中合并,2020年3月9日)

mingw:发送STDIN时挂起的解决方法

签名人:Alexandr Miloslavskiy

说明

这里的问题是poll()的实现存在缺陷。
当尝试查看是否可以不阻塞地编写管道时,它最终调用NtQueryInformationFile()并测试WriteQuotaAvailable

但是,配额的含义被误解了
当某些数据写入管道,在管道上有待处理的读取时,配额的值会减小。
因此,如果有一个待读的读取大小大于管道缓冲区的大小,则poll()会认为该管道不可写,并且将永远挂起,这通常意味着两个管道用户都将死锁。

我研究了问题,发现Windows管道跟踪两个值:QuotaUsedBytesInQueue

poll()中的代码显然想知道BytesInQueue而不是配额。
不幸的是,BytesInQueue仅可从管道的读取端请求,而poll()则可接收写入端。

poll()的git实现是从gnulib复制而来的,到目前为止它还包含一个有缺陷的实现。

我也了解了cygwin中的实现,该实现也以微妙的方式被破坏了。它在pipe_data_available()中使用以下代码:

fpli.WriteQuotaAvailable = (fpli.OutboundQuota - fpli.ReadDataAvailable)

但是,ReadDataAvailable总是在管道的写入端返回0,从而将代码转换为返回管道总缓冲区大小的混淆版本,我想这反过来会使poll()总是说管道可写。
引入代码的提交并没有说明此更改,因此可能只是一些调试代码。

这些是git中使用的典型大小:

  • 0x2000-默认读取大小为strbuf_read()
  • 0x1000-CRT中的默认读取大小,由strbuf_getwholeline()
  • 使用
  • 0x2000-compat\mingw.c中的管道缓冲区大小

结果,一旦子进程使用strbuf_read(),父进程中的poll()将永远挂起,从而使两个进程都死锁。

这会导致两个可观察到的行为:

  1. 如果父进程开始快速发送STDIN(通常是这种情况),则第一个poll()将成功,第一个块将通过。
    MAX_IO_SIZE_DEFAULT是8MB,因此,如果STDIN超过8MB,它将死锁。
  2. 如果父进程出于某种原因(包括操作系统调度程序)稍等,而子进程首先发出strbuf_read(),则即使在小型STDIN上,子进程也会立即死锁。

该问题由git stash push说明,该问题当前会将整个补丁读取到内存中,然后通过STDIN发送到git apply
如果补丁超过8MB,则git会在Windows上挂起。

可能的解决方案

  1. 以某种方式获得BytesInQueue而不是QuotaUsed
    我进行了彻底的搜索,没有找到从管道的写入端获取值的任何方法。
  2. 还要将管道的读取端交给poll()
    可以这样做,但是可能会邀请一些脏代码,因为poll()
    • 可以一次接受多个管道
    • 可以接受不是管道的东西
    • 应该有众所周知的签名。
  3. 使poll()对于管道的写入结束始终回复“可写”
    毕竟,似乎cygwin(偶然地?)已经做了很多年了。
    另外,应注意,pump_io_round()写入8MB的块,完全忽略了管道的缓冲区大小仅为8KB的事实,这意味着管道在该一次写入过程中被阻塞了很多次。
    如果孩子的STDERR / STDOUT在尝试处理8MB STDIN时被阻塞,则可能会导致死锁。
    这样的死锁可以通过以下方式克服:每轮写入少于管道缓冲区的大小,并始终在开始下一轮之前从STDOUT / STDERR读取所有内容。
    因此,使poll()始终答复“可写”不应引起任何新问题或阻止任何将来的解决方案。
  4. 增加管道缓冲区的大小
    BytesInQueueQuotaUsed之间的区别在于待处理读取的大小。因此,如果缓冲区大于读取的大小,poll()将不会轻易挂起。但是,我发现例如strbuf_read()会在读取大量输入时变得越来越饥饿,最终超过任何合理的管道缓冲区大小。

选择的解决方案

使poll()对于管道的写入结束始终返回“可写”。
希望有一天,有人会找到一种正确实施它的方法。

复制

printf "%8388608s" X >large_file.txt 
git stash push --include-untracked -- large_file.txt

为了避免降低测试套件的速度,我决定不将其作为测试。
我不希望再遇到特定的问题,git stash push可能会被重新设计以避免通过STDIN发送整个补丁。

答案 1 :(得分:1)

最后我听说,poll()要么不支持,要么在mingw上提供有限的功能。但是,您可以提供自己内部使用poll()的基本select()

答案 2 :(得分:1)

Windows还提供WSAPoll()功能,但性能比select()差,从MSDN博客开始,它只是实现了改进的可移植性:

http://blogs.msdn.com/b/wndp/archive/2006/10/26/wsapoll.aspx