Skip to content

Commit 9347052

Browse files
committed
We can do all checks in decref function
1 parent 8c76fc6 commit 9347052

File tree

2 files changed

+27
-23
lines changed

2 files changed

+27
-23
lines changed

include/inline-python.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,18 @@
55
#include <Rts.h>
66

77

8-
// Use new stable API from 3.13
8+
// Available since 3.13
99
#ifndef PyCFunctionFast
1010
typedef _PyCFunctionFast PyCFunctionFast;
1111
#endif
1212

13+
// Available since 3.13
14+
//
15+
// We define here compat dummy which always says No
16+
#ifndef Py_IsFinalizing
17+
#define Py_IsFinalizing(x) 0
18+
#endif
19+
1320

1421

1522
// ================================================================
@@ -28,7 +35,7 @@ PyObject *inline_py_callback_METH_FASTCALL(PyCFunctionFast fun);
2835

2936

3037
// ================================================================
31-
// Callbacks
38+
// Marhsalling
3239
// ================================================================
3340

3441
// Unpack iterable into array of PyObjects. Iterable must contain

src/Python/Internal/Eval.hs

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import Control.Concurrent
3333
import Control.Exception
3434
import Control.Monad.IO.Class
3535
import Control.Monad.Trans.Cont
36-
import Foreign.Concurrent qualified as GHC
3736
import Foreign.Ptr
3837
import Foreign.ForeignPtr
3938
import 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

145142
checkInitialized :: IO ()
146143
checkInitialized =
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
278275
newPyObject :: Ptr PyObject -> Py PyObject
279276
-- See NOTE: [GC]
280277
newPyObject 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

Comments
 (0)