-
Notifications
You must be signed in to change notification settings - Fork 38
/
Copy pathDelphiUtils.Async.pas
172 lines (145 loc) · 4.12 KB
/
DelphiUtils.Async.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
unit DelphiUtils.Async;
{
This module provides infrastructure for using anonymous functions as APC
callbacks in asynchronous operations.
}
interface
uses
Ntapi.ntioapi, DelphiApi.Reflection, DelphiUtils.AutoObjects;
type
// A prototype for an anonymous APC callback
TAnonymousApcCallback = reference to procedure (
const IoStatusBlock: TIoStatusBlock
);
{ Interfaces }
IAnonymousApcContext = interface (IAutoReleasable)
function GetCallback: TAnonymousApcCallback;
property Callback: TAnonymousApcCallback read GetCallback;
end;
// An APC context with a dedicated I/O Status Block
IAnonymousIoApcContext = interface (IAnonymousApcContext)
function IoStatusBlock: PIoStatusBlock;
end;
{ Default Implementations }
TAnonymousApcContext = class (TCustomAutoReleasable, IAnonymousApcContext)
protected
Payload: TAnonymousApcCallback;
procedure Release; override;
public
function GetCallback: TAnonymousApcCallback;
constructor Create(ApcCallback: TAnonymousApcCallback);
end;
TAnonymousIoApcContext = class (TAnonymousApcContext, IAnonymousIoApcContext)
protected
Iob: TIoStatusBlock;
public
function IoStatusBlock: PIoStatusBlock;
end;
// Get an APC routine for an anonymous APC callback
function GetApcRoutine(
[opt] AsyncCallback: TAnonymousApcCallback
): TIoApcRoutine;
// Prepare an APC context with an I/O status block for asynchronous operations
// or reference the I/O status block from the stack for synchronous calls
function PrepareApcIsb(
out ApcContext: IAnonymousIoApcContext;
[opt] AsyncCallback: TAnonymousApcCallback;
const [ref] IoStatusBlock: TIoStatusBlock
): PIoStatusBlock;
// Prepare an APC context with an I/O status block for asynchronous operations
// or allocate one from the heap
function PrepareApcIsbEx(
out ApcContext: IAnonymousIoApcContext;
[opt] AsyncCallback: TAnonymousApcCallback;
out xIoStatusBlock: IMemory<PIoStatusBlock>
): PIoStatusBlock;
implementation
{$BOOLEVAL OFF}
{$IFOPT R+}{$DEFINE R+}{$ENDIF}
{$IFOPT Q+}{$DEFINE Q+}{$ENDIF}
{ TAnonymousApcContext }
constructor TAnonymousApcContext.Create;
var
CallbackIntf: IInterface absolute ApcCallback;
begin
inherited Create;
Payload := ApcCallback;
CallbackIntf._AddRef;
end;
function TAnonymousApcContext.GetCallback;
begin
Result := Payload;
end;
procedure TAnonymousApcContext.Release;
var
Callback: TAnonymousApcCallback;
CallbackIntf: IInterface absolute Callback;
begin
Callback := Payload;
CallbackIntf._Release;
inherited;
end;
{ TAnonymousIoApcContext }
function TAnonymousIoApcContext.IoStatusBlock;
begin
Result := @Iob;
end;
{ Functions }
// An APC-compatible wrapper for calling anonymous functions
procedure ApcCallbackForwarder(
[in] ApcContext: Pointer;
const IoStatusBlock: TIoStatusBlock;
[Reserved] Reserved: Cardinal
); stdcall;
var
ContextData: IAnonymousApcContext absolute ApcContext;
begin
if Assigned(ContextData) then
try
ContextData.Callback(IoStatusBlock);
finally
// Clean-up the captured variables of a one-time callbacks
if ContextData.AutoRelease then
ContextData._Release;
end;
end;
function GetApcRoutine;
begin
// All anonymous functions go through a forwarder that manages their lifetime
if Assigned(AsyncCallback) then
Result := ApcCallbackForwarder
else
Result := nil;
end;
function PrepareApcIsb;
begin
if Assigned(AsyncCallback) then
begin
// Allocate the context and use its I/O status block
ApcContext := TAnonymousIoApcContext.Create(AsyncCallback);
Result := ApcContext.IoStatusBlock;
end
else
begin
// Use the I/O status block from the stack
ApcContext := nil;
Result := @IoStatusBlock;
end;
end;
function PrepareApcIsbEx;
begin
if Assigned(AsyncCallback) then
begin
// Allocate the context and use its I/O status block
ApcContext := TAnonymousIoApcContext.Create(AsyncCallback);
Result := ApcContext.IoStatusBlock;
end
else
begin
// Allocate just the I/O status block
ApcContext := nil;
IMemory(xIoStatusBlock) := Auto.AllocateDynamic(SizeOf(TIoStatusBlock));
Result := xIoStatusBlock.Data;
end;
end;
end.