-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathcocinasync.global.pas
146 lines (121 loc) · 2.85 KB
/
cocinasync.global.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
unit cocinasync.global;
interface
uses System.SysUtils, System.Classes, System.SyncObjs;
type
TThreadCounter = class(TObject)
strict private
FTerminating : Boolean;
FThreadCount : Integer;
class var FGlobal : TThreadCounter;
private
class procedure GlobalInitialize;
class procedure GlobalFinalize;
public
constructor Create; reintroduce; virtual;
destructor Destroy; override;
procedure NotifyThreadStart;
procedure NotifyThreadEnd;
property ThreadCount : Integer read FThreadCount;
procedure WaitForAll(Timeout : Cardinal = 0);
class property Global : TThreadCounter read FGlobal;
end;
TConsole = class(TObject)
private
FEvent : TEvent;
public
constructor Create;
destructor Destroy; override;
procedure Wake(Sender : TObject);
procedure CheckSynchronize(Timeout : Cardinal = INFINITE);
class procedure ApplicationLoop(const &Until : TFunc<Boolean>);
end;
implementation
uses DateUtils;
{ TThreadCounter }
constructor TThreadCounter.Create;
begin
inherited Create;
FTerminating := False;
FThreadCount := 0;
end;
destructor TThreadCounter.Destroy;
begin
FTerminating := True;
while FThreadCount > 0 do
sleep(10);
inherited;
end;
class procedure TThreadCounter.GlobalFinalize;
begin
FreeAndNil(FGlobal);
end;
class procedure TThreadCounter.GlobalInitialize;
begin
FGlobal := TThreadCounter.Create;
end;
procedure TThreadCounter.NotifyThreadEnd;
begin
TInterlocked.Decrement(FThreadCount);
end;
procedure TThreadCounter.NotifyThreadStart;
begin
if FTerminating then
Abort;
TInterlocked.Increment(FThreadCount);
end;
procedure TThreadCounter.WaitForAll(Timeout: Cardinal);
var
dtStart : TDateTime;
begin
dtStart := Now;
while (FThreadCount > 0) and
( (Timeout = 0) or
((Timeout > 0) and (MillisecondsBetween(dtStart,Now) >= Timeout))
) do
begin
sleep(10);
if TThread.Current.ThreadID = MainThreadID then
CheckSynchronize;
end;
end;
{ TConsoleSync }
procedure TConsole.CheckSynchronize(Timeout : Cardinal = INFINITE);
begin
FEvent.WaitFor(Timeout);
System.Classes.CheckSynchronize;
end;
constructor TConsole.Create;
begin
inherited Create;
FEvent := TEvent.Create;
WakeMainThread := Wake;
end;
destructor TConsole.Destroy;
begin
FEvent.Free;
inherited;
end;
class procedure TConsole.ApplicationLoop(const &Until: TFunc<Boolean>);
var
CS : TConsole;
begin
CS := TConsole.Create;
try
repeat
CS.CheckSynchronize(1000);
until not &Until();
finally
CS.Free;
end;
end;
procedure TConsole.Wake(Sender: TObject);
begin
FEvent.SetEvent;
end;
initialization
// NOTE: Using initialization and finalization to ensure is referenced
// where class constructor and class destructor may be optimized out.
TThreadCounter.GlobalInitialize;
finalization
TThreadCounter.GlobalFinalize;
end.