如何使FFI呼叫中断

问题描述 投票:5回答:1

根据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方面做错了什么?

haskell interrupt ghc ffi
1个回答
3
投票

根据文档,RTS尝试中断参与外部调用的线程的方式是向其发送SIGPIPE信号。由于RTS安装的处理程序忽略了信号,唯一的影响是 - 如果线程参与长时间运行的系统调用 - 该调用很可能立即返回EINTR。由于你的外部函数没有检查printfsleep的返回调用以查看它们是否被中断,所以线程在路上快乐。

现在在理想的世界中,修改函数以检查返回值是否足以表明函数已被中断,如下所示:

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

不幸的是,sleep()的界面是脑死亡 - 如果它被中断,它会返回剩下的完整秒数,如果这是零 - 它总是在你的函数中 - 它返回零。叹...

您可以切换到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);
}
© www.soinside.com 2019 - 2024. All rights reserved.