2009-06-20 8 views
2

Рассмотрим следующий фрагмент кодаHaskell FFI: ForeignPtr кажется, чтобы не освободила

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" 

Он производит следующий вывод (может быть ошибка GHC?):

start 
end 

я ожидал увидеть «a was deleted» где-то после start ..

Я не знаю, что происходит. У меня есть несколько предположений:

  • сборщик мусора не собирает оставшиеся объекты, когда программа завершает
  • putStrLn перестает работать после main отделки. (Кстати, я попробовал то же самое с foreignly импортируемого puts и получил те же результаты)
  • Моего понимание ForeignPtr так не хватает
  • GHC ошибки? (Окр: 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" 
+2

Я могу воспроизвести это с помощью Foreign.Concurrent.newForeignPtr, но Foreign.ForeignPtr.newForeignPtr работает должным образом. Очень странно, так как они должны быть почти одинаковыми под капотом ... – ephemient

+0

@ephemient: thanks. Я воспроизвел ваш результат и детали на мой вопрос. – yairchu

+1

Я не могу воспроизвести это (GHC 6.10.3 на окнах). Версия Foreign.Concurrent.newForeignPtr работает для меня, и выходы start/end/a были удалены – jitter

ответ

6

Из документации Foreign.Foreign.newForeignPtr:

Обратите внимание, что нет гуара ответьте на то, как скоро финализатор будет выполнен после того, как последняя ссылка будет удалена; это зависит от деталей менеджера хранилища Haskell. Действительно, нет никакой гарантии, что финализатор будет выполнен вообще; программа может выйти с завершающими финализаторами.

Таким образом, вы сталкиваетесь с неопределенным поведением: то есть что-то может произойти, и это может измениться с платформы на платформу (как мы видели в Windows) или освободить для выпуска.

Причина разницы в поведении вы видите между этими двумя функциями может быть намекают документации для Foreign.Concurrent.newForeignPtr:

Эти финализаторы обязательно выполняются в отдельном потоке ...

Если финализаторы для Foreign.Foreign версии функции используют основной поток, но Foreign.Concurrent используют отдельный поток, вполне возможно, что основной поток отключается, не дожидаясь завершения других потоков, поэтому другие потоки никогда не смогут выполнить финализацию.

Разумеется, документы для версии Foreign.Concurrent сделать претензии,

Единственной гарантией является то, что финализации работает до завершения программы.

Я не уверен, что они на самом деле должны быть утверждая, что это, так как если финализаторы работают в других потоках, они могут принимать произвольное количество времени, чтобы делать свою работу (даже блокировать навсегда), и, таким образом, основной поток никогда не сможет заставить программу выйти. Это противоречило бы с этим из Control.Concurrent:

В отдельную программу GHC, только основной поток требуется для завершения для того, чтобы процесс прекратить. Таким образом, все другие разветвленные потоки просто прекратятся одновременно с основным потоком (терминология такого поведения - «демональные потоки»).

+0

вы также можете заблокировать навсегда в destructor Foreign.ForeignPtr.newForeignPtr, и вы можете segfault тоже и т. Д., Поэтому я не думаю, что это проблема, из-за которой программа не может выйти, если ваши деструкторы никогда не заканчиваются. это просто не то, что вы должны сделать – yairchu

+1

Ну, по мнению некоторых людей, это будет проблемой. :-) см. цитату в конце, я просто добавил к ответу. Я думаю, что, как правило, в системах потоков, что возможность принудительного выхода процесса считается более важным, чем предоставление ниток возможности для очистки, ценой возможного блокирования выхода навсегда. –