heap.inc 47 KB

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