unsafePerformIO和FFI库初始化

时间:2013-01-03 18:46:49

标签: c haskell ffi unsafe-perform-io

我正在为C中的库创建一个FFI模块,它希望在其他任何东西之前调用一次非重入函数。这个调用是幂等的,但是有状态的,所以我可以在每个Haskell调用中调用它。但它很慢并且由于不可重入而可能导致冲突。

这是使用unsafePerformIO的正确时间吗?我可以将Bool包装在一个不安全的IORef或MVar中,通过忽略后续调用(全局隐藏IORef状态为False的调用)使这些初始化调用成为幂等的。

如果没有,这样做的正确方法是什么?

2 个答案:

答案 0 :(得分:11)

我更喜欢初始化一次并提供不可伪造的令牌作为您初始化机器的证据。

所以你的证据是:

data Token = Token

你抽象地导出。

然后你的初始化函数可以返回这个证据。

init :: IO Token

现在,您需要将该证明传递给您的API:

bar  :: Token -> IO Int
bar !tok = c_call_bar

现在,您可以使用monad或更高阶的初始化环境来包装这些内容,以使其更清晰,但这是基本的想法。

使用隐藏状态初始化C库的问题是,您最终要么无法并行访问库,要么在GHCi中遇到问题,混合编译和字节码,加载了两个不同版本的C库(其中会因链接器错误而失败。)

答案 1 :(得分:2)

我想要注意的是,目前基于is suggested("强迫它)的/ {而不是withSocketsDo by Neil Mitchell的一些新技巧evaluate当执行结果IO动作时,要被评估为弱头正常形式的参数。"):

withSocketsDo act = do evaluate withSocketsInit; act 

{-# NOINLINE withSocketsInit #-}
withSocketsInit = unsafePerformIO $ do
    initWinsock
    termWinsock
  

我删除调用withSocketsDo的要求的方法是   使它非常便宜,然后将它撒在可能需要的任何地方。

这不一定是个好主意......

(另请参阅his answer在图书馆中宣布此更新。)