heap.inc 35 KB

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