gpt4 book ai didi

haskell - 如何使 FFI 调用可中断

转载 作者:行者123 更新时间:2023-12-02 12:07:19 27 4
gpt4 key购买 nike

根据GHC user guide国外来电可标记interruptible ,但是,我无法让它发挥作用。我正在使用ghc 8.4.3在 GNU/Linux 上。

例如,参见这个 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 秒后停止,但是即使在打印“Finished”之后它仍然继续运行。它确实提到了 This is **usually** enough to cause a blocking system call to return <...>在用户指南中,那么这是我的c函数特别糟糕的情况,还是我在Haskell方面做错了什么?

最佳答案

根据文档,RTS 尝试中断参与外部调用的线程的方式是向其发送 SIGPIPE 信号。由于 RTS 安装的处理程序会忽略该信号,因此唯一的影响是——如果线程正在进行长时间运行的系统调用——该调用可能会立即返回并带有 EINTR。由于您的外部函数不会检查 printfsleep 的返回调用来查看它们是否被中断,因此线程会愉快地继续前进。

现在在理想的世界中,修改函数来检查指示函数已被中断的返回值就足够了,如下所示:

void loopForever()
{
for (;;)
{
if (printf("Tick\n") < 0) break;
if (sleep(1) != 0) break;
}
}

不幸的是,sleep() 的接口(interface)是脑残的——如果它被中断,它会返回剩余的完整秒数,如果这是零——它总是这样在你的函数中——它返回零。叹息...

您可以切换到 usleep,如果中断,它会明智地返回 -1,或者您可以使用将 errno 设置为零的技巧,然后检查 printfsleep 是否更改它(更改为 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);
}

关于haskell - 如何使 FFI 调用可中断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52194264/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com