【问题标题】:STM and unsafePerformIO in HaskellHaskell 中的 STM 和 unsafePerformIO
【发布时间】:2020-09-27 15:15:33
【问题描述】:

documentation for STM 声明:

在原子内部使用 unsafePerformIO 也很危险,但原因不同。有关更多信息,请参阅 unsafeIOToSTM。

在使用线程和异步异常时,有一些函数可以屏蔽异步异常,以便安全地分配和释放资源。

但是有很多函数在后台使用unsafePerformIO,例如memory 包中的allocAndFreeze,并且在STM 事务中强制包含这样的表达式的thunk 并不难。这些函数在 STM 事务中使用真的安全吗?是否存在可能导致内存泄漏或数据损坏的情况?在这种情况下,是否有相当于 mask 的内容?

谢谢

【问题讨论】:

    标签: haskell stm


    【解决方案1】:

    如果 IO 被 STM retry 中断,unsafePerformIO 的通常安全使用可能会导致 资源 泄漏(不一定是内存)或数据损坏。这有两个原因:首先,STM 重试不运行异常处理程序,因此如果不安全的 IO 依赖于异常处理程序来释放资源(例如使用bracket),它们将不会被清理;其次,IO 可能在任何时候被中断或多次执行,因此您需要确保它即使被中断也能保持程序不变量。

    例如allocAndFreeze 不会泄漏,因为它在内部使用ForeignPtr,在 GHC 中它只是固定在托管堆中的内存,因此它不依赖异常处理程序或终结器来回收内存。但是,它可能导致数据损坏,如果不安全的 IO 暂时破坏数据结构中的不变量,例如“分配的数组必须始终排序”,那么如果计算在那个时候被打断了。

    【讨论】:

    • 谢谢约翰!您能否确认 STM 重试也可以中断 FFI 调用?那么,它可能会在写入一些数据的过程中中断对 C 例程的调用?
    • @Scott:我对 STM 的实现不是很熟悉,所以不确定。我期望如果在不可中断外部调用期间发生重试,它仍将等待调用返回,但等待可中断外部调用,不知道会发生什么,可能是未定义的。
    • @Scott:在 STM 内部进行外部调用的一种更安全的方法可能是 not,而在 outside STM,例如atomically setup; foreignCall; atomically teardown 其中setupteardown 获取并释放某种锁(例如TVar Bool);或者您可以在单独的线程中执行它,因此即使重试事务,调用也会正常完成,例如uninterruptibleUnsafeIOToSTM io = unsafeIOToSTM (do { m <- newEmptyMVar; forkIO ((putMVar m . Right =<< io) `catch` \ (e :: SomeException) -> putMVar m (Left e)); either throw pure =<< takeMVar m })
    • @john,当然,但我认为这是一个很好的问题,因为有关该主题的信息非常稀少。在 STM 重试的情况下 Haskell 如何中止计算的详细信息将是 IMO 文档的一个受欢迎的补充,我认为作为图书馆作者,了解这将是一件好事。不清楚的是,如果您使用unsafePerformIO,即使您使用括号之类的东西覆盖异步异常,如果它发生在 STM 中,您的计算仍可能会被中断。在某些情况下,uninterruptableUnsafeIOToSTM 将是一个很好的解决方法。谢谢!
    猜你喜欢
    • 2011-09-23
    • 1970-01-01
    • 2019-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多