2013-01-03 99 views
11

我正在創建一個FFI模塊到C庫中,它希望在其他任何事情之前調用一次性的非重入函數。這個調用是冪等的,但是有狀態的,所以我可以在每個Haskell調用中調用它。但速度很慢,由於非重入可能會導致衝突。unsafePerformIO和FFI庫初始化

那麼這是使用unsafePerformIO的正確時機嗎?我可以將Bool包裝在不安全的IORef或MVar中,通過忽略後續調用(全局隱藏的IORef狀態爲False的調用)來使這些初始化調用爲冪等。

如果沒有,那麼做到這一點的正確方法是什麼?

回答

11

我更喜歡初始化一次的方法,並提供一個不可僞造的標記作爲您已初始化機器的證據。

所以,你的證據是:

data Token = Token 

您導出抽象。

然後你的初始化函數可以返回這個證據。

init :: IO Token 

現在,你需要的是證明傳遞到您的API:

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

您現在可以換這個東西了一個單子,或者一些更高階的初始化環境使它更清潔,但這是基本的想法。

使用隱藏狀態初始化C庫的問題是,您最終無法並行訪問庫,或者在GHCi中遇到問題,將編譯和字節碼混合,加載了兩個不同版本的C庫(這將失敗並出現鏈接錯誤)。

+2

一個選擇,已經看到使用是圍繞主的'withX'包裝。這沒有給出任何靜態保證,我只是說它有優先權(例如,來自網絡包中的「帶有套接字」)。 –

+1

啊,是的,好點。比'withToken $ \ t - >'簡單,但沒有保證。 –

+1

啊,非常棒!這是一個更好的解決方案。我一直擔心全局狀態如何與多線程交互(是一個不安全的MVar線程本地運行時本地?)。這也會使初始化失敗在Haskell運行時中定位,而不僅僅是隱式和隱藏。 –

2

我想指出,當前爲/,而不是withSocketsDoby Neil Mitchell一些新的技巧is suggested,基於evaluate(「強制執行所產生的IO操作時它的參數進行評估,以微弱的頭部正常形態。」):

withSocketsDo act = do evaluate withSocketsInit; act 

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

我的辦法,消除要求調用withSocketsDo是 使它很便宜,然後到處灑它可能是必要的。

不一定,這是一個美好的理想......

(參見his answer宣佈在圖書館此更新。)