heap.inc 43 KB

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