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
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.