在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编译器生成的可执行文件会立即退出.
有人能告诉我这里可能出现什么问题吗?向正确的方向微调会有所帮助.
干杯和问候.
请忽略块引用中的部分,并查看下面的更新 - 由于与之相关的评论,我保留了原始的非解决方案。
您应该将 标记
import为unsafe因为您希望主线程在函数执行时阻塞(请参阅下面 @carl 的评论)。默认情况下,导入是安全的,而不是unsafe。因此,将函数签名更改为此应该会使主线程阻塞:Run Code Online (Sandbox Code Playgroud)foreign import ccall unsafe "wiring.h delay" c_delay :: CUInt -> IO ()另外,如果您计划编写多线程代码,多线程 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)