使用FFI将'C'延迟函数导入Haskell

Jay*_*Jay 5 haskell ffi

delay使用类型调用的wiringPi'C'库中有一个函数

void delay(unsigned int howLong);

此函数延迟执行代码howLong毫秒.我在haskell中编写了绑定代码,以便能够调用此函数.haskell代码如下,

foreign import ccall "wiringPi.h delay" c_delay :: CUInt -> IO ()
hdelay :: Int -> IO ()
hdelay howlong = c_delay (fromIntegral howlong)
Run Code Online (Sandbox Code Playgroud)

在此之后,我写了一个简单的haskell程序来调用这个函数.简单的haskell代码如下..

- 导入相关库后,我做了

main = wiringPiSetup
    >> delay 5000
Run Code Online (Sandbox Code Playgroud)

但延迟不会发生,或者ghc编译器生成的可执行文件会立即退出.

有人能告诉我这里可能出现什么问题吗?向正确的方向微调会有所帮助.

干杯和问候.

Sal*_*Sal 2

请忽略块引用中的部分,并查看下面的更新 - 由于与之相关的评论,我保留了原始的非解决方案。

您应该将 标记importunsafe因为您希望主线程在函数执行时阻塞(请参阅下面 @carl 的评论)。默认情况下,导入是安全的,而不是unsafe。因此,将函数签名更改为此应该会使主线程阻塞:

foreign import ccall unsafe "wiring.h delay" c_delay :: CUInt -> IO ()
Run Code Online (Sandbox Code Playgroud)

另外,如果您计划编写多线程代码,多线程 FFI 的 GHC 文档非常有用。似乎也是一个很好的开始。

更新

该行为似乎是由于信号中断处理造成的(如果我没记错的话,这是在 GHC 7.4+ 中添加的以修复一些错误)。更多详细信息请参见: http://hackage.haskell.org/trac/ghc/wiki/Commentary/Rts/Signals

请注意上一页的评论:Signal handling differs between the threaded version of the runtime and the non-threaded version

方法 1 -在 FFI 代码中处理信号中断:下面是一个玩具代码,用于处理睡眠中的中断。我在 Linux 2.6.18 上使用 ghc 7.6.1 进行了测试。

C代码:

/** ctest.c **/
#include <unistd.h>
#include <stdio.h>
#include <time.h>

unsigned delay(unsigned sec)
{
  struct timespec req={0};
  req.tv_sec = sec;
  req.tv_nsec = 0;

  while (nanosleep(&req, &req) == -1) {
    printf("Got interrupt, continuing\n");
    continue;
    }
  return 1;
}
Run Code Online (Sandbox Code Playgroud)

哈斯克尔代码:

{-# LANGUAGE ForeignFunctionInterface #-}
-- Filename Test.hs
module Main (main) where
import Foreign.C.Types

foreign import ccall safe "delay" delay :: CUInt -> IO CUInt

main =  do
    putStrLn "Sleeping"
    n <- delay 2000
    putStrLn $ "Got return code from sleep: " ++ show n
Run Code Online (Sandbox Code Playgroud)

现在,使用 ghc 7.6.1(命令:)编译后ghc Test.hs ctest.c,它会等到睡眠完成,并在睡眠期间每次收到中断信号时打印一条消息:

./Test
Sleeping
Got interrupt, continuing
Got interrupt, continuing
Got interrupt, continuing
Got interrupt, continuing
....
....
Got return code from sleep: 1
Run Code Online (Sandbox Code Playgroud)

方法 2 -在调用 FFI 代码之前禁用 SIGVTALRM,然后重新启用:

我不确定禁用 SIGVTALRM 有何影响。如果您无法更改 FFI 代码,这是在 FFI 调用期间禁用 SIGVTALRM 的替代方法。因此,FFI 代码在睡眠期间不会被中断(假设是 SIGVTALRM 导致中断)。

{-# LANGUAGE ForeignFunctionInterface #-}
-- Test.hs
module Main (main) where
import Foreign.C.Types
import System.Posix.Signals

foreign import ccall safe "delay" delay :: CUInt -> IO CUInt

main =  do
    putStrLn "Sleeping"
    -- Block SIGVTALRM temporarily to avoid interrupts while sleeping
    blockSignals $ addSignal sigVTALRM emptySignalSet
    n <- delay 2
    putStrLn $ "Got return code from sleep: " ++ show n
    -- Unblock SIGVTALRM
    unblockSignals $ addSignal sigVTALRM emptySignalSet
    return ()
Run Code Online (Sandbox Code Playgroud)

  • 说什么?阻塞的东西应该是“安全的”,而不是“不安全的”。“不安全”的事物会阻止执行上下文在阻塞时切换到其他线程。作为副作用,这意味着它们还会阻止 GC 操作在阻塞时运行,这意味着它们可以暂停 RTS 中的“所有”线程。 (2认同)