-
Notifications
You must be signed in to change notification settings - Fork 4
/
SAPOSBlocks.pas
237 lines (172 loc) · 5.2 KB
/
SAPOSBlocks.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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
{****************************************************************************************
SAPMM v1.01 /17.06.2013/
Working with large (OS) blocks
****************************************************************************************}
unit SAPOSBlocks;
interface
{$Include SAPOptions.inc}
uses SAPDefs;
var
total_os_blocks: LongWord; // number of total OS blocks, allocated for all threads
function SAPAllocateOSBlock(mm: PSAPThreadMM; size: LongWord): PSAPOSBlock;
function SAPCheckAndFreeOSBlock(mm: PSAPThreadMM; f: PSAPFreeBlock): Boolean;
procedure SAPFreeOSBlock(mm: PSAPThreadMM; os: PSAPOSBlock);
implementation
uses Windows,
SAPList
//, SAPDebugUtils
;
var
os_blocks_pool: PSAPOSBlock;
blocks_in_pool: LongWord;
CritSect: _RTL_CRITICAL_SECTION;
//---------------------------------------------------
procedure FreeOSBlockToWindows(os: PSAPOSBlock);
forward;
procedure AddOSBlockToPool(os: PSAPOSBlock);
forward;
function GetOSBlockFromPool(size: LongWord): PSAPOSBlock;
forward;
procedure ClearPool;
forward;
//---------------------------------------------------
procedure InterlockedIncrement(var Value: LongWord);
asm
lock inc [Value]
end;
procedure InterlockedDecrement(var Value: LongWord);
asm
lock dec [Value]
end;
function SAPAllocateOSBlock(mm: PSAPThreadMM; size: LongWord): PSAPOSBlock;
var
p: PSAPOSBlock;
begin
Result := GetOSBlockFromPool(size);
if Result = nil then Exit;
{$IFDEF SAP_STAT}
Inc(mm.no_os_alloc);
{$ENDIF}
p := Result;
p.size := size;
// Add block to list
if mm.os_blocks = nil then
begin
p.next := nil;
p.prev := nil;
mm.os_blocks := p;
end
else begin
// Insert as a second block
p.next := mm.os_blocks.next;
p.prev := mm.os_blocks;
mm.os_blocks.next := p;
if p.next <> nil then
p.next.prev := p;
end;
Inc(mm.no_os_blocks);
end;
function SAPCheckAndFreeOSBlock(mm: PSAPThreadMM; f: PSAPFreeBlock): Boolean;
const
cOSFree = [sap_os_block_first, sap_os_block_last];
var
os: PSAPOSBlock;
begin
Result := false;
if f.block.tags * cOSFree <> cOSFree then Exit;
os := Pointer(LongWord(f) - SizeOf(TSAPOSBlock));
// Single block is not freed
if (mm.os_blocks = os) and (os.next = nil) then
Exit;
UntieFreeBlock(mm.free_blocks, f);
SAPFreeOSBlock(mm, os);
Result := true;
end;
procedure SAPFreeOSBlock(mm: PSAPThreadMM; os: PSAPOSBlock);
begin
if os.next <> nil then os.next.prev := os.prev;
if os.prev <> nil then os.prev.next := os.next;
if mm.os_blocks = os then mm.os_blocks := os.next;
Dec(mm.no_os_blocks);
{$IFDEF SAP_STAT}
Inc(mm.no_os_free);
{$ENDIF}
// If block is not of standard size (larger than cOSBlockSize)
// or too many free blocks in pool, return the block directly to the OS
if (os.size <> cOSBlockSize) or (blocks_in_pool > cMaxOSBlocksInPool) then
FreeOSBlockToWindows(os)
else
AddOSBlockToPool(os);
end;
//-----------------------------------------------
procedure FreeOSBlockToWindows(os: PSAPOSBlock);
begin
VirtualFree(os, 0, Windows.MEM_RELEASE);
InterlockedDecrement(total_os_blocks);
end;
procedure AddOSBlockToPool(os: PSAPOSBlock);
begin
os.next := nil;
os.prev := nil;
Windows.EnterCriticalSection(CritSect);
os.pool_next := os_blocks_pool;
os_blocks_pool := os;
Inc(blocks_in_pool);
Windows.LeaveCriticalSection(CritSect);
end;
function GetOSBlockFromPool(size: LongWord): PSAPOSBlock;
begin
if (size <> cOSBlockSize) or (os_blocks_pool = nil) then
begin
Result := Windows.VirtualAlloc(nil, size, Windows.MEM_RESERVE + Windows.MEM_COMMIT, Windows.PAGE_READWRITE);
if Result = nil then
begin
ClearPool;
Result := Windows.VirtualAlloc(nil, size, Windows.MEM_RESERVE + Windows.MEM_COMMIT, Windows.PAGE_READWRITE);
if Result = nil then Exit;
end;
InterlockedIncrement(total_os_blocks);
Exit;
end;
Windows.EnterCriticalSection(CritSect);
Result := os_blocks_pool;
if Result <> nil then begin
os_blocks_pool := os_blocks_pool.pool_next;
Dec(blocks_in_pool);
end;
Windows.LeaveCriticalSection(CritSect);
if Result = nil then
begin
Result := Windows.VirtualAlloc(nil, size, Windows.MEM_RESERVE + Windows.MEM_COMMIT, Windows.PAGE_READWRITE);
if Result = nil then
Exit;
InterlockedIncrement(total_os_blocks);
end;
end;
// Returns ALL OS blocks from pool to the OS. This logic is used
// if the MM could not allocate the block of non-standard size (larger than cOSBlockSize)
// This is done in unsafe way (without sync), not to slow down other threads
procedure ClearPool;
var
os: PSAPOSBlock;
next: PSAPOSBlock;
begin
asm
mov eax, 0
lock xchg os_blocks_pool,eax
mov os, eax
end;
while os <> nil do
begin
next := os.pool_next;
VirtualFree(os, 0, Windows.MEM_RELEASE);
InterlockedDecrement(total_os_blocks);
os := next;
end;
end;
initialization
total_os_blocks := 0;
os_blocks_pool := nil;
blocks_in_pool := 0;
Windows.InitializeCriticalSection(CritSect);
end.