本技术备忘录是对在 Windows NT 3.51 和 4.0 上使用 NetScape Portable Runtime (NSPR) 的 IO 超时和中断的警示说明。由于 NT 上 NSPR IO 的当前实现存在限制,程序必须遵循以下指南
如果线程在文件描述符上调用 NSPR IO 函数,并且 IO 函数以 <tt>PR_IO_TIMEOUT_ERROR</tt> 或 <tt>PR_PENDING_INTERRUPT_ERROR</tt> 失败,则在线程退出之前必须关闭文件描述符。
在本备忘录中,我们将解释此指南试图解决的问题并讨论其局限性。
NT 上的 NSPR IO¶
NSPR 2.0 的 IO 模型是同步且阻塞的。调用 IO 函数的线程会被阻塞,直到 IO 操作完成,无论是由于 IO 完成成功还是发生错误。如果 IO 操作在指定超时时间之前无法完成,则 IO 函数将返回 <tt>PR_IO_TIMEOUT_ERROR</tt>。如果线程被另一个线程的 <tt>PR_Interrupt()</tt> 调用中断,则 IO 函数将返回 <tt>PR_PENDING_INTERRUPT_ERROR</tt>。
在 Windows NT 上,NSPR IO 使用 NT 的重叠(也称为异步)IO 实现。当线程调用 IO 函数时,线程使用其 <tt>PRThread</tt> 结构中的重叠缓冲区发出重叠 IO 请求。然后线程进入睡眠状态。同时,有专用的内部线程(称为空闲线程)监视 IO 完成端口以获取已完成的 IO 请求。如果 IO 完成端口出现已完成的 IO 请求,则空闲线程会获取它并唤醒先前发出 IO 请求的线程。这是线程被唤醒的正常方式。
IO 超时和中断¶
但是,NSPR 可能会在另外两种情况下唤醒线程
如果重叠 IO 请求在指定超时时间之前未完成。(请注意,我们无法在重叠 IO 请求上指定超时,因此所有超时都在 NSPR 级别处理。)在这种情况下,错误为 <tt>PR_IO_TIMEOUT_ERROR</tt>。
如果线程被另一个线程的 <tt>PR_Interrupt()</tt> 调用中断。在这种情况下,错误为 <tt>PR_PENDING_INTERRUPT_ERROR</tt>。
这两个错误是由 NSPR 层生成的,因此操作系统不知道发生了什么,并且重叠 IO 请求仍在进行中。操作系统仍然拥有指向线程 <tt>PRThread</tt> 结构中重叠缓冲区的指针。如果线程随后退出并且其 <tt>PRThread</tt> 结构被删除,则指向重叠缓冲区的指针将指向已释放的内存。这是有问题的。
通过关闭文件描述符取消重叠 IO¶
因此,我们需要在线程退出之前取消未完成的重叠 IO 请求。NT 的 <tt>CancelIo()</tt> 函数非常适合此目的。不幸的是,<tt>CancelIo()</tt> 在 NT 3.51 上不可用。因此,只要我们支持 NT 3.51,我们就无法采用此方法。在 NT 3.51 和 4.0 上都能可靠地取消未完成的重叠 IO 请求的唯一方法是关闭文件描述符,因此这就是本备忘录开头提到的经验法则。
局限性¶
这种看似严格的方式来强制完成未完成的重叠 IO 请求具有以下局限性
线程很难共享文件描述符。例如,假设线程 A 和线程 B 在同一个套接字上调用 <tt>PR_Accept()</tt>,并且它们同时超时。根据经验法则,这两个线程都会关闭套接字。第一个 <tt>PR_Close()</tt> 会成功,但第二个 <tt>PR_Close()</tt> 将释放已释放的内存。一个可能有效的解决方案是使用锁来确保一次只有一个线程可以使用该套接字。
一旦发生超时或中断错误,文件描述符将不再可用。假设文件描述符旨在在进程的整个生命周期中使用,例如日志文件,这实际上是不可接受的。一个可能的解决方案是添加一个 <tt>PR_DisableInterrupt()</tt> 函数,以便在访问此类文件描述符时关闭中断。
一个相关的已知错误是超时和中断在 NT 上对 <tt>PR_Connect()</tt> 不起作用。此错误是由于我们 NT 实现中的另一个限制造成的。
结论¶
只要我们需要支持 NT 3.51,我们就需要根据以下指南进行编程:在发生 IO 超时或中断错误后,线程必须确保在退出之前关闭文件描述符。程序还应注意共享文件描述符并在需要在整个进程中保持打开状态的文件上使用 IO 超时或中断。
当我们停止支持 NT 3.51 时,我们可以考虑使用 NT 4 的 <tt>CancelIo()</tt> 函数在发生 IO 超时或中断错误时取消未完成的重叠 IO 请求。如果 <tt>CancelIo()</tt> 确实按广告宣传的那样工作,那么它应该从根本上解决这个问题。
如果 IO 超时和中断的这些限制不符合您程序的需求,您可以考虑使用 NSPR 的 Win95 版本。Win95 版本可以在 NT 上无问题地运行,但您将失去 NT 纤程和异步 IO 提供的更好性能。
原始文档信息¶
上次更新日期:2004 年 12 月 1 日