请忽略块引用中的部分,并查看下面的更新 - 由于与之相关的评论,我保留了原始的非解决方案。
您应该将其标记为import
,unsafe
因为您希望主线程在函数执行时阻塞(请参阅@carl 下面的评论)。默认情况下,导入是安全的,而不是unsafe
. 因此,将函数签名更改为此应该使主线程阻塞:
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 代码中处理信号中断:下面是一个玩具代码,它处理睡眠中的中断。我使用 ghc 7.6.1 在 Linux 2.6.18 上对其进行了测试。
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;
}
哈斯克尔代码:
{-# 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 (command: ghc Test.hs ctest.c
) 编译后,它会一直等到 sleep 完成,并在每次在 sleep 期间收到中断信号时打印一条消息:
./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 ()