根据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方面做错了?
答案 0 :(得分:3)
根据文档,RTS尝试中断参与外部调用的线程的方式是通过向其发送SIGPIPE信号。由于RTS安装的处理程序会忽略该信号,因此唯一的效果是-如果线程参与了长时间运行的系统调用-该调用很可能会立即以EINTR返回。由于您的外部函数不会检查printf
和sleep
的返回调用以查看它们是否被中断,因此线程会轻松进行。
现在在理想的世界中,只需修改您的函数以检查指示函数已被中断的返回值就足够了,就像这样:
void loopForever()
{
for (;;)
{
if (printf("Tick\n") < 0) break;
if (sleep(1) != 0) break;
}
}
不幸的是,sleep()
的接口是死脑筋-如果被中断,它将返回剩余的整秒数,如果为零,则它始终在您的函数中- -返回零。 igh ...
您可以切换到usleep
并在中断时明智地返回-1
,也可以使用将errno
设置为零并检查printf
或{{ 1}}对其进行更改(更改为EINTR,但您也可以放弃任何非零数字):
sleep
那应该做你想要的。
现在,根据您的实际用例,您可能会发现安装SIGPIPE处理程序更有用,特别是如果您希望线程在长时间运行的计算中被阻塞(没有任何系统调用中断)时,尤其如此。完成后,请务必确保卸载处理程序。这是一个示例:
/* 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);
}
}