在 `unsafeDupablePerformIO` 中分配内存是否安全?

3

Suppose that I have a foreign function:

-- | Turns char* of the given size into a char* of size 16.
doSomethingFfi :: Ptr CUChar -> Ptr CUChar -> CSize -> IO ()
doSomethingFfi = undefined

这个函数是纯函数,因此我想在Haskell中表示它为纯函数:

doSomething :: ByteArray bytes => bytes -> bytes
doSomething bs = unsafePerformIO $
  alloc 16 $ \outPtr ->
  withByteArray bs $ \inPtr ->
    doSomethingFfi outPtr inPtr (fromIntegral $ length bs)

(在这里我使用了来自memoryalloc。)

我理解unsafePerformIOunsafeDupablePerformIO之间唯一的区别是后者中的IO操作可以在没有任何清理的情况下被静默地终止。

在我上面的例子中,基本上有两个IO操作:1、内存分配;2、外部调用。我不关心第二个,因为它是纯的,但我担心第一个。

如果计算被静默中断,这种方式分配的内存是否有任何保证不会泄漏?如果外部函数还需要我分配/清理临时存储,并且我使用alloca来实现这个目的,那么使用unsafeDupablePerformIO是否仍然安全?


alloca 在这种情况下应该是安全的,我相信。它在垃圾回收堆中分配固定内存,因此如果您的 IO 被中止,它应该被清理。 - dfeuer
在我看来,alloc使用了malloc分配的内存。它会附加一个finalizer以释放内存,但我认为您不能保证在计算被中止之前安装finalizer。 - dfeuer
嗯...实际上,alloc可以为不同的类实例使用不同的分配策略。有些似乎使用malloc;其他的我不确定。 - dfeuer
1
啊,重要的是要注意alloc是一个类方法。我正在查看Bytes的实现,你可能在查看其他东西。所以它也取决于实例... - kirelagin
抱歉造成困惑,我已经更新了问题以反映我对通用情况的兴趣。 - kirelagin
显示剩余4条评论
1个回答

2

大多数情况下,我已在评论中解释过了,但还不完全正确:

alloca

目前实现的alloca是安全的。 alloca通过调用allocaBytesAligned来实现,其定义如下:

allocaBytesAligned :: Int -> Int -> (Ptr a -> IO b) -> IO b
allocaBytesAligned (I# size) (I# align) action = IO $ \ s0 ->
     case newAlignedPinnedByteArray# size align s0 of { (# s1, mbarr# #) ->
     case unsafeFreezeByteArray# mbarr# s1 of { (# s2, barr#  #) ->
     let addr = Ptr (byteArrayContents# barr#) in
     case action addr     of { IO action' ->
     case action' s2      of { (# s3, r #) ->
     case touch# barr# s3 of { s4 ->
     (# s4, r #)
  }}}}}

这将在垃圾回收堆中分配固定内存。如果您的操作被提前终止,那么垃圾回收器将会在早期或晚期回收它所分配的内存。

alloc

这并不是一定安全的,但实际上可能是安全的。 alloc 是使用类方法 allocRet 定义的,不同类型可以以不同的方式实现。

与我在评论中的猜测相反,memory 中定义的实例都很好——它们也分配了固定内存。但该类没有将此作为要求记录下来,原则上某人可以使用 Foreign.Marshall.Alloc.malloc 分配内存,在这种情况下,垃圾回收器不会自动处理内存。如果计算提前中止,这样的假设实现将无法确保释放内存。


GHC是否保证newAlignedPinnedByteArray#在某种意义上是“原子的”,即线程在执行malloc之前或之后被终止时都已经“正确注册”到垃圾收集器中? - kirelagin
1
@kirelagin,primops不会调用malloc。我对固定内存的机制不熟悉,但通常情况下,primops 不能被中断,除非它们分配内存的那一刻。我不会担心这个问题。 - dfeuer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接