@@ -524,9 +524,15 @@ data EvalReq
524524 -- ^ Dummy request. Do nothing
525525
526526
527- -- | Execute python action. It will take and hold global lock while
528- -- code is executed. Python exceptions raised during execution are
529- -- converted to haskell exception 'PyError'.
527+ -- | Execute python action. This is simplest executor with lowest
528+ -- overhead but it comes with several caveats. When thread executes
529+ -- python code it could not be interrupted since it's in foreign
530+ -- call. Use 'runPyAsync' if you need ability to interrupt. Also
531+ -- python uses GIL so only one thread can evaluate python code at
532+ -- time.
533+ --
534+ -- Python exceptions raised during execution are converted to
535+ -- haskell exception 'PyError'.
530536runPy :: Py a -> IO a
531537-- See NOTE: [Python and threading]
532538runPy py
@@ -538,10 +544,15 @@ runPy py
538544 go = ensurePyLock $ mask_ $ unsafeRunPy (ensureGIL py)
539545
540546
541- -- | Same as 'runPy' but will make sure that code is run in python's
542- -- main thread. It's thread in which python's interpreter was
543- -- initialized. Some python's libraries may need that. It has higher
544- -- call overhead compared to 'runPy'.
547+ -- | This function executes python code on python's main thread. It's
548+ -- OS thread in which interpreter was initialized and it has some
549+ -- special status in python. Some libraries could only work when
550+ -- called from main thread. It has higher call overhead compared to
551+ -- 'runPy' and only one haskell thread could be executing something
552+ -- on main thread at time.
553+ --
554+ -- When executing on threaded runtime this function could be
555+ -- interrupted by asynchronous exceptions.
545556runPyInMain :: Py a -> IO a
546557-- See NOTE: [Python and threading, Main thread]
547558runPyInMain py
@@ -625,15 +636,22 @@ unsafeRunPy (Py io) = io
625636-- thread is dead already.
626637
627638
628- -- | Exception thrown to a thread doing async python computation.
639+ -- | Exception thrown to a thread doing async python computation. On
640+ -- python side it corresponds to
641+ -- @inline_python.AsyncCancelled@. Latter is automatically converted
642+ -- to @PyAsyncCancelled@.
643+ --
644+ -- @since 0.3
629645data PyAsyncCancelled = PyAsyncCancelled
630646 deriving (Show , Eq )
631647
632648instance Exception PyAsyncCancelled
633649
634650-- | Handle to asynchronous python computation spawned by
635651-- 'runPyAsync'. It's performed on separate OS thread. Use
636- -- 'wait'\/'waitCatch' to obtain computation result.
652+ -- 'waitPy'\/'waitPyCatch' to obtain computation result.
653+ --
654+ -- @since 0.3
637655data PyAsync a = PyAsync
638656 { asyncTID :: ! ThreadId -- Thread ID
639657 , asyncTidStack :: ! (TVar [ThreadId ]) -- Stack of callback thread ID
@@ -644,15 +662,21 @@ data PyAsync a = PyAsync
644662
645663-- | Wait for result of asynchronous computation. If it threw an
646664-- exception it will be rethrown by @wait@.
665+ --
666+ -- @since 0.3
647667waitPy :: PyAsync a -> STM a
648668waitPy a = either throwSTM pure =<< a. asyncWait
649669
650670-- | Wait for result of asynchronous computation. Exception thrown by
651671-- it will be returned as @Left@.
672+ --
673+ -- @since 0.3
652674waitPyCatch :: PyAsync a -> STM (Either SomeException a )
653675waitPyCatch = (. asyncWait)
654676
655- -- | Create new OS thread and execute python code on it.
677+ -- | Execute python computation on dedicated OS thread.
678+ --
679+ -- @since 0.3
656680runPyAsync :: Py a -> IO (PyAsync a )
657681runPyAsync py = do
658682 ensureInit
@@ -700,15 +724,16 @@ withAsyncInitTLS stack = bracket ini fini . const
700724
701725
702726
703- -- | Cancel execution of asynchronous computation. Most likely thread
704- -- will be executing some python so first it attempts to raise async
705- -- exception in python code. Then it throws 'PyAsyncCancelled' in case
706- -- it executes haskell code. This means thread could be terminate
707- -- either with 'PyError' or 'PyAsyncCancelled'.
727+ -- | Cancel execution of asynchronous computation. It throws
728+ -- 'PyAsyncCancelled' to haskell threads including any haskell
729+ -- callbacks from python. Python will interrupted by asynchronously
730+ -- raising @inline_python.AsyncCancelled@.
708731--
709732-- Note that python code generally is not written under assumption
710733-- that it could be smitten with exception at an absolutely any
711- -- moment.
734+ -- moment. It could cause problems.
735+ --
736+ -- @since 0.3
712737cancelPy :: PyAsync a -> IO ()
713738cancelPy PyAsync {asyncTID= tid, asyncTidStack, asyncPyTID, asyncAlive} = do
714739 -- See NOTE: [Py Async], [Interrupting python]
@@ -744,11 +769,15 @@ cancelPy PyAsync{asyncTID=tid, asyncTidStack, asyncPyTID, asyncAlive} = do
744769 killThread tid_kill_cb
745770
746771-- | Variant of 'cancel' which isn't interruptible.
772+ --
773+ -- @since 0.3
747774uninterruptibleCancelPy :: PyAsync a -> IO ()
748775uninterruptibleCancelPy = uninterruptibleMask_ . cancelPy
749776
750777-- | Create new OS thread and execute python code on it. Will use
751778-- 'uninterruptibleCancel' after callback finishes execution.
779+ --
780+ -- @since 0.3
752781withPyAsync :: Py a -> (PyAsync a -> IO b ) -> IO b
753782withPyAsync py = bracket (runPyAsync py) uninterruptibleCancelPy
754783
0 commit comments