unsafePerformIO和FFI库初始化

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

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

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

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

所以你的证据是:

data Token = Token 

你抽象地导出。

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

 init :: IO Token 

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

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

等等

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

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

我想要注意的是,目前Neil Mitchell 建议使用/而不是withSocketsDo 的一些新技巧,基于evaluate (“强制其参数在执行结果IO操作时被评估为弱头正常forms。”):

 withSocketsDo act = do evaluate withSocketsInit; act {-# NOINLINE withSocketsInit #-} withSocketsInit = unsafePerformIO $ do initWinsock termWinsock 

我删除使用套接字调用的要求的方法是使其非常便宜,然后将其洒到可能需要的任何地方。

不一定这是一个美丽的想法……

(另请参阅他在图书馆中宣布此更新的答案 。)