heap.inc 33 KB

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