unit SynchedThreads;
// Copyright © 1998 by Jon Shemitz, all rights reserved.
// Permission is hereby granted to freely use, modify, and
// distribute this source code PROVIDED that all six lines of
// this copyright and contact notice are included without any
// changes. Questions? Comments? Offers of work?
// mailto:jon@midnightbeach.com - http://www.midnightbeach.com
{$T+} {$hints on} {$warnings on}
interface
uses Windows, Classes, SysUtils, Forms;
// Simple threads
type
TThreadMethod = procedure (Data: pointer) of object;
TSimpleThread =
class (TThread)
public
constructor CreateSimple( CreateSuspended: boolean;
_Action: TThreadMethod;
_Data: pointer );
procedure AbortThread;
protected
ThreadMethod: TThreadMethod;
Data: pointer;
private
procedure Execute; override;
end;
function RunInThread( Handler: TThreadMethod;
Data: pointer ): TSimpleThread;
// Wait threads (basic synchronization)
procedure MsgWaitForSingleObject(Handle: THandle);
function SpawnProcess(const Command: string): TProcessInformation;
type
TWaitThread = class (TSimpleThread)
public
constructor CreateWait( _Action: TThreadMethod;
_Data: pointer );
procedure WaitFor;
procedure MsgWaitFor;
procedure AbortThread;
private
AbortFlag: ^boolean;
procedure Run(MsgWait: boolean);
end;
procedure WaitForThread( Handler: TThreadMethod;
Data: pointer );
procedure MsgWaitForThread( var Thread: TWaitThread;
Handler: TThreadMethod;
Data: pointer );
// Stop/start threads
type
EAbortedThread = class (Exception) end;
EThreadInUse = class (Exception) end;
TStopStartThread =
class (TSimpleThread)
public
Waiting: boolean;
constructor Create;
procedure WaitFor( _Action: TThreadMethod;
_Data: pointer );
procedure MsgWaitFor( _Action: TThreadMethod;
_Data: pointer );
procedure AbortThread;
private
Event: THandle;
Aborted: boolean;
destructor Destroy; override;
procedure Execute; override;
procedure Run( _Action: TThreadMethod;
_Data: pointer;
MsgWait: boolean );
end;
implementation
// TSimpleThread, RunInThread
constructor TSimpleThread.CreateSimple( CreateSuspended: boolean;
_Action: TThreadMethod;
_Data: pointer );
begin
ThreadMethod := _Action; // Set these BEFORE calling
Data := _Data; // inherited Create()!
FreeOnTerminate := True;
inherited Create(CreateSuspended);
end; // TSimpleThread.Create
procedure TSimpleThread.Execute;
begin
ThreadMethod(Data);
end; // TSimpleThread.Execute
procedure TSimpleThread.AbortThread;
begin
Suspend; // // Can't kill a running thread by Freeing it
Free; // Kills thread
end; // TSimpleThread.AbortThread
function RunInThread( Handler: TThreadMethod;
Data: pointer ): TSimpleThread;
begin
Result := TSimpleThread.CreateSimple(False, Handler, Data);
end; // RunInThread
// Basic synchronization
procedure MsgWaitForSingleObject(Handle: THandle);
begin
repeat
if MsgWaitForMultipleObjects( 1, Handle, False,
INFINITE, QS_ALLINPUT)
= WAIT_OBJECT_0 + 1
then Application.ProcessMessages
else BREAK;
until True = False;
end; // MsgWaitForSingleObject
function SpawnProcess(const Command: string): TProcessInformation;
var
StartupInfo: TStartupInfo;
begin
FillChar(StartupInfo, SizeOf(StartupInfo), 0); // use defaults
StartupInfo.cb := SizeOf(StartupInfo);
CreateProcess( Nil, PChar(Command), Nil, Nil, False, 0, Nil,
Nil, StartupInfo, Result );
end; // SpawnProcess
constructor TWaitThread.CreateWait( _Action: TThreadMethod;
_Data: pointer );
begin
CreateSimple(True, _Action, _Data); // CreateSuspended
AbortFlag := Nil;
end; // TWaitThread.CreateWait
procedure TWaitThread.WaitFor;
begin
Run(False);
end; // TWaitThread.WaitFor
procedure TWaitThread.MsgWaitFor;
begin
Run(True);
end; // TWaitThread.MsgWaitFor
procedure TWaitThread.Run(MsgWait: boolean);
var
Aborted: boolean;
begin
AbortFlag := @ Aborted;
Aborted := False;
Resume;
if MsgWait
then MsgWaitForSingleObject(Handle)
else inherited WaitFor;
if Aborted then Abort;
end; // TWaitThread.Run
procedure TWaitThread.AbortThread;
begin
Assert(Assigned(AbortFlag));
AbortFlag^ := True;
inherited;
end; // TWaitThread.CreateWait
procedure WaitForThread( Handler: TThreadMethod;
Data: pointer );
begin
TWaitThread.CreateWait(Handler, Data).WaitFor;
end; // WaitForThread
procedure MsgWaitForThread( var Thread: TWaitThread;
Handler: TThreadMethod;
Data: pointer );
begin
Thread := TWaitThread.CreateWait(Handler, Data);
Thread.MsgWaitFor;
Thread := Nil;
end; // MsgWaitForThread
// Stop/start threads
constructor TStopStartThread.Create;
begin
Event := CreateEvent(Nil, True, False, Nil);
// API call is smaller and simpler than Delphi wrapper
Assert(Event <> NULL);
Waiting := False;
Aborted := False;
inherited Create(True); // Create a suspended thread
end; // TStopStartThread.Create
destructor TStopStartThread.Destroy;
begin
CloseHandle(Event);
inherited;
end; // TStopStartThread.Destroy
procedure TStopStartThread.Execute;
begin
while not Terminated do
begin
Assert(Assigned(ThreadMethod));
ThreadMethod(Data);
SetEvent(Event);
Suspend;
end;
end; // TStopStartThread.Execute
procedure TStopStartThread.Run( _Action: TThreadMethod;
_Data: pointer;
MsgWait: boolean );
begin
if Waiting then raise EThreadInUse.Create('Thread in use');
if Aborted then raise EAbortedThread.Create('Aborted thread');
ThreadMethod := _Action;
Data := _Data;
Waiting := True;
ResetEvent(Event);
Resume;
if MsgWait
then MsgWaitForSingleObject(Event)
else WaitForSingleObject(Event, INFINITE);
Waiting := False;
if Aborted then Abort; // Raise an EAbort exception
end; // TStopStartThread.InternalRun
procedure TStopStartThread.MsgWaitFor( _Action: TThreadMethod;
_Data: pointer );
begin
Run(_Action, _Data, True);
end; // TStopStartThread.Run
procedure TStopStartThread.WaitFor( _Action: TThreadMethod;
_Data: pointer );
begin
Run(_Action, _Data, False);
end; // TStopStartThread.RunBlocking
procedure TStopStartThread.AbortThread;
begin
Suspend; // // Can't kill a running thread by Freeing it
Aborted := True;
SetEvent(Event);
end; // TStopStartThread.AbortThread
end.