2013-01-03 7 views
11

Ich erstelle ein FFI-Modul zu einer Bibliothek in C, das eine einmalige nicht-reentrante Funktion aufrufen möchte, bevor alles andere aufgerufen wird. Dieser Anruf ist idempotent, aber statusbehaftet, also könnte ich ihn bei jedem Haskell-Anruf anrufen. Aber es ist langsam und aufgrund der Nichtreentry kann es zu Konflikten kommen.unsafePerformIO- und FFI-Bibliotheksinitialisierung

Ist dies der richtige Zeitpunkt für unsafePerformIO? Ich könnte ein Bool in eine unsichere IORef oder MVar wickeln, um diese Initialisierungsaufrufe idempotent zu machen, indem ich nachfolgende Aufrufe ignoriere (Aufrufe, bei denen der globale, versteckte IORef-Status Falsch ist).

Wenn nicht, was ist der richtige Weg?

Antwort

11

Ich bevorzuge den Ansatz der Initialisierung einmal und Bereitstellung eines fälschungssicheren Token als Beweis, dass Sie die Maschine initialisiert haben.

So würde Ihr Beweis sein:

data Token = Token 

, die Sie abstrakt exportieren.

Dann kann Ihre Initialisierungsfunktion diese Beweise zurückgeben.

init :: IO Token 

Nun müssen Sie diesen Nachweis zu Ihrem API weitergeben müssen:

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

usw.

Sie jetzt dieses Zeug mit einer Monade wickeln kann, oder eine höhere Ordnung Initialisierungsumgebung zu mach es sauberer, aber das ist die Grundidee.

Das Problem mit der Initialisierung von C-Bibliotheken mit versteckten Zustand ist, dass Sie entweder nicht in der Lage sein, den Zugriff auf die Bibliothek zu parallelisieren, oder Probleme in GHCi, Mischen kompiliert und Bytecode, mit zwei verschiedenen Versionen der C-Bibliothek geladen (was mit einem Linker-Fehler fehlschlägt).

+2

Eine Alternative, die Verwendung ist der 'withX' ​​Wrapper um Haupt gesehen hat. Dies gibt keine statischen Garantien, ich sage nur, dass es Vorrang hat (zB "withSocketsDo" aus dem Netzwerk-Paket). –

+1

Ah ja, guter Punkt. Einfacher als die 'withToken $ \ t ->', aber keine Garantien. –

+1

Ahh, ausgezeichnet! Dies ist eine viel bessere Lösung. Ich hatte mir Sorgen gemacht, wie der globale Staat mit Multithreading interagieren würde (ist ein unsicherer MVar-Thread lokal, Laufzeit lokal?). Dadurch wird auch der Initialisierungsfehler in der Haskell-Laufzeit lokalisierbar anstatt nur implizit und versteckt. –

2

Ich mag, dass derzeit einigen neuen Trick beachten is suggested für/statt withSocketsDoby Neil Mitchell, basierend auf evaluate („Zwingt ihr Argument zu schwacher Kopfnormalform ausgewertet werden, wenn die resultierende Aktion IO ausgeführt wird.“):

withSocketsDo act = do evaluate withSocketsInit; act 

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

Mein Ansatz, um die Anforderung zu entfernen withSocketsDo zu es sehr billig machen war zu rufen, es dann überall streut es erforderlich sein könnte.

Nicht unbedingt dieses eine schöne Idee ist ...

(Siehe auch his answer dieses Update in der Bibliothek ankündigt.)