根据GHC user guide外国电话可以标记interruptible
,但是,我无法使其工作。我在GNU / Linux上使用ghc 8.4.3
。
例如,参见cbits.h:
/* cbits.h */
void loopForever();
cbits.c:
/* cbits.c */
#include <stdio.h>
#include <unistd.h>
#include "cbits.h"
void loopForever()
{
for (;;)
{
printf("Tick\n");
sleep(1);
}
}
最后是Test.hs:
-- Test.hs
{-# LANGUAGE InterruptibleFFI #-}
module Main where
import Control.Concurrent
import Control.Concurrent.Async
main :: IO ()
main = race_ loopForever $ do
threadDelay 2000000
putStrLn "Finished"
foreign import ccall interruptible "cbits.h"
loopForever :: IO ()
我和ghc -threaded -o a.out cbits.c Test.hs
一起编译了这一切。
现在,我预计代码会在2秒后停止,但是在打印完“完成”后它仍会继续运行。它确实在用户指南中提到了This is **usually** enough to cause a blocking system call to return <...>
,所以我的c函数特别糟糕,或者我在Haskell方面做错了什么?
根据文档,RTS尝试中断参与外部调用的线程的方式是向其发送SIGPIPE信号。由于RTS安装的处理程序忽略了信号,唯一的影响是 - 如果线程参与长时间运行的系统调用 - 该调用很可能立即返回EINTR。由于你的外部函数没有检查printf
和sleep
的返回调用以查看它们是否被中断,所以线程在路上快乐。
现在在理想的世界中,修改函数以检查返回值是否足以表明函数已被中断,如下所示:
void loopForever()
{
for (;;)
{
if (printf("Tick\n") < 0) break;
if (sleep(1) != 0) break;
}
}
不幸的是,sleep()
的界面是脑死亡 - 如果它被中断,它会返回剩下的完整秒数,如果这是零 - 它总是在你的函数中 - 它返回零。叹...
您可以切换到usleep
,如果被中断,它会明智地返回-1
,或者您可以使用设置errno
为零的技巧并检查printf
或sleep
是否更改它(到EINTR,但您也可以在任何非零数字上中止):
/* cbits.c */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include "cbits.h"
void loopForever()
{
errno = 0;
while (!errno)
{
printf("Tick\n");
sleep(1);
}
}
那应该做你想要的。
现在,根据您的实际用例,您可能会发现安装SIGPIPE处理程序更有帮助,特别是如果您希望在长时间运行的计算中阻塞该线程(没有任何系统调用中断)。完成后,请确保卸载处理程序。这是一个例子:
/* cbits.c */
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "cbits.h"
volatile sig_atomic_t stop = 0;
void sigpipe_handler()
{
stop = 1;
}
void loopForever()
{
struct sigaction oldact, newact;
bzero(&newact, sizeof(newact));
newact.sa_handler = sigpipe_handler;
sigaction(SIGPIPE, &newact, &oldact);
while (!stop)
{
// loop forever until interrupted
}
printf("Stopped!");
sigaction(SIGPIPE, &oldact, NULL);
}