2013-01-03 24 views
11

Sto creando un modulo FFI in una libreria in C che richiede una funzione 1-tempo e non-rientranti da chiamare prima di qualsiasi altra cosa. Questa chiamata è idempotente, ma statica, quindi potrei semplicemente chiamarla in ogni chiamata Haskell. Ma è lento e a causa della non-rientranza potrebbe causare conflitti.Inizializzazione della libreria unsafePerformIO e FFI

Quindi è questo il momento giusto per usare unsafePerformIO? Potrei avvolgere un Bool in un IORef o MVar non sicuro per rendere idenominate queste chiamate di inizializzazione ignorando le chiamate successive (chiamate in cui lo stato IORef nascosto e globale è False).

In caso contrario, qual è il modo giusto per farlo?

risposta

11

Preferisco l'approccio di inizializzazione una volta e fornendo un token indimenticabile come prova dell'avvenuta inizializzazione della macchina.

Così la vostra testimonianza potrebbe essere:

data Token = Token 

che si esporta in modo astratto.

Quindi la funzione di inizializzazione può restituire questa prova.

init :: IO Token 

Ora, è necessario passare che prova a vostro API:

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

ecc

È ora possibile avvolgere questa roba con una monade, o qualche alto ambiente di inizializzazione per renderlo più pulito, ma questa è l'idea di base.

Il problema con l'inizializzazione delle librerie C che utilizzano lo stato nascosto è che si finisce per non essere in grado di parallelizzare l'accesso alla libreria, o di avere problemi in GHCi, mescolando compilato e bytecode, con due diverse versioni della libreria C caricate (che fallirà con un errore del linker).

+2

Un'alternativa che ha visto l'uso è il 'withX' ​​wrapper principale. Questo non dà garanzie statiche, sto solo dicendo che c'è la precedenza (ad esempio 'withSocketsDo' dal pacchetto di rete). –

+1

Ah si, buon punto. Più semplice del 'withToken $ \ t ->', ma nessuna garanzia. –

+1

Ah, eccellente! Questa è una soluzione molto migliore. Ero preoccupato di come lo stato globale avrebbe interagito con il multithreading (è un thread MVar non sicuro locale, locale di runtime?). Questo rende anche l'errore di inizializzazione localizzabile nel runtime di Haskell invece che solo implicito e nascosto. –

2

mi piacerebbe sottolineare che attualmente qualche nuovo trucco is suggested per/al posto di withSocketsDoby Neil Mitchell, sulla base di evaluate ("le forze il suo argomento da valutare a testa debole forma normale quando viene eseguita l'azione risultante IO."):

withSocketsDo act = do evaluate withSocketsInit; act 

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

il mio approccio a rimuovere l'obbligo di chiamare withSocketsDo è stato quello di rendere molto a buon mercato, poi cospargere ovunque potrebbe essere necessaria.

Non necessariamente questa è una bella idea ...

(Vedi anche his answer annunciando questo aggiornamento nella biblioteca.)

Problemi correlati