heap.inc 47 KB

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