heap.inc 44 KB

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