我正在为C中的库创建一个FFI模块,它希望在其他任何东西之前调用一次非重入函数。这个调用是幂等的,但是有状态的,所以我可以在每个Haskell调用中调用它。但它很慢并且由于不可重入而可能导致冲突。
那么这是使用unsafePerformIO的合适时机吗?我可以将Bool包装在一个不安全的IORef或MVar中,通过忽略后续调用(全局隐藏IORef状态为False的调用)使这些初始化调用成为幂等的。
如果没有,这样做的正确方法是什么?
我正在为C中的库创建一个FFI模块,它希望在其他任何东西之前调用一次非重入函数。这个调用是幂等的,但是有状态的,所以我可以在每个Haskell调用中调用它。但它很慢并且由于不可重入而可能导致冲突。
那么这是使用unsafePerformIO的合适时机吗?我可以将Bool包装在一个不安全的IORef或MVar中,通过忽略后续调用(全局隐藏IORef状态为False的调用)使这些初始化调用成为幂等的。
如果没有,这样做的正确方法是什么?
我更喜欢初始化一次并提供不可伪造的令牌作为您初始化机器的证据。
所以你的证据是:
data Token = Token
你抽象地导出。
然后你的初始化函数可以返回这个证据。
init :: IO Token
现在,您需要将该证明传递给您的API:
bar :: Token -> IO Int
bar !tok = c_call_bar
等等
现在,您可以使用monad或更高阶的初始化环境来包装这些内容,以使其更清晰,但这是基本的想法。
使用隐藏状态初始化C库的问题是,您最终要么无法并行访问库,要么在GHCi中遇到问题,混合编译和字节码,加载了两个不同版本的C库(这将失败链接器错误)。
我更喜欢初始化一次并提供不可伪造的令牌作为您初始化机器的证据。
所以你的证据是:
data Token = Token
你抽象地导出。
然后你的初始化函数可以返回这个证据。
init :: IO Token
现在,您需要将该证明传递给您的API:
bar :: Token -> IO Int
bar !tok = c_call_bar
等等
现在,您可以使用monad或更高阶的初始化环境来包装这些内容,以使其更清晰,但这是基本的想法。
使用隐藏状态初始化C库的问题是,您最终要么无法并行访问库,要么在GHCi中遇到问题,混合编译和字节码,加载了两个不同版本的C库(这将失败链接器错误)。
我想指出目前一些新技巧 有人建议 为/而不是 withSocketsDo
作者:尼尔米切尔, 基于 evaluate
(“当执行生成的IO操作时,强制将其参数计算为弱头正常形式。”):
withSocketsDo act = do evaluate withSocketsInit; act
{-# NOINLINE withSocketsInit #-}
withSocketsInit = unsafePerformIO $ do
initWinsock
termWinsock
我删除调用withSocketsDo的要求的方法是 使它非常便宜,然后将它洒在可能需要的任何地方。
不一定这是一个美丽的想法......
(也可以看看 他的回答 在库中宣布此更新。)