@@ -33,7 +33,6 @@ import Control.Concurrent
3333import Control.Exception
3434import Control.Monad.IO.Class
3535import Control.Monad.Trans.Cont
36- import Foreign.Concurrent qualified as GHC
3736import Foreign.Ptr
3837import Foreign.ForeignPtr
3938import Foreign.C.Types
@@ -87,17 +86,15 @@ C.include "<inline-python.h>"
8786-- ~~~~~~~~~~
8887--
8988-- CPython uses reference counting which works very well with
90- -- ForeignPtr. But there's a catch decrementing counter is only
91- -- possible if one holds GIL. This brings out two problems:
89+ -- ForeignPtr. But there's a catch: decrementing counter is only
90+ -- possible if one holds GIL. And one could not touch GIL if
91+ -- interpreter is not initialized or being finalized.
9292--
93- -- 1. Is it OK to run potentially blocking code in finalizer?
93+ -- We do not need to care whether thread is bound or not since this is
94+ -- single C call which will not getting migrated.
9495--
95- -- 2. Overhead of `runInBoundThread` is significant for GC (~1μs)
96- -- will this cause problem or if there're only few object on
97- -- haskell heap it would be fine?
98- --
99- -- In addition we must not do anything after interpreter shutdown.
100- -- It already released memory. Most of it at least.
96+ -- Still it's a question whether it's OK to call blocking code in
97+ -- ForeignPtr's finalizers.
10198
10299
103100
@@ -144,7 +141,7 @@ unPy (Py io) = io
144141
145142checkInitialized :: IO ()
146143checkInitialized =
147- [CU. exp | int { Py_IsInitialized() } |] >>= \ case
144+ [CU. exp | int { !Py_IsFinalizing() && Py_IsInitialized() } |] >>= \ case
148145 0 -> error " Python is not initialized"
149146 _ -> pure ()
150147
@@ -278,16 +275,16 @@ takeOwnership p = ContT $ \c -> c p `finallyPy` decref p
278275newPyObject :: Ptr PyObject -> Py PyObject
279276-- See NOTE: [GC]
280277newPyObject p = Py $ do
281- fptr <- newForeignPtr_ p
282- -- FIXME: We still have race between check and interpreter
283- -- shutdown. At least it's narrow race
284- GHC. addForeignPtrFinalizer fptr $ do
285- [ CU. exp | int { Py_IsInitialized() } |] >>= \ case
286- 0 -> pure ()
287- _ -> runPy $ decref p
288- pure $ PyObject fptr
289-
290-
278+ PyObject <$> newForeignPtr fptrXDECREF p
279+
280+ fptrXDECREF :: FunPtr ( Ptr PyObject -> IO () )
281+ fptrXDECREF = [ C. funPtr | void inline_py_fptr_XDECREF(PyObject* p) {
282+ if( Py_IsFinalizing() || ! Py_IsInitialized () )
283+ return;
284+ PyGILState_STATE st = PyGILState_Ensure();
285+ Py_XDECREF(p);
286+ PyGILState_Release(st);
287+ } |]
291288
292289----------------------------------------------------------------
293290-- Conversion of exceptions
0 commit comments