Haskell FFI:ForeignPtr 似乎没有被释放(也许是 GHC bug?)
考虑以下代码片段
import qualified Foreign.Concurrent
import Foreign.Ptr (nullPtr)
main :: IO ()
main = do
putStrLn "start"
a <- Foreign.Concurrent.newForeignPtr nullPtr $
putStrLn "a was deleted"
putStrLn "end"
它会产生以下输出:
start
end
我本来希望在 start
之后的某个地方看到“a was returned
”..
我不知道发生了什么。 我有一些猜测:
- 当程序完成时,垃圾收集器不会收集剩余的对象
putStrLn
在main
完成后停止工作。 (顺便说一句,我对国外导入的 puts 尝试了同样的操作,并得到了相同的结果)- 我对ForeignPtr的理解缺乏
- GHC错误吗? (env:GHC 6.10.3,Intel Mac)
当使用 Foreign.ForeignPtr.newForeignPtr
而不是 Foreign.Concurrent.newForeignPtr
时,它似乎可以工作:
{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign.C.String (CString, newCString)
import Foreign.ForeignPtr (newForeignPtr)
import Foreign.Ptr (FunPtr)
foreign import ccall "&puts" puts :: FunPtr (CString -> IO ())
main :: IO ()
main = do
putStrLn "start"
message <- newCString "a was \"deleted\""
a <- newForeignPtr puts message
putStrLn "end"
输出:
start
end
a was "deleted"
Consider the following code snippet
import qualified Foreign.Concurrent
import Foreign.Ptr (nullPtr)
main :: IO ()
main = do
putStrLn "start"
a <- Foreign.Concurrent.newForeignPtr nullPtr $
putStrLn "a was deleted"
putStrLn "end"
It produces the following output:
start
end
I would had expected to see "a was deleted
" somewhere after start
..
I don't know what's going on. I have a few guesses:
- The garbage collector doesn't collect remaining objects when the program finishes
putStrLn
stops working aftermain
finishes. (btw I tried same thing with foreignly importedputs
and got the same results)- My understanding of
ForeignPtr
is lacking - GHC bug? (env: GHC 6.10.3, Intel Mac)
When using Foreign.ForeignPtr.newForeignPtr
instead of Foreign.Concurrent.newForeignPtr
it seems to work:
{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign.C.String (CString, newCString)
import Foreign.ForeignPtr (newForeignPtr)
import Foreign.Ptr (FunPtr)
foreign import ccall "&puts" puts :: FunPtr (CString -> IO ())
main :: IO ()
main = do
putStrLn "start"
message <- newCString "a was \"deleted\""
a <- newForeignPtr puts message
putStrLn "end"
outputs:
start
end
a was "deleted"
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
来自 Foreign.Foreign 的文档。新的ForeignPtr:
因此,您会遇到未定义的行为:即,任何事情都可能发生,并且它可能会因平台而异(正如我们在 Windows 下看到的那样)或版本不同。
Foreign.Concurrent.newForeignPtr:
如果该函数的Foreign.Foreign版本的终结器使用主线程,但Foreign.Concurrent版本的终结器使用单独的线程,那么主线程很可能会在没有关闭的情况下关闭等待其他线程完成其工作,因此其他线程永远无法运行终结。
当然,Foreign.Concurrent 版本的文档确实声称,
我不确定他们实际上应该声明这一点,因为如果终结器在其他线程中运行,他们可以花费任意数量的时间来完成他们的工作(甚至永远阻塞),因此主线程永远不会能够强制程序退出。 这将与 Control.Concurrent:
From the documentation of Foreign.Foreign.newForeignPtr:
So you're running into undefined behaviour: i.e., anything can happen, and it may change from platform to platform (as we saw under Windows) or release to release.
The cause of the difference in behaviour you're seeing between the two functions may be hinted at by the documentation for Foreign.Concurrent.newForeignPtr:
If the finalizers for the Foreign.Foreign version of the function use the main thread, but the Foreign.Concurrent ones use a separate thread, it could well be that the main thread shuts down without waiting for other threads to complete their work, so the other threads never get to run the finalization.
Of course, the docs for the Foreign.Concurrent version do claim,
I'm not sure that they actually ought to be claiming this, since if the finalizers are running in other threads, they can take an arbitrary amount of time to do their work (even block forever), and thus the main thread would never be able to force the program to exit. That would conflict with this from Control.Concurrent: