source: trunk/Packages/Common/Threading.pas

Last change on this file was 89, checked in by chronos, 7 months ago
  • Added: Allow to select color palette in new game dialog.
  • Fixed: Use scrollboxes in options dialogs.
File size: 8.9 KB
Line 
1unit Threading;
2
3interface
4
5uses
6 Classes, SysUtils, Forms, Generics.Collections, SyncObjs;
7
8type
9 TExceptionEvent = procedure (Sender: TObject; E: Exception) of object;
10 TMethodCall = procedure of object;
11
12 { TVirtualThread }
13
14 TVirtualThread = class
15 private
16 function GetFinished: Boolean; virtual; abstract;
17 function GetFreeOnTerminate: Boolean; virtual; abstract;
18 function GetPriority: TThreadPriority; virtual; abstract;
19 function GetSuspended: Boolean; virtual; abstract;
20 function GetTerminated: Boolean; virtual; abstract;
21 function GetThreadId: TThreadID; virtual; abstract;
22 procedure SetFreeOnTerminate(const AValue: Boolean); virtual; abstract;
23 procedure SetPriority(const AValue: TThreadPriority); virtual; abstract;
24 procedure SetSuspended(const AValue: Boolean); virtual; abstract;
25 procedure SetTerminated(const AValue: Boolean); virtual; abstract;
26 public
27 Name: string;
28 procedure Execute; virtual; abstract;
29 procedure Start; virtual; abstract;
30 procedure Terminate; virtual; abstract;
31 procedure Synchronize(AMethod: TThreadMethod); virtual; abstract;
32 procedure WaitFor; virtual; abstract;
33 procedure Sleep(Delay: Integer); virtual; abstract;
34 property FreeOnTerminate: Boolean read GetFreeOnTerminate
35 write SetFreeOnTerminate;
36 property Suspended: Boolean read GetSuspended
37 write SetSuspended;
38 property Priority: TThreadPriority read GetPriority write SetPriority;
39 property Terminated: Boolean read GetTerminated write SetTerminated;
40 property Finished: Boolean read GetFinished;
41 property ThreadId: TThreadID read GetThreadId;
42 end;
43
44 TVirtualThreadClass = class of TVirtualThread;
45
46 TListedThread = class;
47
48 { TListedThreadExecute }
49
50 TListedThreadExecute = class(TThread)
51 Parent: TListedThread;
52 procedure Execute; override;
53 end;
54
55 { TListedThread }
56
57 TListedThread = class(TVirtualThread)
58 private
59 FTerminated: Boolean;
60 FFinished: Boolean;
61 FThread: TListedThreadExecute;
62 function GetFinished: Boolean; override;
63 function GetFreeOnTerminate: Boolean; override;
64 function GetPriority: TThreadPriority; override;
65 function GetSuspended: Boolean; override;
66 function GetTerminated: Boolean; override;
67 function GetThreadId: TThreadID; override;
68 procedure SetFreeOnTerminate(const AValue: Boolean); override;
69 procedure SetPriority(const AValue: TThreadPriority); override;
70 procedure SetSuspended(const AValue: Boolean); override;
71 procedure SetTerminated(const AValue: Boolean); override;
72 public
73 constructor Create(CreateSuspended: Boolean;
74 const StackSize: SizeUInt = DefaultStackSize);
75 destructor Destroy; override;
76 procedure Sleep(Delay: Integer); override;
77 procedure Execute; override;
78 procedure Start; override;
79 procedure Terminate; override;
80 procedure Synchronize(AMethod: TThreadMethod); override;
81 procedure WaitFor; override;
82 end;
83
84 TTermThreadState = (ttsReady, ttsRunning, ttsFinished, ttsExceptionOccured);
85
86 { TTermThread }
87
88 TTermThread = class(TListedThread)
89 private
90 FOnFinished: TNotifyEvent;
91 public
92 State: TTermThreadState;
93 ExceptionMessage: string;
94 Method: TMethodCall;
95 procedure Execute; override;
96 property OnFinished: TNotifyEvent read FOnFinished write FOnFinished;
97 end;
98
99 { TThreadList }
100
101 TThreadList = class(TObjectList<TVirtualThread>)
102 function FindById(Id: TThreadID): TVirtualThread;
103 constructor Create; virtual;
104 end;
105
106var
107 ThreadList: TThreadList;
108 ThreadListLock: TCriticalSection;
109 OnException: TExceptionEvent;
110
111procedure RunInThread(Method: TMethodCall);
112procedure RunInThreadAsync(Method: TMethodCall; Callback: TNotifyEvent = nil);
113procedure Synchronize(Method: TMethodCall);
114
115resourcestring
116 SCurrentThreadNotFound = 'Current thread ID %d not found in virtual thread list.';
117
118
119implementation
120
121procedure RunInThread(Method: TMethodCall);
122var
123 Thread: TTermThread;
124begin
125 try
126 Thread := TTermThread.Create(True);
127 Thread.FreeOnTerminate := False;
128 Thread.Method := Method;
129 Thread.Start;
130 while (Thread.State = ttsRunning) or (Thread.State = ttsReady) do begin
131 if MainThreadID = ThreadID then Application.ProcessMessages;
132 Sleep(1);
133 end;
134 if Thread.State = ttsExceptionOccured then
135 raise Exception.Create(Thread.ExceptionMessage);
136 finally
137 Thread.Free;
138 end;
139end;
140
141procedure RunInThreadAsync(Method: TMethodCall; Callback: TNotifyEvent = nil);
142var
143 Thread: TTermThread;
144begin
145 try
146 Thread := TTermThread.Create(True);
147 Thread.FreeOnTerminate := True;
148 Thread.Method := Method;
149 Thread.OnFinished := CallBack;
150 Thread.Start;
151 //if Thread.State = ttsExceptionOccured then
152 // raise Exception.Create(Thread.ExceptionMessage);
153 finally
154 end;
155end;
156
157procedure Synchronize(Method: TMethodCall);
158var
159 Thread: TVirtualThread;
160begin
161 if MainThreadID = ThreadID then Method
162 else begin
163 try
164 ThreadListLock.Acquire;
165 Thread := ThreadList.FindById(ThreadID);
166 finally
167 ThreadListLock.Release;
168 end;
169 if Assigned(Thread) then begin
170 Thread.Synchronize(Method);
171 end else raise Exception.Create(Format(SCurrentThreadNotFound, [ThreadID]));
172 end;
173end;
174
175{ TThreadList }
176
177function TThreadList.FindById(Id: TThreadID): TVirtualThread;
178var
179 I: Integer;
180begin
181 I := 0;
182 while (I < ThreadList.Count) and (ThreadList[I].ThreadID <> Id) do
183 Inc(I);
184 if I < ThreadList.Count then Result := ThreadList[I]
185 else Result := nil;
186end;
187
188constructor TThreadList.Create;
189begin
190 inherited;
191end;
192
193{ TListedThreadExecute }
194
195procedure TListedThreadExecute.Execute;
196begin
197 try
198 try
199 Parent.Execute;
200 except
201 on E: Exception do
202 if Assigned(OnException) then
203 OnException(Parent.FThread, E);
204 end;
205 finally
206 Parent.FFinished := True;
207 end;
208end;
209
210{ TListedThread }
211
212function TListedThread.GetFinished: Boolean;
213begin
214 Result := FFinished;
215end;
216
217function TListedThread.GetFreeOnTerminate: Boolean;
218begin
219 Result := FThread.FreeOnTerminate;
220end;
221
222function TListedThread.GetPriority: TThreadPriority;
223begin
224 Result := FThread.Priority;
225end;
226
227function TListedThread.GetSuspended: Boolean;
228begin
229 Result := FThread.Suspended;
230end;
231
232function TListedThread.GetTerminated: Boolean;
233begin
234 Result := FTerminated;
235end;
236
237function TListedThread.GetThreadId: TThreadID;
238begin
239 Result := FThread.ThreadID;
240end;
241
242procedure TListedThread.SetFreeOnTerminate(const AValue: Boolean);
243begin
244 FThread.FreeOnTerminate := AValue;
245end;
246
247procedure TListedThread.SetPriority(const AValue: TThreadPriority);
248begin
249 FThread.Priority := AValue;
250end;
251
252procedure TListedThread.SetSuspended(const AValue: Boolean);
253begin
254 FThread.Suspended := AValue;
255end;
256
257procedure TListedThread.SetTerminated(const AValue: Boolean);
258begin
259 FTerminated := AValue;
260 if AValue then FThread.Terminate;
261end;
262
263constructor TListedThread.Create(CreateSuspended: Boolean;
264 const StackSize: SizeUInt);
265begin
266 FFinished := False;
267 FTerminated := False;
268
269 FThread := TListedThreadExecute.Create(True, StackSize);
270 FThread.Parent := Self;
271 try
272 ThreadListLock.Acquire;
273 ThreadList.Add(Self);
274 finally
275 ThreadListLock.Release;
276 end;
277 if CreateSuspended = False then FThread.Start;
278end;
279
280destructor TListedThread.Destroy;
281begin
282 if not Suspended then
283 begin
284 Terminate;
285 WaitFor;
286 end;
287 try
288 ThreadListLock.Acquire;
289 ThreadList.Delete(ThreadList.IndexOf(Self));
290 finally
291 ThreadListLock.Release;
292 end;
293 FThread.Free;
294 inherited;
295end;
296
297procedure TListedThread.Sleep(Delay: Integer);
298const
299 Quantum = 20;
300var
301 I: Integer;
302begin
303 if Terminated then Exit;
304 SysUtils.Sleep(Delay mod Quantum);
305 for I := 1 to (Delay div Quantum) do begin
306 if Terminated then Break;
307 SysUtils.Sleep(Quantum);
308 end;
309end;
310
311procedure TListedThread.Execute;
312begin
313end;
314
315procedure TListedThread.Start;
316begin
317 FThread.Start;
318end;
319
320procedure TListedThread.Terminate;
321begin
322 FTerminated := True;
323 FThread.Terminate;
324end;
325
326procedure TListedThread.Synchronize(AMethod: TThreadMethod);
327begin
328 FThread.Synchronize(FThread, AMethod);
329end;
330
331procedure TListedThread.WaitFor;
332begin
333 FThread.WaitFor;
334end;
335
336{ TTermThread }
337
338procedure TTermThread.Execute;
339begin
340 try
341 State := ttsRunning;
342 Method;
343 State := ttsFinished;
344 if Assigned(FOnFinished) then
345 FOnFinished(Self);
346 except
347 on E: Exception do begin
348 ExceptionMessage := E.Message;
349 State := ttsExceptionOccured;
350 if Assigned(OnException) then
351 OnException(FThread, E);
352 end;
353 end;
354end;
355
356initialization
357
358ThreadListLock := TCriticalSection.Create;
359ThreadList := TThreadList.Create;
360ThreadList.OwnsObjects := False;
361
362finalization
363
364ThreadList.Free;
365ThreadListLock.Free;
366
367end.
Note: See TracBrowser for help on using the repository browser.