heap.inc 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338
  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. { Custom memory manager is Multi Threaded and does not require locking }
  14. { define HAS_MT_MEMORYMANAGER}
  15. { Do not use standard memory manager }
  16. { Custom memory manager requires locking when threading is used }
  17. { define HAS_MEMORYMANAGER}
  18. { Try to find the best matching block in general freelist }
  19. { define BESTMATCH}
  20. { DEBUG: Dump info when the heap needs to grow }
  21. { define DUMPGROW}
  22. {$ifdef HAS_MT_MEMORYMANAGER}
  23. {$define HAS_MEMORYMANAGER}
  24. {$endif HAS_MT_MEMORYMANAGER}
  25. const
  26. {$ifdef CPU64}
  27. blocksize = 32; { at least size of freerecord }
  28. blockshift = 5; { shr value for blocksize=2^blockshift}
  29. maxblocksize = 512+blocksize; { 1024+8 needed for heaprecord }
  30. {$else}
  31. blocksize = 16; { at least size of freerecord }
  32. blockshift = 4; { shr value for blocksize=2^blockshift}
  33. maxblocksize = 512+blocksize; { 1024+8 needed for heaprecord }
  34. {$endif}
  35. maxblockindex = maxblocksize div blocksize; { highest index in array of lists of memchunks }
  36. maxreusebigger = 8; { max reuse bigger tries }
  37. { common flags }
  38. fixedsizeflag = 1; { flag if the block is of fixed size }
  39. { memchunk var flags }
  40. usedflag = 2; { flag if the block is used or not }
  41. lastblockflag = 4; { flag if the block is the last in os chunk }
  42. firstblockflag = 8; { flag if the block is the first in os chunk }
  43. sizemask = not(blocksize-1);
  44. fixedsizemask = sizemask and $ffff;
  45. {****************************************************************************}
  46. {$ifdef DUMPGROW}
  47. {$define DUMPBLOCKS}
  48. {$endif}
  49. { Forward defines }
  50. procedure SysHeapMutexInit;forward;
  51. procedure SysHeapMutexDone;forward;
  52. procedure SysHeapMutexLock;forward;
  53. procedure SysHeapMutexUnlock;forward;
  54. { Memory manager }
  55. const
  56. MemoryManager: TMemoryManager = (
  57. {$ifdef HAS_MT_MEMORYMANAGER}
  58. NeedLock: false;
  59. {$else HAS_MT_MEMORYMANAGER}
  60. NeedLock: true;
  61. {$endif HAS_MT_MEMORYMANAGER}
  62. GetMem: @SysGetMem;
  63. FreeMem: @SysFreeMem;
  64. FreeMemSize: @SysFreeMemSize;
  65. AllocMem: @SysAllocMem;
  66. ReAllocMem: @SysReAllocMem;
  67. MemSize: @SysMemSize;
  68. GetHeapStatus: @SysGetHeapStatus;
  69. GetFPCHeapStatus: @SysGetFPCHeapStatus;
  70. );
  71. MemoryMutexManager: TMemoryMutexManager = (
  72. MutexInit: @SysHeapMutexInit;
  73. MutexDone: @SysHeapMutexDone;
  74. MutexLock: @SysHeapMutexLock;
  75. MutexUnlock: @SysHeapMutexUnlock;
  76. );
  77. {$ifndef HAS_MEMORYMANAGER}
  78. type
  79. poschunk = ^toschunk;
  80. { keep size of this record dividable by 16 }
  81. toschunk = record
  82. size,
  83. used,
  84. chunkindex : ptrint;
  85. next,
  86. prev : poschunk;
  87. {$ifdef CPU64}
  88. pad1 : array[0..0] of pointer;
  89. {$else CPU64}
  90. pad1 : array[0..2] of pointer;
  91. {$endif CPU64}
  92. end;
  93. pmemchunk_fixed = ^tmemchunk_fixed;
  94. tmemchunk_fixed = record
  95. size : ptrint;
  96. poc : poschunk;
  97. next_fixed,
  98. prev_fixed : pmemchunk_fixed;
  99. end;
  100. pmemchunk_var = ^tmemchunk_var;
  101. tmemchunk_var = record
  102. size : ptrint;
  103. prevsize : ptrint;
  104. next_var,
  105. prev_var : pmemchunk_var;
  106. end;
  107. { ``header'', ie. size of structure valid when chunk is in use }
  108. { should correspond to tmemchunk_var_hdr structure starting with the
  109. last field. Reason is that the overlap is starting from the end of the
  110. record.
  111. Alignment is 8 bytes for 32bit machines. This required
  112. for x86 MMX/SSE and for sparc Double values }
  113. tmemchunk_fixed_hdr = record
  114. size : ptrint;
  115. poschunk : pointer;
  116. end;
  117. tmemchunk_var_hdr = record
  118. prevsize,
  119. size : ptrint;
  120. end;
  121. tfreelists = array[1..maxblockindex] of pmemchunk_fixed;
  122. pfreelists = ^tfreelists;
  123. var
  124. internal_status : TFPCHeapStatus;
  125. freelists_fixed : tfreelists;
  126. freelists_free_chunk : array[1..maxblockindex] of boolean;
  127. freelist_var : pmemchunk_var;
  128. freeoslist : poschunk;
  129. freeoslistcount : dword;
  130. {$endif HAS_MEMORYMANAGER}
  131. {*****************************************************************************
  132. Memory Manager
  133. *****************************************************************************}
  134. procedure SetMemoryMutexManager(var MutexMgr: TMemoryMutexManager);
  135. begin
  136. { Release old mutexmanager, the default manager does nothing so
  137. calling this without initializing is safe }
  138. MemoryMutexManager.MutexDone;
  139. { Copy new mutexmanager }
  140. MemoryMutexManager := MutexMgr;
  141. { Init new mutexmanager }
  142. MemoryMutexManager.MutexInit;
  143. end;
  144. procedure GetMemoryManager(var MemMgr:TMemoryManager);
  145. begin
  146. if IsMultiThread and MemoryManager.NeedLock then
  147. begin
  148. try
  149. MemoryMutexManager.MutexLock;
  150. MemMgr := MemoryManager;
  151. finally
  152. MemoryMutexManager.MutexUnlock;
  153. end;
  154. end
  155. else
  156. begin
  157. MemMgr := MemoryManager;
  158. end;
  159. end;
  160. procedure SetMemoryManager(const MemMgr:TMemoryManager);
  161. begin
  162. if IsMultiThread and MemoryManager.NeedLock then
  163. begin
  164. try
  165. MemoryMutexManager.MutexLock;
  166. MemoryManager := MemMgr;
  167. finally
  168. MemoryMutexManager.MutexUnlock;
  169. end;
  170. end
  171. else
  172. begin
  173. MemoryManager := MemMgr;
  174. end;
  175. end;
  176. function IsMemoryManagerSet:Boolean;
  177. begin
  178. if IsMultiThread and MemoryManager.NeedLock then
  179. begin
  180. try
  181. MemoryMutexManager.MutexLock;
  182. IsMemoryManagerSet := (MemoryManager.GetMem<>@SysGetMem) or
  183. (MemoryManager.FreeMem<>@SysFreeMem);
  184. finally
  185. MemoryMutexManager.MutexUnlock;
  186. end;
  187. end
  188. else
  189. begin
  190. IsMemoryManagerSet := (MemoryManager.GetMem<>@SysGetMem) or
  191. (MemoryManager.FreeMem<>@SysFreeMem);
  192. end;
  193. end;
  194. procedure GetMem(Var p:pointer;Size:ptrint);
  195. begin
  196. if IsMultiThread and MemoryManager.NeedLock then
  197. begin
  198. try
  199. MemoryMutexManager.MutexLock;
  200. p := MemoryManager.GetMem(Size);
  201. finally
  202. MemoryMutexManager.MutexUnlock;
  203. end;
  204. end
  205. else
  206. begin
  207. p := MemoryManager.GetMem(Size);
  208. end;
  209. end;
  210. procedure GetMemory(Var p:pointer;Size:ptrint);
  211. begin
  212. GetMem(p,size);
  213. end;
  214. procedure FreeMem(p:pointer;Size:ptrint);
  215. begin
  216. if IsMultiThread and MemoryManager.NeedLock then
  217. begin
  218. try
  219. MemoryMutexManager.MutexLock;
  220. MemoryManager.FreeMemSize(p,Size);
  221. finally
  222. MemoryMutexManager.MutexUnlock;
  223. end;
  224. end
  225. else
  226. begin
  227. MemoryManager.FreeMemSize(p,Size);
  228. end;
  229. end;
  230. procedure FreeMemory(p:pointer;Size:ptrint);
  231. begin
  232. FreeMem(p,size);
  233. end;
  234. function GetHeapStatus:THeapStatus;
  235. begin
  236. if IsMultiThread and MemoryManager.NeedLock then
  237. begin
  238. try
  239. MemoryMutexManager.MutexLock;
  240. result:=MemoryManager.GetHeapStatus();
  241. finally
  242. MemoryMutexManager.MutexUnlock;
  243. end;
  244. end
  245. else
  246. begin
  247. result:=MemoryManager.GetHeapStatus();
  248. end;
  249. end;
  250. function GetFPCHeapStatus:TFPCHeapStatus;
  251. begin
  252. if IsMultiThread and MemoryManager.NeedLock then
  253. begin
  254. try
  255. MemoryMutexManager.MutexLock;
  256. result:=MemoryManager.GetFPCHeapStatus();
  257. finally
  258. MemoryMutexManager.MutexUnlock;
  259. end;
  260. end
  261. else
  262. begin
  263. Result:=MemoryManager.GetFPCHeapStatus();
  264. end;
  265. end;
  266. function MemSize(p:pointer):ptrint;
  267. begin
  268. if IsMultiThread and MemoryManager.NeedLock then
  269. begin
  270. try
  271. MemoryMutexManager.MutexLock;
  272. MemSize := MemoryManager.MemSize(p);
  273. finally
  274. MemoryMutexManager.MutexUnlock;
  275. end;
  276. end
  277. else
  278. begin
  279. MemSize := MemoryManager.MemSize(p);
  280. end;
  281. end;
  282. { Delphi style }
  283. function FreeMem(p:pointer):ptrint;[Public,Alias:'FPC_FREEMEM_X'];
  284. begin
  285. if IsMultiThread and MemoryManager.NeedLock then
  286. begin
  287. try
  288. MemoryMutexManager.MutexLock;
  289. Freemem := MemoryManager.FreeMem(p);
  290. finally
  291. MemoryMutexManager.MutexUnlock;
  292. end;
  293. end
  294. else
  295. begin
  296. Freemem := MemoryManager.FreeMem(p);
  297. end;
  298. end;
  299. function FreeMemory(p:pointer):ptrint;
  300. begin
  301. FreeMemory := FreeMem(p);
  302. end;
  303. function GetMem(size:ptrint):pointer;
  304. begin
  305. if IsMultiThread and MemoryManager.NeedLock then
  306. begin
  307. try
  308. MemoryMutexManager.MutexLock;
  309. GetMem := MemoryManager.GetMem(Size);
  310. finally
  311. MemoryMutexManager.MutexUnlock;
  312. end;
  313. end
  314. else
  315. begin
  316. GetMem := MemoryManager.GetMem(Size);
  317. end;
  318. end;
  319. function GetMemory(size:ptrint):pointer;
  320. begin
  321. GetMemory := Getmem(size);
  322. end;
  323. function AllocMem(Size:ptrint):pointer;
  324. begin
  325. if IsMultiThread and MemoryManager.NeedLock then
  326. begin
  327. try
  328. MemoryMutexManager.MutexLock;
  329. AllocMem := MemoryManager.AllocMem(size);
  330. finally
  331. MemoryMutexManager.MutexUnlock;
  332. end;
  333. end
  334. else
  335. begin
  336. AllocMem := MemoryManager.AllocMem(size);
  337. end;
  338. end;
  339. function ReAllocMem(var p:pointer;Size:ptrint):pointer;
  340. begin
  341. if IsMultiThread and MemoryManager.NeedLock then
  342. begin
  343. try
  344. MemoryMutexManager.MutexLock;
  345. ReAllocMem := MemoryManager.ReAllocMem(p,size);
  346. finally
  347. MemoryMutexManager.MutexUnlock;
  348. end;
  349. end
  350. else
  351. begin
  352. ReAllocMem := MemoryManager.ReAllocMem(p,size);
  353. end;
  354. end;
  355. function ReAllocMemory(var p:pointer;Size:ptrint):pointer;
  356. begin
  357. ReAllocMemory := ReAllocMem(p,size);
  358. end;
  359. { Needed for calls from Assembler }
  360. function fpc_getmem(size:ptrint):pointer;compilerproc;[public,alias:'FPC_GETMEM'];
  361. begin
  362. if IsMultiThread and MemoryManager.NeedLock then
  363. begin
  364. try
  365. MemoryMutexManager.MutexLock;
  366. fpc_GetMem := MemoryManager.GetMem(size);
  367. finally
  368. MemoryMutexManager.MutexUnlock;
  369. end;
  370. end
  371. else
  372. begin
  373. fpc_GetMem := MemoryManager.GetMem(size);
  374. end;
  375. end;
  376. procedure fpc_freemem(p:pointer);compilerproc;[public,alias:'FPC_FREEMEM'];
  377. begin
  378. if IsMultiThread and MemoryManager.NeedLock then
  379. begin
  380. try
  381. MemoryMutexManager.MutexLock;
  382. if p <> nil then
  383. MemoryManager.FreeMem(p);
  384. finally
  385. MemoryMutexManager.MutexUnlock;
  386. end;
  387. end
  388. else
  389. begin
  390. if p <> nil then
  391. MemoryManager.FreeMem(p);
  392. end;
  393. end;
  394. {$ifndef HAS_MEMORYMANAGER}
  395. {*****************************************************************************
  396. GetHeapStatus
  397. *****************************************************************************}
  398. function SysGetFPCHeapStatus:TFPCHeapStatus;
  399. begin
  400. internal_status.CurrHeapFree:=internal_status.CurrHeapSize-internal_status.CurrHeapUsed;
  401. result:=internal_status;
  402. end;
  403. function SysGetHeapStatus :THeapStatus;
  404. begin
  405. internal_status.CurrHeapFree:=internal_status.CurrHeapSize-internal_status.CurrHeapUsed;
  406. result.TotalAllocated :=internal_status.CurrHeapUsed;
  407. result.TotalFree :=internal_status.CurrHeapFree;
  408. result.TotalAddrSpace :=0;
  409. result.TotalUncommitted :=0;
  410. result.TotalCommitted :=0;
  411. result.FreeSmall :=0;
  412. result.FreeBig :=0;
  413. result.Unused :=0;
  414. result.Overhead :=0;
  415. result.HeapErrorCode :=0;
  416. end;
  417. {$ifdef DUMPBLOCKS} // TODO
  418. procedure DumpBlocks;
  419. var
  420. s,i,j : ptrint;
  421. hpfixed : pmemchunk_fixed;
  422. hpvar : pmemchunk_var;
  423. begin
  424. { fixed freelist }
  425. for i := 1 to maxblockindex do
  426. begin
  427. hpfixed := freelists_fixed[i];
  428. j := 0;
  429. while assigned(hpfixed) do
  430. begin
  431. inc(j);
  432. hpfixed := hpfixed^.next_fixed;
  433. end;
  434. writeln('Block ',i*blocksize,': ',j);
  435. end;
  436. { var freelist }
  437. hpvar := freelist_var;
  438. j := 0;
  439. s := 0;
  440. while assigned(hpvar) do
  441. begin
  442. inc(j);
  443. if hpvar^.size>s then
  444. s := hpvar^.size;
  445. hpvar := hpvar^.next_var;
  446. end;
  447. writeln('Variable: ',j,' maxsize: ',s);
  448. end;
  449. {$endif}
  450. {*****************************************************************************
  451. List adding/removal
  452. *****************************************************************************}
  453. procedure append_to_list_var(pmc: pmemchunk_var);inline;
  454. begin
  455. pmc^.prev_var := nil;
  456. pmc^.next_var := freelist_var;
  457. if freelist_var<>nil then
  458. freelist_var^.prev_var := pmc;
  459. freelist_var := pmc;
  460. end;
  461. procedure remove_from_list_var(pmc: pmemchunk_var);inline;
  462. begin
  463. if assigned(pmc^.next_var) then
  464. pmc^.next_var^.prev_var := pmc^.prev_var;
  465. if assigned(pmc^.prev_var) then
  466. pmc^.prev_var^.next_var := pmc^.next_var
  467. else
  468. freelist_var := pmc^.next_var;
  469. end;
  470. procedure append_to_oslist(poc: poschunk);
  471. begin
  472. { decide whether to free block or add to list }
  473. {$ifdef HAS_SYSOSFREE}
  474. if (freeoslistcount >= MaxKeptOSChunks) or
  475. (poc^.size > growheapsize2) then
  476. begin
  477. dec(internal_status.currheapsize, poc^.size);
  478. SysOSFree(poc, poc^.size);
  479. end
  480. else
  481. begin
  482. {$endif}
  483. poc^.prev := nil;
  484. poc^.next := freeoslist;
  485. if freeoslist <> nil then
  486. freeoslist^.prev := poc;
  487. freeoslist := poc;
  488. inc(freeoslistcount);
  489. {$ifdef HAS_SYSOSFREE}
  490. end;
  491. {$endif}
  492. end;
  493. procedure remove_from_oslist(poc: poschunk);
  494. begin
  495. if assigned(poc^.next) then
  496. poc^.next^.prev := poc^.prev;
  497. if assigned(poc^.prev) then
  498. poc^.prev^.next := poc^.next
  499. else
  500. freeoslist := poc^.next;
  501. dec(freeoslistcount);
  502. end;
  503. procedure append_to_oslist_var(pmc: pmemchunk_var);
  504. var
  505. poc: poschunk;
  506. begin
  507. // block eligable for freeing
  508. poc := pointer(pmc)-sizeof(toschunk);
  509. remove_from_list_var(pmc);
  510. append_to_oslist(poc);
  511. end;
  512. procedure append_to_oslist_fixed(poc: poschunk);
  513. var
  514. pmc: pmemchunk_fixed;
  515. chunksize,
  516. chunkindex,
  517. i, count: ptrint;
  518. begin
  519. chunkindex:=poc^.chunkindex;
  520. chunksize:=chunkindex shl blockshift;
  521. pmc := pmemchunk_fixed(pointer(poc)+sizeof(toschunk));
  522. count := (poc^.size - sizeof(toschunk)) div chunksize;
  523. for i := 0 to count - 1 do
  524. begin
  525. if assigned(pmc^.next_fixed) then
  526. pmc^.next_fixed^.prev_fixed := pmc^.prev_fixed;
  527. if assigned(pmc^.prev_fixed) then
  528. pmc^.prev_fixed^.next_fixed := pmc^.next_fixed
  529. else
  530. freelists_fixed[chunkindex] := pmc^.next_fixed;
  531. pmc := pointer(pmc)+chunksize;
  532. end;
  533. append_to_oslist(poc);
  534. end;
  535. {*****************************************************************************
  536. Split block
  537. *****************************************************************************}
  538. procedure split_block(pcurr: pmemchunk_var; size: ptrint);
  539. var
  540. pcurr_tmp : pmemchunk_var;
  541. sizeleft: ptrint;
  542. begin
  543. sizeleft := (pcurr^.size and sizemask)-size;
  544. if sizeleft>=blocksize then
  545. begin
  546. pcurr_tmp := pmemchunk_var(pointer(pcurr)+size);
  547. { update prevsize of block to the right }
  548. if (pcurr^.size and lastblockflag) = 0 then
  549. pmemchunk_var(pointer(pcurr)+(pcurr^.size and sizemask))^.prevsize := sizeleft;
  550. { inherit the lastblockflag }
  551. pcurr_tmp^.size := sizeleft or (pcurr^.size and lastblockflag);
  552. pcurr_tmp^.prevsize := size;
  553. { the block we return is not the last one anymore (there's now a block after it) }
  554. { decrease size of block to new size }
  555. pcurr^.size := size or (pcurr^.size and (not sizemask and not lastblockflag));
  556. { insert the block in the freelist }
  557. append_to_list_var(pcurr_tmp);
  558. end;
  559. end;
  560. {*****************************************************************************
  561. Try concat freerecords
  562. *****************************************************************************}
  563. procedure concat_two_blocks(mc_left, mc_right: pmemchunk_var);
  564. var
  565. mc_tmp : pmemchunk_var;
  566. size_right : ptrint;
  567. begin
  568. // mc_right can't be a fixed size block
  569. if mc_right^.size and fixedsizeflag<>0 then
  570. HandleError(204);
  571. // left block free, concat with right-block
  572. size_right := mc_right^.size and sizemask;
  573. inc(mc_left^.size, size_right);
  574. // if right-block was last block, copy flag
  575. if (mc_right^.size and lastblockflag) <> 0 then
  576. begin
  577. mc_left^.size := mc_left^.size or lastblockflag;
  578. end
  579. else
  580. begin
  581. // there is a block to the right of the right-block, adjust it's prevsize
  582. mc_tmp := pmemchunk_var(pointer(mc_right)+size_right);
  583. mc_tmp^.prevsize := mc_left^.size and sizemask;
  584. end;
  585. // remove right-block from doubly linked list
  586. remove_from_list_var(mc_right);
  587. end;
  588. procedure try_concat_free_chunk_forward(mc: pmemchunk_var);
  589. var
  590. mc_tmp : pmemchunk_var;
  591. begin
  592. { try concat forward }
  593. if (mc^.size and lastblockflag) = 0 then
  594. begin
  595. mc_tmp := pmemchunk_var(pointer(mc)+(mc^.size and sizemask));
  596. if (mc_tmp^.size and usedflag) = 0 then
  597. begin
  598. // next block free: concat
  599. concat_two_blocks(mc, mc_tmp);
  600. end;
  601. end;
  602. end;
  603. function try_concat_free_chunk(mc: pmemchunk_var): pmemchunk_var;
  604. var
  605. mc_tmp : pmemchunk_var;
  606. begin
  607. try_concat_free_chunk_forward(mc);
  608. { try concat backward }
  609. if (mc^.size and firstblockflag) = 0 then
  610. begin
  611. mc_tmp := pmemchunk_var(pointer(mc)-mc^.prevsize);
  612. if (mc_tmp^.size and usedflag) = 0 then
  613. begin
  614. // prior block free: concat
  615. concat_two_blocks(mc_tmp, mc);
  616. mc := mc_tmp;
  617. end;
  618. end;
  619. result := mc;
  620. end;
  621. function check_concat_free_chunk_forward(mc: pmemchunk_var;reqsize:ptrint):boolean;
  622. var
  623. mc_tmp : pmemchunk_var;
  624. freesize : ptrint;
  625. begin
  626. check_concat_free_chunk_forward:=false;
  627. freesize:=0;
  628. mc_tmp:=mc;
  629. repeat
  630. inc(freesize,mc_tmp^.size and sizemask);
  631. if freesize>=reqsize then
  632. begin
  633. check_concat_free_chunk_forward:=true;
  634. exit;
  635. end;
  636. if (mc_tmp^.size and lastblockflag) <> 0 then
  637. break;
  638. mc_tmp := pmemchunk_var(pointer(mc_tmp)+(mc_tmp^.size and sizemask));
  639. if (mc_tmp^.size and usedflag) <> 0 then
  640. break;
  641. until false;
  642. end;
  643. {*****************************************************************************
  644. Grow Heap
  645. *****************************************************************************}
  646. {$ifndef gba}
  647. function alloc_oschunk(chunkindex, size: ptrint):pointer;
  648. var
  649. pmcfirst,
  650. pmclast,
  651. pmc : pmemchunk_fixed;
  652. pmcv : pmemchunk_var;
  653. poc : poschunk;
  654. chunksize,
  655. minsize,
  656. maxsize,
  657. i, count : ptrint;
  658. begin
  659. result:=nil;
  660. chunksize:=chunkindex shl blockshift;
  661. { increase size by size needed for os block header }
  662. minsize := size + sizeof(toschunk);
  663. if chunkindex<>0 then
  664. maxsize := (chunksize * $ffff) + sizeof(toschunk)
  665. else
  666. maxsize := high(ptrint);
  667. { blocks available in freelist? }
  668. poc := freeoslist;
  669. while poc <> nil do
  670. begin
  671. if (poc^.size >= minsize) and
  672. (poc^.size <= maxsize) then
  673. begin
  674. size := poc^.size;
  675. remove_from_oslist(poc);
  676. break;
  677. end;
  678. poc := poc^.next;
  679. end;
  680. if poc = nil then
  681. begin
  682. {$ifdef DUMPGROW}
  683. writeln('growheap(',size,') allocating ',(size+sizeof(toschunk)+$ffff) and $ffff0000);
  684. DumpBlocks;
  685. {$endif}
  686. { allocate by 64K size }
  687. size := (size+sizeof(toschunk)+$ffff) and not $ffff;
  688. { allocate smaller blocks for fixed-size chunks }
  689. if chunksize<>0 then
  690. begin
  691. poc := SysOSAlloc(GrowHeapSizeSmall);
  692. if poc<>nil then
  693. size := GrowHeapSizeSmall;
  694. end
  695. { first try 256K (default) }
  696. else if size<=GrowHeapSize1 then
  697. begin
  698. poc := SysOSAlloc(GrowHeapSize1);
  699. if poc<>nil then
  700. size := GrowHeapSize1;
  701. end
  702. { second try 1024K (default) }
  703. else if size<=GrowHeapSize2 then
  704. begin
  705. poc := SysOSAlloc(GrowHeapSize2);
  706. if poc<>nil then
  707. size := GrowHeapSize2;
  708. end
  709. { else allocate the needed bytes }
  710. else
  711. poc := SysOSAlloc(size);
  712. { try again }
  713. if poc=nil then
  714. begin
  715. poc := SysOSAlloc(size);
  716. if (poc=nil) then
  717. begin
  718. if ReturnNilIfGrowHeapFails then
  719. exit
  720. else
  721. HandleError(203);
  722. end;
  723. end;
  724. { set the total new heap size }
  725. inc(internal_status.currheapsize,size);
  726. if internal_status.currheapsize>internal_status.maxheapsize then
  727. internal_status.maxheapsize:=internal_status.currheapsize;
  728. end;
  729. { initialize os-block }
  730. poc^.used := 0;
  731. poc^.size := size;
  732. poc^.chunkindex := chunkindex;
  733. { initialized oschunck for fixed chunks }
  734. if chunkindex<>0 then
  735. begin
  736. { chop os chunk in fixedsize parts,
  737. maximum of $ffff elements are allowed, otherwise
  738. there will be an overflow }
  739. count := (size-sizeof(toschunk)) div chunksize;
  740. if count>$ffff then
  741. HandleError(204);
  742. { Initialize linkedlist of chunks, the first chunk
  743. is pmemchunk_fixed(poc) and the last chunk will be in pmc at
  744. the end of the loop }
  745. pmcfirst := pmemchunk_fixed(pointer(poc)+sizeof(toschunk));
  746. pmc:=pmcfirst;
  747. for i:=1 to count do
  748. begin
  749. pmc^.poc:=poc;
  750. pmc^.size:=chunksize or fixedsizeflag;
  751. pmc^.prev_fixed := pointer(pmc)-chunksize;
  752. pmc^.next_fixed := pointer(pmc)+chunksize;
  753. pmc := pmemchunk_fixed(pointer(pmc)+chunksize);
  754. end;
  755. { undo last increase to get last chunk }
  756. pmclast := pmemchunk_fixed(pointer(pmc)-chunksize);
  757. { Add to freelist and fixup first and last chunk }
  758. pmclast^.next_fixed := freelists_fixed[chunkindex];
  759. if freelists_fixed[chunkindex]<>nil then
  760. freelists_fixed[chunkindex]^.prev_fixed := pmclast;
  761. freelists_fixed[chunkindex] := pmcfirst;
  762. pmemchunk_fixed(poc)^.prev_fixed:=nil;
  763. result:=pmcfirst;
  764. end
  765. else
  766. begin
  767. pmcv := pmemchunk_var(pointer(poc)+sizeof(toschunk));
  768. append_to_list_var(pmcv);
  769. pmcv^.size := ((size-sizeof(toschunk)) and sizemask) or (firstblockflag or lastblockflag);
  770. pmcv^.prevsize := 0;
  771. result:=pmcv;
  772. end;
  773. end;
  774. {$else gba}
  775. function alloc_oschunk(chunkindex, size: ptrint):pointer;
  776. begin
  777. alloc_oschunk := nil;
  778. if not ReturnNilIfGrowHeapFails then
  779. runerror(203);
  780. end;
  781. {$endif gba}
  782. {*****************************************************************************
  783. SysGetMem
  784. *****************************************************************************}
  785. function SysGetMem_Fixed(size: ptrint): pointer;
  786. var
  787. pmc,hp : pmemchunk_fixed;
  788. poc : poschunk;
  789. chunkindex : ptrint;
  790. begin
  791. { try to find a block in one of the freelists per size }
  792. chunkindex := size shr blockshift;
  793. pmc := freelists_fixed[chunkindex];
  794. result:=nil;
  795. { no free blocks ? }
  796. if not assigned(pmc) then
  797. begin
  798. pmc:=alloc_oschunk(chunkindex, size);
  799. if not assigned(pmc) then
  800. exit;
  801. end;
  802. { get a pointer to the block we should return }
  803. result := pointer(pmc)+sizeof(tmemchunk_fixed_hdr);
  804. { update freelist }
  805. hp:=pmc^.next_fixed;
  806. poc := pmc^.poc;
  807. freelists_fixed[chunkindex] := hp;
  808. if assigned(hp) then
  809. hp^.prev_fixed := nil;
  810. if (poc^.used = 0) then
  811. freelists_free_chunk[chunkindex] := false;
  812. inc(poc^.used);
  813. { statistics }
  814. inc(internal_status.currheapused,size);
  815. if internal_status.currheapused>internal_status.maxheapused then
  816. internal_status.maxheapused:=internal_status.currheapused;
  817. end;
  818. function SysGetMem_Var(size: ptrint): pointer;
  819. var
  820. pcurr : pmemchunk_var;
  821. {$ifdef BESTMATCH}
  822. pbest : pmemchunk_var;
  823. {$endif}
  824. begin
  825. result:=nil;
  826. {$ifdef BESTMATCH}
  827. pbest := nil;
  828. {$endif}
  829. pcurr := freelist_var;
  830. while assigned(pcurr) do
  831. begin
  832. {$ifdef BESTMATCH}
  833. if pcurr^.size=size then
  834. begin
  835. break;
  836. end
  837. else
  838. begin
  839. if (pcurr^.size>size) then
  840. begin
  841. if (not assigned(pbest)) or
  842. (pcurr^.size<pbest^.size) then
  843. pbest := pcurr;
  844. end;
  845. end;
  846. {$else BESTMATCH}
  847. if pcurr^.size>=size then
  848. break;
  849. {$endif BESTMATCH}
  850. pcurr := pcurr^.next_var;
  851. end;
  852. {$ifdef BESTMATCH}
  853. if not assigned(pcurr) then
  854. pcurr := pbest;
  855. {$endif}
  856. if not assigned(pcurr) then
  857. begin
  858. // all os-chunks full, allocate a new one
  859. pcurr := alloc_oschunk(0, size);
  860. if not assigned(pcurr) then
  861. exit;
  862. end;
  863. { get pointer of the block we should return }
  864. result := pointer(pcurr)+sizeof(tmemchunk_var_hdr);
  865. { remove the current block from the freelist }
  866. remove_from_list_var(pcurr);
  867. { create the left over freelist block, if at least 16 bytes are free }
  868. split_block(pcurr, size);
  869. { flag block as used }
  870. pcurr^.size := pcurr^.size or usedflag;
  871. { statistics }
  872. inc(internal_status.currheapused,size);
  873. if internal_status.currheapused>internal_status.maxheapused then
  874. internal_status.maxheapused:=internal_status.currheapused;
  875. end;
  876. function SysGetMem(size : ptrint):pointer;
  877. begin
  878. { Something to allocate ? }
  879. if size<=0 then
  880. begin
  881. { give an error for < 0 }
  882. if size<0 then
  883. HandleError(204);
  884. { we always need to allocate something, using heapend is not possible,
  885. because heappend can be changed by growheap (PFV) }
  886. size := 1;
  887. end;
  888. { calc to multiple of 16 after adding the needed bytes for memchunk header }
  889. if size <= (maxblocksize - sizeof(tmemchunk_fixed_hdr)) then
  890. begin
  891. size := (size+(sizeof(tmemchunk_fixed_hdr)+(blocksize-1))) and fixedsizemask;
  892. result := sysgetmem_fixed(size);
  893. end
  894. else
  895. begin
  896. size := (size+(sizeof(tmemchunk_var_hdr)+(blocksize-1))) and sizemask;
  897. result := sysgetmem_var(size);
  898. end;
  899. end;
  900. {*****************************************************************************
  901. SysFreeMem
  902. *****************************************************************************}
  903. function SysFreeMem_Fixed(pmc: pmemchunk_fixed): ptrint;
  904. var
  905. hp : pmemchunk_fixed;
  906. chunksize,
  907. chunkindex : ptrint;
  908. poc : poschunk;
  909. begin
  910. poc := pmc^.poc;
  911. chunkindex:=poc^.chunkindex;
  912. chunksize:=chunkindex shl blockshift;
  913. { statistics }
  914. dec(internal_status.currheapused,chunksize);
  915. hp:=freelists_fixed[chunkindex];
  916. { insert the block in it's freelist }
  917. pmc^.prev_fixed := nil;
  918. pmc^.next_fixed := hp;
  919. if assigned(hp) then
  920. hp^.prev_fixed := pmc;
  921. freelists_fixed[chunkindex] := pmc;
  922. dec(poc^.used);
  923. if poc^.used <= 0 then
  924. begin
  925. { decrease used blocks count }
  926. if poc^.used=-1 then
  927. HandleError(204);
  928. { osblock can be freed? }
  929. if freelists_free_chunk[chunkindex] then
  930. append_to_oslist_fixed(poc)
  931. else
  932. freelists_free_chunk[chunkindex] := true;
  933. end;
  934. result := chunksize;
  935. end;
  936. function SysFreeMem_Var(pcurr: pmemchunk_var): ptrint;
  937. var
  938. chunksize: ptrint;
  939. begin
  940. chunksize := pcurr^.size and sizemask;
  941. dec(internal_status.currheapused,chunksize);
  942. { insert the block in it's freelist }
  943. pcurr^.size := pcurr^.size and (not usedflag);
  944. append_to_list_var(pcurr);
  945. result := chunksize;
  946. pcurr := try_concat_free_chunk(pcurr);
  947. if (pcurr^.size and (firstblockflag or lastblockflag)) = (firstblockflag or lastblockflag) then
  948. append_to_oslist_var(pcurr);
  949. end;
  950. function SysFreeMem(p: pointer): ptrint;
  951. var
  952. hp : pmemchunk_fixed;
  953. begin
  954. if p=nil then
  955. begin
  956. result:=0;
  957. exit;
  958. end;
  959. hp:=pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr));
  960. { check if this is a fixed- or var-sized chunk }
  961. if (hp^.size and fixedsizeflag) = 0 then
  962. result := sysfreemem_var(pmemchunk_var(p-sizeof(tmemchunk_var_hdr)))
  963. else
  964. result := sysfreemem_fixed(hp);
  965. end;
  966. {*****************************************************************************
  967. SysFreeMemSize
  968. *****************************************************************************}
  969. Function SysFreeMemSize(p: pointer; size: ptrint):ptrint;
  970. var
  971. hp : pmemchunk_fixed;
  972. begin
  973. SysFreeMemSize := 0;
  974. if p=nil then
  975. exit;
  976. if size<=0 then
  977. begin
  978. if size<0 then
  979. HandleError(204);
  980. exit;
  981. end;
  982. hp:=pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr));
  983. { check if this is a fixed- or var-sized chunk. We can't check the passed
  984. size parameter since the block can be resized (by reallocmem) to an
  985. optimized value that the user doesn't know }
  986. if (hp^.size and fixedsizeflag) = 0 then
  987. result := sysfreemem_var(pmemchunk_var(p-sizeof(tmemchunk_var_hdr)))
  988. else
  989. result := sysfreemem_fixed(hp);
  990. end;
  991. {*****************************************************************************
  992. SysMemSize
  993. *****************************************************************************}
  994. function SysMemSize(p: pointer): ptrint;
  995. begin
  996. result := pmemchunk_fixed(pointer(p)-sizeof(tmemchunk_fixed_hdr))^.size;
  997. if (result and fixedsizeflag) = 0 then
  998. begin
  999. result := SysMemSize and sizemask;
  1000. dec(result, sizeof(tmemchunk_var_hdr));
  1001. end
  1002. else
  1003. begin
  1004. result := SysMemSize and fixedsizemask;
  1005. dec(result, sizeof(tmemchunk_fixed_hdr));
  1006. end;
  1007. end;
  1008. {*****************************************************************************
  1009. SysAllocMem
  1010. *****************************************************************************}
  1011. function SysAllocMem(size: ptrint): pointer;
  1012. begin
  1013. result := MemoryManager.GetMem(size);
  1014. if result<>nil then
  1015. FillChar(result^,MemoryManager.MemSize(result),0);
  1016. end;
  1017. {*****************************************************************************
  1018. SysResizeMem
  1019. *****************************************************************************}
  1020. function SysTryResizeMem(var p: pointer; size: ptrint): boolean;
  1021. var
  1022. chunksize,
  1023. oldsize,
  1024. currsize : ptrint;
  1025. pcurr : pmemchunk_var;
  1026. begin
  1027. SysTryResizeMem := false;
  1028. { fix p to point to the heaprecord }
  1029. chunksize := pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr))^.size;
  1030. { handle fixed memchuncks separate. Only allow resizes when the
  1031. new size fits in the same block }
  1032. if (chunksize and fixedsizeflag) <> 0 then
  1033. begin
  1034. currsize := chunksize and fixedsizemask;
  1035. { 1. Resizing to smaller sizes will never allocate a new block. We just keep the current block. This
  1036. is needed for the expectations that resizing to a small block will not move the contents of
  1037. a memory block
  1038. 2. For resizing to greater size first check if the size fits in the fixed block range to prevent
  1039. "truncating" the size by the fixedsizemask }
  1040. if ((size <= (maxblocksize - sizeof(tmemchunk_fixed_hdr))) and
  1041. ((size+sizeof(tmemchunk_fixed_hdr)+(blocksize-1)) and sizemask<=currsize )) then
  1042. begin
  1043. systryresizemem:=true;
  1044. exit;
  1045. end;
  1046. { we need to allocate a new fixed or var memchunck }
  1047. exit;
  1048. end;
  1049. { var memchunck }
  1050. currsize := chunksize and sizemask;
  1051. size := (size+sizeof(tmemchunk_var_hdr)+(blocksize-1)) and sizemask;
  1052. { is the allocated block still correct? }
  1053. if (currsize>=size) and (size>(currsize-blocksize)) then
  1054. begin
  1055. SysTryResizeMem := true;
  1056. exit;
  1057. end;
  1058. { get pointer to block }
  1059. pcurr := pmemchunk_var(pointer(p)-sizeof(tmemchunk_var_hdr));
  1060. oldsize := currsize;
  1061. { do we need to allocate more memory ? }
  1062. if size>currsize then
  1063. begin
  1064. { the size is bigger than the previous size, we need to allocated more mem.
  1065. We first check if the blocks after the current block are free. If not then we
  1066. simply call getmem/freemem to get the new block }
  1067. if check_concat_free_chunk_forward(pcurr,size) then
  1068. repeat
  1069. concat_two_blocks(pcurr,pmemchunk_var(pointer(pcurr)+currsize));
  1070. currsize := pcurr^.size and sizemask;
  1071. until currsize>=size
  1072. else
  1073. exit;
  1074. end;
  1075. { is the size smaller then we can adjust the block to that size and insert
  1076. the other part into the freelist }
  1077. if currsize>size then
  1078. split_block(pcurr, size);
  1079. inc(internal_status.currheapused,size-oldsize);
  1080. SysTryResizeMem := true;
  1081. end;
  1082. {*****************************************************************************
  1083. SysResizeMem
  1084. *****************************************************************************}
  1085. function SysReAllocMem(var p: pointer; size: ptrint):pointer;
  1086. var
  1087. newsize,
  1088. oldsize,
  1089. minsize : ptrint;
  1090. p2 : pointer;
  1091. begin
  1092. { Free block? }
  1093. if size=0 then
  1094. begin
  1095. if p<>nil then
  1096. begin
  1097. MemoryManager.FreeMem(p);
  1098. p := nil;
  1099. end;
  1100. end
  1101. else
  1102. { Allocate a new block? }
  1103. if p=nil then
  1104. begin
  1105. p := MemoryManager.GetMem(size);
  1106. end
  1107. else
  1108. { Resize block }
  1109. if not SysTryResizeMem(p,size) then
  1110. begin
  1111. oldsize:=MemoryManager.MemSize(p);
  1112. { Grow with bigger steps to prevent the need for
  1113. multiple getmem/freemem calls for fixed blocks. It might cost a bit
  1114. of extra memory, but in most cases a reallocmem is done multiple times. }
  1115. if oldsize<maxblocksize then
  1116. begin
  1117. newsize:=oldsize*2+blocksize;
  1118. if size>newsize then
  1119. newsize:=size;
  1120. end
  1121. else
  1122. newsize:=size;
  1123. { calc size of data to move }
  1124. minsize:=oldsize;
  1125. if newsize < minsize then
  1126. minsize := newsize;
  1127. p2 := MemoryManager.GetMem(newsize);
  1128. if p2<>nil then
  1129. Move(p^,p2^,minsize);
  1130. MemoryManager.FreeMem(p);
  1131. p := p2;
  1132. end;
  1133. SysReAllocMem := p;
  1134. end;
  1135. {$endif HAS_MEMORYMANAGER}
  1136. {*****************************************************************************
  1137. MemoryMutexManager default hooks
  1138. *****************************************************************************}
  1139. procedure SysHeapMutexInit;
  1140. begin
  1141. { nothing todo }
  1142. end;
  1143. procedure SysHeapMutexDone;
  1144. begin
  1145. { nothing todo }
  1146. end;
  1147. procedure SysHeapMutexLock;
  1148. begin
  1149. { give an runtime error. the program is running multithreaded without
  1150. any heap protection. this will result in unpredictable errors so
  1151. stopping here with an error is more safe (PFV) }
  1152. runerror(244);
  1153. end;
  1154. procedure SysHeapMutexUnLock;
  1155. begin
  1156. { see SysHeapMutexLock for comment }
  1157. runerror(244);
  1158. end;
  1159. {$ifndef HAS_MEMORYMANAGER}
  1160. {*****************************************************************************
  1161. InitHeap
  1162. *****************************************************************************}
  1163. { This function will initialize the Heap manager and need to be called from
  1164. the initialization of the system unit }
  1165. procedure InitHeap;
  1166. begin
  1167. FillChar(freelists_fixed,sizeof(tfreelists),0);
  1168. FillChar(freelists_free_chunk,sizeof(freelists_free_chunk),0);
  1169. freelist_var := nil;
  1170. freeoslist := nil;
  1171. freeoslistcount := 0;
  1172. fillchar(internal_status,sizeof(internal_status),0);
  1173. end;
  1174. procedure FinalizeHeap;
  1175. var
  1176. poc : poschunk;
  1177. i : longint;
  1178. begin
  1179. {$ifdef HAS_SYSOSFREE}
  1180. for i:=low(freelists_free_chunk) to high(freelists_free_chunk) do
  1181. if freelists_free_chunk[i] then
  1182. SysOSFree(freelists_fixed[i]^.poc,freelists_fixed[i]^.poc^.size);
  1183. while assigned(freeoslist) do
  1184. begin
  1185. poc:=freeoslist^.next;
  1186. SysOSFree(freeoslist, freeoslist^.size);
  1187. dec(freeoslistcount);
  1188. freeoslist:=poc;
  1189. end;
  1190. {$endif HAS_SYSOSFREE}
  1191. { release mutex }
  1192. MemoryMutexManager.MutexDone;
  1193. end;
  1194. {$endif HAS_MEMORYMANAGER}