heap.inc 43 KB

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