Search code examples
haskellstm

STM and unsafePerformIO in Haskell


The documentation for STM states that:

Using unsafePerformIO inside of atomically is also dangerous but for different reasons. See unsafeIOToSTM for more on this.

When it comes to using threads and async exceptions, there are functions to mask asynchronous exceptions so that resources can be safely allocated and freed.

But there are alot of functions that use unsafePerformIO behind the scenes, for example allocAndFreeze in the memory package, and it's not hard to force a thunk containing such an expression inside an STM transaction. Are those functions actually safe to use inside an STM transaction? Are there circumstances where it could lead to memory leaks or data corruption? Is there an equivalent of mask for this circumstance?

Thanks


Solution

  • Ordinarily safe uses of unsafePerformIO may lead to resource leaks (not necessarily memory specifically) or data corruption if the IO is interrupted by an STM retry. This is for two reasons: first, STM retries do not run exception handlers, so if the unsafe IO relies on exception handlers to release resources (e.g. with bracket), they will not be cleaned up; and second, the IO may be interrupted at any point or executed multiple times, so it’s up to you to ensure it maintains program invariants even if interrupted.

    So for example allocAndFreeze will not leak, because it uses ForeignPtr internally, which in GHC is just pinned memory in the managed heap, so it doesn’t rely on exception handlers or finalizers to reclaim the memory. However, it may cause data corruption in the sense that, if the unsafe IO temporarily breaks invariants in a data structure such as “the allocated array must always be sorted”, that breakage may become visible if the computation is interrupted at that point.