@@ -2931,8 +2931,18 @@ function PyStatus_Exception(const APyStatus: PyStatus): Boolean;
2931
2931
end ;
2932
2932
2933
2933
// Access the PythonEngine with thread safety
2934
+
2935
+ // Gets the GIL and releases it automatically when the interface is cleared
2934
2936
function SafePyEngine : IPyEngineAndGIL;
2935
2937
2938
+ { $IFNDEF FPC}
2939
+ {
2940
+ Executes Python code in a Delphi thread - Wrapper around TPythonThread
2941
+ The TerminateProc is called using TThread.Queue
2942
+ }
2943
+ procedure ThreadPythonExec (ExecuteProc : TProc; TerminateProc : TProc = nil ;
2944
+ WaitToFinish: Boolean = False; ThreadExecMode : TThreadExecMode = emNewState);
2945
+ { $ENDIF FPC}
2936
2946
2937
2947
{ Helper functions}
2938
2948
@@ -9858,5 +9868,76 @@ function SafePyEngine: IPyEngineAndGIL;
9858
9868
9859
9869
9860
9870
9871
+ { $IFNDEF FPC}
9872
+
9873
+ { TAnonymousPythonThread }
9874
+
9875
+ type
9876
+ TAnonymousPythonThread = class (TPythonThread)
9877
+ private
9878
+ fTerminateProc : TProc;
9879
+ fExecuteProc : TProc;
9880
+ procedure DoTerminate ; override;
9881
+ public
9882
+ procedure ExecuteWithPython ; override;
9883
+ constructor Create(ExecuteProc : TProc; TerminateProc : TProc = nil ;
9884
+ Suspended: Boolean = False; AThreadExecMode : TThreadExecMode = emNewState);
9885
+ end ;
9886
+
9887
+ constructor TAnonymousPythonThread.Create(ExecuteProc : TProc; TerminateProc : TProc;
9888
+ Suspended: Boolean; AThreadExecMode : TThreadExecMode);
9889
+ begin
9890
+ inherited Create(Suspended);
9891
+ fExecuteProc := ExecuteProc;
9892
+ fTerminateProc := TerminateProc;
9893
+ FreeOnTerminate := True;
9894
+ ThreadExecMode := AThreadExecMode;
9895
+ end ;
9896
+
9897
+ procedure TAnonymousPythonThread.ExecuteWithPython ;
9898
+ begin
9899
+ if Assigned(fExecuteProc) then
9900
+ try
9901
+ fExecuteProc();
9902
+ except
9903
+ end ;
9904
+ end ;
9905
+
9906
+ procedure TAnonymousPythonThread.DoTerminate ;
9907
+ // Use Thread.Queue to run the TerminateProc in the main thread
9908
+ // Could use Synchronize instead, but such calls better be avoided
9909
+ var
9910
+ TerminateProc: TProc;
9911
+ begin
9912
+ TerminateProc := fTerminateProc; // to keep fTerminateProc alive at destruction
9913
+ if Assigned(TerminateProc) then
9914
+ TThread.Queue(nil , procedure
9915
+ begin
9916
+ TerminateProc();
9917
+ end );
9918
+ end ;
9919
+
9920
+
9921
+ { InternalThreadPythonExec }
9922
+
9923
+ procedure ThreadPythonExec (ExecuteProc : TProc; TerminateProc : TProc;
9924
+ WaitToFinish: Boolean; ThreadExecMode : TThreadExecMode);
9925
+ var
9926
+ Thread: TAnonymousPythonThread;
9927
+ begin
9928
+ if GetCurrentThreadId <> MainThreadID then
9929
+ raise Exception.Create(' ThreadPythonExec should only be called from the main thread' );
9930
+ Thread := TAnonymousPythonThread.Create(ExecuteProc, TerminateProc, WaitToFinish, ThreadExecMode);
9931
+ if WaitToFinish then
9932
+ begin
9933
+ Thread.FreeOnTerminate := False;
9934
+ Thread.Start;
9935
+ Thread.WaitFor; // Note that it calls CheckSyncrhonize
9936
+ Thread.Free;
9937
+ end ;
9938
+ end ;
9939
+
9940
+ { $ENDIF FPC}
9941
+
9861
9942
end .
9862
9943
0 commit comments