heap.inc 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by the Free Pascal development team.
  4. functions for heap management in the data segment
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {****************************************************************************}
  12. { Try to find the best matching block in general freelist }
  13. { define BESTMATCH}
  14. { DEBUG: Dump info when the heap needs to grow }
  15. { define DUMPGROW}
  16. const
  17. {$ifdef CPU64}
  18. blocksize = 32; { at least size of freerecord }
  19. blockshift = 5; { shr value for blocksize=2^blockshift}
  20. maxblocksize = 512+blocksize; { 1024+8 needed for heaprecord }
  21. {$else}
  22. blocksize = 16; { at least size of freerecord }
  23. blockshift = 4; { shr value for blocksize=2^blockshift}
  24. maxblocksize = 512+blocksize; { 1024+8 needed for heaprecord }
  25. {$endif}
  26. maxblockindex = maxblocksize div blocksize; { highest index in array of lists of memchunks }
  27. maxreusebigger = 8; { max reuse bigger tries }
  28. { common flags }
  29. fixedsizeflag = 1; { flag if the block is of fixed size }
  30. { memchunk var flags }
  31. usedflag = 2; { flag if the block is used or not }
  32. lastblockflag = 4; { flag if the block is the last in os chunk }
  33. firstblockflag = 8; { flag if the block is the first in os chunk }
  34. sizemask = not(blocksize-1);
  35. fixedsizemask = sizemask and $ffff;
  36. {****************************************************************************}
  37. {$ifdef DUMPGROW}
  38. {$define DUMPBLOCKS}
  39. {$endif}
  40. { Forward defines }
  41. procedure SysHeapMutexInit;forward;
  42. procedure SysHeapMutexDone;forward;
  43. procedure SysHeapMutexLock;forward;
  44. procedure SysHeapMutexUnlock;forward;
  45. { Memory manager }
  46. const
  47. MemoryManager: TMemoryManager = (
  48. NeedLock: true;
  49. GetMem: @SysGetMem;
  50. FreeMem: @SysFreeMem;
  51. FreeMemSize: @SysFreeMemSize;
  52. AllocMem: @SysAllocMem;
  53. ReAllocMem: @SysReAllocMem;
  54. MemSize: @SysMemSize;
  55. GetHeapStatus: @SysGetHeapStatus;
  56. GetFPCHeapStatus: @SysGetFPCHeapStatus;
  57. );
  58. MemoryMutexManager: TMemoryMutexManager = (
  59. MutexInit: @SysHeapMutexInit;
  60. MutexDone: @SysHeapMutexDone;
  61. MutexLock: @SysHeapMutexLock;
  62. MutexUnlock: @SysHeapMutexUnlock;
  63. );
  64. type
  65. poschunk = ^toschunk;
  66. { keep size of this record dividable by 16 }
  67. toschunk = record
  68. size,
  69. used,
  70. chunkindex : ptrint;
  71. next,
  72. prev : poschunk;
  73. {$ifdef CPU64}
  74. pad1 : array[0..0] of pointer;
  75. {$else CPU64}
  76. pad1 : array[0..2] of pointer;
  77. {$endif CPU64}
  78. end;
  79. pmemchunk_fixed = ^tmemchunk_fixed;
  80. tmemchunk_fixed = record
  81. size : ptrint;
  82. poc : poschunk;
  83. next_fixed,
  84. prev_fixed : pmemchunk_fixed;
  85. end;
  86. pmemchunk_var = ^tmemchunk_var;
  87. tmemchunk_var = record
  88. size : ptrint;
  89. prevsize : ptrint;
  90. next_var,
  91. prev_var : pmemchunk_var;
  92. end;
  93. { ``header'', ie. size of structure valid when chunk is in use }
  94. { should correspond to tmemchunk_var_hdr structure starting with the
  95. last field. Reason is that the overlap is starting from the end of the
  96. record.
  97. Alignment is 8 bytes for 32bit machines. This required
  98. for x86 MMX/SSE and for sparc Double values }
  99. tmemchunk_fixed_hdr = record
  100. size : ptrint;
  101. poschunk : pointer;
  102. end;
  103. tmemchunk_var_hdr = record
  104. prevsize,
  105. size : ptrint;
  106. end;
  107. tfreelists = array[1..maxblockindex] of pmemchunk_fixed;
  108. pfreelists = ^tfreelists;
  109. var
  110. internal_status : TFPCHeapStatus;
  111. freelists_fixed : tfreelists;
  112. freelists_free_chunk : array[1..maxblockindex] of boolean;
  113. freelist_var : pmemchunk_var;
  114. freeoslist : poschunk;
  115. freeoslistcount : dword;
  116. {*****************************************************************************
  117. Memory Manager
  118. *****************************************************************************}
  119. procedure SetMemoryMutexManager(var MutexMgr: TMemoryMutexManager);
  120. begin
  121. { Release old mutexmanager, the default manager does nothing so
  122. calling this without initializing is safe }
  123. MemoryMutexManager.MutexDone;
  124. { Copy new mutexmanager }
  125. MemoryMutexManager := MutexMgr;
  126. { Init new mutexmanager }
  127. MemoryMutexManager.MutexInit;
  128. end;
  129. procedure GetMemoryManager(var MemMgr:TMemoryManager);
  130. begin
  131. if IsMultiThread and MemoryManager.NeedLock then
  132. begin
  133. try
  134. MemoryMutexManager.MutexLock;
  135. MemMgr := MemoryManager;
  136. finally
  137. MemoryMutexManager.MutexUnlock;
  138. end;
  139. end
  140. else
  141. begin
  142. MemMgr := MemoryManager;
  143. end;
  144. end;
  145. procedure SetMemoryManager(const MemMgr:TMemoryManager);
  146. begin
  147. if IsMultiThread and MemoryManager.NeedLock then
  148. begin
  149. try
  150. MemoryMutexManager.MutexLock;
  151. MemoryManager := MemMgr;
  152. finally
  153. MemoryMutexManager.MutexUnlock;
  154. end;
  155. end
  156. else
  157. begin
  158. MemoryManager := MemMgr;
  159. end;
  160. end;
  161. function IsMemoryManagerSet:Boolean;
  162. begin
  163. if IsMultiThread and MemoryManager.NeedLock then
  164. begin
  165. try
  166. MemoryMutexManager.MutexLock;
  167. IsMemoryManagerSet := (MemoryManager.GetMem<>@SysGetMem) or
  168. (MemoryManager.FreeMem<>@SysFreeMem);
  169. finally
  170. MemoryMutexManager.MutexUnlock;
  171. end;
  172. end
  173. else
  174. begin
  175. IsMemoryManagerSet := (MemoryManager.GetMem<>@SysGetMem) or
  176. (MemoryManager.FreeMem<>@SysFreeMem);
  177. end;
  178. end;
  179. procedure GetMem(Var p:pointer;Size:ptrint);
  180. begin
  181. if IsMultiThread and MemoryManager.NeedLock then
  182. begin
  183. try
  184. MemoryMutexManager.MutexLock;
  185. p := MemoryManager.GetMem(Size);
  186. finally
  187. MemoryMutexManager.MutexUnlock;
  188. end;
  189. end
  190. else
  191. begin
  192. p := MemoryManager.GetMem(Size);
  193. end;
  194. end;
  195. procedure GetMemory(Var p:pointer;Size:ptrint);
  196. begin
  197. GetMem(p,size);
  198. end;
  199. procedure FreeMem(p:pointer;Size:ptrint);
  200. begin
  201. if IsMultiThread and MemoryManager.NeedLock then
  202. begin
  203. try
  204. MemoryMutexManager.MutexLock;
  205. MemoryManager.FreeMemSize(p,Size);
  206. finally
  207. MemoryMutexManager.MutexUnlock;
  208. end;
  209. end
  210. else
  211. begin
  212. MemoryManager.FreeMemSize(p,Size);
  213. end;
  214. end;
  215. procedure FreeMemory(p:pointer;Size:ptrint);
  216. begin
  217. FreeMem(p,size);
  218. end;
  219. function GetHeapStatus:THeapStatus;
  220. begin
  221. if IsMultiThread and MemoryManager.NeedLock then
  222. begin
  223. try
  224. MemoryMutexManager.MutexLock;
  225. result:=MemoryManager.GetHeapStatus();
  226. finally
  227. MemoryMutexManager.MutexUnlock;
  228. end;
  229. end
  230. else
  231. begin
  232. result:=MemoryManager.GetHeapStatus();
  233. end;
  234. end;
  235. function GetFPCHeapStatus:TFPCHeapStatus;
  236. begin
  237. if IsMultiThread and MemoryManager.NeedLock then
  238. begin
  239. try
  240. MemoryMutexManager.MutexLock;
  241. result:=MemoryManager.GetFPCHeapStatus();
  242. finally
  243. MemoryMutexManager.MutexUnlock;
  244. end;
  245. end
  246. else
  247. begin
  248. Result:=MemoryManager.GetFPCHeapStatus();
  249. end;
  250. end;
  251. function MemSize(p:pointer):ptrint;
  252. begin
  253. if IsMultiThread and MemoryManager.NeedLock then
  254. begin
  255. try
  256. MemoryMutexManager.MutexLock;
  257. MemSize := MemoryManager.MemSize(p);
  258. finally
  259. MemoryMutexManager.MutexUnlock;
  260. end;
  261. end
  262. else
  263. begin
  264. MemSize := MemoryManager.MemSize(p);
  265. end;
  266. end;
  267. { Delphi style }
  268. function FreeMem(p:pointer):ptrint;[Public,Alias:'FPC_FREEMEM_X'];
  269. begin
  270. if IsMultiThread and MemoryManager.NeedLock then
  271. begin
  272. try
  273. MemoryMutexManager.MutexLock;
  274. Freemem := MemoryManager.FreeMem(p);
  275. finally
  276. MemoryMutexManager.MutexUnlock;
  277. end;
  278. end
  279. else
  280. begin
  281. Freemem := MemoryManager.FreeMem(p);
  282. end;
  283. end;
  284. function FreeMemory(p:pointer):ptrint;
  285. begin
  286. FreeMemory := FreeMem(p);
  287. end;
  288. function GetMem(size:ptrint):pointer;
  289. begin
  290. if IsMultiThread and MemoryManager.NeedLock then
  291. begin
  292. try
  293. MemoryMutexManager.MutexLock;
  294. GetMem := MemoryManager.GetMem(Size);
  295. finally
  296. MemoryMutexManager.MutexUnlock;
  297. end;
  298. end
  299. else
  300. begin
  301. GetMem := MemoryManager.GetMem(Size);
  302. end;
  303. end;
  304. function GetMemory(size:ptrint):pointer;
  305. begin
  306. GetMemory := Getmem(size);
  307. end;
  308. function AllocMem(Size:ptrint):pointer;
  309. begin
  310. if IsMultiThread and MemoryManager.NeedLock then
  311. begin
  312. try
  313. MemoryMutexManager.MutexLock;
  314. AllocMem := MemoryManager.AllocMem(size);
  315. finally
  316. MemoryMutexManager.MutexUnlock;
  317. end;
  318. end
  319. else
  320. begin
  321. AllocMem := MemoryManager.AllocMem(size);
  322. end;
  323. end;
  324. function ReAllocMem(var p:pointer;Size:ptrint):pointer;
  325. begin
  326. if IsMultiThread and MemoryManager.NeedLock then
  327. begin
  328. try
  329. MemoryMutexManager.MutexLock;
  330. ReAllocMem := MemoryManager.ReAllocMem(p,size);
  331. finally
  332. MemoryMutexManager.MutexUnlock;
  333. end;
  334. end
  335. else
  336. begin
  337. ReAllocMem := MemoryManager.ReAllocMem(p,size);
  338. end;
  339. end;
  340. function ReAllocMemory(var p:pointer;Size:ptrint):pointer;
  341. begin
  342. ReAllocMemory := ReAllocMem(p,size);
  343. end;
  344. { Needed for calls from Assembler }
  345. function fpc_getmem(size:ptrint):pointer;compilerproc;[public,alias:'FPC_GETMEM'];
  346. begin
  347. if IsMultiThread and MemoryManager.NeedLock then
  348. begin
  349. try
  350. MemoryMutexManager.MutexLock;
  351. fpc_GetMem := MemoryManager.GetMem(size);
  352. finally
  353. MemoryMutexManager.MutexUnlock;
  354. end;
  355. end
  356. else
  357. begin
  358. fpc_GetMem := MemoryManager.GetMem(size);
  359. end;
  360. end;
  361. procedure fpc_freemem(p:pointer);compilerproc;[public,alias:'FPC_FREEMEM'];
  362. begin
  363. if IsMultiThread and MemoryManager.NeedLock then
  364. begin
  365. try
  366. MemoryMutexManager.MutexLock;
  367. if p <> nil then
  368. MemoryManager.FreeMem(p);
  369. finally
  370. MemoryMutexManager.MutexUnlock;
  371. end;
  372. end
  373. else
  374. begin
  375. if p <> nil then
  376. MemoryManager.FreeMem(p);
  377. end;
  378. end;
  379. {*****************************************************************************
  380. GetHeapStatus
  381. *****************************************************************************}
  382. function SysGetFPCHeapStatus:TFPCHeapStatus;
  383. begin
  384. internal_status.CurrHeapFree:=internal_status.CurrHeapSize-internal_status.CurrHeapUsed;
  385. result:=internal_status;
  386. end;
  387. function SysGetHeapStatus :THeapStatus;
  388. begin
  389. internal_status.CurrHeapFree:=internal_status.CurrHeapSize-internal_status.CurrHeapUsed;
  390. result.TotalAllocated :=internal_status.CurrHeapUsed;
  391. result.TotalFree :=internal_status.CurrHeapFree;
  392. result.TotalAddrSpace :=0;
  393. result.TotalUncommitted :=0;
  394. result.TotalCommitted :=0;
  395. result.FreeSmall :=0;
  396. result.FreeBig :=0;
  397. result.Unused :=0;
  398. result.Overhead :=0;
  399. result.HeapErrorCode :=0;
  400. end;
  401. {$ifdef DUMPBLOCKS} // TODO
  402. procedure DumpBlocks;
  403. var
  404. s,i,j : ptrint;
  405. hpfixed : pmemchunk_fixed;
  406. hpvar : pmemchunk_var;
  407. begin
  408. { fixed freelist }
  409. for i := 1 to maxblockindex do
  410. begin
  411. hpfixed := freelists_fixed[i];
  412. j := 0;
  413. while assigned(hpfixed) do
  414. begin
  415. inc(j);
  416. hpfixed := hpfixed^.next_fixed;
  417. end;
  418. writeln('Block ',i*blocksize,': ',j);
  419. end;
  420. { var freelist }
  421. hpvar := freelist_var;
  422. j := 0;
  423. s := 0;
  424. while assigned(hpvar) do
  425. begin
  426. inc(j);
  427. if hpvar^.size>s then
  428. s := hpvar^.size;
  429. hpvar := hpvar^.next_var;
  430. end;
  431. writeln('Variable: ',j,' maxsize: ',s);
  432. end;
  433. {$endif}
  434. {*****************************************************************************
  435. List adding/removal
  436. *****************************************************************************}
  437. procedure append_to_list_var(pmc: pmemchunk_var);inline;
  438. begin
  439. pmc^.prev_var := nil;
  440. pmc^.next_var := freelist_var;
  441. if freelist_var<>nil then
  442. freelist_var^.prev_var := pmc;
  443. freelist_var := pmc;
  444. end;
  445. procedure remove_from_list_var(pmc: pmemchunk_var);inline;
  446. begin
  447. if assigned(pmc^.next_var) then
  448. pmc^.next_var^.prev_var := pmc^.prev_var;
  449. if assigned(pmc^.prev_var) then
  450. pmc^.prev_var^.next_var := pmc^.next_var
  451. else
  452. freelist_var := pmc^.next_var;
  453. end;
  454. procedure append_to_oslist(poc: poschunk);
  455. begin
  456. { decide whether to free block or add to list }
  457. {$ifdef HAS_SYSOSFREE}
  458. if (freeoslistcount >= MaxKeptOSChunks) or
  459. (poc^.size > growheapsize2) then
  460. begin
  461. dec(internal_status.currheapsize, poc^.size);
  462. SysOSFree(poc, poc^.size);
  463. end
  464. else
  465. begin
  466. {$endif}
  467. poc^.prev := nil;
  468. poc^.next := freeoslist;
  469. if freeoslist <> nil then
  470. freeoslist^.prev := poc;
  471. freeoslist := poc;
  472. inc(freeoslistcount);
  473. {$ifdef HAS_SYSOSFREE}
  474. end;
  475. {$endif}
  476. end;
  477. procedure remove_from_oslist(poc: poschunk);
  478. begin
  479. if assigned(poc^.next) then
  480. poc^.next^.prev := poc^.prev;
  481. if assigned(poc^.prev) then
  482. poc^.prev^.next := poc^.next
  483. else
  484. freeoslist := poc^.next;
  485. dec(freeoslistcount);
  486. end;
  487. procedure append_to_oslist_var(pmc: pmemchunk_var);
  488. var
  489. poc: poschunk;
  490. begin
  491. // block eligable for freeing
  492. poc := pointer(pmc)-sizeof(toschunk);
  493. remove_from_list_var(pmc);
  494. append_to_oslist(poc);
  495. end;
  496. procedure append_to_oslist_fixed(poc: poschunk);
  497. var
  498. pmc: pmemchunk_fixed;
  499. chunksize,
  500. chunkindex,
  501. i, count: ptrint;
  502. begin
  503. chunkindex:=poc^.chunkindex;
  504. chunksize:=chunkindex shl blockshift;
  505. pmc := pmemchunk_fixed(pointer(poc)+sizeof(toschunk));
  506. count := (poc^.size - sizeof(toschunk)) div chunksize;
  507. for i := 0 to count - 1 do
  508. begin
  509. if assigned(pmc^.next_fixed) then
  510. pmc^.next_fixed^.prev_fixed := pmc^.prev_fixed;
  511. if assigned(pmc^.prev_fixed) then
  512. pmc^.prev_fixed^.next_fixed := pmc^.next_fixed
  513. else
  514. freelists_fixed[chunkindex] := pmc^.next_fixed;
  515. pmc := pointer(pmc)+chunksize;
  516. end;
  517. append_to_oslist(poc);
  518. end;
  519. {*****************************************************************************
  520. Split block
  521. *****************************************************************************}
  522. procedure split_block(pcurr: pmemchunk_var; size: ptrint);
  523. var
  524. pcurr_tmp : pmemchunk_var;
  525. sizeleft: ptrint;
  526. begin
  527. sizeleft := (pcurr^.size and sizemask)-size;
  528. if sizeleft>=blocksize then
  529. begin
  530. pcurr_tmp := pmemchunk_var(pointer(pcurr)+size);
  531. { update prevsize of block to the right }
  532. if (pcurr^.size and lastblockflag) = 0 then
  533. pmemchunk_var(pointer(pcurr)+(pcurr^.size and sizemask))^.prevsize := sizeleft;
  534. { inherit the lastblockflag }
  535. pcurr_tmp^.size := sizeleft or (pcurr^.size and lastblockflag);
  536. pcurr_tmp^.prevsize := size;
  537. { the block we return is not the last one anymore (there's now a block after it) }
  538. { decrease size of block to new size }
  539. pcurr^.size := size or (pcurr^.size and (not sizemask and not lastblockflag));
  540. { insert the block in the freelist }
  541. append_to_list_var(pcurr_tmp);
  542. end;
  543. end;
  544. {*****************************************************************************
  545. Try concat freerecords
  546. *****************************************************************************}
  547. procedure concat_two_blocks(mc_left, mc_right: pmemchunk_var);
  548. var
  549. mc_tmp : pmemchunk_var;
  550. size_right : ptrint;
  551. begin
  552. // mc_right can't be a fixed size block
  553. if mc_right^.size and fixedsizeflag<>0 then
  554. HandleError(204);
  555. // left block free, concat with right-block
  556. size_right := mc_right^.size and sizemask;
  557. inc(mc_left^.size, size_right);
  558. // if right-block was last block, copy flag
  559. if (mc_right^.size and lastblockflag) <> 0 then
  560. begin
  561. mc_left^.size := mc_left^.size or lastblockflag;
  562. end
  563. else
  564. begin
  565. // there is a block to the right of the right-block, adjust it's prevsize
  566. mc_tmp := pmemchunk_var(pointer(mc_right)+size_right);
  567. mc_tmp^.prevsize := mc_left^.size and sizemask;
  568. end;
  569. // remove right-block from doubly linked list
  570. remove_from_list_var(mc_right);
  571. end;
  572. procedure try_concat_free_chunk_forward(mc: pmemchunk_var);
  573. var
  574. mc_tmp : pmemchunk_var;
  575. begin
  576. { try concat forward }
  577. if (mc^.size and lastblockflag) = 0 then
  578. begin
  579. mc_tmp := pmemchunk_var(pointer(mc)+(mc^.size and sizemask));
  580. if (mc_tmp^.size and usedflag) = 0 then
  581. begin
  582. // next block free: concat
  583. concat_two_blocks(mc, mc_tmp);
  584. end;
  585. end;
  586. end;
  587. function try_concat_free_chunk(mc: pmemchunk_var): pmemchunk_var;
  588. var
  589. mc_tmp : pmemchunk_var;
  590. begin
  591. try_concat_free_chunk_forward(mc);
  592. { try concat backward }
  593. if (mc^.size and firstblockflag) = 0 then
  594. begin
  595. mc_tmp := pmemchunk_var(pointer(mc)-mc^.prevsize);
  596. if (mc_tmp^.size and usedflag) = 0 then
  597. begin
  598. // prior block free: concat
  599. concat_two_blocks(mc_tmp, mc);
  600. mc := mc_tmp;
  601. end;
  602. end;
  603. result := mc;
  604. end;
  605. function check_concat_free_chunk_forward(mc: pmemchunk_var;reqsize:ptrint):boolean;
  606. var
  607. mc_tmp : pmemchunk_var;
  608. freesize : ptrint;
  609. begin
  610. check_concat_free_chunk_forward:=false;
  611. freesize:=0;
  612. mc_tmp:=mc;
  613. repeat
  614. inc(freesize,mc_tmp^.size and sizemask);
  615. if freesize>=reqsize then
  616. begin
  617. check_concat_free_chunk_forward:=true;
  618. exit;
  619. end;
  620. if (mc_tmp^.size and lastblockflag) <> 0 then
  621. break;
  622. mc_tmp := pmemchunk_var(pointer(mc_tmp)+(mc_tmp^.size and sizemask));
  623. if (mc_tmp^.size and usedflag) <> 0 then
  624. break;
  625. until false;
  626. end;
  627. {*****************************************************************************
  628. Grow Heap
  629. *****************************************************************************}
  630. function alloc_oschunk(chunkindex, size: ptrint):pointer;
  631. var
  632. pmcfirst,
  633. pmclast,
  634. pmc : pmemchunk_fixed;
  635. pmcv : pmemchunk_var;
  636. poc : poschunk;
  637. chunksize,
  638. minsize,
  639. maxsize,
  640. i, count : ptrint;
  641. begin
  642. result:=nil;
  643. chunksize:=chunkindex shl blockshift;
  644. { increase size by size needed for os block header }
  645. minsize := size + sizeof(toschunk);
  646. if chunkindex<>0 then
  647. maxsize := (chunksize * $ffff) + sizeof(toschunk)
  648. else
  649. maxsize := high(ptrint);
  650. { blocks available in freelist? }
  651. poc := freeoslist;
  652. while poc <> nil do
  653. begin
  654. if (poc^.size >= minsize) and
  655. (poc^.size <= maxsize) then
  656. begin
  657. size := poc^.size;
  658. remove_from_oslist(poc);
  659. break;
  660. end;
  661. poc := poc^.next;
  662. end;
  663. if poc = nil then
  664. begin
  665. {$ifdef DUMPGROW}
  666. writeln('growheap(',size,') allocating ',(size+sizeof(toschunk)+$ffff) and $ffff0000);
  667. DumpBlocks;
  668. {$endif}
  669. { allocate by 64K size }
  670. size := (size+sizeof(toschunk)+$ffff) and not $ffff;
  671. { allocate smaller blocks for fixed-size chunks }
  672. if chunksize<>0 then
  673. begin
  674. poc := SysOSAlloc(GrowHeapSizeSmall);
  675. if poc<>nil then
  676. size := GrowHeapSizeSmall;
  677. end
  678. { first try 256K (default) }
  679. else if size<=GrowHeapSize1 then
  680. begin
  681. poc := SysOSAlloc(GrowHeapSize1);
  682. if poc<>nil then
  683. size := GrowHeapSize1;
  684. end
  685. { second try 1024K (default) }
  686. else if size<=GrowHeapSize2 then
  687. begin
  688. poc := SysOSAlloc(GrowHeapSize2);
  689. if poc<>nil then
  690. size := GrowHeapSize2;
  691. end
  692. { else allocate the needed bytes }
  693. else
  694. poc := SysOSAlloc(size);
  695. { try again }
  696. if poc=nil then
  697. begin
  698. poc := SysOSAlloc(size);
  699. if (poc=nil) then
  700. begin
  701. if ReturnNilIfGrowHeapFails then
  702. exit
  703. else
  704. HandleError(203);
  705. end;
  706. end;
  707. { set the total new heap size }
  708. inc(internal_status.currheapsize,size);
  709. if internal_status.currheapsize>internal_status.maxheapsize then
  710. internal_status.maxheapsize:=internal_status.currheapsize;
  711. end;
  712. { initialize os-block }
  713. poc^.used := 0;
  714. poc^.size := size;
  715. poc^.chunkindex := chunkindex;
  716. { initialized oschunck for fixed chunks }
  717. if chunkindex<>0 then
  718. begin
  719. { chop os chunk in fixedsize parts,
  720. maximum of $ffff elements are allowed, otherwise
  721. there will be an overflow }
  722. count := (size-sizeof(toschunk)) div chunksize;
  723. if count>$ffff then
  724. HandleError(204);
  725. { Initialize linkedlist of chunks, the first chunk
  726. is pmemchunk_fixed(poc) and the last chunk will be in pmc at
  727. the end of the loop }
  728. pmcfirst := pmemchunk_fixed(pointer(poc)+sizeof(toschunk));
  729. pmc:=pmcfirst;
  730. for i:=1 to count do
  731. begin
  732. pmc^.poc:=poc;
  733. pmc^.size:=chunksize or fixedsizeflag;
  734. pmc^.prev_fixed := pointer(pmc)-chunksize;
  735. pmc^.next_fixed := pointer(pmc)+chunksize;
  736. pmc := pmemchunk_fixed(pointer(pmc)+chunksize);
  737. end;
  738. { undo last increase to get last chunk }
  739. pmclast := pmemchunk_fixed(pointer(pmc)-chunksize);
  740. { Add to freelist and fixup first and last chunk }
  741. pmclast^.next_fixed := freelists_fixed[chunkindex];
  742. if freelists_fixed[chunkindex]<>nil then
  743. freelists_fixed[chunkindex]^.prev_fixed := pmclast;
  744. freelists_fixed[chunkindex] := pmcfirst;
  745. pmemchunk_fixed(poc)^.prev_fixed:=nil;
  746. result:=pmcfirst;
  747. end
  748. else
  749. begin
  750. pmcv := pmemchunk_var(pointer(poc)+sizeof(toschunk));
  751. append_to_list_var(pmcv);
  752. pmcv^.size := ((size-sizeof(toschunk)) and sizemask) or (firstblockflag or lastblockflag);
  753. pmcv^.prevsize := 0;
  754. result:=pmcv;
  755. end;
  756. end;
  757. {*****************************************************************************
  758. SysGetMem
  759. *****************************************************************************}
  760. function SysGetMem_Fixed(size: ptrint): pointer;
  761. var
  762. pmc,hp : pmemchunk_fixed;
  763. poc : poschunk;
  764. chunkindex : ptrint;
  765. begin
  766. { try to find a block in one of the freelists per size }
  767. chunkindex := size shr blockshift;
  768. pmc := freelists_fixed[chunkindex];
  769. result:=nil;
  770. { no free blocks ? }
  771. if not assigned(pmc) then
  772. begin
  773. pmc:=alloc_oschunk(chunkindex, size);
  774. if not assigned(pmc) then
  775. exit;
  776. end;
  777. { get a pointer to the block we should return }
  778. result := pointer(pmc)+sizeof(tmemchunk_fixed_hdr);
  779. { update freelist }
  780. hp:=pmc^.next_fixed;
  781. poc := pmc^.poc;
  782. freelists_fixed[chunkindex] := hp;
  783. if assigned(hp) then
  784. hp^.prev_fixed := nil;
  785. if (poc^.used = 0) then
  786. freelists_free_chunk[chunkindex] := false;
  787. inc(poc^.used);
  788. { statistics }
  789. inc(internal_status.currheapused,size);
  790. if internal_status.currheapused>internal_status.maxheapused then
  791. internal_status.maxheapused:=internal_status.currheapused;
  792. end;
  793. function SysGetMem_Var(size: ptrint): pointer;
  794. var
  795. pcurr : pmemchunk_var;
  796. {$ifdef BESTMATCH}
  797. pbest : pmemchunk_var;
  798. {$endif}
  799. begin
  800. result:=nil;
  801. {$ifdef BESTMATCH}
  802. pbest := nil;
  803. {$endif}
  804. pcurr := freelist_var;
  805. while assigned(pcurr) do
  806. begin
  807. {$ifdef BESTMATCH}
  808. if pcurr^.size=size then
  809. begin
  810. break;
  811. end
  812. else
  813. begin
  814. if (pcurr^.size>size) then
  815. begin
  816. if (not assigned(pbest)) or
  817. (pcurr^.size<pbest^.size) then
  818. pbest := pcurr;
  819. end;
  820. end;
  821. {$else BESTMATCH}
  822. if pcurr^.size>=size then
  823. break;
  824. {$endif BESTMATCH}
  825. pcurr := pcurr^.next_var;
  826. end;
  827. {$ifdef BESTMATCH}
  828. if not assigned(pcurr) then
  829. pcurr := pbest;
  830. {$endif}
  831. if not assigned(pcurr) then
  832. begin
  833. // all os-chunks full, allocate a new one
  834. pcurr := alloc_oschunk(0, size);
  835. if not assigned(pcurr) then
  836. exit;
  837. end;
  838. { get pointer of the block we should return }
  839. result := pointer(pcurr)+sizeof(tmemchunk_var_hdr);
  840. { remove the current block from the freelist }
  841. remove_from_list_var(pcurr);
  842. { create the left over freelist block, if at least 16 bytes are free }
  843. split_block(pcurr, size);
  844. { flag block as used }
  845. pcurr^.size := pcurr^.size or usedflag;
  846. { statistics }
  847. inc(internal_status.currheapused,size);
  848. if internal_status.currheapused>internal_status.maxheapused then
  849. internal_status.maxheapused:=internal_status.currheapused;
  850. end;
  851. function SysGetMem(size : ptrint):pointer;
  852. begin
  853. { Something to allocate ? }
  854. if size<=0 then
  855. begin
  856. { give an error for < 0 }
  857. if size<0 then
  858. HandleError(204);
  859. { we always need to allocate something, using heapend is not possible,
  860. because heappend can be changed by growheap (PFV) }
  861. size := 1;
  862. end;
  863. { calc to multiple of 16 after adding the needed bytes for memchunk header }
  864. if size <= (maxblocksize - sizeof(tmemchunk_fixed_hdr)) then
  865. begin
  866. size := (size+(sizeof(tmemchunk_fixed_hdr)+(blocksize-1))) and fixedsizemask;
  867. result := sysgetmem_fixed(size);
  868. end
  869. else
  870. begin
  871. size := (size+(sizeof(tmemchunk_var_hdr)+(blocksize-1))) and sizemask;
  872. result := sysgetmem_var(size);
  873. end;
  874. end;
  875. {*****************************************************************************
  876. SysFreeMem
  877. *****************************************************************************}
  878. function SysFreeMem_Fixed(pmc: pmemchunk_fixed): ptrint;
  879. var
  880. hp : pmemchunk_fixed;
  881. chunksize,
  882. chunkindex : ptrint;
  883. poc : poschunk;
  884. begin
  885. poc := pmc^.poc;
  886. chunkindex:=poc^.chunkindex;
  887. chunksize:=chunkindex shl blockshift;
  888. { statistics }
  889. dec(internal_status.currheapused,chunksize);
  890. hp:=freelists_fixed[chunkindex];
  891. { insert the block in it's freelist }
  892. pmc^.prev_fixed := nil;
  893. pmc^.next_fixed := hp;
  894. if assigned(hp) then
  895. hp^.prev_fixed := pmc;
  896. freelists_fixed[chunkindex] := pmc;
  897. dec(poc^.used);
  898. if poc^.used <= 0 then
  899. begin
  900. { decrease used blocks count }
  901. if poc^.used=-1 then
  902. HandleError(204);
  903. { osblock can be freed? }
  904. if freelists_free_chunk[chunkindex] then
  905. append_to_oslist_fixed(poc)
  906. else
  907. freelists_free_chunk[chunkindex] := true;
  908. end;
  909. result := chunksize;
  910. end;
  911. function SysFreeMem_Var(pcurr: pmemchunk_var): ptrint;
  912. var
  913. chunksize: ptrint;
  914. begin
  915. chunksize := pcurr^.size and sizemask;
  916. dec(internal_status.currheapused,chunksize);
  917. { insert the block in it's freelist }
  918. pcurr^.size := pcurr^.size and (not usedflag);
  919. append_to_list_var(pcurr);
  920. result := chunksize;
  921. pcurr := try_concat_free_chunk(pcurr);
  922. if (pcurr^.size and (firstblockflag or lastblockflag)) = (firstblockflag or lastblockflag) then
  923. append_to_oslist_var(pcurr);
  924. end;
  925. function SysFreeMem(p: pointer): ptrint;
  926. var
  927. hp : pmemchunk_fixed;
  928. begin
  929. if p=nil then
  930. begin
  931. result:=0;
  932. exit;
  933. end;
  934. hp:=pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr));
  935. { check if this is a fixed- or var-sized chunk }
  936. if (hp^.size and fixedsizeflag) = 0 then
  937. result := sysfreemem_var(pmemchunk_var(p-sizeof(tmemchunk_var_hdr)))
  938. else
  939. result := sysfreemem_fixed(hp);
  940. end;
  941. {*****************************************************************************
  942. SysFreeMemSize
  943. *****************************************************************************}
  944. Function SysFreeMemSize(p: pointer; size: ptrint):ptrint;
  945. var
  946. hp : pmemchunk_fixed;
  947. begin
  948. SysFreeMemSize := 0;
  949. if p=nil then
  950. exit;
  951. if size<=0 then
  952. begin
  953. if size<0 then
  954. HandleError(204);
  955. exit;
  956. end;
  957. hp:=pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr));
  958. { check if this is a fixed- or var-sized chunk. We can't check the passed
  959. size parameter since the block can be resized (by reallocmem) to an
  960. optimized value that the user doesn't know }
  961. if (hp^.size and fixedsizeflag) = 0 then
  962. result := sysfreemem_var(pmemchunk_var(p-sizeof(tmemchunk_var_hdr)))
  963. else
  964. result := sysfreemem_fixed(hp);
  965. end;
  966. {*****************************************************************************
  967. SysMemSize
  968. *****************************************************************************}
  969. function SysMemSize(p: pointer): ptrint;
  970. begin
  971. result := pmemchunk_fixed(pointer(p)-sizeof(tmemchunk_fixed_hdr))^.size;
  972. if (result and fixedsizeflag) = 0 then
  973. begin
  974. result := SysMemSize and sizemask;
  975. dec(result, sizeof(tmemchunk_var_hdr));
  976. end
  977. else
  978. begin
  979. result := SysMemSize and fixedsizemask;
  980. dec(result, sizeof(tmemchunk_fixed_hdr));
  981. end;
  982. end;
  983. {*****************************************************************************
  984. SysAllocMem
  985. *****************************************************************************}
  986. function SysAllocMem(size: ptrint): pointer;
  987. begin
  988. result := MemoryManager.GetMem(size);
  989. if result<>nil then
  990. FillChar(result^,MemoryManager.MemSize(result),0);
  991. end;
  992. {*****************************************************************************
  993. SysResizeMem
  994. *****************************************************************************}
  995. function SysTryResizeMem(var p: pointer; size: ptrint): boolean;
  996. var
  997. chunksize,
  998. oldsize,
  999. currsize : ptrint;
  1000. pcurr : pmemchunk_var;
  1001. begin
  1002. SysTryResizeMem := false;
  1003. { fix p to point to the heaprecord }
  1004. chunksize := pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr))^.size;
  1005. { handle fixed memchuncks separate. Only allow resizes when the
  1006. new size fits in the same block }
  1007. if (chunksize and fixedsizeflag) <> 0 then
  1008. begin
  1009. currsize := chunksize and fixedsizemask;
  1010. { first check if the size fits in the fixed block range to prevent
  1011. "truncating" the size by the fixedsizemask }
  1012. if (size <= (maxblocksize - sizeof(tmemchunk_fixed_hdr))) and
  1013. ((size+sizeof(tmemchunk_fixed_hdr)+(blocksize-1)) and sizemask =currsize ) then
  1014. begin
  1015. systryresizemem:=true;
  1016. exit;
  1017. end;
  1018. { we need to allocate a new fixed or var memchunck }
  1019. exit;
  1020. end;
  1021. { var memchunck }
  1022. currsize := chunksize and sizemask;
  1023. size := (size+sizeof(tmemchunk_var_hdr)+(blocksize-1)) and sizemask;
  1024. { is the allocated block still correct? }
  1025. if (currsize>=size) and (size>(currsize-blocksize)) then
  1026. begin
  1027. SysTryResizeMem := true;
  1028. exit;
  1029. end;
  1030. { get pointer to block }
  1031. pcurr := pmemchunk_var(pointer(p)-sizeof(tmemchunk_var_hdr));
  1032. oldsize := currsize;
  1033. { do we need to allocate more memory ? }
  1034. if size>currsize then
  1035. begin
  1036. { the size is bigger than the previous size, we need to allocated more mem.
  1037. We first check if the blocks after the current block are free. If not then we
  1038. simply call getmem/freemem to get the new block }
  1039. if check_concat_free_chunk_forward(pcurr,size) then
  1040. repeat
  1041. concat_two_blocks(pcurr,pmemchunk_var(pointer(pcurr)+currsize));
  1042. currsize := pcurr^.size and sizemask;
  1043. until currsize>=size
  1044. else
  1045. exit;
  1046. end;
  1047. { is the size smaller then we can adjust the block to that size and insert
  1048. the other part into the freelist }
  1049. if currsize>size then
  1050. split_block(pcurr, size);
  1051. inc(internal_status.currheapused,size-oldsize);
  1052. SysTryResizeMem := true;
  1053. end;
  1054. {*****************************************************************************
  1055. SysResizeMem
  1056. *****************************************************************************}
  1057. function SysReAllocMem(var p: pointer; size: ptrint):pointer;
  1058. var
  1059. newsize,
  1060. oldsize,
  1061. minsize : ptrint;
  1062. p2 : pointer;
  1063. begin
  1064. { Free block? }
  1065. if size=0 then
  1066. begin
  1067. if p<>nil then
  1068. begin
  1069. MemoryManager.FreeMem(p);
  1070. p := nil;
  1071. end;
  1072. end
  1073. else
  1074. { Allocate a new block? }
  1075. if p=nil then
  1076. begin
  1077. p := MemoryManager.GetMem(size);
  1078. end
  1079. else
  1080. { Resize block }
  1081. if not SysTryResizeMem(p,size) then
  1082. begin
  1083. oldsize:=MemoryManager.MemSize(p);
  1084. { Grow with bigger steps to prevent the need for
  1085. multiple getmem/freemem calls for fixed blocks. It might cost a bit
  1086. of extra memory, but in most cases a reallocmem is done multiple times. }
  1087. if oldsize<maxblocksize then
  1088. begin
  1089. newsize:=oldsize*2+blocksize;
  1090. if size>newsize then
  1091. newsize:=size;
  1092. end
  1093. else
  1094. newsize:=size;
  1095. { calc size of data to move }
  1096. minsize:=oldsize;
  1097. if newsize < minsize then
  1098. minsize := newsize;
  1099. p2 := MemoryManager.GetMem(newsize);
  1100. if p2<>nil then
  1101. Move(p^,p2^,minsize);
  1102. MemoryManager.FreeMem(p);
  1103. p := p2;
  1104. end;
  1105. SysReAllocMem := p;
  1106. end;
  1107. {*****************************************************************************
  1108. MemoryMutexManager default hooks
  1109. *****************************************************************************}
  1110. procedure SysHeapMutexInit;
  1111. begin
  1112. { nothing todo }
  1113. end;
  1114. procedure SysHeapMutexDone;
  1115. begin
  1116. { nothing todo }
  1117. end;
  1118. procedure SysHeapMutexLock;
  1119. begin
  1120. { give an runtime error. the program is running multithreaded without
  1121. any heap protection. this will result in unpredictable errors so
  1122. stopping here with an error is more safe (PFV) }
  1123. runerror(244);
  1124. end;
  1125. procedure SysHeapMutexUnLock;
  1126. begin
  1127. { see SysHeapMutexLock for comment }
  1128. runerror(244);
  1129. end;
  1130. {*****************************************************************************
  1131. InitHeap
  1132. *****************************************************************************}
  1133. { This function will initialize the Heap manager and need to be called from
  1134. the initialization of the system unit }
  1135. procedure InitHeap;
  1136. begin
  1137. FillChar(freelists_fixed,sizeof(tfreelists),0);
  1138. FillChar(freelists_free_chunk,sizeof(freelists_free_chunk),0);
  1139. freelist_var := nil;
  1140. freeoslist := nil;
  1141. freeoslistcount := 0;
  1142. fillchar(internal_status,sizeof(internal_status),0);
  1143. end;