可以在Linux上实现正确的故障安全过程共享屏障吗?

在过去的一个问题中,我曾经问过没有破坏种族的pthread障碍:

How can barriers be destroyable as soon as pthread_barrier_wait returns?

并从迈克尔·伯尔(Michael Burr后来我们通过一些想法,但从来没有达到一个令人满意的结论,甚至没有开始陷入资源失败的情况.

在Linux上有可能做出满足以下条件的障碍:

>进程共享(可以在任何共享内存中创建).
>在屏障等待功能返回之后,立即安全地取消映射或破坏任何线程的屏障.
>由于资源分配失败,不能失败.

迈克尔解决流程共享案例(见相关问题)的尝试有一种不幸的属性,即某种系统资源必须在等待时间分配,这意味着等待可能会失败.而且当屏障等待失败时,呼叫者可以合理地做什么是不清楚的,因为屏障的整个一点是在剩余的N-1线程到达之前不安全

内核空间解决方案可能是唯一的方法,但即使这是困难的,因为信号中断等待的可能性,没有可靠的方式来恢复它…

最佳答案
这对于Linux futex API是不可能的,我认为这也是可以证明的.

我们在这里基本上是一个场景,其中N个进程必须被一个最终进程可靠地唤醒,并且进一步的任何进程可能在最后的唤醒之后可能触及任何共享的内存(因为它可能被异步地破坏或重用).虽然我们可以轻松地唤醒所有流程,但基本的竞争条件是在唤醒和等待之间;如果我们在等待之前发出唤醒,那么分辨率就不会唤醒.

通常的解决方案是这样的,让straggler用等待原子地检查状态变量;如果唤醒已经发生,这样可以避免睡眠.但是,我们无法在这里做到这一点 – 一旦唤醒成为可能,触摸共享内存就不安全了!

另一种方法是实际检查所有进程是否已经进入睡眠状态.但是,这是不可能的Linux futex API;来自FUTEX_WAKE的返回值是侍室数量的唯一指示;如果回报少于您预期的服务员人数,您会知道有些还没有睡着.然而,即使我们发现我们没有唤醒足够的服务员,现在做任何事情还为时太晚 – 其中一个醒来的过程可能已经破坏了这个障碍!

所以,不幸的是,这种立即可破坏的原语不能用Linux futex API来构建.

请注意,在一个服务员的具体情况下,一个服务员,可能有可能解决问题;如果FUTEX_WAKE返回零,我们知道没有人实际上已经被唤醒了,所以你有机会恢复.然而,使其成为一种有效的算法是非常棘手的.

为了解决这个问题的futex模型添加一个强大的扩展是棘手的.基本的问题是,我们需要知道什么时候N线程已经成功地进入了等待,并且原子地唤醒它们.然而,任何这些线程可能会随时等待运行一个信号处理程序 – 实际上,Waker线程也可能会等待信号处理程序.

然而,可能有效的一种可能方式是NT API中的keyed event模型的扩展.使用键控事件,线程从锁中成对释放;如果你没有’等待”’发布”’释放’调用阻止’等待’.

这本身是不够的,由于信号处理程序的问题;然而,如果我们允许“释放”调用来指定要以原子方式唤醒的线程数,这是有效的.你只需在屏障中的每个线程减去一个计数,然后在该地址上的一个键控事件上“等待”.最后一个线程“释放”N-1个线程.内核不允许任何唤醒事件被处理,直到所有N-1线程已经进入该键控事件状态;如果任何线程由于信号(包括释放线程)而离开了futex调用,这将防止所有线程返回之前的任何唤醒.

转载注明原文:可以在Linux上实现正确的故障安全过程共享屏障吗? - 代码日志