Инициализация библиотеки unsafePerformIO и FFI

Я создаю модуль FFI для библиотеки в C, которая хочет, чтобы единовременная не реентерабельная функция вызывалась раньше, чем что-либо еще. Этот вызов идемпотентен, но с состоянием, так что я мог бы просто вызывать его при каждом вызове на Haskell. Но это медленно и из-за невступления может вызвать конфликты.

Так что это подходящее время для использования unsafePerformIO? Я мог бы обернуть Bool в небезопасный IORef или MVar, чтобы сделать эти вызовы инициализации идемпотентными, игнорируя последующие вызовы (вызовы, в которых глобальное скрытое состояние IORef равно False).

Если нет, как правильно это сделать?

2 ответа

Решение

Я предпочитаю подход, состоящий в том, чтобы выполнить однократную инициализацию и предоставить не поддаваемый токен в качестве доказательства того, что вы инициализировали машину.

Таким образом, ваши доказательства будут:

data Token = Token

которые вы экспортируете абстрактно.

Тогда ваша функция инициализации может вернуть это свидетельство.

init :: IO Token

Теперь вам нужно передать это доказательство вашему API:

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

и т.п.

Теперь вы можете обернуть все это монадой или некоторой средой инициализации более высокого порядка, чтобы сделать ее чище, но это основная идея.

Проблема с инициализацией библиотек C с использованием скрытого состояния заключается в том, что вы в конечном итоге либо не можете распараллелить доступ к библиотеке, либо у вас возникают проблемы в GHCi, смешивании скомпилированного и байт-кода с двумя разными версиями загруженной библиотеки C (что не получится при ошибка компоновщика).

Я хотел бы отметить, что в настоящее время предлагается новый трюк для / вместо withSocketsDo Нил Митчелл, основанный на evaluate ("Приводит к тому, что его аргумент оценивается как нормальная форма слабой головы при выполнении результирующего действия ввода-вывода."):

withSocketsDo act = do evaluate withSocketsInit; act 

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

Мой подход к устранению требования вызывать withSocketsDo состоял в том, чтобы сделать его очень дешевым, а затем разбрасывать его везде, где это может понадобиться.

Не обязательно это прекрасная идея...

(См. Также его ответ, объявляющий об этом обновлении в библиотеке.)

Другие вопросы по тегам