1
{ Unit for light weight threads.
3
This file is part of the Free Pascal run time library.
5
Copyright (C) 2008 Mattias Gaertner mattias@freepascal.org
7
See the file COPYING.FPC, included in this distribution,
8
for details about the copyright.
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
**********************************************************************}
18
This unit provides methods to easily run a procedure/method with several
30
Classes, SysUtils, MTPCPU;
33
TProcThreadGroup = class;
34
TProcThreadPool = class;
37
{ TMultiThreadProcItem }
48
TMultiThreadProcItem = class
50
FGroup: TProcThreadGroup;
53
FWaitingForIndexEnd: PtrInt;
54
FWaitingForIndexStart: PtrInt;
55
fWaitForPool: PRTLEvent;
56
FState: TMTPThreadState;
58
destructor Destroy; override;
59
function WaitForIndexRange(StartIndex, EndIndex: PtrInt): boolean;
60
function WaitForIndex(Index: PtrInt): boolean; inline;
61
procedure CalcBlock(Index, BlockSize, LoopLength: PtrInt;
62
out BlockStart, BlockEnd: PtrInt); inline;
63
property Index: PtrInt read FIndex;
64
property Group: TProcThreadGroup read FGroup;
65
property WaitingForIndexStart: PtrInt read FWaitingForIndexStart;
66
property WaitingForIndexEnd: PtrInt read FWaitingForIndexEnd;
67
property Thread: TProcThread read FThread;
77
TProcThread = class(TThread)
79
FItem: TMultiThreadProcItem;
80
FNext, FPrev: array[TMTPThreadList] of TProcThread;
81
procedure AddToList(var First: TProcThread; ListType: TMTPThreadList); inline;
82
procedure RemoveFromList(var First: TProcThread; ListType: TMTPThreadList); inline;
83
procedure Terminating(aPool: TProcThreadPool; E: Exception);
86
destructor Destroy; override;
87
procedure Execute; override;
88
property Item: TMultiThreadProcItem read FItem;
91
TMTMethod = procedure(Index: PtrInt; Data: Pointer;
92
Item: TMultiThreadProcItem) of object;
93
TMTProcedure = procedure(Index: PtrInt; Data: Pointer;
94
Item: TMultiThreadProcItem);
97
Each task creates a new group of threads.
98
A group can either need more threads or it has finished and waits for its
100
The thread that created the group is not in the list FFirstThread. }
104
mtpgsNeedThreads, // the groups waiting for more threads to help
105
mtpgsFinishing, // the groups waiting for its threads to finish
106
mtpgsException // there was an exception => close asap
109
TProcThreadGroup = class
112
FException: Exception;
113
FFirstRunningIndex: PtrInt;
114
FFirstThread: TProcThread;
115
FLastRunningIndex: PtrInt;
117
FNext, FPrev: TProcThreadGroup;
118
FPool: TProcThreadPool;
119
FStarterItem: TMultiThreadProcItem;
121
FState: TMTPGroupState;
124
FTaskMethod: TMTMethod;
125
FTaskProcedure: TMTProcedure;
126
FThreadCount: PtrInt;
127
procedure AddToList(var First: TProcThreadGroup; ListType: TMTPGroupState); inline;
128
procedure RemoveFromList(var First: TProcThreadGroup); inline;
129
function NeedMoreThreads: boolean; inline;
130
procedure AddThread(AThread: TProcThread);
131
procedure RemoveThread(AThread: TProcThread); inline;
132
procedure Run(Index: PtrInt; Data: Pointer; Item: TMultiThreadProcItem); inline;
133
procedure IndexComplete(Index: PtrInt);
134
procedure WakeThreadsWaitingForIndex;
135
function HasFinishedIndex(aStartIndex, aEndIndex: PtrInt): boolean;
136
procedure EnterExceptionState(E: Exception);
139
destructor Destroy; override;
140
property Pool: TProcThreadPool read FPool;
141
property StartIndex: PtrInt read FStartIndex;
142
property EndIndex: PtrInt read FEndIndex;
143
property FirstRunningIndex: PtrInt read FFirstRunningIndex; // first started
144
property LastRunningIndex: PtrInt read FLastRunningIndex; // last started
145
property TaskData: Pointer read FTaskData;
146
property TaskMethod: TMTMethod read FTaskMethod;
147
property TaskProcedure: TMTProcedure read FTaskProcedure;
148
property TaskFrame: Pointer read FTaskFrame;
149
property MaxThreads: PtrInt read FMaxThreads;
150
property StarterItem: TMultiThreadProcItem read FStarterItem;
153
{ TLightWeightThreadPool
154
Group 0 are the inactive threads }
158
TProcThreadPool = class
160
FMaxThreadCount: PtrInt;
161
FThreadCount: PtrInt;
162
FFirstInactiveThread: TProcThread;
163
FFirstActiveThread: TProcThread;
164
FFirstTerminatedThread: TProcThread;
165
FFirstGroupNeedThreads: TProcThreadGroup;
166
FFirstGroupFinishing: TProcThreadGroup;
167
FCritSection: TRTLCriticalSection;
168
FDestroying: boolean;
170
procedure SetMaxThreadCount(const AValue: PtrInt);
171
procedure CleanTerminatedThreads;
172
procedure DoParallelIntern(const AMethod: TMTMethod;
173
const AProc: TMTProcedure; const AFrame: Pointer;
174
StartIndex, EndIndex: PtrInt;
175
Data: Pointer = nil; MaxThreads: PtrInt = 0);
177
// for debugging only: the critical section is public:
178
procedure EnterPoolCriticalSection; inline;
179
procedure LeavePoolCriticalSection; inline;
182
destructor Destroy; override;
184
procedure DoParallel(const AMethod: TMTMethod;
185
StartIndex, EndIndex: PtrInt;
186
Data: Pointer = nil; MaxThreads: PtrInt = 0); inline;
187
procedure DoParallel(const AProc: TMTProcedure;
188
StartIndex, EndIndex: PtrInt;
189
Data: Pointer = nil; MaxThreads: PtrInt = 0); inline;
192
procedure DoParallelLocalProc(const LocalProc: Pointer;
193
StartIndex, EndIndex: PtrInt;
194
Data: Pointer = nil; MaxThreads: PtrInt = 0); // do not make this inline!
196
// utility functions for loops:
197
procedure CalcBlockSize(LoopLength: PtrInt;
198
out BlockCount, BlockSize: PtrInt; MinBlockSize: PtrInt = 0); inline;
199
procedure StopThreads;
201
property MaxThreadCount: PtrInt read FMaxThreadCount write SetMaxThreadCount;
202
property ThreadCount: PtrInt read FThreadCount;
206
ProcThreadPool: TProcThreadPool = nil;
211
{ TMultiThreadProcItem }
213
destructor TMultiThreadProcItem.Destroy;
215
if fWaitForPool<>nil then begin
216
RTLeventdestroy(fWaitForPool);
222
function TMultiThreadProcItem.WaitForIndexRange(
223
StartIndex, EndIndex: PtrInt): boolean;
225
aPool: TProcThreadPool;
227
//WriteLn('TLightWeightThreadItem.WaitForIndexRange START Index='+IntToStr(Index)+' StartIndex='+IntToStr(StartIndex)+' EndIndex='+IntToStr(EndIndex));
228
if (EndIndex>=Index) then exit(false);
229
if EndIndex<StartIndex then exit(true);
230
if Group=nil then exit(true); // a single threaded group has no group object
231
// multi threaded group
233
if aPool.FDestroying then exit(false); // no more wait allowed
234
aPool.EnterPoolCriticalSection;
236
if Group.FState=mtpgsException then begin
237
//WriteLn('TLightWeightThreadItem.WaitForIndexRange Index='+IntToStr(Index)+', Group closing because of error');
240
if Group.HasFinishedIndex(StartIndex,EndIndex) then begin
241
//WriteLn('TLightWeightThreadItem.WaitForIndexRange Index='+IntToStr(Index)+', range already finished');
244
FState:=mtptsWaitingForIndex;
245
FWaitingForIndexStart:=StartIndex;
246
FWaitingForIndexEnd:=EndIndex;
247
if fWaitForPool=nil then
248
fWaitForPool:=RTLEventCreate;
249
RTLeventResetEvent(fWaitForPool);
251
aPool.LeavePoolCriticalSection;
253
//WriteLn('TLightWeightThreadItem.WaitForIndexRange '+IntToStr(Index)+' waiting ... ');
254
RTLeventWaitFor(fWaitForPool);
255
Result:=FState=mtptsActive;
257
//WriteLn('TLightWeightThreadItem.WaitForIndexRange END '+IntToStr(Index));
260
function TMultiThreadProcItem.WaitForIndex(Index: PtrInt): boolean; inline;
262
Result:=WaitForIndexRange(Index,Index);
265
procedure TMultiThreadProcItem.CalcBlock(Index, BlockSize, LoopLength: PtrInt;
266
out BlockStart, BlockEnd: PtrInt);
268
BlockStart:=BlockSize*Index;
269
BlockEnd:=BlockStart+BlockSize;
270
if LoopLength<BlockEnd then BlockEnd:=LoopLength;
276
procedure TProcThread.AddToList(var First: TProcThread;
277
ListType: TMTPThreadList);
279
FNext[ListType]:=First;
280
if FNext[ListType]<>nil then
281
FNext[ListType].FPrev[ListType]:=Self;
285
procedure TProcThread.RemoveFromList(var First: TProcThread;
286
ListType: TMTPThreadList);
289
First:=FNext[ListType];
290
if FNext[ListType]<>nil then
291
FNext[ListType].FPrev[ListType]:=FPrev[ListType];
292
if FPrev[ListType]<>nil then
293
FPrev[ListType].FNext[ListType]:=FNext[ListType];
294
FNext[ListType]:=nil;
295
FPrev[ListType]:=nil;
298
procedure TProcThread.Terminating(aPool: TProcThreadPool;
301
aPool.EnterPoolCriticalSection;
304
if Item.FGroup<>nil then begin
305
// an exception occured
306
Item.FGroup.EnterExceptionState(E);
307
Item.FGroup.RemoveThread(Self);
310
// move to pool's terminated threads
312
mtptsActive: RemoveFromList(aPool.FFirstActiveThread,mtptlPool);
313
mtptsInactive: RemoveFromList(aPool.FFirstInactiveThread,mtptlPool);
315
AddToList(aPool.FFirstTerminatedThread,mtptlPool);
316
Item.FState:=mtptsTerminated;
318
aPool.LeavePoolCriticalSection;
322
constructor TProcThread.Create;
324
inherited Create(true);
325
fItem:=TMultiThreadProcItem.Create;
326
fItem.fWaitForPool:=RTLEventCreate;
330
destructor TProcThread.Destroy;
336
procedure TProcThread.Execute;
338
aPool: TProcThreadPool;
339
Group: TProcThreadGroup;
343
aPool:=Item.Group.Pool;
349
Group.Run(Item.Index,Group.TaskData,Item);
351
aPool.EnterPoolCriticalSection;
353
Group.IndexComplete(Item.Index);
356
if Group.LastRunningIndex<Group.EndIndex then begin
357
// next index of group
358
inc(Group.FLastRunningIndex);
359
Item.FIndex:=Group.FLastRunningIndex;
362
RemoveFromList(Group.FFirstThread,mtptlGroup);
363
dec(Group.FThreadCount);
366
if aPool.FFirstGroupNeedThreads<>nil then begin
368
aPool.FFirstGroupNeedThreads.AddThread(Self);
372
RemoveFromList(aPool.FFirstActiveThread,mtptlPool);
373
AddToList(aPool.FFirstInactiveThread,mtptlPool);
374
Item.FState:=mtptsInactive;
375
RTLeventResetEvent(Item.fWaitForPool);
379
aPool.LeavePoolCriticalSection;
382
if Item.FState=mtptsInactive then
383
RTLeventWaitFor(Item.fWaitForPool);
384
until Item.Group=nil;
387
// stop the exception and store it
388
E:=Exception(AcquireExceptionObject);
389
Terminating(aPool,E);
392
Terminating(aPool,nil);
397
procedure TProcThreadGroup.AddToList(var First: TProcThreadGroup;
398
ListType: TMTPGroupState);
407
procedure TProcThreadGroup.RemoveFromList(
408
var First: TProcThreadGroup);
421
function TProcThreadGroup.NeedMoreThreads: boolean;
423
Result:=(FLastRunningIndex<FEndIndex) and (FThreadCount<FMaxThreads)
424
and (FState<>mtpgsException);
427
procedure TProcThreadGroup.AddThread(AThread: TProcThread);
429
AThread.Item.FGroup:=Self;
430
AThread.AddToList(FFirstThread,mtptlGroup);
432
inc(FLastRunningIndex);
433
AThread.Item.FIndex:=FLastRunningIndex;
434
if not NeedMoreThreads then begin
435
RemoveFromList(Pool.FFirstGroupNeedThreads);
436
AddToList(Pool.FFirstGroupFinishing,mtpgsFinishing);
440
procedure TProcThreadGroup.RemoveThread(AThread: TProcThread);
442
AThread.RemoveFromList(FFirstThread,mtptlGroup);
446
procedure TProcThreadGroup.Run(Index: PtrInt; Data: Pointer;
447
Item: TMultiThreadProcItem); inline;
449
if Assigned(FTaskFrame) then begin
450
CallLocalProc(FTaskProcedure,FTaskFrame,Index,Data,Item)
452
if Assigned(FTaskProcedure) then
453
FTaskProcedure(Index,Data,Item)
455
FTaskMethod(Index,Data,Item)
459
procedure TProcThreadGroup.IndexComplete(Index: PtrInt);
461
AThread: TProcThread;
462
NewFirstRunningThread: PtrInt;
464
// update FirstRunningIndex
465
NewFirstRunningThread:=FStarterItem.Index;
466
AThread:=FFirstThread;
467
while AThread<>nil do begin
468
if (NewFirstRunningThread>aThread.Item.Index)
469
and (aThread.Item.Index<>Index) then
470
NewFirstRunningThread:=aThread.Item.Index;
471
aThread:=aThread.FNext[mtptlGroup];
473
FFirstRunningIndex:=NewFirstRunningThread;
474
// wake up threads (Note: do this even if FFirstRunningIndex has not changed)
475
WakeThreadsWaitingForIndex;
478
procedure TProcThreadGroup.WakeThreadsWaitingForIndex;
480
aThread: TProcThread;
482
if FState<>mtpgsException then begin
483
// wake up waiting threads
484
aThread:=FFirstThread;
485
while aThread<>nil do begin
486
if (aThread.Item.FState=mtptsWaitingForIndex)
487
and HasFinishedIndex(aThread.Item.WaitingForIndexStart,
488
aThread.Item.WaitingForIndexEnd)
490
// wake up the thread
491
aThread.Item.FState:=mtptsActive;
492
RTLeventSetEvent(aThread.Item.fWaitForPool);
494
aThread:=aThread.FNext[mtptlGroup];
496
if (FStarterItem.FState=mtptsWaitingForIndex)
497
and HasFinishedIndex(FStarterItem.WaitingForIndexStart,FStarterItem.WaitingForIndexEnd)
499
// wake up the starter thread of this group
500
FStarterItem.FState:=mtptsActive;
501
RTLeventSetEvent(FStarterItem.fWaitForPool);
504
// end group: wake up waiting threads
505
aThread:=FFirstThread;
506
while aThread<>nil do begin
507
if (aThread.Item.FState=mtptsWaitingForIndex)
509
// end group: wake up the thread
510
aThread.Item.FState:=mtptsWaitingFailed;
511
RTLeventSetEvent(aThread.Item.fWaitForPool);
513
aThread:=aThread.FNext[mtptlGroup];
515
if (FStarterItem.FState=mtptsWaitingForIndex)
517
// end group: wake up the starter thread of this group
518
FStarterItem.FState:=mtptsWaitingFailed;
519
RTLeventSetEvent(FStarterItem.fWaitForPool);
524
function TProcThreadGroup.HasFinishedIndex(
525
aStartIndex, aEndIndex: PtrInt): boolean;
527
AThread: TProcThread;
529
// test the finished range
530
if FFirstRunningIndex>aEndIndex then exit(true);
531
// test the unfinished range
532
if FLastRunningIndex<aEndIndex then exit(false);
533
// test the active range
534
AThread:=FFirstThread;
535
while AThread<>nil do begin
536
if (AThread.Item.Index>=aStartIndex)
537
and (AThread.Item.Index<=aEndIndex) then
539
AThread:=AThread.FNext[mtptlGroup];
541
if (FStarterItem.Index>=aStartIndex)
542
and (FStarterItem.Index<=aEndIndex) then
547
procedure TProcThreadGroup.EnterExceptionState(E: Exception);
549
if FState=mtpgsException then exit;
551
mtpgsFinishing: RemoveFromList(Pool.FFirstGroupFinishing);
552
mtpgsNeedThreads: RemoveFromList(Pool.FFirstGroupNeedThreads);
554
FState:=mtpgsException;
556
WakeThreadsWaitingForIndex;
559
constructor TProcThreadGroup.Create;
561
FStarterItem:=TMultiThreadProcItem.Create;
562
FStarterItem.FGroup:=Self;
565
destructor TProcThreadGroup.Destroy;
567
FreeAndNil(FStarterItem);
573
procedure TProcThreadPool.SetMaxThreadCount(const AValue: PtrInt);
575
if FMaxThreadCount=AValue then exit;
576
if AValue<1 then raise Exception.Create('TLightWeightThreadPool.SetMaxThreadCount');
577
FMaxThreadCount:=AValue;
580
procedure TProcThreadPool.CleanTerminatedThreads;
582
AThread: TProcThread;
584
while FFirstTerminatedThread<>nil do begin
585
AThread:=FFirstTerminatedThread;
586
AThread.RemoveFromList(FFirstTerminatedThread,mtptlPool);
591
constructor TProcThreadPool.Create;
593
FMaxThreadCount:=GetSystemThreadCount;
594
if FMaxThreadCount<1 then
596
InitCriticalSection(FCritSection);
599
destructor TProcThreadPool.Destroy;
601
procedure WakeWaitingStarterItems(Group: TProcThreadGroup);
603
while Group<>nil do begin
604
if Group.StarterItem.FState=mtptsWaitingForIndex then begin
605
Group.StarterItem.FState:=mtptsWaitingFailed;
606
RTLeventSetEvent(Group.StarterItem.fWaitForPool);
613
AThread: TProcThread;
616
// wake up all waiting threads
617
EnterPoolCriticalSection;
619
AThread:=FFirstActiveThread;
620
while AThread<>nil do begin
621
if aThread.Item.FState=mtptsWaitingForIndex then begin
622
aThread.Item.FState:=mtptsWaitingFailed;
623
RTLeventSetEvent(AThread.Item.fWaitForPool);
625
AThread:=AThread.FNext[mtptlPool];
627
WakeWaitingStarterItems(FFirstGroupNeedThreads);
628
WakeWaitingStarterItems(FFirstGroupFinishing);
630
LeavePoolCriticalSection;
633
// wait for all active threads to become inactive
634
while FFirstActiveThread<>nil do
637
// wake up all inactive threads (without new work they will terminate)
638
EnterPoolCriticalSection;
640
AThread:=FFirstInactiveThread;
641
while AThread<>nil do begin
642
RTLeventSetEvent(AThread.Item.fWaitForPool);
643
AThread:=AThread.FNext[mtptlPool];
646
LeavePoolCriticalSection;
649
// wait for all threads to terminate
650
while FFirstInactiveThread<>nil do
654
CleanTerminatedThreads;
656
DoneCriticalsection(FCritSection);
660
procedure TProcThreadPool.EnterPoolCriticalSection;
662
EnterCriticalsection(FCritSection);
665
procedure TProcThreadPool.LeavePoolCriticalSection;
667
LeaveCriticalsection(FCritSection);
670
procedure TProcThreadPool.DoParallel(const AMethod: TMTMethod;
671
StartIndex, EndIndex: PtrInt; Data: Pointer; MaxThreads: PtrInt);
673
if not Assigned(AMethod) then exit;
674
DoParallelIntern(AMethod,nil,nil,StartIndex,EndIndex,Data,MaxThreads);
677
procedure TProcThreadPool.DoParallel(const AProc: TMTProcedure;
678
StartIndex, EndIndex: PtrInt; Data: Pointer; MaxThreads: PtrInt);
680
if not Assigned(AProc) then exit;
681
DoParallelIntern(nil,AProc,nil,StartIndex,EndIndex,Data,MaxThreads);
684
procedure TProcThreadPool.StopThreads;
689
procedure TProcThreadPool.DoParallelLocalProc(const LocalProc: Pointer;
690
StartIndex, EndIndex: PtrInt; Data: Pointer; MaxThreads: PtrInt);
694
if not Assigned(LocalProc) then exit;
695
Frame:=get_caller_frame(get_frame);
696
DoParallelIntern(nil,TMTProcedure(LocalProc),Frame,StartIndex,EndIndex,
700
procedure TProcThreadPool.CalcBlockSize(LoopLength: PtrInt; out BlockCount,
701
BlockSize: PtrInt; MinBlockSize: PtrInt);
703
if LoopLength<=0 then begin
708
// split work into equally sized blocks
709
BlockCount:=ProcThreadPool.MaxThreadCount;
710
BlockSize:=(LoopLength div BlockCount);
711
if (BlockSize<MinBlockSize) then BlockSize:=MinBlockSize;
712
BlockCount:=((LoopLength-1) div BlockSize)+1;
715
procedure TProcThreadPool.DoParallelIntern(const AMethod: TMTMethod;
716
const AProc: TMTProcedure; const AFrame: Pointer;
717
StartIndex, EndIndex: PtrInt; Data: Pointer; MaxThreads: PtrInt);
719
Group: TProcThreadGroup;
721
AThread: TProcThread;
723
Item: TMultiThreadProcItem;
724
HelperThreadException: Exception;
726
if (StartIndex>EndIndex) then exit; // nothing to do
727
if FDestroying then raise Exception.Create('Pool destroyed');
729
if (MaxThreads>MaxThreadCount) or (MaxThreads<=0) then
730
MaxThreads:=MaxThreadCount;
731
if (StartIndex=EndIndex) or (MaxThreads<=1) then begin
733
Item:=TMultiThreadProcItem.Create;
735
for Index:=StartIndex to EndIndex do begin
737
if Assigned(AFrame) then begin
738
CallLocalProc(AProc,AFrame,Index,Data,Item)
740
if Assigned(AProc) then
741
AProc(Index,Data,Item)
743
AMethod(Index,Data,Item)
752
// create a new group
753
Group:=TProcThreadGroup.Create;
755
Group.FTaskData:=Data;
756
Group.FTaskMethod:=AMethod;
757
Group.FTaskProcedure:=AProc;
758
Group.FTaskFrame:=AFrame;
759
Group.FStartIndex:=StartIndex;
760
Group.FEndIndex:=EndIndex;
761
Group.FFirstRunningIndex:=StartIndex;
762
Group.FLastRunningIndex:=StartIndex;
763
Group.FMaxThreads:=MaxThreads;
764
Group.FThreadCount:=1;
765
Group.FStarterItem.FState:=mtptsActive;
766
Group.FStarterItem.FIndex:=StartIndex;
767
HelperThreadException:=nil;
770
EnterPoolCriticalSection;
772
Group.AddToList(FFirstGroupNeedThreads,mtpgsNeedThreads);
773
while Group.NeedMoreThreads do begin
774
AThread:=FFirstInactiveThread;
776
if AThread<>nil then begin
777
AThread.RemoveFromList(FFirstInactiveThread,mtptlPool);
778
end else if FThreadCount<FMaxThreadCount then begin
779
AThread:=TProcThread.Create;
780
if Assigned(AThread.FatalException) then
781
raise AThread.FatalException;
788
Group.AddThread(AThread);
790
AThread.AddToList(FFirstActiveThread,mtptlPool);
791
AThread.Item.FState:=mtptsActive;
795
RTLeventSetEvent(AThread.Item.fWaitForPool);
798
LeavePoolCriticalSection;
801
// run until no more Index left
804
Group.FStarterItem.FIndex:=Index;
805
Group.Run(Index,Data,Group.FStarterItem);
807
EnterPoolCriticalSection;
809
Group.IndexComplete(Index);
810
if (Group.FLastRunningIndex<Group.EndIndex) and (Group.FState<>mtpgsException)
812
inc(Group.FLastRunningIndex);
813
Index:=Group.FLastRunningIndex;
818
LeavePoolCriticalSection;
820
until (Index=StartIndex)or(Aborted);
822
// wait for Group to finish
823
if Group.FFirstThread<>nil then begin
824
EnterPoolCriticalSection;
826
Group.FStarterItem.FState:=mtptsInactive;
827
Group.FStarterItem.fIndex:=EndIndex;// needed for Group.HasFinishedIndex
828
// wake threads waiting for starter thread to finish
829
if Group.FStarterItem.FState<>mtptsInactive then
830
Group.EnterExceptionState(nil)
832
Group.WakeThreadsWaitingForIndex;
834
LeavePoolCriticalSection;
836
// waiting with exponential spin lock
838
while Group.FFirstThread<>nil do begin
841
if Index>30 then Index:=30;
844
// remove group from pool
845
EnterPoolCriticalSection;
848
mtpgsNeedThreads: Group.RemoveFromList(FFirstGroupNeedThreads);
849
mtpgsFinishing: Group.RemoveFromList(FFirstGroupFinishing);
852
LeavePoolCriticalSection;
854
HelperThreadException:=Group.FException;
856
// free terminated threads (terminated, because of exceptions)
857
CleanTerminatedThreads;
859
// if the exception occured in a helper thread raise it now
860
if HelperThreadException<>nil then
861
raise HelperThreadException;
865
ProcThreadPool:=TProcThreadPool.Create;