Effect of Hyperthreading on a Thread Object for Windows Animation: Part 3

Update Thread Class

Paul Milenkovic

Copyright 2003

Return to Part 2

An animation update thread has state – the value of the thread handle, the signal event handle, and a window handle to post animation update messages – and it has methods that operate on or employ that state. It is natural to bundle state and methods into an object class.

The class UpdateThread has a method UpdateLoop to process the update thread loop along with a method UpdateCmd to respond to animation update messages posted by the update thread to a window handle. Thus the same class has code for both GUI and update threads, but a given method is invoked by only one thread. A Windows thread object cannot directly invoke an object method, but it can invoke a standalone function that receives a pointer to an UpdateThread instance as an argument, which it can use to forward thread processing to the UpdateLoop method. The UpdateThread class also needs to be linked to a cooperating window handle object. The method Set_upd_wH() creates a Windows thread object and initiates the update thread when supplied with a window handle; it terminates the update thread and destroys the thread object when it is supplied a zero-valued window handle; supplying a different window handle keeps the same thread but changes the window handle where animation update messages are posted.

The window handle will be owned by whatever object in the framework you are using to manage a window handle, but the underlying window class to that framework object needs to be extended to process the WM_USER+1000 message (saved in upd_msg). The necessary processing is explained in a comment to the source listing. Each class framework – MFC, Borland VCL – has its own set of classes for window handle controls that embed and manage the window handle, and class UpdateThread is linked to such a window class through the window handle so it is usable with all of these frameworks.

Virtual abstract method UpdateNext is invoked once for each animation update – one can override it in a class derived from UpdateThread, or one can rewrite it as an implemented method if UpdateThread is extended by rewriting it. A Delphi Pascal source listing to unit UpdThrd.pas follows

unit UpdThrd;

{Copyright 2003 Paul Milenkovic, 118 Shiloh Dr., Madison, WI 53705 U.S.A.

Permission granted to use, copy, excerpt or share the source code provided

this notice is maintained.}


interface


uses Windows, Messages;


type

UpdateThread = class

private

upd_wH: HWnd;

upd_msg: Integer;

running, fcheck_paint, fupdating: Boolean;

nsleep: Integer;

// Thread handle, event handle

upd_threadH, upd_nextH: THandle;

function UpdateLoop: Integer;

protected

procedure Set_updating(updating: Boolean);

// Override this method with update code

procedure UpdateNext; virtual; abstract;

public

property check_paint: Boolean read fcheck_paint write fcheck_paint;

property updating: Boolean read fupdating write Set_updating;

procedure UpdateCmd;

procedure Set_upd_wH(wH: HWnd; msg: Integer);

constructor Create;

destructor Destroy; override;

end;


implementation


function RunUpdThread(update: UpdateThread): Integer;

begin

Result := update.UpdateLoop;

end;


{UpdateThread}

function UpdateThread.UpdateLoop: Integer;

begin

Result := 0;

while running do begin

WaitForSingleObject(upd_nextH,1000);

if running then begin

Sleep(nsleep);

PostMessage(upd_wH,upd_msg,0,Integer(Self));

// WindowProc of upd_wH must implement:

// upd_msg: UpdateThread(LParam).UpdCmd;

end;

end;

EndThread(0);

end;


procedure UpdateThread.Set_updating(updating: Boolean);

begin

if running and updating and (not fupdating) then begin

fupdating := True;

SetEvent(upd_nextH);

end;

fupdating := updating;

end;


procedure UpdateThread.UpdateCmd;

var msg: TMsg;

begin

if updating then begin

if check_paint and PeekMessage(msg,0,WM_PAINT,WM_PAINT,PM_NOREMOVE) then

//If paint messages are backed up, signal update thread without doing

// next update so Sleep(nsleep) can dispatch paint messages from the

// active message loop -- main program, message box, or menu.

Inc(nsleep)

else begin

if nsleep > 0 then

Dec(nsleep);

UpdateNext;

end;

SetEvent(upd_nextH);

end;

end;


procedure UpdateThread.Set_upd_wH(wH: HWnd; msg: Integer);

var pid: DWORD;

begin

upd_msg := msg;

if (wH <> 0) and (upd_wH = 0) then begin

upd_wH := wH;

running := True;

upd_nextH := CreateEvent(nil,False,False,nil);

upd_threadH := BeginThread(nil,0,@RunUpdThread,Pointer(Self),0,pid);

SetThreadPriority(upd_threadH,THREAD_PRIORITY_LOWEST);

if updating then

SetEvent(upd_nextH);

end else

if (wH = 0) and (upd_wH <> 0) then begin

running := False;

SetEvent(upd_nextH);

WaitForSingleObject(upd_threadH,10000);

CloseHandle(upd_threadH);

CloseHandle(upd_nextH);

upd_wH := 0;

end else

upd_wH := wH;

end;


constructor UpdateThread.Create;

begin

inherited Create;

upd_wH := 0;

running := False;

fcheck_paint := True;

fupdating := False;

nsleep := 0;

end;


destructor UpdateThread.Destroy;

begin

Set_upd_wH(0,0);

inherited Destroy;

end;



end.



Return to Part 2