@@ -800,19 +800,29 @@ PyObject *ConvertToPy(PyObject *self, const T &in)
800
800
// this is defined elsewhere for managing the opaque global_handle object
801
801
extern " C" PyThreadState *GetExecutingThreadState (PyObject *global_handle);
802
802
extern " C" void HandleException (PyObject *global_handle);
803
+ extern " C" bool IsThreadBlocking (PyObject *global_handle);
804
+ extern " C" void SetThreadBlocking (PyObject *global_handle, bool block);
805
+
806
+ struct ExceptionHandling
807
+ {
808
+ bool failFlag = false ;
809
+ PyObject *exObj = NULL ;
810
+ PyObject *valueObj = NULL ;
811
+ PyObject *tracebackObj = NULL ;
812
+ };
803
813
804
814
// this function handles failures in callback functions. If we're synchronously calling the callback
805
815
// from within an execute scope, then we can assign to failflag and let the error propagate upwards.
806
816
// If we're not, then the callback is being executed on another thread with no knowledge of python,
807
817
// so we need to use the global handle to try and emit the exception through the context. None of
808
818
// this is multi-threaded because we're inside the GIL at all times
809
- inline void HandleCallbackFailure (PyObject *global_handle, bool &fail_flag )
819
+ inline void HandleCallbackFailure (PyObject *global_handle, ExceptionHandling &exHandle )
810
820
{
811
821
// if there's no global handle assume we are not running in the usual environment, so there are no
812
822
// external-to-python threads
813
823
if (!global_handle)
814
824
{
815
- fail_flag = true ;
825
+ exHandle. failFlag = true ;
816
826
return ;
817
827
}
818
828
@@ -822,7 +832,25 @@ inline void HandleCallbackFailure(PyObject *global_handle, bool &fail_flag)
822
832
// we are executing synchronously, set the flag and return
823
833
if (current == executing)
824
834
{
825
- fail_flag = true ;
835
+ exHandle.failFlag = true ;
836
+ return ;
837
+ }
838
+
839
+ // if we have the blocking flag set, then we may be on another thread but we can still propagate
840
+ // up the error
841
+ if (IsThreadBlocking (global_handle))
842
+ {
843
+ exHandle.failFlag = true ;
844
+
845
+ // we need to rethrow the exception to that thread, so fetch (and clear it) on this thread.
846
+ //
847
+ // Note that the exception can only propagate up to one place. However since we know that python
848
+ // is inherently single threaded, so if we're doing this blocking funciton call on another
849
+ // thread then we *know* there isn't python further up the stack. Therefore we're safe to
850
+ // swallow the exception here (since there's nowhere for it to bubble up to anyway) and rethrow
851
+ // on the python thread.
852
+ PyErr_Fetch (&exHandle.exObj , &exHandle.valueObj , &exHandle.tracebackObj );
853
+
826
854
return ;
827
855
}
828
856
@@ -832,15 +860,16 @@ inline void HandleCallbackFailure(PyObject *global_handle, bool &fail_flag)
832
860
}
833
861
834
862
template <typename T>
835
- inline T get_return (const char *funcname, PyObject *result, PyObject *global_handle, bool &failflag)
863
+ inline T get_return (const char *funcname, PyObject *result, PyObject *global_handle,
864
+ ExceptionHandling &exHandle)
836
865
{
837
866
T val = T ();
838
867
839
868
int res = ConvertToPy (result, val);
840
869
841
870
if (!SWIG_IsOK (res))
842
871
{
843
- HandleCallbackFailure (global_handle, failflag );
872
+ HandleCallbackFailure (global_handle, exHandle );
844
873
845
874
PyErr_Format (PyExc_TypeError, " Expected a '%s' for return value of callback in %s" ,
846
875
TypeName<T>(), funcname);
@@ -852,7 +881,8 @@ inline T get_return(const char *funcname, PyObject *result, PyObject *global_han
852
881
}
853
882
854
883
template <>
855
- inline void get_return (const char *funcname, PyObject *result, PyObject *global_handle, bool &failflag)
884
+ inline void get_return (const char *funcname, PyObject *result, PyObject *global_handle,
885
+ ExceptionHandling &exHandle)
856
886
{
857
887
Py_XDECREF (result);
858
888
}
@@ -897,22 +927,23 @@ struct varfunc
897
927
}
898
928
899
929
~varfunc () { Py_XDECREF (args); }
900
- rettype call (const char *funcname, PyObject *func, PyObject *global_handle, bool &failflag)
930
+ rettype call (const char *funcname, PyObject *func, PyObject *global_handle,
931
+ ExceptionHandling &exHandle)
901
932
{
902
933
if (!func || func == Py_None || !PyCallable_Check (func) || !args)
903
934
{
904
- HandleCallbackFailure (global_handle, failflag );
935
+ HandleCallbackFailure (global_handle, exHandle );
905
936
return rettype ();
906
937
}
907
938
908
939
PyObject *result = PyObject_Call (func, args, 0 );
909
940
910
941
if (result == NULL )
911
- HandleCallbackFailure (global_handle, failflag );
942
+ HandleCallbackFailure (global_handle, exHandle );
912
943
913
944
Py_DECREF (args);
914
945
915
- return get_return<rettype>(funcname, result, global_handle, failflag );
946
+ return get_return<rettype>(funcname, result, global_handle, exHandle );
916
947
}
917
948
918
949
int currentarg = 0 ;
@@ -939,7 +970,7 @@ struct ScopedFuncCall
939
970
};
940
971
941
972
template <typename funcType>
942
- funcType ConvertFunc (PyObject *self, const char *funcname, PyObject *func, bool &failflag )
973
+ funcType ConvertFunc (PyObject *self, const char *funcname, PyObject *func, ExceptionHandling &exHandle )
943
974
{
944
975
// add a reference to the global object so it stays alive while we execute, in case this is an
945
976
// async call
@@ -949,11 +980,11 @@ funcType ConvertFunc(PyObject *self, const char *funcname, PyObject *func, bool
949
980
if (globals)
950
981
global_internal_handle = PyDict_GetItemString (globals, " _renderdoc_internal" );
951
982
952
- return [global_internal_handle, self, funcname, func, &failflag ](auto ... param) {
983
+ return [global_internal_handle, self, funcname, func, &exHandle ](auto ... param) {
953
984
ScopedFuncCall gil (global_internal_handle);
954
985
955
986
varfunc<typename funcType::result_type, decltype (param)...> f (self, funcname, param...);
956
- return f.call (funcname, func, global_internal_handle, failflag );
987
+ return f.call (funcname, func, global_internal_handle, exHandle );
957
988
};
958
989
}
959
990
0 commit comments