heap.inc 49 KB

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