我正在创建一个FFI模块到C库中,它希望在其他任何事情之前调用一次性的非重入函数。这个调用是幂等的,但是有状态的,所以我可以在每个Haskell调用中调用它。但速度很慢,由于非重入可能会导致冲突。unsafePerformIO和FFI库初始化
那么这是使用unsafePerformIO的正确时机吗?我可以将Bool包装在不安全的IORef或MVar中,通过忽略后续调用(全局隐藏的IORef状态为False的调用)来使这些初始化调用为幂等。
如果没有,那么做到这一点的正确方法是什么?
我正在创建一个FFI模块到C库中,它希望在其他任何事情之前调用一次性的非重入函数。这个调用是幂等的,但是有状态的,所以我可以在每个Haskell调用中调用它。但速度很慢,由于非重入可能会导致冲突。unsafePerformIO和FFI库初始化
那么这是使用unsafePerformIO的正确时机吗?我可以将Bool包装在不安全的IORef或MVar中,通过忽略后续调用(全局隐藏的IORef状态为False的调用)来使这些初始化调用为幂等。
如果没有,那么做到这一点的正确方法是什么?
我更喜欢初始化一次的方法,并提供一个不可伪造的标记作为您已初始化机器的证据。
所以,你的证据是:
data Token = Token
您导出抽象。
然后你的初始化函数可以返回这个证据。
init :: IO Token
现在,你需要的是证明传递到您的API:
bar :: Token -> IO Int
bar !tok = c_call_bar
等
您现在可以换这个东西了一个单子,或者一些更高阶的初始化环境使它更清洁,但这是基本的想法。
使用隐藏状态初始化C库的问题是,您最终无法并行访问库,或者在GHCi中遇到问题,将编译和字节码混合,加载了两个不同版本的C库(这将失败并出现链接错误)。
我想指出,当前为/,而不是withSocketsDo
by Neil Mitchell一些新的技巧is suggested,基于evaluate
(“强制执行所产生的IO操作时它的参数进行评估,以微弱的头部正常形态。”):
withSocketsDo act = do evaluate withSocketsInit; act
{-# NOINLINE withSocketsInit #-}
withSocketsInit = unsafePerformIO $ do
initWinsock
termWinsock
我的办法,消除要求调用withSocketsDo是 使它很便宜,然后到处洒它可能是必要的。
不一定,这是一个美好的理想......
(参见his answer宣布在图书馆此更新。)
一个选择,已经看到使用是围绕主的'withX'包装。这没有给出任何静态保证,我只是说它有优先权(例如,来自网络包中的“带有套接字”)。 –
啊,是的,好点。比'withToken $ \ t - >'简单,但没有保证。 –
啊,非常棒!这是一个更好的解决方案。我一直担心全局状态如何与多线程交互(是一个不安全的MVar线程本地运行时本地?)。这也会使初始化失败在Haskell运行时中定位,而不仅仅是隐式和隐藏。 –