heap.inc 35 KB

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