heap.inc 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334
  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: @SysGetHeapStatus;
  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 : TFPCHeapStatus;
  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. function GetHeapStatus:THeapStatus;
  223. begin
  224. if IsMultiThread and MemoryManager.NeedLock then
  225. begin
  226. try
  227. MemoryMutexManager.MutexLock;
  228. result:=MemoryManager.GetHeapStatus();
  229. finally
  230. MemoryMutexManager.MutexUnlock;
  231. end;
  232. end
  233. else
  234. begin
  235. result:=MemoryManager.GetHeapStatus();
  236. end;
  237. end;
  238. function GetFPCHeapStatus:TFPCHeapStatus;
  239. begin
  240. if IsMultiThread and MemoryManager.NeedLock then
  241. begin
  242. try
  243. MemoryMutexManager.MutexLock;
  244. result:=MemoryManager.GetFPCHeapStatus();
  245. finally
  246. MemoryMutexManager.MutexUnlock;
  247. end;
  248. end
  249. else
  250. begin
  251. Result:=MemoryManager.GetFPCHeapStatus();
  252. end;
  253. end;
  254. function MemSize(p:pointer):ptrint;
  255. begin
  256. if IsMultiThread and MemoryManager.NeedLock then
  257. begin
  258. try
  259. MemoryMutexManager.MutexLock;
  260. MemSize := MemoryManager.MemSize(p);
  261. finally
  262. MemoryMutexManager.MutexUnlock;
  263. end;
  264. end
  265. else
  266. begin
  267. MemSize := MemoryManager.MemSize(p);
  268. end;
  269. end;
  270. { Delphi style }
  271. function FreeMem(p:pointer):ptrint;
  272. begin
  273. if IsMultiThread and MemoryManager.NeedLock then
  274. begin
  275. try
  276. MemoryMutexManager.MutexLock;
  277. Freemem := MemoryManager.FreeMem(p);
  278. finally
  279. MemoryMutexManager.MutexUnlock;
  280. end;
  281. end
  282. else
  283. begin
  284. Freemem := MemoryManager.FreeMem(p);
  285. end;
  286. end;
  287. function FreeMemory(p:pointer):ptrint;
  288. begin
  289. FreeMemory := FreeMem(p);
  290. end;
  291. function GetMem(size:ptrint):pointer;
  292. begin
  293. if IsMultiThread and MemoryManager.NeedLock then
  294. begin
  295. try
  296. MemoryMutexManager.MutexLock;
  297. GetMem := MemoryManager.GetMem(Size);
  298. finally
  299. MemoryMutexManager.MutexUnlock;
  300. end;
  301. end
  302. else
  303. begin
  304. GetMem := MemoryManager.GetMem(Size);
  305. end;
  306. end;
  307. function GetMemory(size:ptrint):pointer;
  308. begin
  309. GetMemory := Getmem(size);
  310. end;
  311. function AllocMem(Size:ptrint):pointer;
  312. begin
  313. if IsMultiThread and MemoryManager.NeedLock then
  314. begin
  315. try
  316. MemoryMutexManager.MutexLock;
  317. AllocMem := MemoryManager.AllocMem(size);
  318. finally
  319. MemoryMutexManager.MutexUnlock;
  320. end;
  321. end
  322. else
  323. begin
  324. AllocMem := MemoryManager.AllocMem(size);
  325. end;
  326. end;
  327. function ReAllocMem(var p:pointer;Size:ptrint):pointer;
  328. begin
  329. if IsMultiThread and MemoryManager.NeedLock then
  330. begin
  331. try
  332. MemoryMutexManager.MutexLock;
  333. ReAllocMem := MemoryManager.ReAllocMem(p,size);
  334. finally
  335. MemoryMutexManager.MutexUnlock;
  336. end;
  337. end
  338. else
  339. begin
  340. ReAllocMem := MemoryManager.ReAllocMem(p,size);
  341. end;
  342. end;
  343. function ReAllocMemory(var p:pointer;Size:ptrint):pointer;
  344. begin
  345. ReAllocMemory := ReAllocMem(p,size);
  346. end;
  347. {$ifdef ValueGetmem}
  348. { Needed for calls from Assembler }
  349. function fpc_getmem(size:ptrint):pointer;compilerproc;[public,alias:'FPC_GETMEM'];
  350. begin
  351. if IsMultiThread and MemoryManager.NeedLock then
  352. begin
  353. try
  354. MemoryMutexManager.MutexLock;
  355. fpc_GetMem := MemoryManager.GetMem(size);
  356. finally
  357. MemoryMutexManager.MutexUnlock;
  358. end;
  359. end
  360. else
  361. begin
  362. fpc_GetMem := MemoryManager.GetMem(size);
  363. end;
  364. end;
  365. {$else ValueGetmem}
  366. { Needed for calls from Assembler }
  367. procedure AsmGetMem(var p:pointer;size:ptrint);[public,alias:'FPC_GETMEM'];
  368. begin
  369. p := MemoryManager.GetMem(size);
  370. end;
  371. {$endif ValueGetmem}
  372. {$ifdef ValueFreemem}
  373. procedure fpc_freemem(p:pointer);compilerproc;[public,alias:'FPC_FREEMEM'];
  374. begin
  375. if IsMultiThread and MemoryManager.NeedLock then
  376. begin
  377. try
  378. MemoryMutexManager.MutexLock;
  379. if p <> nil then
  380. MemoryManager.FreeMem(p);
  381. finally
  382. MemoryMutexManager.MutexUnlock;
  383. end;
  384. end
  385. else
  386. begin
  387. if p <> nil then
  388. MemoryManager.FreeMem(p);
  389. end;
  390. end;
  391. {$else ValueFreemem}
  392. procedure AsmFreeMem(var p:pointer);[public,alias:'FPC_FREEMEM'];
  393. begin
  394. if p <> nil then
  395. MemoryManager.FreeMem(p);
  396. end;
  397. {$endif ValueFreemem}
  398. { Bootstrapping }
  399. {$ifndef HASGETHEAPSTATUS}
  400. Function Memavail:ptrint;
  401. begin
  402. result:=0;
  403. end;
  404. Function Maxavail:ptrint;
  405. begin
  406. result:=0;
  407. end;
  408. Function Heapsize:ptrint;
  409. begin
  410. result:=0;
  411. end;
  412. {$endif HASGETHEAPSTATUS}
  413. {*****************************************************************************
  414. GetHeapStatus
  415. *****************************************************************************}
  416. function SysFPCGetHeapStatus:TFPCHeapStatus;
  417. begin
  418. internal_status.CurrHeapFree:=internal_status.CurrHeapSize-internal_status.CurrHeapUsed;
  419. result:=internal_status;
  420. end;
  421. function SysGetHeapStatus :THeapStatus;
  422. begin
  423. internal_status.CurrHeapFree:=internal_status.CurrHeapSize-internal_status.CurrHeapUsed;
  424. result.TotalAllocated :=internal_status.CurrHeapUsed;
  425. result.TotalFree :=internal_status.CurrHeapFree;
  426. result.TotalAddrSpace :=0;
  427. result.TotalUncommitted :=0;
  428. result.TotalCommitted :=0;
  429. result.FreeSmall :=0;
  430. result.FreeBig :=0;
  431. result.Unused :=0;
  432. result.Overhead :=0;
  433. result.HeapErrorCode :=0;
  434. end;
  435. {$ifdef DUMPBLOCKS} // TODO
  436. procedure DumpBlocks;
  437. var
  438. s,i,j : ptrint;
  439. hp : pfreerecord;
  440. begin
  441. for i := 1 to maxblock do
  442. begin
  443. hp := freelists[i];
  444. j := 0;
  445. while assigned(hp) do
  446. begin
  447. inc(j);
  448. hp := hp^.next;
  449. end;
  450. writeln('Block ',i*blocksize,': ',j);
  451. end;
  452. { freelist 0 }
  453. hp := freelists[0];
  454. j := 0;
  455. s := 0;
  456. while assigned(hp) do
  457. begin
  458. inc(j);
  459. if hp^.size>s then
  460. s := hp^.size;
  461. hp := hp^.next;
  462. end;
  463. writeln('Main: ',j,' maxsize: ',s);
  464. end;
  465. {$endif}
  466. {$ifdef TestFreeLists}
  467. procedure TestFreeLists;
  468. var
  469. i,j : ptrint;
  470. mc : pmemchunk_fixed;
  471. begin
  472. for i := 1 to maxblockindex do
  473. begin
  474. j := 0;
  475. mc := freelists_fixed[i];
  476. while assigned(mc) do
  477. begin
  478. inc(j);
  479. if ((mc^.size and fixedsizemask) <> i * blocksize) then
  480. RunError(204);
  481. mc := mc^.next_fixed;
  482. end;
  483. end;
  484. end;
  485. {$endif TestFreeLists}
  486. {*****************************************************************************
  487. List adding/removal
  488. *****************************************************************************}
  489. procedure append_to_list_fixed(blockindex: ptrint; pmc: pmemchunk_fixed);
  490. begin
  491. pmc^.prev_fixed := nil;
  492. pmc^.next_fixed := freelists_fixed[blockindex];
  493. if freelists_fixed[blockindex]<>nil then
  494. freelists_fixed[blockindex]^.prev_fixed := pmc;
  495. freelists_fixed[blockindex] := pmc;
  496. end;
  497. procedure append_to_list_var(pmc: pmemchunk_var);
  498. begin
  499. pmc^.prev_var := nil;
  500. pmc^.next_var := freelist_var;
  501. if freelist_var<>nil then
  502. freelist_var^.prev_var := pmc;
  503. freelist_var := pmc;
  504. end;
  505. procedure remove_from_list_fixed(blockindex: ptrint; pmc: pmemchunk_fixed);
  506. begin
  507. if assigned(pmc^.next_fixed) then
  508. pmc^.next_fixed^.prev_fixed := pmc^.prev_fixed;
  509. if assigned(pmc^.prev_fixed) then
  510. pmc^.prev_fixed^.next_fixed := pmc^.next_fixed
  511. else
  512. freelists_fixed[blockindex] := pmc^.next_fixed;
  513. end;
  514. procedure remove_from_list_var(pmc: pmemchunk_var);
  515. begin
  516. if assigned(pmc^.next_var) then
  517. pmc^.next_var^.prev_var := pmc^.prev_var;
  518. if assigned(pmc^.prev_var) then
  519. pmc^.prev_var^.next_var := pmc^.next_var
  520. else
  521. freelist_var := pmc^.next_var;
  522. end;
  523. procedure append_to_oslist(poc: poschunk);
  524. begin
  525. { decide whether to free block or add to list }
  526. {$ifdef HAS_SYSOSFREE}
  527. if freeoslistcount >= 3 then
  528. begin
  529. dec(internal_status.currheapsize, poc^.size);
  530. SysOSFree(poc, poc^.size);
  531. end
  532. else
  533. begin
  534. {$endif}
  535. poc^.prev := nil;
  536. poc^.next := freeoslist;
  537. if freeoslist <> nil then
  538. freeoslist^.prev := poc;
  539. freeoslist := poc;
  540. inc(freeoslistcount);
  541. {$ifdef HAS_SYSOSFREE}
  542. end;
  543. {$endif}
  544. end;
  545. procedure remove_from_oslist(poc: poschunk);
  546. begin
  547. if assigned(poc^.next) then
  548. poc^.next^.prev := poc^.prev;
  549. if assigned(poc^.prev) then
  550. poc^.prev^.next := poc^.next
  551. else
  552. freeoslist := poc^.next;
  553. dec(freeoslistcount);
  554. end;
  555. procedure append_to_oslist_var(pmc: pmemchunk_var);
  556. var
  557. poc: poschunk;
  558. begin
  559. // block eligable for freeing
  560. poc := pointer(pmc)-sizeof(toschunk);
  561. remove_from_list_var(pmc);
  562. append_to_oslist(poc);
  563. end;
  564. procedure append_to_oslist_fixed(blockindex, chunksize: ptrint; poc: poschunk);
  565. var
  566. pmc: pmemchunk_fixed;
  567. i, count: ptrint;
  568. begin
  569. count := (poc^.size - sizeof(toschunk)) div chunksize;
  570. pmc := pmemchunk_fixed(pointer(poc)+sizeof(toschunk));
  571. for i := 0 to count - 1 do
  572. begin
  573. remove_from_list_fixed(blockindex, pmc);
  574. pmc := pointer(pmc)+chunksize;
  575. end;
  576. append_to_oslist(poc);
  577. end;
  578. {*****************************************************************************
  579. Split block
  580. *****************************************************************************}
  581. procedure split_block(pcurr: pmemchunk_var; size: ptrint);
  582. var
  583. pcurr_tmp : pmemchunk_var;
  584. sizeleft: ptrint;
  585. begin
  586. sizeleft := (pcurr^.size and sizemask)-size;
  587. if sizeleft>=blocksize then
  588. begin
  589. pcurr_tmp := pmemchunk_var(pointer(pcurr)+size);
  590. { update prevsize of block to the right }
  591. if (pcurr^.size and lastblockflag) = 0 then
  592. pmemchunk_var(pointer(pcurr)+(pcurr^.size and sizemask))^.prevsize := sizeleft;
  593. { inherit the lastblockflag }
  594. pcurr_tmp^.size := sizeleft or (pcurr^.size and lastblockflag);
  595. pcurr_tmp^.prevsize := size;
  596. { the block we return is not the last one anymore (there's now a block after it) }
  597. { decrease size of block to new size }
  598. pcurr^.size := size or (pcurr^.size and (not sizemask and not lastblockflag));
  599. { insert the block in the freelist }
  600. append_to_list_var(pcurr_tmp);
  601. end;
  602. end;
  603. {*****************************************************************************
  604. Try concat freerecords
  605. *****************************************************************************}
  606. procedure concat_two_blocks(mc_left, mc_right: pmemchunk_var);
  607. var
  608. mc_tmp : pmemchunk_var;
  609. size_right : ptrint;
  610. begin
  611. // left block free, concat with right-block
  612. size_right := mc_right^.size and sizemask;
  613. inc(mc_left^.size, size_right);
  614. // if right-block was last block, copy flag
  615. if (mc_right^.size and lastblockflag) <> 0 then
  616. begin
  617. mc_left^.size := mc_left^.size or lastblockflag;
  618. end
  619. else
  620. begin
  621. // there is a block to the right of the right-block, adjust it's prevsize
  622. mc_tmp := pmemchunk_var(pointer(mc_right)+size_right);
  623. mc_tmp^.prevsize := mc_left^.size and sizemask;
  624. end;
  625. // remove right-block from doubly linked list
  626. remove_from_list_var(mc_right);
  627. end;
  628. procedure try_concat_free_chunk_forward(mc: pmemchunk_var);
  629. var
  630. mc_tmp : pmemchunk_var;
  631. begin
  632. { try concat forward }
  633. if (mc^.size and lastblockflag) = 0 then
  634. begin
  635. mc_tmp := pmemchunk_var(pointer(mc)+(mc^.size and sizemask));
  636. if (mc_tmp^.size and usedflag) = 0 then
  637. begin
  638. // next block free: concat
  639. concat_two_blocks(mc, mc_tmp);
  640. end;
  641. end;
  642. end;
  643. function try_concat_free_chunk(mc: pmemchunk_var): pmemchunk_var;
  644. var
  645. mc_tmp : pmemchunk_var;
  646. begin
  647. try_concat_free_chunk_forward(mc);
  648. { try concat backward }
  649. if (mc^.size and firstblockflag) = 0 then
  650. begin
  651. mc_tmp := pmemchunk_var(pointer(mc)-mc^.prevsize);
  652. if (mc_tmp^.size and usedflag) = 0 then
  653. begin
  654. // prior block free: concat
  655. concat_two_blocks(mc_tmp, mc);
  656. mc := mc_tmp;
  657. end;
  658. end;
  659. result := mc;
  660. end;
  661. {*****************************************************************************
  662. Grow Heap
  663. *****************************************************************************}
  664. function alloc_oschunk(blockindex, size: ptrint): pointer;
  665. var
  666. pmc : pmemchunk_fixed;
  667. pmcv : pmemchunk_var;
  668. minsize,
  669. maxsize,
  670. i, count : ptrint;
  671. chunksize : ptrint;
  672. begin
  673. { increase size by size needed for os block header }
  674. minsize := size + sizeof(toschunk);
  675. if blockindex<>0 then
  676. maxsize := (size * $ffff) + sizeof(toschunk)
  677. else
  678. maxsize := high(ptrint);
  679. { blocks available in freelist? }
  680. result := freeoslist;
  681. while result <> nil do
  682. begin
  683. if (poschunk(result)^.size >= minsize) and
  684. (poschunk(result)^.size <= maxsize) then
  685. begin
  686. size := poschunk(result)^.size;
  687. remove_from_oslist(poschunk(result));
  688. break;
  689. end;
  690. result := poschunk(result)^.next;
  691. end;
  692. if result = nil then
  693. begin
  694. {$ifdef DUMPGROW}
  695. writeln('growheap(',size,') allocating ',(size+sizeof(toschunk)+$ffff) and $ffff0000);
  696. DumpBlocks;
  697. {$endif}
  698. { allocate by 64K size }
  699. size := (size+sizeof(toschunk)+$ffff) and not $ffff;
  700. { allocate smaller blocks for fixed-size chunks }
  701. if blockindex<>0 then
  702. begin
  703. result := SysOSAlloc(GrowHeapSizeSmall);
  704. if result<>nil then
  705. size := GrowHeapSizeSmall;
  706. end
  707. { first try 256K (default) }
  708. else if size<=GrowHeapSize1 then
  709. begin
  710. result := SysOSAlloc(GrowHeapSize1);
  711. if result<>nil then
  712. size := GrowHeapSize1;
  713. end
  714. { second try 1024K (default) }
  715. else if size<=GrowHeapSize2 then
  716. begin
  717. result := SysOSAlloc(GrowHeapSize2);
  718. if result<>nil then
  719. size := GrowHeapSize2;
  720. end
  721. { else allocate the needed bytes }
  722. else
  723. result := SysOSAlloc(size);
  724. { try again }
  725. if result=nil then
  726. begin
  727. result := SysOSAlloc(size);
  728. if (result=nil) then
  729. begin
  730. if ReturnNilIfGrowHeapFails then
  731. exit
  732. else
  733. HandleError(203);
  734. end;
  735. end;
  736. { set the total new heap size }
  737. inc(internal_status.currheapsize,size);
  738. if internal_status.currheapsize>internal_status.maxheapsize then
  739. internal_status.maxheapsize:=internal_status.currheapsize;
  740. end;
  741. { initialize os-block }
  742. poschunk(result)^.used := 0;
  743. poschunk(result)^.size := size;
  744. inc(result, sizeof(toschunk));
  745. if blockindex<>0 then
  746. begin
  747. { chop os chunk in fixedsize parts,
  748. maximum of $ffff elements are allowed, otherwise
  749. there will be an overflow }
  750. chunksize := blockindex shl blockshr;
  751. count := (size-sizeof(toschunk)) div chunksize;
  752. if count>$ffff then
  753. HandleError(204);
  754. pmc := pmemchunk_fixed(result);
  755. pmc^.prev_fixed := nil;
  756. i := 0;
  757. repeat
  758. pmc^.size := fixedsizeflag or chunksize or (i shl 16);
  759. pmc^.next_fixed := pointer(pmc)+chunksize;
  760. inc(i);
  761. if i < count then
  762. begin
  763. pmc := pmemchunk_fixed(pointer(pmc)+chunksize);
  764. pmc^.prev_fixed := pointer(pmc)-chunksize;
  765. end
  766. else
  767. begin
  768. break;
  769. end;
  770. until false;
  771. append_to_list_fixed(blockindex, pmc);
  772. pmc^.prev_fixed := pointer(pmc)-chunksize;
  773. freelists_fixed[blockindex] := pmemchunk_fixed(result);
  774. end
  775. else
  776. begin
  777. pmcv := pmemchunk_var(result);
  778. append_to_list_var(pmcv);
  779. pmcv^.size := ((size-sizeof(toschunk)) and sizemask) or (firstblockflag or lastblockflag);
  780. pmcv^.prevsize := 0;
  781. end;
  782. {$ifdef TestFreeLists}
  783. TestFreeLists;
  784. {$endif TestFreeLists}
  785. end;
  786. {*****************************************************************************
  787. SysGetMem
  788. *****************************************************************************}
  789. function SysGetMem_Fixed(size: ptrint): pointer;
  790. var
  791. pcurr: pmemchunk_fixed;
  792. poc: poschunk;
  793. s: ptrint;
  794. begin
  795. result:=nil;
  796. { try to find a block in one of the freelists per size }
  797. s := size shr blockshr;
  798. pcurr := freelists_fixed[s];
  799. { no free blocks ? }
  800. if not assigned(pcurr) then
  801. begin
  802. pcurr := alloc_oschunk(s, size);
  803. if not assigned(pcurr) then
  804. exit;
  805. end;
  806. { get a pointer to the block we should return }
  807. result := pointer(pcurr)+sizeof(tmemchunk_fixed_hdr);
  808. { flag as in-use }
  809. pcurr^.size := pcurr^.size or usedflag;
  810. { update freelist }
  811. freelists_fixed[s] := pcurr^.next_fixed;
  812. if assigned(freelists_fixed[s]) then
  813. freelists_fixed[s]^.prev_fixed := nil;
  814. poc := poschunk(pointer(pcurr)-((pcurr^.size shr 16)*(pcurr^.size and fixedsizemask)+sizeof(toschunk)));
  815. inc(poc^.used);
  816. { statistics }
  817. inc(internal_status.currheapused,size);
  818. if internal_status.currheapused>internal_status.maxheapused then
  819. internal_status.maxheapused:=internal_status.currheapused;
  820. {$ifdef TestFreeLists}
  821. if test_each then
  822. TestFreeLists;
  823. {$endif TestFreeLists}
  824. end;
  825. function SysGetMem_Var(size: ptrint): pointer;
  826. var
  827. pcurr, pcurr_tmp : pmemchunk_var;
  828. {$ifdef BESTMATCH}
  829. pbest : pmemchunk_var;
  830. {$endif}
  831. begin
  832. result:=nil;
  833. {$ifdef BESTMATCH}
  834. pbest := nil;
  835. {$endif}
  836. pcurr := freelist_var;
  837. while assigned(pcurr) do
  838. begin
  839. {$ifdef BESTMATCH}
  840. if pcurr^.size=size then
  841. begin
  842. break;
  843. end
  844. else
  845. begin
  846. if (pcurr^.size>size) then
  847. begin
  848. if (not assigned(pbest)) or
  849. (pcurr^.size<pbest^.size) then
  850. pbest := pcurr;
  851. end;
  852. end;
  853. {$else BESTMATCH}
  854. if pcurr^.size>=size then
  855. break;
  856. {$endif BESTMATCH}
  857. pcurr := pcurr^.next_var;
  858. end;
  859. {$ifdef BESTMATCH}
  860. if not assigned(pcurr) then
  861. pcurr := pbest;
  862. {$endif}
  863. if not assigned(pcurr) then
  864. begin
  865. // all os-chunks full, allocate a new one
  866. pcurr := alloc_oschunk(0, size);
  867. if not assigned(pcurr) then
  868. exit;
  869. end;
  870. { get pointer of the block we should return }
  871. result := pointer(pcurr)+sizeof(tmemchunk_var_hdr);
  872. { remove the current block from the freelist }
  873. remove_from_list_var(pcurr);
  874. { create the left over freelist block, if at least 16 bytes are free }
  875. split_block(pcurr, size);
  876. { flag block as used }
  877. pcurr^.size := pcurr^.size or usedflag;
  878. { statistics }
  879. inc(internal_status.currheapused,size);
  880. if internal_status.currheapused>internal_status.maxheapused then
  881. internal_status.maxheapused:=internal_status.currheapused;
  882. {$ifdef TestFreeLists}
  883. if test_each then
  884. TestFreeLists;
  885. {$endif TestFreeLists}
  886. end;
  887. function SysGetMem(size : ptrint):pointer;
  888. begin
  889. { Something to allocate ? }
  890. if size<=0 then
  891. begin
  892. { give an error for < 0 }
  893. if size<0 then
  894. HandleError(204);
  895. { we always need to allocate something, using heapend is not possible,
  896. because heappend can be changed by growheap (PFV) }
  897. size := 1;
  898. end;
  899. { calc to multiple of 16 after adding the needed bytes for memchunk header }
  900. if size <= (maxblocksize - sizeof(tmemchunk_fixed_hdr)) then
  901. begin
  902. size := (size+sizeof(tmemchunk_fixed_hdr)+(blocksize-1)) and fixedsizemask;
  903. sysgetmem := sysgetmem_fixed(size);
  904. end
  905. else
  906. begin
  907. size := (size+sizeof(tmemchunk_var_hdr)+(blocksize-1)) and sizemask;
  908. sysgetmem := sysgetmem_var(size);
  909. end;
  910. end;
  911. {*****************************************************************************
  912. SysFreeMem
  913. *****************************************************************************}
  914. function SysFreeMem_Fixed(pcurr: pmemchunk_fixed; size: ptrint): ptrint;
  915. var
  916. pcurrsize: ptrint;
  917. blockindex: ptrint;
  918. poc: poschunk;
  919. begin
  920. pcurrsize := pcurr^.size and fixedsizemask;
  921. if size<>pcurrsize then
  922. HandleError(204);
  923. dec(internal_status.currheapused,pcurrsize);
  924. { insert the block in it's freelist }
  925. pcurr^.size := pcurr^.size and (not usedflag);
  926. blockindex := pcurrsize shr blockshr;
  927. append_to_list_fixed(blockindex, pcurr);
  928. { decrease used blocks count }
  929. poc := poschunk(pointer(pcurr)-(pcurr^.size shr 16)*pcurrsize-sizeof(toschunk));
  930. if poc^.used = 0 then
  931. HandleError(204);
  932. dec(poc^.used);
  933. if poc^.used = 0 then
  934. begin
  935. // block eligable for freeing
  936. append_to_oslist_fixed(blockindex, pcurrsize, poc);
  937. end;
  938. SysFreeMem_Fixed := pcurrsize;
  939. {$ifdef TestFreeLists}
  940. if test_each then
  941. TestFreeLists;
  942. {$endif TestFreeLists}
  943. end;
  944. function SysFreeMem_Var(pcurr: pmemchunk_var; size: ptrint): ptrint;
  945. var
  946. pcurrsize: ptrint;
  947. begin
  948. pcurrsize := pcurr^.size and sizemask;
  949. if size<>pcurrsize then
  950. HandleError(204);
  951. dec(internal_status.currheapused,pcurrsize);
  952. { insert the block in it's freelist }
  953. pcurr^.size := pcurr^.size and (not usedflag);
  954. append_to_list_var(pcurr);
  955. SysFreeMem_Var := pcurrsize;
  956. pcurr := try_concat_free_chunk(pcurr);
  957. if (pcurr^.size and (firstblockflag or lastblockflag)) = (firstblockflag or lastblockflag) then
  958. begin
  959. append_to_oslist_var(pcurr);
  960. end;
  961. {$ifdef TestFreeLists}
  962. if test_each then
  963. TestFreeLists;
  964. {$endif TestFreeLists}
  965. end;
  966. function SysFreeMem(p: pointer): ptrint;
  967. var
  968. pcurrsize: ptrint;
  969. begin
  970. if p=nil then
  971. exit;
  972. pcurrsize := pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr))^.size;
  973. { check if this is a fixed- or var-sized chunk }
  974. if (pcurrsize and fixedsizeflag) = 0 then
  975. begin
  976. result := sysfreemem_var(pmemchunk_var(p-sizeof(tmemchunk_var_hdr)), pcurrsize and sizemask);
  977. end
  978. else
  979. begin
  980. result := sysfreemem_fixed(pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr)), pcurrsize and fixedsizemask);
  981. end;
  982. end;
  983. {*****************************************************************************
  984. SysFreeMemSize
  985. *****************************************************************************}
  986. Function SysFreeMemSize(p: pointer; size: ptrint):ptrint;
  987. var
  988. pcurrsize: ptrint;
  989. begin
  990. SysFreeMemSize := 0;
  991. if size<=0 then
  992. begin
  993. if size<0 then
  994. HandleError(204);
  995. exit;
  996. end;
  997. if p=nil then
  998. HandleError(204);
  999. pcurrsize := pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr))^.size;
  1000. { check if this is a fixed- or var-sized chunk }
  1001. if (pcurrsize and fixedsizeflag) = 0 then
  1002. begin
  1003. size := (size+sizeof(tmemchunk_var_hdr)+(blocksize-1)) and sizemask;
  1004. result := sysfreemem_var(pmemchunk_var(p-sizeof(tmemchunk_var_hdr)), size);
  1005. end
  1006. else
  1007. begin
  1008. size := (size+sizeof(tmemchunk_fixed_hdr)+(blocksize-1)) and fixedsizemask;
  1009. result := sysfreemem_fixed(pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr)), size);
  1010. end;
  1011. end;
  1012. {*****************************************************************************
  1013. SysMemSize
  1014. *****************************************************************************}
  1015. function SysMemSize(p: pointer): ptrint;
  1016. begin
  1017. SysMemSize := pmemchunk_fixed(pointer(p)-sizeof(tmemchunk_fixed_hdr))^.size;
  1018. if (SysMemSize and fixedsizeflag) = 0 then
  1019. begin
  1020. SysMemSize := SysMemSize and sizemask;
  1021. dec(SysMemSize, sizeof(tmemchunk_var_hdr));
  1022. end
  1023. else
  1024. begin
  1025. SysMemSize := SysMemSize and fixedsizemask;
  1026. dec(SysMemSize, sizeof(tmemchunk_fixed_hdr));
  1027. end;
  1028. end;
  1029. {*****************************************************************************
  1030. SysAllocMem
  1031. *****************************************************************************}
  1032. function SysAllocMem(size: ptrint): pointer;
  1033. begin
  1034. sysallocmem := MemoryManager.GetMem(size);
  1035. if sysallocmem<>nil then
  1036. FillChar(sysallocmem^,MemoryManager.MemSize(sysallocmem),0);
  1037. end;
  1038. {*****************************************************************************
  1039. SysResizeMem
  1040. *****************************************************************************}
  1041. function SysTryResizeMem(var p: pointer; size: ptrint): boolean;
  1042. var
  1043. pcurrsize,
  1044. oldsize,
  1045. currsize,
  1046. sizeleft : ptrint;
  1047. pnew,
  1048. pcurr : pmemchunk_var;
  1049. begin
  1050. { fix needed size }
  1051. if size <= (maxblocksize - sizeof(tmemchunk_fixed_hdr)) then
  1052. begin
  1053. size := (size+sizeof(tmemchunk_fixed_hdr)+(blocksize-1)) and fixedsizemask;
  1054. end
  1055. else
  1056. begin
  1057. size := (size+sizeof(tmemchunk_var_hdr)+(blocksize-1)) and sizemask;
  1058. end;
  1059. { fix p to point to the heaprecord }
  1060. pcurrsize := pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr))^.size;
  1061. if (pcurrsize and fixedsizeflag) = 0 then
  1062. begin
  1063. currsize := pcurrsize and sizemask;
  1064. end
  1065. else
  1066. begin
  1067. currsize := pcurrsize and fixedsizemask;
  1068. end;
  1069. oldsize := currsize;
  1070. { is the allocated block still correct? }
  1071. if (currsize>=size) and (size>(currsize-16)) then
  1072. begin
  1073. SysTryResizeMem := true;
  1074. {$ifdef TestFreeLists}
  1075. if test_each then
  1076. TestFreeLists;
  1077. {$endif TestFreeLists}
  1078. exit;
  1079. end;
  1080. { don't do resizes on fixed-size blocks }
  1081. // if (pcurrsize and fixedsizeflag) <> 0 then
  1082. // begin
  1083. SysTryResizeMem := false;
  1084. exit;
  1085. // end;
  1086. { get pointer to block }
  1087. pcurr := pmemchunk_var(pointer(p)-sizeof(tmemchunk_var_hdr));
  1088. { do we need to allocate more memory ? }
  1089. if size>currsize then
  1090. begin
  1091. { the size is bigger than the previous size, we need to allocated more mem.
  1092. We first check if the blocks after the current block are free. If not we
  1093. simply call getmem/freemem to get the new block }
  1094. try_concat_free_chunk_forward(pcurr);
  1095. currsize := (pcurr^.size and sizemask);
  1096. SysTryResizeMem := currsize>=size;
  1097. end;
  1098. if currsize>size then
  1099. begin
  1100. { is the size smaller then we can adjust the block to that size and insert
  1101. the other part into the freelist }
  1102. { create the left over freelist block, if at least 16 bytes are free }
  1103. split_block(pcurr, size);
  1104. SysTryResizeMem := true;
  1105. end;
  1106. inc(internal_status.currheapused,size-oldsize);
  1107. {$ifdef TestFreeLists}
  1108. if test_each then
  1109. TestFreeLists;
  1110. {$endif TestFreeLists}
  1111. end;
  1112. {*****************************************************************************
  1113. SysResizeMem
  1114. *****************************************************************************}
  1115. function SysReAllocMem(var p: pointer; size: ptrint):pointer;
  1116. var
  1117. minsize : ptrint;
  1118. p2 : pointer;
  1119. begin
  1120. { Free block? }
  1121. if size=0 then
  1122. begin
  1123. if p<>nil then
  1124. begin
  1125. MemoryManager.FreeMem(p);
  1126. p := nil;
  1127. end;
  1128. end else
  1129. { Allocate a new block? }
  1130. if p=nil then
  1131. begin
  1132. p := MemoryManager.AllocMem(size);
  1133. end else
  1134. { Resize block }
  1135. if not SysTryResizeMem(p,size) then
  1136. begin
  1137. minsize := MemoryManager.MemSize(p);
  1138. if size < minsize then
  1139. minsize := size;
  1140. p2 := MemoryManager.AllocMem(size);
  1141. if p2<>nil then
  1142. Move(p^,p2^,minsize);
  1143. MemoryManager.FreeMem(p);
  1144. p := p2;
  1145. end;
  1146. SysReAllocMem := p;
  1147. end;
  1148. {*****************************************************************************
  1149. MemoryMutexManager default hooks
  1150. *****************************************************************************}
  1151. procedure SysHeapMutexInit;
  1152. begin
  1153. { nothing todo }
  1154. end;
  1155. procedure SysHeapMutexDone;
  1156. begin
  1157. { nothing todo }
  1158. end;
  1159. procedure SysHeapMutexLock;
  1160. begin
  1161. { give an runtime error. the program is running multithreaded without
  1162. any heap protection. this will result in unpredictable errors so
  1163. stopping here with an error is more safe (PFV) }
  1164. runerror(244);
  1165. end;
  1166. procedure SysHeapMutexUnLock;
  1167. begin
  1168. { see SysHeapMutexLock for comment }
  1169. runerror(244);
  1170. end;
  1171. {*****************************************************************************
  1172. InitHeap
  1173. *****************************************************************************}
  1174. { This function will initialize the Heap manager and need to be called from
  1175. the initialization of the system unit }
  1176. procedure InitHeap;
  1177. begin
  1178. FillChar(freelists_fixed,sizeof(tfreelists),0);
  1179. freelist_var := nil;
  1180. freeoslist := nil;
  1181. freeoslistcount := 0;
  1182. fillchar(internal_status,sizeof(internal_status),0);
  1183. end;
  1184. {
  1185. $Log$
  1186. Revision 1.44 2005-02-28 15:38:38 marco
  1187. * getFPCheapstatus (no, FPC HEAP, not FP CHEAP!)
  1188. Revision 1.43 2005/02/14 17:13:22 peter
  1189. * truncate log
  1190. Revision 1.42 2005/01/30 11:56:29 peter
  1191. * allow Freemem(nil)
  1192. }