.....
function CheckSynchronized(Timeout: Integer =
0): Boolean;
var
ticks: DWORD;
procedure YieldTo;
var
ticksnew: DWORD;
begin
if Assigned(OnCheckSynchronize) then
begin
ticksnew := GetTicksDurate(ticks);
if ticksNew > CheckSynchronizeTimeout
then
begin
OnCheckSynchronize(nil);
ticks := GetTickCount;
end;
end;
end;
var
SyncNode: TSyncNode;
begin
// we only call this method from the mainthread
if GetCurrentThreadID <> MainThreadID then
raise EThread.CreateResFmt(@SCheckSynchronizeError, [GetCurrentThreadID]);
if (Timeout = 0) or ThreadsSyncEvent.Signaled(Timeout)
then
ThreadsSyncEvent.ResetEvent;
ticks := GetTickCount;
SyncNode := SyncList.Pop;
Result := SyncNode <> nil;
if Result then
while SyncNode <> nil do
begin
SyncNode.Run;
YieldTo;
SyncNode := SyncList.Pop;
if SyncNode <> nil
then
ThreadsSyncEvent.SetEvent;
end
else
YieldTo;
end;
procedure TExThread.DoAfterProcess;
begin
FOnAfterProcessOK(Self);
end;
procedure TExThread.DoIdle;
begin
Inc(FResponsiveness);
Status := THREAD_STATE_PROCESS; // being Idle is sort of processing
anyway
end;
procedure TExThread.AfterProcess;
begin
end;
procedure TExThread.BeforeProcess;
begin
Inc(FResponsiveness);
end;
function TExThread.ProcessWait(ResWait: DWORD): Boolean;
begin
Result := True;
if ResWait = WAIT_OBJECT_0 + WaitHandlesCount then // input
available
ProcessMessages // process the new input
else
if ResWait = WAIT_TIMEOUT then // wait timeout
DoIdle
else
Result := False; // this includes:
// WAIT_OBJECT_0 --> Thread Event
// WAIT_OBJECT_0 + 1 --> shut down Application event
// WAIT_ABANDONED_0..n --> an abandoned mutex, but won't happen in my
code
// WAIT_IO_COMPLETION --> queued APC
end;
procedure TExThread.Execute;
begin
if Terminated then Exit;
{$IFDEF ATLEAST_D14}
NameThreadForDebugging(FriendlyName);
{$ELSE}
if bIsDebuggerPresent then SetName;
{$ENDIF}
StartExec;
try
if not Terminated then
begin
repeat
(* When tasking, TExThread basically
loops calling Process (which descendants override)
every FTaskSlice for a total durate
of FDurate, checking for termination between loops.
Between loops, the thread sleeps
calling SleepUI.*)
if
FUseTask then Task
else
(* When not tasking and using wait
message calls, the thread sets up an array of wait objects
and sits waiting for at least one of
them to be signaled. Always the first handle in the array
is the thread handle. Always the
second object in the array is the application shut down event
handle. Your code must always process
these two handles first, then other handles you may want
to add to the array (wait for file
notification, printer notification, etc and lastly
call inherited ProcessWait to process
errors, timeouts, abandons...*)
if FUseWaitMsg then
repeat
Status :=
THREAD_STATE_SUSPENDED;
if FAlertableWait
then
ProcessWait(MsgWaitForMultipleObjectsEx(WaitHandlesCount, WaitHandles[0],
FTaskSlice, QS_SENDMESSAGE, 0))
else
ProcessWait(MsgWaitForMultipleObjects(WaitHandlesCount, WaitHandles[0],
False, FTaskSlice, QS_SENDMESSAGE))
until Terminated
(* When not tasking, nor waiting for
events, we call straight Process which descendants
override as needed. We can still loop
Process calls, this time by using the flag FCanCycleExec.
"Process" calls are wrapped between 2
other methods: BeforeProcess and AfterProcess.*)
else
begin
BeforeProcess; //
guaranteed to execute once per Process calls
Process;
AfterProcess; // not
guaranteed to be executed
end;
until Terminated or not
FCanCycleExec;
if Assigned(FOnAfterProcessOK) then
if FSynchAfterProcess then
Synchronize(DoAfterProcess)
else
DoAfterProcess;
end;
finally
EndExec;
end;
end;
procedure TExThread.Task;
procedure Perform(Data: Pointer; var NewSlice: DWORD;
var Done: Boolean);
begin
Process(NewSlice, Done);
end;
begin
if not Terminated then
LoopTask(@Perform, // repeat Perform
FTaskSlice, // every FTaskSlice
FDurate, // for a total of FDurate
TRUE); // check for termination between loops
end;