@@ -2931,8 +2931,18 @@ function PyStatus_Exception(const APyStatus: PyStatus): Boolean;
29312931 end ;
29322932
29332933// Access the PythonEngine with thread safety
2934+
2935+ // Gets the GIL and releases it automatically when the interface is cleared
29342936function SafePyEngine : IPyEngineAndGIL;
29352937
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}
29362946
29372947{ Helper functions}
29382948
@@ -9858,5 +9868,76 @@ function SafePyEngine: IPyEngineAndGIL;
98589868
98599869
98609870
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+
98619942end .
98629943
0 commit comments