@@ -36,7 +36,6 @@ module Python.Internal.Eval
3636
3737import Control.Concurrent
3838import Control.Concurrent.STM
39- import Control.Exception (AsyncException (.. ),SomeAsyncException )
4039import Control.Monad
4140import Control.Monad.Catch
4241import Control.Monad.IO.Class
@@ -277,10 +276,7 @@ initializePython = [CU.exp| int { Py_IsInitialized() } |] >>= \case
277276
278277-- | Destroy python interpreter.
279278finalizePython :: IO ()
280- -- See NOTE: [Python and threading]
281- finalizePython
282- | rtsSupportsBoundThreads = runInBoundThread $ mask_ doFinalizePython
283- | otherwise = mask_ $ doFinalizePython
279+ finalizePython = mask_ doFinalizePython
284280
285281-- | Bracket which ensures that action is executed with properly
286282-- initialized interpreter
@@ -313,24 +309,7 @@ doInializePython = do
313309 lock_init <- newEmptyMVar
314310 lock_eval <- newEmptyMVar
315311 -- Main thread
316- tid_main <- forkOS $ do
317- r <- doInializePythonIO
318- putMVar lock_init r
319- case r of
320- False -> pure ()
321- True -> mask_ $ do
322- let loop = takeMVar lock_eval >>= \ case
323- EvalReq py resp -> do
324- res <- (Right <$> runPy py) `catch` (pure . Left )
325- putMVar resp res
326- loop
327- StopReq resp -> do
328- [C. block | void {
329- PyGILState_Ensure();
330- Py_Finalize();
331- } |]
332- putMVar resp ()
333- loop
312+ tid_main <- forkOS $ mainThread lock_init lock_eval
334313 takeMVar lock_init >>= \ case
335314 True -> pure ()
336315 False -> throwM PyInitializationFailed
@@ -346,6 +325,31 @@ doInializePython = do
346325 fini Running1
347326 ) `onException` atomically (writeTVar globalPyState InitFailed )
348327
328+ -- This action is executed on python's main thread
329+ mainThread :: MVar Bool -> MVar EvalReq -> IO ()
330+ mainThread lock_init lock_eval = do
331+ r_init <- doInializePythonIO
332+ putMVar lock_init r_init
333+ case r_init of
334+ False -> pure ()
335+ True -> mask_ $ do
336+ let loop
337+ = handle (\ InterruptMain -> pure () )
338+ $ takeMVar lock_eval >>= \ case
339+ EvalReq py resp -> do
340+ res <- (Right <$> runPy py) `catch` (pure . Left )
341+ putMVar resp res
342+ loop
343+ StopReq resp -> do
344+ [C. block | void {
345+ PyGILState_Ensure();
346+ Py_Finalize();
347+ } |]
348+ putMVar resp ()
349+ loop
350+
351+
352+
349353doInializePythonIO :: IO Bool
350354doInializePythonIO = do
351355 -- FIXME: I'd like more direct access to argv
@@ -402,32 +406,26 @@ doFinalizePython = join $ atomically $ readTVar globalPyState >>= \case
402406 InInitialization -> retry
403407 InFinalization -> retry
404408 -- We can simply call Py_Finalize
405- Running1 -> readTVar globalPyLock >>= \ case
406- LockUninialized -> error " Internal error: Lock not initialized"
407- LockFinalized -> error " Internal error: Lock is already finalized"
408- Locked {} -> retry
409- LockedByGC -> retry
410- LockUnlocked -> do
411- writeTVar globalPyLock LockFinalized
412- writeTVar globalPyState Finalized
413- pure $ [C. block | void {
414- PyGILState_Ensure();
415- Py_Finalize();
416- } |]
417- -- We need to call Py_Finalize on main thread specifically
418- RunningN _ eval tid_main tid_gc -> readTVar globalPyLock >>= \ case
419- LockUninialized -> error " Internal error: Lock not initialized"
420- LockFinalized -> error " Internal error: Lock is already finalized"
421- Locked {} -> retry
422- LockedByGC -> retry
423- LockUnlocked -> do
424- writeTVar globalPyLock LockFinalized
425- writeTVar globalPyState Finalized
426- pure $ do
427- resp <- newEmptyMVar
428- putMVar eval $ StopReq resp
429- takeMVar resp
430- killThread tid_gc
409+ Running1 -> checkLock $ [C. block | void {
410+ PyGILState_Ensure();
411+ Py_Finalize();
412+ } |]
413+ -- We need to call Py_Finalize on main thread
414+ RunningN _ eval _ tid_gc -> checkLock $ do
415+ killThread tid_gc
416+ resp <- newEmptyMVar
417+ putMVar eval $ StopReq resp
418+ takeMVar resp
419+ where
420+ checkLock action = readTVar globalPyLock >>= \ case
421+ LockUninialized -> error " Internal error: Lock not initialized"
422+ LockFinalized -> error " Internal error: Lock is already finalized"
423+ Locked {} -> retry
424+ LockedByGC -> retry
425+ LockUnlocked -> do
426+ writeTVar globalPyLock LockFinalized
427+ writeTVar globalPyState Finalized
428+ pure action
431429
432430
433431----------------------------------------------------------------
@@ -438,6 +436,10 @@ data EvalReq
438436 = forall a . EvalReq (Py a ) (MVar (Either SomeException a ))
439437 | StopReq (MVar () )
440438
439+ data InterruptMain = InterruptMain
440+ deriving stock Show
441+ deriving anyclass Exception
442+
441443-- | Execute python action. It will take global lock and no other
442444-- python action could start execution until one currently running
443445-- finished execution normally or with exception.
@@ -464,13 +466,12 @@ runPyInMain py
464466 Running1 -> error " INTERNAL ERROR"
465467 RunningN _ eval tid_main _ -> do
466468 acquireLock tid_main
467- pure $
468- (( do resp <- newEmptyMVar
469- putMVar eval $ EvalReq py resp
470- either throwM pure =<< takeMVar resp
471- ) `onException` throwTo tid_main UserInterrupt
472- ) `finally` atomically (releaseLock tid_main)
473- -- resp <- newEmptyMVar
469+ pure
470+ $ flip finally (atomically (releaseLock tid_main))
471+ $ flip onException (throwTo tid_main InterruptMain )
472+ $ do resp <- newEmptyMVar
473+ putMVar eval $ EvalReq py resp
474+ either throwM pure =<< takeMVar resp
474475 -- Single-threaded RTS
475476 | otherwise = runPy py
476477
0 commit comments