source: trunk/Components/HT.PAS@ 91

Last change on this file since 91 was 15, checked in by RBRi, 19 years ago

+ components stuff

  • Property svn:eol-style set to native
File size: 17.4 KB
Line 
1Unit HT;
2// Helperthread and supporting classes - Erik Huelsmann
3// Vandalised by Aaron Lawrence
4// V1.2
5// Add simple string message send method.
6// Seems that Erik forgot to set FScheduledOn anywhere!!
7//
8// V1.1
9// Removed OnSchedule complexities. Now TStdTask.Schedule takes
10// parameters object.
11Interface
12
13Uses
14 Classes, Forms, SyncObjects;
15
16Type
17 TTaskThread = class;
18 EThreadingException = class(SysException);
19
20 // A running instance of a TTask, basically
21 TTaskItem = class(TObject) // used to schedule tasks in the TaskThread
22 protected
23 FCanceled: boolean;
24 FErrorNr: longint;
25 FFinished: boolean;
26 FScheduledOn: TTaskThread;
27 FFreeOnFinish: boolean;
28 function GetScheduled: boolean;
29 function GetRunning: boolean;
30 procedure SetCanceled( C: boolean);
31 procedure SetErrorNr( E: longint);
32 procedure SetFreeOnFinish( F: boolean);
33 public
34 procedure Execute; virtual;
35 procedure TaskProcedure; virtual; abstract;
36
37 procedure Cancel; virtual; abstract;
38 procedure Error; virtual; abstract;
39 procedure Finish; virtual; abstract;
40 procedure Start; virtual; abstract;
41
42 property Canceled: boolean
43 read FCanceled write SetCanceled;
44 property ErrorNr: longint
45 read FErrorNr write SetErrorNr;
46 property Finished: boolean
47 read FFinished write FFinished;
48 property FreeOnFinish: boolean
49 read FFreeOnFinish write SetFreeOnFinish;
50 property Scheduled: boolean
51 read GetScheduled;
52 property Running: boolean
53 read GetRunning;
54 property Thread: TTaskThread
55 read FScheduledOn;
56 end;
57
58 TTaskEvent = class(TObject) // used to send events to the primary thread
59 public
60 procedure Event; virtual; abstract;
61 end;
62
63// TTaskList = class(TList)
64// end;
65
66 // Actual THread for running TTasks
67 TTaskThread = class(TThread)
68 protected
69 FTasks: TList;
70 FTasksMutex: TMutexSem;
71 FTasksChanged: TEventSem;
72 FStop: boolean;
73 public
74 constructor Create;
75 destructor Destroy; override;
76 procedure Execute; override;
77 procedure ExecuteTask; virtual;
78 function FindTask( Task: TTaskItem): longint; virtual;
79 procedure Schedule( Task: TTaskItem); virtual;
80 procedure Stop; virtual;
81 procedure UnSchedule( Task: TTaskItem); virtual;
82 end;
83
84
85type
86 TBasicTask = class;
87 TEventWait = (None,All,AllButProgress);
88
89{Declare new class}
90Type
91 // Component which owns the actual thread
92 THelperThread=Class(TComponent)
93 Protected
94 FThread: TTaskThread;
95 FPriority: TThreadPriority;
96 function GetThread: TTaskThread;
97 procedure SetPriority( Prio: TThreadPriority);
98 Procedure SetupComponent; Override;
99 Public
100 constructor Create(Parent: TComponent); override;
101 Destructor Destroy; Override;
102 procedure Schedule(Task: TTaskItem);
103
104 property Thread: TTaskThread
105 read GetThread;
106 published
107 property Priority: TThreadPriority
108 read FPriority write SetPriority;
109 End;
110
111Type
112 // Component which encapsulates a task
113 TBasicTask = class(TComponent)
114 private
115 FThread: THelperThread;
116 FFreeTasksOnComplete: boolean;
117
118 procedure SetThread(N: THelperThread);
119 Protected
120 Procedure SetupComponent; Override;
121 procedure Notification(AComponent:TComponent;Operation:TOperation); override;
122 Public
123 procedure Schedule;
124 published
125 property Thread: THelperThread
126 read FThread write SetThread;
127 property FreeTasksOnComplete: boolean
128 read FFreeTasksOnComplete write FFreeTasksOnComplete;
129
130 end;
131
132{Declare new class}
133Type
134 TStdTaskItem = class;
135 TTaskEventProc = procedure (Sender: TStdTaskItem) of object;
136 TTaskProgressProc = procedure (Sender: TStdTaskItem; I: longint; const S: String) of object;
137 TTaskProcedureProc = procedure (Parameters: TObject; Context: TStdTaskItem) of object;
138
139 TStdTaskEventProc = class (TTaskEvent)
140 public
141 FOnTaskEvent: TTaskEventProc;
142 FContext: TStdTaskItem;
143 constructor Create( T: TStdTaskItem; FOTE: TTaskEventProc );
144 procedure Event; override;
145 end;
146
147 TStdTaskProgressProc = class (TTaskEvent)
148 public
149 FOnTaskProgress: TTaskProgressProc;
150 FContext: TStdTaskItem;
151 FI: longint;
152 Str: String;
153 constructor Create( T: TStdTaskItem;
154 FOTP: TTaskProgressProc;
155 I: longint;
156 const S: String);
157 procedure Event; override;
158 end;
159
160 TStringEvent = procedure( S: String ) of Object;
161 TDataEvent = procedure( Data: TObject ) of Object;
162
163
164 TStdTaskItem = class(TTaskItem)
165 protected
166 // AaronL - add temp storage for passing a string message
167 FMessageString: string;
168 FStringMessageHandler: TStringEvent;
169 FDataMessageHandler: TDataEvent;
170
171 FData: TObject;
172
173 // Procedure executed in main thread context to do string callback
174 Procedure DoStringMessage;
175
176 Procedure DoDataMessage;
177
178 public
179 FOnStart,
180 FOnFinish,
181 FOnCancel,
182 FOnError: TTaskEventProc;
183 FOnProgress: TTaskProgressProc;
184 FOnExecute: TTaskProcedureProc;
185 Parameters: TObject;
186 Errors: TObject;
187 FEventWait: TEventWait;
188 FEventWaitSem: TEventSem;
189 destructor Destroy; override;
190 procedure Cancel; override;
191 procedure Error; override;
192 procedure Finish; override;
193 procedure Progress( I: longint; const S: String);
194 procedure Start; override;
195 procedure TaskProcedure; override;
196
197 // Simple string message sender.
198 procedure SendStringMessage( const S: string;
199 EventHandler: TStringEvent );
200 procedure SendData( Data: TObject;
201 EventHandler: TDataEvent );
202 end;
203
204 TStdTask=Class(TBasicTask)
205 private
206 FDefaultCaption: PString;
207 FOnStart,
208 FOnFinish,
209 FOnCancel,
210 FOnError: TTaskEventProc;
211 FOnProgress: TTaskProgressProc;
212 FOnExecute: TTaskProcedureProc;
213 FEventWait: TEventWait;
214 FEventWaitSem: TEventSem;
215 function GetDefaultCaption: String;
216 procedure SetDefaultCaption( C: String);
217 procedure SetEventWait( E: TEventWait);
218 public
219 constructor Create(Parent: TComponent); override;
220 procedure Schedule( Parameters: TObject );
221 published
222 property DefaultCaption: String
223 read GetDefaultCaption write SetDefaultCaption;
224 property EventWait: TEventWait
225 read FEventWait write SetEventWait;
226
227 property OnStart: TTaskEventProc
228 read FOnStart write FOnStart;
229 property OnFinish: TTaskEventProc
230 read FOnFinish write FOnFinish;
231 property OnError: TTaskEventProc
232 read FOnError write FOnError;
233 property OnCancel: TTaskEventProc
234 read FOnCancel write FOnCancel;
235 property OnProgress: TTaskProgressProc
236 read FOnProgress write FOnProgress;
237 property OnExecute: TTaskProcedureProc
238 read FOnExecute write FOnExecute;
239 End;
240
241
242procedure SendTaskEvent( Event: TTaskEvent; Complete: TEventSem);
243
244var
245 PrimaryThread: TTaskThread;
246
247{Define components to export}
248{You may define a page of the component palette and a component bitmap file}
249Exports
250 THelperThread,'User','HT.BMP',
251 TBasicTask,'User','',
252 TStdTask,'User','Task.BMP';
253
254Implementation
255
256uses Messages, SysUtils;
257
258/***
259 *
260 * THiddenWindow - internal class for the HelperThread library
261 *
262 */
263
264Const
265 WM_TaskEvent = WM_USER + 600; { not sent outside this unit }
266 WM_TaskData = WM_USER + 601; { not sent outside this unit }
267
268
269type
270 THiddenWindow = class(TControl)
271 private
272 procedure WMTaskEvent( var Msg: TMessage); message WM_TaskEvent;
273 public
274 procedure CreateWnd; override;
275 end;
276
277
278procedure THiddenWindow.WMTaskEvent;
279begin
280 TTaskEvent (Msg.Param1).Event;
281
282 if (Msg.Param2<>0) then
283 TObject(Msg.Param2) as TEventSem.Post;
284 TTaskEvent.Destroy;
285end;
286
287procedure THiddenWindow.CreateWnd;
288begin
289 inherited CreateWnd;
290end;
291
292var
293 EventsWindow: THiddenWindow;
294
295
296procedure SendTaskEvent( Event: TTaskEvent; Complete: TEventSem);
297begin
298 PostMsg (EventsWindow.Handle, WM_TaskEvent, longint(Event), longint(Complete));
299 if Complete<>Nil then
300 begin
301 Complete.WaitFor(INDEFINITE_WAIT);
302 Complete.Reset;
303 end;
304end;
305
306
307/***
308 *
309 * TTaskItem - class to schedule tasks on the TTaskThread class
310 *
311 */
312
313
314
315function TTaskItem.GetScheduled: boolean;
316begin
317 GetScheduled := FScheduledOn<>Nil
318end;
319
320
321function TTaskItem.GetRunning: boolean;
322begin
323 GetRunning := (FScheduledOn<>Nil) and (FScheduledOn.FindTask(Self)=0);
324end;
325
326
327procedure TTaskItem.SetCanceled( C: boolean);
328begin
329 if C then
330 begin
331 Cancel;
332 FCanceled := C
333 end
334end;
335
336
337procedure TTaskItem.SetErrorNr( E: longint);
338begin
339 FErrorNr := E;
340 if E<>0 then Error;
341end;
342
343
344procedure TTaskItem.SetFreeOnFinish( F: boolean);
345begin
346 if Finished and F then
347 Free
348 else
349 FFreeOnFinish := F;
350end;
351
352
353procedure TTaskItem.Execute;
354begin
355 Start;
356 TaskProcedure;
357 if (not Canceled) and (not ErrorNr<>0) then
358 Finish;
359end;
360
361
362/***
363 *
364 * TTaskThread - internal class for the HelperThread library
365 *
366 */
367
368
369
370
371constructor TTaskThread.Create;
372begin
373 inherited Create( true);
374
375 FTasks := TList.Create;
376 FTasksMutex := TMutexSem.Create(false,'',false);
377 FTasksChanged := TEventSem.Create(false,'',false);
378
379 FStop := False;
380
381 Suspended := false;
382end;
383
384destructor TTaskThread.Destroy;
385begin
386 if FTasks<>Nil then FTasks.Destroy;
387 if FTasksMutex<>Nil then FTasksMutex.Destroy;
388 if FTasksChanged<>Nil then FTasksChanged.Destroy;
389
390 inherited Destroy;
391end;
392
393
394
395procedure TTaskThread.Schedule(Task: TTaskItem);
396begin
397 FTasksMutex.Request(INDEFINITE_WAIT);
398 FTasks.Add(Task);
399 FTasksMutex.Release;
400 FTasksChanged.Post;
401 // AaronL - set the tasks' thread to ourselves.
402 Task.FScheduledOn:= self;
403end;
404
405
406procedure TTaskThread.UnSchedule( Task: TTaskItem);
407begin
408 FTasksMutex.Request(INDEFINITE_WAIT);
409 if not Task.Running then
410 FTasks.Delete(FindTask( Task))
411 else
412 raise EThreadingException.Create('Cannot unschedule a running task!');
413 FTasksMutex.Release;
414end;
415
416
417FUNCTION TTASKTHREAD.FINDTASK( Task: TTaskItem): LONGINT;
418VAR
419 I: LONGINT;
420BEGIN
421 I := 0;
422 WHILE (TTaskItem(FTASKS[I]) <> Task) DO
423 INC(I);
424 IF (TTaskItem(FTASKS[I]) <> Task) THEN
425 RESULT := -1
426 ELSE
427 RESULT := I
428END;
429
430
431procedure TTaskThread.Execute;
432begin
433 {Place thread code here}
434 while (not FStop) do
435 begin
436
437 if (FTasks.Count > 0) then // tasks to be run
438 begin
439 FTasksChanged.Reset;
440
441 ExecuteTask;
442 // AaronL - set the task's thread to nil so
443 // it can be unscheduled. This seems a bit wrong but it works.
444 TTaskItem( FTasks[0] ).FScheduledOn:= nil;
445 UnSchedule(FTasks[0]);
446 end
447 else
448 FTasksChanged.WaitFor(INDEFINITE_WAIT);
449 end;
450end;
451
452
453procedure TTaskThread.ExecuteTask;
454var
455 Task: TTaskItem;
456begin
457 FTasksMutex.Request(INDEFINITE_WAIT);
458 Task := FTasks.Items[0];
459 FTasksMutex.Release;
460 if not Task.Canceled then
461 Task.Execute;
462end;
463
464
465
466procedure TTaskThread.Stop;
467begin
468 FStop := true;
469 FTasksChanged.Post;
470end;
471
472
473/***
474 *
475 * THelperThread -
476 *
477 * Component which encapsulates a HelperThread type of thread
478 *
479 */
480
481
482function THelperThread.GetThread: TTaskThread;
483begin
484 Result := FThread;
485end;
486
487
488procedure THelperThread.SetPriority( Prio: TThreadPriority);
489begin
490 FPriority := Prio;
491 if FThread<>Nil then
492 FThread.Priority := Prio;
493end;
494
495
496constructor THelperThread.Create;
497begin
498 inherited Create(Parent);
499
500 FPriority := tpNormal;
501end;
502
503Procedure THelperThread.SetupComponent;
504Begin
505 Inherited SetupComponent;
506
507 Include(ComponentState,csHandleLinks);
508
509 FThread := TTaskThread.Create;
510 FThread.Priority := FPriority;
511end;
512
513
514Destructor THelperThread.Destroy;
515Begin
516 if FThread<>Nil then FThread.Stop;
517
518 Inherited Destroy;
519End;
520
521
522procedure THelperThread.Schedule;
523begin
524 if FThread<>Nil then
525 FThread.Schedule(Task)
526 else
527 raise EThreadingException.Create(Name+': Helper thread not started!');
528end;
529
530
531/***
532 *
533 * TStdTaskXXXXX -
534 *
535 * Events sent by TStdTask
536 *
537 */
538
539
540constructor TStdTaskEventProc.Create( T: TStdTaskItem; FOTE: TTaskEventProc );
541begin
542 inherited Create;
543
544 FOnTaskEvent := FOTE;
545 FContext := T;
546end;
547
548
549procedure TStdTaskEventProc.Event;
550begin
551 FOnTaskEvent(FContext);
552end;
553
554
555
556constructor TStdTaskProgressProc.Create( T: TStdTaskItem;
557 FOTP: TTaskProgressProc;
558 I: longint;
559 const S: String);
560begin
561 inherited Create;
562
563 FOnTaskProgress := FOTP;
564 FContext := T;
565 FI:= I;
566 Str := S;
567end;
568
569procedure TStdTaskProgressProc.Event;
570begin
571 FOnTaskProgress(FContext,FI,Str);
572end;
573
574
575
576/***
577 *
578 * TStdTaskItem -
579 *
580 * Component to administer parameters sent from a StdTask
581 *
582 */
583
584
585destructor TStdTaskItem.Destroy;
586begin
587 if Errors<>Nil then Errors.Destroy;
588 if Parameters<>Nil then Parameters.Destroy;
589
590 inherited Destroy;
591end;
592
593
594procedure TStdTaskItem.Cancel;
595begin
596 if FOnCancel<>Nil then
597 SendTaskEvent(TStdTaskEventProc.Create(Self,FOnCancel),FEventWaitSem);
598end;
599
600procedure TStdTaskItem.Error;
601begin
602 if FOnError<>Nil then
603 SendTaskEvent(TStdTaskEventProc.Create(Self,FOnError),FEventWaitSem);
604end;
605
606
607procedure TStdTaskItem.Finish;
608begin
609 if FOnFinish<>Nil then
610 SendTaskEvent(TStdTaskEventProc.Create(Self,FOnFinish),FEventWaitSem);
611end;
612
613
614procedure TStdTaskItem.Progress( I: longint; const S: String);
615var
616 ES: TEventSem;
617begin
618 if FOnProgress<>Nil then
619 begin
620 if (FEventWait<>All) then
621 ES := Nil
622 else
623 ES := FEventWaitSem;
624 SendTaskEvent(TStdTaskProgressProc.Create(Self,FOnProgress,I,S),ES);
625 end;
626end;
627
628procedure TStdTaskItem.SendStringMessage( const S: string;
629 EventHandler: TStringEvent );
630begin
631 FMessageString:= S;
632 FStringMessageHandler:= EventHandler;
633 FScheduledOn.Synchronize( DoStringMessage );
634end;
635
636procedure TStdTaskItem.SendData( Data: TObject;
637 EventHandler: TDataEvent );
638begin
639 FData:= Data;
640 FDataMessageHandler:= EventHandler;
641 FScheduledOn.Synchronize( DoDataMessage );
642end;
643
644Procedure TStdTaskItem.DoStringMessage;
645begin
646 FStringMessageHandler( FMessageString );
647end;
648
649Procedure TStdTaskItem.DoDataMessage;
650begin
651 FDataMessageHandler( FData );
652 FData.Free;
653 FData:= nil;
654end;
655
656procedure TStdTaskItem.Start;
657begin
658 if FOnStart<>Nil then
659 SendTaskEvent(TStdTaskEventProc.Create(Self,FOnStart),FEventWaitSem);
660end;
661
662
663procedure TStdTaskItem.TaskProcedure;
664begin
665 if FOnExecute<>Nil then
666 FOnExecute(Parameters,Self);
667end;
668
669
670/***
671 *
672 * TBasicTask -
673 *
674 * Component to schedule tasks on a TTaskThread (using the THelperThread)
675 *
676 */
677
678
679Procedure TBasicTask.SetupComponent;
680Begin
681 Inherited SetupComponent;
682
683 Include(ComponentState,csHandleLinks);
684End;
685
686
687procedure TBasicTask.Notification;
688begin
689 inherited Notification(AComponent,Operation);
690
691 if (Operation=opRemove) and (AComponent=TComponent(FThread)) then
692 FThread := NIL;
693end;
694
695
696procedure TBasicTask.SetThread(N: THelperThread);
697begin
698 if FThread=N then Exit;
699 if FThread<>Nil then FThread.Notification(Self,opRemove);
700 fthread := n;
701 if N<>Nil then N.FreeNotification(Self);
702end;
703
704procedure TBasicTask.Schedule;
705var
706 T: TTaskItem;
707begin
708 if FThread=NIL then
709 raise EThreadingException.Create('Task: '+Name+' not bound to thread context')
710 else
711 begin
712 T := Nil;
713 if T<>Nil then
714 Thread.Schedule(T);
715 end;
716end;
717
718
719
720/***
721 *
722 * TStdTask -
723 *
724 * Component to schedule procedures on a TTaskThread. Allows the use of the IDE
725 * code generator to generate event-handlers.
726 *
727 */
728
729
730CONSTRUCTOR TSTDTASK.CREATE(PARENT: TCOMPONENT);
731BEGIN
732 INHERITED CREATE(PARENT);
733
734 EVENTWAIT := ALL;
735END;
736
737procedure TStdTask.Schedule( Parameters: TObject );
738var
739 T: TStdTaskItem;
740begin
741 if FThread=NIL then
742 raise EThreadingException.Create('Task: '+Name+' not bound to thread context');
743
744 T := TStdTaskItem.Create;
745
746 T.Parameters:= Parameters;
747 T.FOnStart := FOnStart;
748 T.FOnFinish := FOnFinish;
749 T.FOnCancel := FOnCancel;
750 T.FOnError := FOnError;
751 T.FOnProgress := FOnProgress;
752 T.FOnExecute := FOnExecute;
753 T.FEventWait := FEventWait;
754 T.FEventWaitSem := FEventWaitSem;
755
756 Thread.Schedule(T);
757
758end;
759
760function TStdTask.GetDefaultCaption: String;
761begin
762 if FDefaultCaption=Nil then
763 Result := ''
764 else
765 Result := FDefaultCaption^;
766end;
767
768
769procedure TStdTask.SetEventWait( E: TEventWait);
770begin
771 FEventWait := E;
772 if (E=None) and (FEventWaitSem<>Nil) then
773 begin
774 FEventWaitSem.Destroy;
775 FEventWaitSem := Nil;
776 end
777 else if (E<>None) and (FEventWaitSem=Nil) then
778 FEventWaitSem := TEventSem.Create(false,'',false);
779end;
780
781
782procedure TStdTask.SetDefaultCaption( C: String);
783begin
784 if FDefaultCaption<>Nil then DisposeStr(FDefaultCaption);
785 FDefaultCaption := NewStr( C);
786end;
787
788{$HINTS OFF}
789
790type
791 TPrimaryTaskThread = class(TTaskThread)
792 constructor Create;
793 destructor Destroy; override;
794 procedure Execute; override;
795 procedure ExecuteTask; override;
796 function FindTask( Task: TTaskItem): longint; override;
797 procedure Schedule( Task: TTaskItem); override;
798 procedure Stop; override;
799 procedure UnSchedule( Task: TTaskItem); override;
800 end;
801
802constructor TPrimaryTaskThread.Create;
803begin
804end;
805
806destructor TPrimaryTaskThread.Destroy;
807begin
808end;
809
810procedure TPrimaryTaskThread.Execute;
811begin
812end;
813
814procedure TPrimaryTaskThread.ExecuteTask;
815begin
816end;
817
818function TPrimaryTaskThread.FindTask( Task: TTaskItem): longint;
819begin
820 Result := -1;
821end;
822
823procedure TPrimaryTaskThread.Schedule;
824begin
825 Task.Execute;
826end;
827
828procedure TPrimaryTaskThread.UnSchedule;
829begin
830end;
831
832procedure TPrimaryTaskThread.Stop;
833begin
834end;
835
836{$HINTS ON}
837
838Initialization
839 EventsWindow := THiddenWindow.Create(nil);
840 EventsWindow.CreateWnd;
841 PrimaryThread := TPrimaryTaskThread.Create;
842
843 {Register classes}
844 RegisterClasses([THelperThread,TBasicTask,TStdTask]);
845End.
846
Note: See TracBrowser for help on using the repository browser.