heap.inc 52 KB

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