heap.inc 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498
  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. { Do not use standard memory manager }
  13. { define HAS_MEMORYMANAGER}
  14. { Try to find the best matching block in general freelist }
  15. { define BESTMATCH}
  16. { DEBUG: Dump info when the heap needs to grow }
  17. { define DUMPGROW}
  18. { Memory profiling: at moment in time of max heap size usage,
  19. keep statistics of number of each size allocated
  20. (with 16 byte granularity) }
  21. { define DUMP_MEM_USAGE}
  22. {$ifdef DUMP_MEM_USAGE}
  23. {$define SHOW_MEM_USAGE}
  24. {$endif}
  25. const
  26. {$ifdef CPU64}
  27. blocksize = 32; { at least size of freerecord }
  28. blockshift = 5; { shr value for blocksize=2^blockshift}
  29. maxblocksize = 512+blocksize; { 1024+8 needed for heaprecord }
  30. {$else}
  31. blocksize = 16; { at least size of freerecord }
  32. blockshift = 4; { shr value for blocksize=2^blockshift}
  33. maxblocksize = 512+blocksize; { 1024+8 needed for heaprecord }
  34. {$endif}
  35. maxblockindex = maxblocksize div blocksize; { highest index in array of lists of memchunks }
  36. { common flags }
  37. fixedsizeflag = 1; { flag if the block is of fixed size }
  38. { memchunk var flags }
  39. usedflag = 2; { flag if the block is used or not }
  40. lastblockflag = 4; { flag if the block is the last in os chunk }
  41. firstblockflag = 8; { flag if the block is the first in os chunk }
  42. { os chunk flags }
  43. ocrecycleflag = 1;
  44. { above flags stored in size field }
  45. sizemask = not(blocksize-1);
  46. fixedoffsetshift = 16;
  47. fixedsizemask = sizemask and ((1 shl fixedoffsetshift) - 1);
  48. {****************************************************************************}
  49. {$ifdef DUMPGROW}
  50. {$define DUMPBLOCKS}
  51. {$endif}
  52. { Memory manager }
  53. const
  54. MemoryManager: TMemoryManager = (
  55. NeedLock: false; // Obsolete
  56. GetMem: @SysGetMem;
  57. FreeMem: @SysFreeMem;
  58. FreeMemSize: @SysFreeMemSize;
  59. AllocMem: @SysAllocMem;
  60. ReAllocMem: @SysReAllocMem;
  61. MemSize: @SysMemSize;
  62. InitThread: nil;
  63. DoneThread: nil;
  64. RelocateHeap: nil;
  65. GetHeapStatus: @SysGetHeapStatus;
  66. GetFPCHeapStatus: @SysGetFPCHeapStatus;
  67. );
  68. {$ifndef HAS_MEMORYMANAGER}
  69. {
  70. We use 'fixed' size chunks for small allocations,
  71. and os chunks with variable sized blocks for big
  72. allocations.
  73. * a block is an area allocated by user
  74. * a chunk is a block plus our bookkeeping
  75. * an os chunk is a collection of chunks
  76. Memory layout:
  77. fixed: < chunk size > [ ... user data ... ]
  78. variable: < prev chunk size > < chunk size > [ ... user data ... ]
  79. When all chunks in an os chunk are free, we keep a few around
  80. but otherwise it will be freed to the OS.
  81. Fixed os chunks can be converted to variable os chunks and back
  82. (if not too big). To prevent repeated conversion overhead in case
  83. of user freeing/allocing same or a small set of sizes, we only do
  84. the conversion to the new fixed os chunk size format after we
  85. reuse the os chunk for another fixed size, or variable. Note that
  86. while the fixed size os chunk is on the freelists.oslist, it is also
  87. still present in a freelists.fixedlists, therefore we can easily remove
  88. the os chunk from the freelists.oslist if this size is needed again; we
  89. don't need to search freelists.oslist in alloc_oschunk, since it won't
  90. be present anymore if alloc_oschunk is reached. Note that removing
  91. from the freelists.oslist is not really done, only the recycleflag is
  92. set, allowing to reset the flag easily. alloc_oschunk will clean up
  93. the list while passing over it, that was a slow function anyway.
  94. }
  95. type
  96. pfreelists = ^tfreelists;
  97. poschunk = ^toschunk;
  98. toschunk = record
  99. size : 0..high(ptrint); {Cannot be ptruint because used field is signed.}
  100. next_free : poschunk;
  101. prev_any : poschunk;
  102. next_any : poschunk;
  103. used : ptrint; { 0: free, >0: fixed, -1: var }
  104. freelists : pfreelists;
  105. { padding inserted automatically by alloc_oschunk }
  106. end;
  107. ppmemchunk_fixed = ^pmemchunk_fixed;
  108. pmemchunk_fixed = ^tmemchunk_fixed;
  109. tmemchunk_fixed = record
  110. { aligning is done automatically in alloc_oschunk }
  111. size : ptruint;
  112. next_fixed,
  113. prev_fixed : pmemchunk_fixed;
  114. end;
  115. ppmemchunk_var = ^pmemchunk_var;
  116. pmemchunk_var = ^tmemchunk_var;
  117. tmemchunk_var = record
  118. prevsize : ptruint;
  119. freelists : pfreelists;
  120. size : ptruint;
  121. next_var,
  122. prev_var : pmemchunk_var;
  123. end;
  124. { ``header'', ie. size of structure valid when chunk is in use }
  125. { should correspond to tmemchunk_var_hdr structure starting with the
  126. last field. Reason is that the overlap is starting from the end of the
  127. record. }
  128. tmemchunk_fixed_hdr = record
  129. { aligning is done automatically in alloc_oschunk }
  130. size : ptruint;
  131. end;
  132. tmemchunk_var_hdr = record
  133. prevsize : ptruint;
  134. freelists : pfreelists;
  135. size : ptruint;
  136. end;
  137. pfpcheapstatus = ^tfpcheapstatus;
  138. tfixedfreelists = array[1..maxblockindex] of pmemchunk_fixed;
  139. tfreelists = record
  140. oslist : poschunk; { os chunks free, available for use }
  141. oscount : dword; { number of os chunks on oslist }
  142. oslist_all : poschunk; { all os chunks allocated }
  143. fixedlists : tfixedfreelists;
  144. varlist : pmemchunk_var;
  145. { chunks waiting to be freed from other thread }
  146. waitfixed : pmemchunk_fixed;
  147. waitvar : pmemchunk_var;
  148. { heap statistics }
  149. internal_status : TFPCHeapStatus;
  150. end;
  151. const
  152. fixedfirstoffset = ((sizeof(toschunk) + sizeof(tmemchunk_fixed_hdr) + $f)
  153. and not $f) - sizeof(tmemchunk_fixed_hdr);
  154. varfirstoffset = ((sizeof(toschunk) + sizeof(tmemchunk_var_hdr) + $f)
  155. and not $f) - sizeof(tmemchunk_var_hdr);
  156. {$ifdef BESTMATCH}
  157. matcheffort = high(longint);
  158. {$else}
  159. matcheffort = 10;
  160. {$endif}
  161. var
  162. orphaned_freelists : tfreelists;
  163. heap_lock : trtlcriticalsection;
  164. heap_lock_use : integer;
  165. threadvar
  166. freelists : tfreelists;
  167. {$ifdef DUMP_MEM_USAGE}
  168. const
  169. sizeusageshift = 4;
  170. sizeusageindex = 2049;
  171. sizeusagesize = sizeusageindex shl sizeusageshift;
  172. type
  173. tsizeusagelist = array[0..sizeusageindex] of longint;
  174. threadvar
  175. sizeusage, maxsizeusage: tsizeusagelist;
  176. {$endif}
  177. {$endif HAS_MEMORYMANAGER}
  178. {*****************************************************************************
  179. Memory Manager
  180. *****************************************************************************}
  181. procedure GetMemoryManager(var MemMgr:TMemoryManager);
  182. begin
  183. MemMgr := MemoryManager;
  184. end;
  185. procedure SetMemoryManager(const MemMgr:TMemoryManager);
  186. begin
  187. MemoryManager := MemMgr;
  188. end;
  189. function IsMemoryManagerSet:Boolean;
  190. begin
  191. IsMemoryManagerSet := (MemoryManager.GetMem<>@SysGetMem)
  192. or (MemoryManager.FreeMem<>@SysFreeMem);
  193. end;
  194. procedure GetMem(Var p:pointer;Size:ptruint);
  195. begin
  196. p := MemoryManager.GetMem(Size);
  197. end;
  198. procedure GetMemory(Var p:pointer;Size:ptruint);
  199. begin
  200. GetMem(p,size);
  201. end;
  202. procedure FreeMem(p:pointer;Size:ptruint);
  203. begin
  204. MemoryManager.FreeMemSize(p,Size);
  205. end;
  206. procedure FreeMemory(p:pointer;Size:ptruint);
  207. begin
  208. FreeMem(p,size);
  209. end;
  210. function GetHeapStatus:THeapStatus;
  211. begin
  212. Result:=MemoryManager.GetHeapStatus();
  213. end;
  214. function GetFPCHeapStatus:TFPCHeapStatus;
  215. begin
  216. Result:=MemoryManager.GetFPCHeapStatus();
  217. end;
  218. function MemSize(p:pointer):ptruint;
  219. begin
  220. MemSize := MemoryManager.MemSize(p);
  221. end;
  222. { Delphi style }
  223. function FreeMem(p:pointer):ptruint;[Public,Alias:'FPC_FREEMEM_X'];
  224. begin
  225. FreeMem := MemoryManager.FreeMem(p);
  226. end;
  227. function FreeMemory(p:pointer):ptruint;
  228. begin
  229. FreeMemory := FreeMem(p);
  230. end;
  231. function GetMem(size:ptruint):pointer;
  232. begin
  233. GetMem := MemoryManager.GetMem(Size);
  234. end;
  235. function GetMemory(size:ptruint):pointer;
  236. begin
  237. GetMemory := GetMem(size);
  238. end;
  239. function AllocMem(Size:ptruint):pointer;
  240. begin
  241. AllocMem := MemoryManager.AllocMem(size);
  242. end;
  243. function ReAllocMem(var p:pointer;Size:ptruint):pointer;
  244. begin
  245. ReAllocMem := MemoryManager.ReAllocMem(p,size);
  246. end;
  247. function ReAllocMemory(var p:pointer;Size:ptruint):pointer;
  248. begin
  249. ReAllocMemory := ReAllocMem(p,size);
  250. end;
  251. { Needed for calls from Assembler }
  252. function fpc_getmem(size:ptruint):pointer;compilerproc;[public,alias:'FPC_GETMEM'];
  253. begin
  254. fpc_GetMem := MemoryManager.GetMem(size);
  255. end;
  256. procedure fpc_freemem(p:pointer);compilerproc;[public,alias:'FPC_FREEMEM'];
  257. begin
  258. MemoryManager.FreeMem(p);
  259. end;
  260. {$ifndef HAS_MEMORYMANAGER}
  261. {*****************************************************************************
  262. GetHeapStatus
  263. *****************************************************************************}
  264. function SysGetFPCHeapStatus:TFPCHeapStatus;
  265. var
  266. status: pfpcheapstatus;
  267. begin
  268. status := @freelists.internal_status;
  269. status^.CurrHeapFree := status^.CurrHeapSize - status^.CurrHeapUsed;
  270. result := status^;
  271. end;
  272. function SysGetHeapStatus :THeapStatus;
  273. var
  274. status: pfpcheapstatus;
  275. begin
  276. status := @freelists.internal_status;
  277. status^.CurrHeapFree := status^.CurrHeapSize - status^.CurrHeapUsed;
  278. result.TotalAllocated :=status^.CurrHeapUsed;
  279. result.TotalFree :=status^.CurrHeapFree;
  280. result.TotalAddrSpace :=status^.CurrHeapSize;
  281. result.TotalUncommitted :=0;
  282. result.TotalCommitted :=0;
  283. result.FreeSmall :=0;
  284. result.FreeBig :=0;
  285. result.Unused :=0;
  286. result.Overhead :=0;
  287. result.HeapErrorCode :=0;
  288. end;
  289. {$ifdef DUMPBLOCKS} // TODO
  290. procedure DumpBlocks(loc_freelists: pfreelists);
  291. var
  292. s,i,j : ptruint;
  293. hpfixed : pmemchunk_fixed;
  294. hpvar : pmemchunk_var;
  295. begin
  296. { fixed freelist }
  297. for i := 1 to maxblockindex do
  298. begin
  299. hpfixed := loc_freelists^.fixedlists[i];
  300. j := 0;
  301. while assigned(hpfixed) do
  302. begin
  303. inc(j);
  304. hpfixed := hpfixed^.next_fixed;
  305. end;
  306. writeln('Block ',i*blocksize,': ',j);
  307. end;
  308. { var freelist }
  309. hpvar := loc_freelists^.varlist;
  310. j := 0;
  311. s := 0;
  312. while assigned(hpvar) do
  313. begin
  314. inc(j);
  315. if hpvar^.size>s then
  316. s := hpvar^.size;
  317. hpvar := hpvar^.next_var;
  318. end;
  319. writeln('Variable: ',j,' maxsize: ',s);
  320. end;
  321. {$endif}
  322. {*****************************************************************************
  323. Forwards
  324. *****************************************************************************}
  325. procedure finish_waitfixedlist(loc_freelists: pfreelists); forward;
  326. procedure finish_waitvarlist(loc_freelists: pfreelists); forward;
  327. function try_finish_waitfixedlist(loc_freelists: pfreelists): boolean; forward;
  328. procedure try_finish_waitvarlist(loc_freelists: pfreelists); forward;
  329. {*****************************************************************************
  330. List adding/removal
  331. *****************************************************************************}
  332. procedure append_to_list_var(pmc: pmemchunk_var); inline;
  333. var
  334. varlist: ppmemchunk_var;
  335. begin
  336. varlist := @pmc^.freelists^.varlist;
  337. pmc^.prev_var := nil;
  338. pmc^.next_var := varlist^;
  339. if varlist^<>nil then
  340. varlist^^.prev_var := pmc;
  341. varlist^ := pmc;
  342. end;
  343. {$ifdef HEAP_DEBUG}
  344. function find_fixed_mc(loc_freelists: pfreelists; chunkindex: ptruint;
  345. pmc: pmemchunk_fixed): boolean;
  346. var
  347. pmc_temp: pmemchunk_fixed;
  348. begin
  349. pmc_temp := loc_freelists^.fixedlists[chunkindex];
  350. while pmc_temp <> nil do
  351. begin
  352. if pmc_temp = pmc then exit(true);
  353. pmc_temp := pmc_temp^.next_fixed;
  354. end;
  355. result := false;
  356. end;
  357. {$endif}
  358. procedure remove_from_list_fixed(pmc: pmemchunk_fixed; fixedlist: ppmemchunk_fixed); inline;
  359. begin
  360. if assigned(pmc^.next_fixed) then
  361. pmc^.next_fixed^.prev_fixed := pmc^.prev_fixed;
  362. if assigned(pmc^.prev_fixed) then
  363. pmc^.prev_fixed^.next_fixed := pmc^.next_fixed
  364. else
  365. fixedlist^ := pmc^.next_fixed;
  366. end;
  367. procedure remove_from_list_var(pmc: pmemchunk_var); inline;
  368. begin
  369. if assigned(pmc^.next_var) then
  370. pmc^.next_var^.prev_var := pmc^.prev_var;
  371. if assigned(pmc^.prev_var) then
  372. pmc^.prev_var^.next_var := pmc^.next_var
  373. else
  374. pmc^.freelists^.varlist := pmc^.next_var;
  375. end;
  376. procedure remove_freed_fixed_chunks(poc: poschunk);
  377. { remove all fixed chunks from the fixed free list, as this os chunk
  378. is going to be used for other purpose }
  379. var
  380. pmc, pmc_end: pmemchunk_fixed;
  381. fixedlist: ppmemchunk_fixed;
  382. chunksize: ptruint;
  383. begin
  384. { exit if this is a var size os chunk, function only applicable to fixed size }
  385. if poc^.used < 0 then
  386. exit;
  387. pmc := pmemchunk_fixed(pointer(poc)+fixedfirstoffset);
  388. chunksize := pmc^.size and fixedsizemask;
  389. pmc_end := pmemchunk_fixed(pointer(poc)+(poc^.size and sizemask)-chunksize);
  390. fixedlist := @poc^.freelists^.fixedlists[chunksize shr blockshift];
  391. repeat
  392. remove_from_list_fixed(pmc, fixedlist);
  393. pmc := pointer(pmc)+chunksize;
  394. until pmc > pmc_end;
  395. end;
  396. procedure free_oschunk(loc_freelists: pfreelists; poc: poschunk);
  397. var
  398. pocsize: ptruint;
  399. begin
  400. remove_freed_fixed_chunks(poc);
  401. if assigned(poc^.prev_any) then
  402. poc^.prev_any^.next_any := poc^.next_any
  403. else
  404. loc_freelists^.oslist_all := poc^.next_any;
  405. if assigned(poc^.next_any) then
  406. poc^.next_any^.prev_any := poc^.prev_any;
  407. pocsize := poc^.size and sizemask;
  408. dec(loc_freelists^.internal_status.currheapsize, pocsize);
  409. SysOSFree(poc, pocsize);
  410. end;
  411. procedure append_to_oslist(poc: poschunk);
  412. var
  413. loc_freelists: pfreelists;
  414. begin
  415. loc_freelists := poc^.freelists;
  416. { check if already on list }
  417. if (poc^.size and ocrecycleflag) <> 0 then
  418. begin
  419. inc(loc_freelists^.oscount);
  420. poc^.size := poc^.size and not ocrecycleflag;
  421. exit;
  422. end;
  423. { decide whether to free block or add to list }
  424. {$ifdef HAS_SYSOSFREE}
  425. if (loc_freelists^.oscount >= MaxKeptOSChunks) or
  426. ((poc^.size and sizemask) > growheapsize2) then
  427. begin
  428. free_oschunk(loc_freelists, poc);
  429. end
  430. else
  431. begin
  432. {$endif}
  433. poc^.next_free := loc_freelists^.oslist;
  434. loc_freelists^.oslist := poc;
  435. inc(loc_freelists^.oscount);
  436. {$ifdef HAS_SYSOSFREE}
  437. end;
  438. {$endif}
  439. end;
  440. procedure append_to_oslist_var(pmc: pmemchunk_var);
  441. var
  442. poc: poschunk;
  443. begin
  444. // block eligable for freeing
  445. poc := pointer(pmc)-varfirstoffset;
  446. remove_from_list_var(pmc);
  447. append_to_oslist(poc);
  448. end;
  449. procedure modify_oschunk_freelists(poc: poschunk; new_freelists: pfreelists);
  450. var
  451. pmcv: pmemchunk_var;
  452. begin
  453. poc^.freelists := new_freelists;
  454. { only if oschunk contains var memchunks, we need additional assignments }
  455. if poc^.used <> -1 then exit;
  456. pmcv := pmemchunk_var(pointer(poc)+varfirstoffset);
  457. repeat
  458. pmcv^.freelists := new_freelists;
  459. if (pmcv^.size and lastblockflag) <> 0 then
  460. break;
  461. pmcv := pmemchunk_var(pointer(pmcv)+(pmcv^.size and sizemask));
  462. until false;
  463. end;
  464. function modify_freelists(loc_freelists, new_freelists: pfreelists): poschunk;
  465. var
  466. poc: poschunk;
  467. begin
  468. poc := loc_freelists^.oslist_all;
  469. if assigned(poc) then
  470. begin
  471. repeat
  472. { fixed and var freelist for orphaned freelists do not need maintenance }
  473. { we assume the heap is not severely fragmented at thread exit }
  474. modify_oschunk_freelists(poc, new_freelists);
  475. if not assigned(poc^.next_any) then
  476. exit(poc);
  477. poc := poc^.next_any;
  478. until false;
  479. end;
  480. modify_freelists := nil;
  481. end;
  482. {*****************************************************************************
  483. Split block
  484. *****************************************************************************}
  485. function split_block(pcurr: pmemchunk_var; size: ptruint): ptruint;
  486. var
  487. pcurr_tmp : pmemchunk_var;
  488. size_flags, oldsize, sizeleft: ptruint;
  489. begin
  490. size_flags := pcurr^.size;
  491. oldsize := size_flags and sizemask;
  492. sizeleft := oldsize-size;
  493. if sizeleft>=sizeof(tmemchunk_var) then
  494. begin
  495. pcurr_tmp := pmemchunk_var(pointer(pcurr)+size);
  496. { update prevsize of block to the right }
  497. if (size_flags and lastblockflag) = 0 then
  498. pmemchunk_var(pointer(pcurr)+oldsize)^.prevsize := sizeleft;
  499. { inherit the lastblockflag }
  500. pcurr_tmp^.size := sizeleft or (size_flags and lastblockflag);
  501. pcurr_tmp^.prevsize := size;
  502. pcurr_tmp^.freelists := pcurr^.freelists;
  503. { the block we return is not the last one anymore (there's now a block after it) }
  504. { decrease size of block to new size }
  505. pcurr^.size := size or (size_flags and (not sizemask and not lastblockflag));
  506. { insert the block in the freelist }
  507. append_to_list_var(pcurr_tmp);
  508. result := size;
  509. end
  510. else
  511. result := oldsize;
  512. end;
  513. {*****************************************************************************
  514. Try concat freerecords
  515. *****************************************************************************}
  516. procedure concat_two_blocks(mc_left, mc_right: pmemchunk_var);
  517. var
  518. mc_tmp : pmemchunk_var;
  519. size_right : ptruint;
  520. begin
  521. // mc_right can't be a fixed size block
  522. if mc_right^.size and fixedsizeflag<>0 then
  523. HandleError(204);
  524. // left block free, concat with right-block
  525. size_right := mc_right^.size and sizemask;
  526. inc(mc_left^.size, size_right);
  527. // if right-block was last block, copy flag
  528. if (mc_right^.size and lastblockflag) <> 0 then
  529. begin
  530. mc_left^.size := mc_left^.size or lastblockflag;
  531. end
  532. else
  533. begin
  534. // there is a block to the right of the right-block, adjust it's prevsize
  535. mc_tmp := pmemchunk_var(pointer(mc_right)+size_right);
  536. mc_tmp^.prevsize := mc_left^.size and sizemask;
  537. end;
  538. // remove right-block from doubly linked list
  539. remove_from_list_var(mc_right);
  540. end;
  541. function try_concat_free_chunk_forward(mc: pmemchunk_var): boolean;
  542. var
  543. mc_tmp : pmemchunk_var;
  544. begin
  545. { try concat forward }
  546. result := false;
  547. if (mc^.size and lastblockflag) = 0 then
  548. begin
  549. mc_tmp := pmemchunk_var(pointer(mc)+(mc^.size and sizemask));
  550. if (mc_tmp^.size and usedflag) = 0 then
  551. begin
  552. // next block free: concat
  553. concat_two_blocks(mc, mc_tmp);
  554. result := true;
  555. end;
  556. end;
  557. end;
  558. function try_concat_free_chunk(mc: pmemchunk_var): pmemchunk_var;
  559. var
  560. mc_tmp : pmemchunk_var;
  561. begin
  562. try_concat_free_chunk_forward(mc);
  563. { try concat backward }
  564. if (mc^.size and firstblockflag) = 0 then
  565. begin
  566. mc_tmp := pmemchunk_var(pointer(mc)-mc^.prevsize);
  567. if (mc_tmp^.size and usedflag) = 0 then
  568. begin
  569. // prior block free: concat
  570. concat_two_blocks(mc_tmp, mc);
  571. mc := mc_tmp;
  572. end;
  573. end;
  574. result := mc;
  575. end;
  576. {*****************************************************************************
  577. Grow Heap
  578. *****************************************************************************}
  579. function find_free_oschunk(loc_freelists: pfreelists;
  580. minsize, maxsize: ptruint; var size: ptruint): poschunk;
  581. var
  582. prev_poc, poc: poschunk;
  583. pocsize: ptruint;
  584. begin
  585. poc := loc_freelists^.oslist;
  586. prev_poc := nil;
  587. while poc <> nil do
  588. begin
  589. if (poc^.size and ocrecycleflag) <> 0 then
  590. begin
  591. { oops! we recycled this chunk; remove it from list }
  592. poc^.size := poc^.size and not ocrecycleflag;
  593. poc := poc^.next_free;
  594. if prev_poc = nil then
  595. loc_freelists^.oslist := poc
  596. else
  597. prev_poc^.next_free := poc;
  598. continue;
  599. end;
  600. pocsize := poc^.size and sizemask;
  601. if (pocsize >= minsize) and
  602. (pocsize <= maxsize) then
  603. begin
  604. size := pocsize;
  605. if prev_poc = nil then
  606. loc_freelists^.oslist := poc^.next_free
  607. else
  608. prev_poc^.next_free := poc^.next_free;
  609. dec(loc_freelists^.oscount);
  610. remove_freed_fixed_chunks(poc);
  611. break;
  612. end;
  613. prev_poc := poc;
  614. poc := poc^.next_free;
  615. end;
  616. result := poc;
  617. end;
  618. function alloc_oschunk(loc_freelists: pfreelists; chunkindex, size: ptruint): pointer;
  619. var
  620. pmc,
  621. pmc_next : pmemchunk_fixed;
  622. pmcv : pmemchunk_var;
  623. poc : poschunk;
  624. minsize,
  625. maxsize,
  626. i : ptruint;
  627. chunksize : ptruint;
  628. status : pfpcheapstatus;
  629. begin
  630. { increase size by size needed for os block header }
  631. minsize := size + varfirstoffset;
  632. { for fixed size chunks we keep offset from os chunk to mem chunk in
  633. upper bits, so maximum os chunk size is 64K on 32bit for fixed size }
  634. if chunkindex<>0 then
  635. maxsize := 1 shl (32-fixedoffsetshift)
  636. else
  637. maxsize := high(ptruint);
  638. poc:=nil;
  639. if (loc_freelists^.oscount>=MaxKeptOSChunks) then
  640. begin
  641. { blocks available in freelist? }
  642. poc := find_free_oschunk(loc_freelists, minsize, maxsize, size);
  643. if not assigned(poc) and (assigned(orphaned_freelists.waitfixed)
  644. or assigned(orphaned_freelists.waitvar) or (orphaned_freelists.oscount > 0)) then
  645. begin
  646. entercriticalsection(heap_lock);
  647. finish_waitfixedlist(@orphaned_freelists);
  648. finish_waitvarlist(@orphaned_freelists);
  649. if orphaned_freelists.oscount > 0 then
  650. begin
  651. { blocks available in orphaned freelist ? }
  652. poc := find_free_oschunk(@orphaned_freelists, minsize, maxsize, size);
  653. if assigned(poc) then
  654. begin
  655. { adopt this os chunk }
  656. poc^.freelists := loc_freelists;
  657. if assigned(poc^.prev_any) then
  658. poc^.prev_any^.next_any := poc^.next_any
  659. else
  660. orphaned_freelists.oslist_all := poc^.next_any;
  661. if assigned(poc^.next_any) then
  662. poc^.next_any^.prev_any := poc^.prev_any;
  663. poc^.next_any := loc_freelists^.oslist_all;
  664. if assigned(loc_freelists^.oslist_all) then
  665. loc_freelists^.oslist_all^.prev_any := poc;
  666. poc^.prev_any := nil;
  667. loc_freelists^.oslist_all := poc;
  668. end;
  669. end;
  670. leavecriticalsection(heap_lock);
  671. end;
  672. end;
  673. if poc = nil then
  674. begin
  675. {$ifdef DUMPGROW}
  676. writeln('growheap(',size,') allocating ',(size+sizeof(toschunk)+$ffff) and not $ffff);
  677. DumpBlocks(loc_freelists);
  678. {$endif}
  679. { allocate by 64K size }
  680. size := (size+varfirstoffset+$ffff) and not $ffff;
  681. { allocate smaller blocks for fixed-size chunks }
  682. if chunkindex<>0 then
  683. begin
  684. poc := SysOSAlloc(GrowHeapSizeSmall);
  685. if poc<>nil then
  686. size := GrowHeapSizeSmall;
  687. end
  688. { first try 256K (default) }
  689. else if size<=GrowHeapSize1 then
  690. begin
  691. poc := SysOSAlloc(GrowHeapSize1);
  692. if poc<>nil then
  693. size := GrowHeapSize1;
  694. end
  695. { second try 1024K (default) }
  696. else if size<=GrowHeapSize2 then
  697. begin
  698. poc := SysOSAlloc(GrowHeapSize2);
  699. if poc<>nil then
  700. size := GrowHeapSize2;
  701. end
  702. { else allocate the needed bytes }
  703. else
  704. poc := SysOSAlloc(size);
  705. { try again }
  706. if poc=nil then
  707. begin
  708. poc := SysOSAlloc(size);
  709. if poc=nil then
  710. begin
  711. if ReturnNilIfGrowHeapFails then
  712. begin
  713. result := nil;
  714. exit
  715. end
  716. else
  717. HandleError(203);
  718. end;
  719. end;
  720. poc^.freelists := loc_freelists;
  721. poc^.prev_any := nil;
  722. poc^.next_any := loc_freelists^.oslist_all;
  723. if assigned(loc_freelists^.oslist_all) then
  724. loc_freelists^.oslist_all^.prev_any := poc;
  725. loc_freelists^.oslist_all := poc;
  726. { set the total new heap size }
  727. status := @loc_freelists^.internal_status;
  728. inc(status^.currheapsize, size);
  729. if status^.currheapsize > status^.maxheapsize then
  730. status^.maxheapsize := status^.currheapsize;
  731. end;
  732. { initialize os-block }
  733. poc^.size := size;
  734. if chunkindex<>0 then
  735. begin
  736. poc^.used := 0;
  737. { chop os chunk in fixedsize parts,
  738. maximum of $ffff elements are allowed, otherwise
  739. there will be an overflow }
  740. chunksize := chunkindex shl blockshift;
  741. if ptruint(size-chunksize)>maxsize then
  742. HandleError(204);
  743. { we need to align the user pointers to 8 byte at least for
  744. mmx/sse and doubles on sparc, align to 16 bytes }
  745. i := fixedfirstoffset;
  746. result := pointer(poc) + i;
  747. pmc := pmemchunk_fixed(result);
  748. pmc^.prev_fixed := nil;
  749. repeat
  750. pmc^.size := fixedsizeflag or chunksize or (i shl fixedoffsetshift);
  751. inc(i, chunksize);
  752. if i > ptruint(size - chunksize) then break;
  753. pmc_next := pmemchunk_fixed(pointer(pmc)+chunksize);
  754. pmc^.next_fixed := pmc_next;
  755. pmc_next^.prev_fixed := pmc;
  756. pmc := pmc_next;
  757. until false;
  758. pmc_next := loc_freelists^.fixedlists[chunkindex];
  759. pmc^.next_fixed := pmc_next;
  760. if pmc_next<>nil then
  761. pmc_next^.prev_fixed := pmc;
  762. loc_freelists^.fixedlists[chunkindex] := pmemchunk_fixed(result);
  763. end
  764. else
  765. begin
  766. poc^.used := -1;
  767. { we need to align the user pointers to 8 byte at least for
  768. mmx/sse and doubles on sparc, align to 16 bytes }
  769. result := pointer(poc)+varfirstoffset;
  770. pmcv := pmemchunk_var(result);
  771. pmcv^.size := (ptruint(size-varfirstoffset) and sizemask) or (firstblockflag or lastblockflag);
  772. pmcv^.prevsize := 0;
  773. pmcv^.freelists := loc_freelists;
  774. append_to_list_var(pmcv);
  775. end;
  776. end;
  777. {*****************************************************************************
  778. SysGetMem
  779. *****************************************************************************}
  780. function SysGetMem_Fixed(chunksize: ptruint): pointer;
  781. var
  782. pmc, pmc_next: pmemchunk_fixed;
  783. poc: poschunk;
  784. chunkindex: ptruint;
  785. loc_freelists: pfreelists;
  786. begin
  787. { try to find a block in one of the freelists per size }
  788. chunkindex := chunksize shr blockshift;
  789. loc_freelists := @freelists;
  790. pmc := loc_freelists^.fixedlists[chunkindex];
  791. { no free blocks ? }
  792. if assigned(pmc) then
  793. begin
  794. { remove oschunk from free list in case we recycle it }
  795. poc := poschunk(pointer(pmc) - (pmc^.size shr fixedoffsetshift));
  796. if poc^.used = 0 then
  797. begin
  798. poc^.size := poc^.size or ocrecycleflag;
  799. dec(loc_freelists^.oscount);
  800. end;
  801. end
  802. else if try_finish_waitfixedlist(loc_freelists) then
  803. { freed some to-be freed chunks, retry allocation }
  804. exit(SysGetMem_Fixed(chunksize))
  805. else
  806. begin
  807. pmc := alloc_oschunk(loc_freelists, chunkindex, chunksize);
  808. if not assigned(pmc) then
  809. exit(nil);
  810. poc := poschunk(pointer(pmc)-fixedfirstoffset);
  811. end;
  812. { get a pointer to the block we should return }
  813. result := pointer(pmc)+sizeof(tmemchunk_fixed_hdr);
  814. { update freelist }
  815. pmc_next := pmc^.next_fixed;
  816. loc_freelists^.fixedlists[chunkindex] := pmc_next;
  817. if assigned(pmc_next) then
  818. pmc_next^.prev_fixed := nil;
  819. inc(poc^.used);
  820. { statistics }
  821. with loc_freelists^.internal_status do
  822. begin
  823. inc(currheapused, chunksize);
  824. if currheapused > maxheapused then
  825. begin
  826. maxheapused := currheapused;
  827. {$ifdef DUMP_MEM_USAGE}
  828. maxsizeusage := sizeusage;
  829. {$endif}
  830. end;
  831. end;
  832. end;
  833. function SysGetMem_Var(size: ptruint): pointer;
  834. var
  835. pcurr : pmemchunk_var;
  836. pbest : pmemchunk_var;
  837. loc_freelists : pfreelists;
  838. iter : cardinal;
  839. begin
  840. result:=nil;
  841. { free pending items }
  842. loc_freelists := @freelists;
  843. try_finish_waitvarlist(loc_freelists);
  844. pbest := nil;
  845. pcurr := loc_freelists^.varlist;
  846. iter := high(iter);
  847. while assigned(pcurr) and (iter>0) do
  848. begin
  849. if (pcurr^.size>size) then
  850. begin
  851. if not assigned(pbest) or (pcurr^.size<pbest^.size) then
  852. begin
  853. pbest := pcurr;
  854. if pcurr^.size = size then
  855. break;
  856. iter := matcheffort;
  857. end;
  858. end;
  859. pcurr := pcurr^.next_var;
  860. dec(iter);
  861. end;
  862. pcurr := pbest;
  863. if not assigned(pcurr) then
  864. begin
  865. // all os-chunks full, allocate a new one
  866. pcurr := alloc_oschunk(loc_freelists, 0, size);
  867. if not assigned(pcurr) then
  868. exit;
  869. end;
  870. { get pointer of the block we should return }
  871. result := pointer(pcurr)+sizeof(tmemchunk_var_hdr);
  872. { remove the current block from the freelist }
  873. remove_from_list_var(pcurr);
  874. { create the left over freelist block, if at least 16 bytes are free }
  875. size := split_block(pcurr, size);
  876. { flag block as used }
  877. pcurr^.size := pcurr^.size or usedflag;
  878. { statistics }
  879. with loc_freelists^.internal_status do
  880. begin
  881. inc(currheapused, size);
  882. if currheapused > maxheapused then
  883. begin
  884. maxheapused := currheapused;
  885. {$ifdef DUMP_MEM_USAGE}
  886. maxsizeusage := sizeusage;
  887. {$endif}
  888. end;
  889. end;
  890. end;
  891. function SysGetMem(size : ptruint):pointer;
  892. begin
  893. { Something to allocate ? }
  894. if size=0 then
  895. { we always need to allocate something, using heapend is not possible,
  896. because heappend can be changed by growheap (PFV) }
  897. size := 1;
  898. { calc to multiple of 16 after adding the needed bytes for memchunk header }
  899. if size <= (maxblocksize - sizeof(tmemchunk_fixed_hdr)) then
  900. begin
  901. size := (size+(sizeof(tmemchunk_fixed_hdr)+(blocksize-1))) and fixedsizemask;
  902. result := sysgetmem_fixed(size);
  903. end
  904. else
  905. begin
  906. size := (size+(sizeof(tmemchunk_var_hdr)+(blocksize-1))) and sizemask;
  907. result := sysgetmem_var(size);
  908. end;
  909. {$ifdef DUMP_MEM_USAGE}
  910. size := sysmemsize(result);
  911. if size > sizeusagesize then
  912. inc(sizeusage[sizeusageindex])
  913. else
  914. inc(sizeusage[size shr sizeusageshift]);
  915. {$endif}
  916. end;
  917. {*****************************************************************************
  918. SysFreeMem
  919. *****************************************************************************}
  920. procedure waitfree_fixed(pmc: pmemchunk_fixed; poc: poschunk);
  921. begin
  922. entercriticalsection(heap_lock);
  923. pmc^.next_fixed := poc^.freelists^.waitfixed;
  924. poc^.freelists^.waitfixed := pmc;
  925. leavecriticalsection(heap_lock);
  926. end;
  927. procedure waitfree_var(pmcv: pmemchunk_var);
  928. begin
  929. entercriticalsection(heap_lock);
  930. pmcv^.next_var := pmcv^.freelists^.waitvar;
  931. pmcv^.freelists^.waitvar := pmcv;
  932. leavecriticalsection(heap_lock);
  933. end;
  934. function SysFreeMem_Fixed(loc_freelists: pfreelists; pmc: pmemchunk_fixed): ptruint;
  935. var
  936. chunkindex,
  937. chunksize: ptruint;
  938. poc: poschunk;
  939. pmc_next: pmemchunk_fixed;
  940. begin
  941. poc := poschunk(pointer(pmc)-(pmc^.size shr fixedoffsetshift));
  942. chunksize := pmc^.size and fixedsizemask;
  943. if loc_freelists <> poc^.freelists then
  944. begin
  945. { deallocated in wrong thread! add to to-be-freed list of correct thread }
  946. waitfree_fixed(pmc, poc);
  947. exit(chunksize);
  948. end;
  949. dec(loc_freelists^.internal_status.currheapused, chunksize);
  950. { insert the block in its freelist }
  951. chunkindex := chunksize shr blockshift;
  952. pmc_next := loc_freelists^.fixedlists[chunkindex];
  953. pmc^.prev_fixed := nil;
  954. pmc^.next_fixed := pmc_next;
  955. if assigned(pmc_next) then
  956. pmc_next^.prev_fixed := pmc;
  957. loc_freelists^.fixedlists[chunkindex] := pmc;
  958. { decrease used blocks count }
  959. dec(poc^.used);
  960. if poc^.used <= 0 then
  961. begin
  962. { decrease used blocks count }
  963. if poc^.used<0 then
  964. HandleError(204);
  965. { osblock can be freed? }
  966. append_to_oslist(poc);
  967. end;
  968. result := chunksize;
  969. end;
  970. function SysFreeMem_Var(loc_freelists: pfreelists; pmcv: pmemchunk_var): ptruint;
  971. var
  972. chunksize: ptruint;
  973. begin
  974. chunksize := pmcv^.size and sizemask;
  975. if loc_freelists <> pmcv^.freelists then
  976. begin
  977. { deallocated in wrong thread! add to to-be-freed list of correct thread }
  978. waitfree_var(pmcv);
  979. exit(chunksize);
  980. end;
  981. dec(loc_freelists^.internal_status.currheapused, chunksize);
  982. { insert the block in it's freelist }
  983. pmcv^.size := pmcv^.size and (not usedflag);
  984. append_to_list_var(pmcv);
  985. pmcv := try_concat_free_chunk(pmcv);
  986. if (pmcv^.size and (firstblockflag or lastblockflag)) = (firstblockflag or lastblockflag) then
  987. append_to_oslist_var(pmcv);
  988. result := chunksize;
  989. end;
  990. function SysFreeMem(p: pointer): ptruint;
  991. var
  992. pmc: pmemchunk_fixed;
  993. loc_freelists: pfreelists;
  994. {$ifdef DUMP_MEM_USAGE}
  995. size: sizeint;
  996. {$endif}
  997. begin
  998. if p=nil then
  999. begin
  1000. result:=0;
  1001. exit;
  1002. end;
  1003. {$ifdef DUMP_MEM_USAGE}
  1004. size := sysmemsize(p);
  1005. if size > sizeusagesize then
  1006. dec(sizeusage[sizeusageindex])
  1007. else
  1008. dec(sizeusage[size shr sizeusageshift]);
  1009. {$endif}
  1010. loc_freelists := @freelists;
  1011. pmc := pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr));
  1012. { check if this is a fixed- or var-sized chunk }
  1013. if (pmc^.size and fixedsizeflag) = 0 then
  1014. result := sysfreemem_var(loc_freelists, pmemchunk_var(p-sizeof(tmemchunk_var_hdr)))
  1015. else
  1016. result := sysfreemem_fixed(loc_freelists, pmc);
  1017. end;
  1018. procedure finish_waitfixedlist(loc_freelists: pfreelists);
  1019. { free to-be-freed chunks, return whether we freed anything }
  1020. var
  1021. pmc: pmemchunk_fixed;
  1022. begin
  1023. while loc_freelists^.waitfixed <> nil do
  1024. begin
  1025. { keep next_fixed, might be destroyed }
  1026. pmc := loc_freelists^.waitfixed;
  1027. loc_freelists^.waitfixed := pmc^.next_fixed;
  1028. SysFreeMem_Fixed(loc_freelists, pmc);
  1029. end;
  1030. end;
  1031. function try_finish_waitfixedlist(loc_freelists: pfreelists): boolean;
  1032. begin
  1033. if loc_freelists^.waitfixed = nil then
  1034. exit(false);
  1035. entercriticalsection(heap_lock);
  1036. finish_waitfixedlist(loc_freelists);
  1037. leavecriticalsection(heap_lock);
  1038. result := true;
  1039. end;
  1040. procedure finish_waitvarlist(loc_freelists: pfreelists);
  1041. { free to-be-freed chunks, return whether we freed anything }
  1042. var
  1043. pmcv: pmemchunk_var;
  1044. begin
  1045. while loc_freelists^.waitvar <> nil do
  1046. begin
  1047. { keep next_var, might be destroyed }
  1048. pmcv := loc_freelists^.waitvar;
  1049. loc_freelists^.waitvar := pmcv^.next_var;
  1050. SysFreeMem_Var(loc_freelists, pmcv);
  1051. end;
  1052. end;
  1053. procedure try_finish_waitvarlist(loc_freelists: pfreelists);
  1054. begin
  1055. if loc_freelists^.waitvar = nil then
  1056. exit;
  1057. entercriticalsection(heap_lock);
  1058. finish_waitvarlist(loc_freelists);
  1059. leavecriticalsection(heap_lock);
  1060. end;
  1061. {*****************************************************************************
  1062. SysFreeMemSize
  1063. *****************************************************************************}
  1064. Function SysFreeMemSize(p: pointer; size: ptruint):ptruint;
  1065. begin
  1066. if size=0 then
  1067. exit(0);
  1068. { can't free partial blocks, ignore size }
  1069. result := SysFreeMem(p);
  1070. end;
  1071. {*****************************************************************************
  1072. SysMemSize
  1073. *****************************************************************************}
  1074. function SysMemSize(p: pointer): ptruint;
  1075. begin
  1076. result := pmemchunk_fixed(pointer(p)-sizeof(tmemchunk_fixed_hdr))^.size;
  1077. if (result and fixedsizeflag) = 0 then
  1078. begin
  1079. result := result and sizemask;
  1080. dec(result, sizeof(tmemchunk_var_hdr));
  1081. end
  1082. else
  1083. begin
  1084. result := result and fixedsizemask;
  1085. dec(result, sizeof(tmemchunk_fixed_hdr));
  1086. end;
  1087. end;
  1088. {*****************************************************************************
  1089. SysAllocMem
  1090. *****************************************************************************}
  1091. function SysAllocMem(size: ptruint): pointer;
  1092. begin
  1093. result := MemoryManager.GetMem(size);
  1094. if result<>nil then
  1095. FillChar(result^,MemoryManager.MemSize(result),0);
  1096. end;
  1097. {*****************************************************************************
  1098. SysResizeMem
  1099. *****************************************************************************}
  1100. function SysTryResizeMem(var p: pointer; size: ptruint): boolean;
  1101. var
  1102. chunksize,
  1103. oldsize,
  1104. currsize : ptruint;
  1105. pcurr : pmemchunk_var;
  1106. begin
  1107. SysTryResizeMem := false;
  1108. { fix p to point to the heaprecord }
  1109. chunksize := pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr))^.size;
  1110. { handle fixed memchuncks separate. Only allow resizes when the
  1111. new size fits in the same block }
  1112. if (chunksize and fixedsizeflag) <> 0 then
  1113. begin
  1114. currsize := chunksize and fixedsizemask;
  1115. { 1. Resizing to smaller sizes will never allocate a new block. We just keep the current block. This
  1116. is needed for the expectations that resizing to a small block will not move the contents of
  1117. a memory block
  1118. 2. For resizing to greater size first check if the size fits in the fixed block range to prevent
  1119. "truncating" the size by the fixedsizemask }
  1120. if ((size <= (maxblocksize - sizeof(tmemchunk_fixed_hdr))) and
  1121. ((size+(sizeof(tmemchunk_fixed_hdr)+(blocksize-1))) and sizemask <= currsize)) then
  1122. begin
  1123. systryresizemem:=true;
  1124. exit;
  1125. end;
  1126. { we need to allocate a new fixed or var memchunck }
  1127. exit;
  1128. end;
  1129. { var memchunk }
  1130. { do not fragment the heap with small shrinked blocks }
  1131. { also solves problem with var sized chunks smaller than sizeof(tmemchunk_var) }
  1132. if size < maxblocksize div 2 then
  1133. exit(false);
  1134. currsize := chunksize and sizemask;
  1135. size := (size+sizeof(tmemchunk_var_hdr)+(blocksize-1)) and sizemask;
  1136. { is the allocated block still correct? }
  1137. if (currsize>=size) and (size>ptruint(currsize-blocksize)) then
  1138. begin
  1139. SysTryResizeMem := true;
  1140. exit;
  1141. end;
  1142. { get pointer to block }
  1143. pcurr := pmemchunk_var(pointer(p)-sizeof(tmemchunk_var_hdr));
  1144. oldsize := currsize;
  1145. { do we need to allocate more memory ? }
  1146. if try_concat_free_chunk_forward(pcurr) then
  1147. currsize := pcurr^.size and sizemask;
  1148. if size>currsize then
  1149. begin
  1150. { the size is bigger than the previous size, we need to allocate more mem
  1151. but we could not concatenate with next block or not big enough }
  1152. exit;
  1153. end
  1154. else
  1155. { is the size smaller then we can adjust the block to that size and insert
  1156. the other part into the freelist }
  1157. if currsize>size then
  1158. currsize := split_block(pcurr, size);
  1159. with pcurr^.freelists^.internal_status do
  1160. begin
  1161. inc(currheapused, currsize-oldsize);
  1162. if currheapused > maxheapused then
  1163. maxheapused := currheapused;
  1164. end;
  1165. SysTryResizeMem := true;
  1166. end;
  1167. {*****************************************************************************
  1168. SysResizeMem
  1169. *****************************************************************************}
  1170. function SysReAllocMem(var p: pointer; size: ptruint):pointer;
  1171. var
  1172. newsize,
  1173. oldsize,
  1174. minsize : ptruint;
  1175. p2 : pointer;
  1176. begin
  1177. { Free block? }
  1178. if size=0 then
  1179. begin
  1180. if p<>nil then
  1181. begin
  1182. MemoryManager.FreeMem(p);
  1183. p := nil;
  1184. end;
  1185. end
  1186. else
  1187. { Allocate a new block? }
  1188. if p=nil then
  1189. begin
  1190. p := MemoryManager.GetMem(size);
  1191. end
  1192. else
  1193. begin
  1194. { Resize block }
  1195. {$ifdef DUMP_MEM_USAGE}
  1196. oldsize:=SysMemSize(p);
  1197. {$endif}
  1198. if not SysTryResizeMem(p,size) then
  1199. begin
  1200. oldsize:=MemoryManager.MemSize(p);
  1201. { Grow with bigger steps to prevent the need for
  1202. multiple getmem/freemem calls for fixed blocks. It might cost a bit
  1203. of extra memory, but in most cases a reallocmem is done multiple times. }
  1204. if oldsize<maxblocksize then
  1205. begin
  1206. newsize:=oldsize*2+blocksize;
  1207. if size>newsize then
  1208. newsize:=size;
  1209. end
  1210. else
  1211. newsize:=size;
  1212. { calc size of data to move }
  1213. minsize:=oldsize;
  1214. if newsize < minsize then
  1215. minsize := newsize;
  1216. p2 := MemoryManager.GetMem(newsize);
  1217. if p2<>nil then
  1218. Move(p^,p2^,minsize);
  1219. MemoryManager.FreeMem(p);
  1220. p := p2;
  1221. {$ifdef DUMP_MEM_USAGE}
  1222. end else begin
  1223. size := sysmemsize(p);
  1224. if size <> oldsize then
  1225. begin
  1226. if oldsize > sizeusagesize then
  1227. dec(sizeusage[sizeusageindex])
  1228. else if oldsize >= 0 then
  1229. dec(sizeusage[oldsize shr sizeusageshift]);
  1230. if size > sizeusagesize then
  1231. inc(sizeusage[sizeusageindex])
  1232. else if size >= 0 then
  1233. inc(sizeusage[size shr sizeusageshift]);
  1234. end;
  1235. {$endif}
  1236. end;
  1237. end;
  1238. SysReAllocMem := p;
  1239. end;
  1240. {$endif HAS_MEMORYMANAGER}
  1241. {$ifndef HAS_MEMORYMANAGER}
  1242. {*****************************************************************************
  1243. InitHeap
  1244. *****************************************************************************}
  1245. { This function will initialize the Heap manager and need to be called from
  1246. the initialization of the system unit }
  1247. procedure InitHeapThread;
  1248. var
  1249. loc_freelists: pfreelists;
  1250. begin
  1251. if heap_lock_use > 0 then
  1252. begin
  1253. entercriticalsection(heap_lock);
  1254. inc(heap_lock_use);
  1255. leavecriticalsection(heap_lock);
  1256. end;
  1257. loc_freelists := @freelists;
  1258. fillchar(loc_freelists^,sizeof(tfreelists),0);
  1259. {$ifdef DUMP_MEM_USAGE}
  1260. fillchar(sizeusage,sizeof(sizeusage),0);
  1261. fillchar(maxsizeusage,sizeof(sizeusage),0);
  1262. {$endif}
  1263. end;
  1264. procedure InitHeap;
  1265. var
  1266. loc_freelists: pfreelists;
  1267. begin
  1268. { we cannot initialize the locks here yet, thread support is
  1269. not loaded yet }
  1270. heap_lock_use := 0;
  1271. loc_freelists := @freelists;
  1272. fillchar(loc_freelists^,sizeof(tfreelists),0);
  1273. fillchar(orphaned_freelists,sizeof(orphaned_freelists),0);
  1274. end;
  1275. procedure RelocateHeap;
  1276. var
  1277. loc_freelists: pfreelists;
  1278. begin
  1279. { this function should be called in main thread context }
  1280. loc_freelists := @freelists;
  1281. heap_lock_use := 1;
  1282. initcriticalsection(heap_lock);
  1283. { loc_freelists still points to main thread's freelists, but they
  1284. have a reference to the global main freelists, fix them to point
  1285. to the main thread specific variable }
  1286. modify_freelists(loc_freelists, loc_freelists);
  1287. if MemoryManager.RelocateHeap <> nil then
  1288. MemoryManager.RelocateHeap();
  1289. end;
  1290. procedure FinalizeHeap;
  1291. var
  1292. poc, poc_next: poschunk;
  1293. loc_freelists: pfreelists;
  1294. last_thread: boolean;
  1295. {$ifdef DUMP_MEM_USAGE}
  1296. i : longint;
  1297. {$endif}
  1298. begin
  1299. loc_freelists := @freelists;
  1300. if heap_lock_use > 0 then
  1301. begin
  1302. entercriticalsection(heap_lock);
  1303. finish_waitfixedlist(loc_freelists);
  1304. finish_waitvarlist(loc_freelists);
  1305. {$ifdef HAS_SYSOSFREE}
  1306. end;
  1307. poc := loc_freelists^.oslist;
  1308. while assigned(poc) do
  1309. begin
  1310. poc_next := poc^.next_free;
  1311. { check if this os chunk was 'recycled' i.e. taken in use again }
  1312. if (poc^.size and ocrecycleflag) = 0 then
  1313. free_oschunk(loc_freelists, poc)
  1314. else
  1315. poc^.size := poc^.size and not ocrecycleflag;
  1316. poc := poc_next;
  1317. end;
  1318. loc_freelists^.oslist := nil;
  1319. loc_freelists^.oscount := 0;
  1320. if heap_lock_use > 0 then
  1321. begin
  1322. {$endif HAS_SYSOSFREE}
  1323. poc := modify_freelists(loc_freelists, @orphaned_freelists);
  1324. if assigned(poc) then
  1325. begin
  1326. poc^.next_any := orphaned_freelists.oslist_all;
  1327. if assigned(orphaned_freelists.oslist_all) then
  1328. orphaned_freelists.oslist_all^.prev_any := poc;
  1329. orphaned_freelists.oslist_all := loc_freelists^.oslist_all;
  1330. end;
  1331. dec(heap_lock_use);
  1332. last_thread := heap_lock_use = 0;
  1333. leavecriticalsection(heap_lock);
  1334. if last_thread then
  1335. donecriticalsection(heap_lock);
  1336. end;
  1337. {$ifdef SHOW_MEM_USAGE}
  1338. writeln('Max heap used/size: ', loc_freelists^.internal_status.maxheapused, '/',
  1339. loc_freelists^.internal_status.maxheapsize);
  1340. flush(output);
  1341. {$endif}
  1342. {$ifdef DUMP_MEM_USAGE}
  1343. for i := 0 to sizeusageindex-1 do
  1344. if maxsizeusage[i] <> 0 then
  1345. writeln('size ', i shl sizeusageshift, ' usage ', maxsizeusage[i]);
  1346. writeln('size >', sizeusagesize, ' usage ', maxsizeusage[sizeusageindex]);
  1347. flush(output);
  1348. {$endif}
  1349. end;
  1350. {$endif HAS_MEMORYMANAGER}