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

时间:2013-03-05 14:50:24

标签: haskell ffi

在wiringPi'C'库中有一个名为delay的函数,类型为

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)

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

- 导入相关库后我做了

main = wiringPiSetup
    >> delay 5000

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

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

干杯和问候。

1 个答案:

答案 0 :(得分:2)

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

  

您应该将import标记为unsafe,因为您需要主广告   函数执行时阻塞的线程(请参阅下面的注释   @carl)。默认情况下,导入为safe,而不是unsafe。所以,改变   这个函数签名应该成为主线程块:

foreign import ccall unsafe "wiring.h delay" c_delay :: CUInt -> IO ()
     

另外,如果您打算编写多线程代码,GHC docs for multi-threaded FFI非常有用。 This似乎也是一个很好的首发。

<强>更新

这种行为似乎是由于信号中断处理(如果我没记错的话,这是在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;
}

Haskell代码:

{-# 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

现在,在使用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

方法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 ()