glbheap.inc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2015 by the Free Pascal development team
  4. This file implements heap management for 16-bit Windows
  5. using the Windows global heap.
  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. The heap, implemented here is BP7-compatible for the Win16 and 286 protected
  14. mode targets.
  15. Large blocks (>=HeapLimit) are allocated as separate blocks on the global heap
  16. via a separate call to GlobalAlloc(). Since this allocates a new segment
  17. descriptor and there's a limit of how many of these are available to the system,
  18. small blocks (<HeapLimit) are suballocated from blocks of size HeapBlock. Each
  19. such heap block starts with a header of type TGlobalHeapBlockHeader, which is
  20. always located at offset 0 of the heap block segment. These heap blocks form a
  21. circular linked list.
  22. }
  23. const
  24. GlobalHeapBlockID=20564;
  25. type
  26. PGlobalHeapBlockHeader=^TGlobalHeapBlockHeader;far;
  27. TGlobalHeapBlockHeader=record
  28. ID: LongWord; { =GlobalHeapBlockID }
  29. FirstFreeOfs: Word;
  30. Unknown: Word; { don't know what this is; seems to be 0 }
  31. TotalFreeSpaceInBlock: Word;
  32. NextBlockSeg: Word; { the link to the next heap block }
  33. end;
  34. PFreeSubBlock=^TFreeSubBlock;far;
  35. TFreeSubBlock=record
  36. Next: Word;
  37. Size: Word;
  38. end;
  39. function NewHeapBlock(LastBlock: Word): Boolean;
  40. var
  41. hglob: HGLOBAL;
  42. pb: PGlobalHeapBlockHeader;
  43. begin
  44. hglob:=GlobalAlloc(HeapAllocFlags, HeapBlock);
  45. if hglob=0 then
  46. if ReturnNilIfGrowHeapFails then
  47. begin
  48. result:=false;
  49. exit;
  50. end
  51. else
  52. HandleError(203);
  53. pb:=GlobalLock(hglob);
  54. if (pb=nil) or (Ofs(pb^)<>0) then
  55. HandleError(204);
  56. with pb^ do
  57. begin
  58. ID:=GlobalHeapBlockID;
  59. FirstFreeOfs:=SizeOf(TGlobalHeapBlockHeader);
  60. Unknown:=0;
  61. TotalFreeSpaceInBlock:=HeapBlock-SizeOf(TGlobalHeapBlockHeader);
  62. if HeapList<>0 then
  63. NextBlockSeg:=HeapList
  64. else
  65. NextBlockSeg:=Seg(pb^);
  66. with PFreeSubBlock(Ptr(Seg(pb^),SizeOf(TGlobalHeapBlockHeader)))^ do
  67. begin
  68. Next:=0;
  69. Size:=HeapBlock-SizeOf(TGlobalHeapBlockHeader);
  70. end;
  71. end;
  72. HeapList:=Seg(pb^);
  73. if LastBlock<>0 then
  74. PGlobalHeapBlockHeader(Ptr(LastBlock,0))^.NextBlockSeg:=HeapList;
  75. result:=true;
  76. end;
  77. { tries to suballocate from the existing blocks. Returns nil if not enough
  78. free space is available. ASize must be aligned by 4. }
  79. function TryBlockGetMem(ASize: Word; out LastBlock: Word): FarPointer;
  80. var
  81. CurBlock: Word;
  82. CurBlockP: PGlobalHeapBlockHeader;
  83. CurSubBlock, PrevSubBlock: PFreeSubBlock;
  84. begin
  85. CurBlock:=HeapList;
  86. result:=nil;
  87. LastBlock:=0;
  88. if CurBlock=0 then
  89. exit;
  90. repeat
  91. CurBlockP:=Ptr(CurBlock,0);
  92. if CurBlockP^.TotalFreeSpaceInBlock>=ASize then
  93. begin
  94. PrevSubBlock:=nil;
  95. CurSubBlock:=Ptr(CurBlock,CurBlockP^.FirstFreeOfs);
  96. while Ofs(CurSubBlock^)<>0 do
  97. begin
  98. if CurSubBlock^.Size>=ASize then
  99. begin
  100. result:=CurSubBlock;
  101. if CurSubBlock^.Size=ASize then
  102. begin
  103. if PrevSubBlock<>nil then
  104. PrevSubBlock^.Next:=CurSubBlock^.Next
  105. else
  106. CurBlockP^.FirstFreeOfs:=CurSubBlock^.Next;
  107. end
  108. else
  109. begin
  110. with PFreeSubBlock(Ptr(CurBlock,Ofs(CurSubBlock^)+ASize))^ do
  111. begin
  112. Next:=CurSubBlock^.Next;
  113. Size:=CurSubBlock^.Size-ASize;
  114. end;
  115. if PrevSubBlock<>nil then
  116. PrevSubBlock^.Next:=Ofs(CurSubBlock^)+ASize
  117. else
  118. CurBlockP^.FirstFreeOfs:=Ofs(CurSubBlock^)+ASize;
  119. end;
  120. Dec(CurBlockP^.TotalFreeSpaceInBlock,ASize);
  121. { if TotalFreeSpaceInBlock becomes 0, then FirstFreeOfs
  122. should also become 0, but that is already handled
  123. correctly in the code above (in this case, by the
  124. line 'CurBlockP^.FirstFreeOfs:=CurSubBlock^.Next',
  125. so there's no need to set it explicitly here. }
  126. exit;
  127. end;
  128. PrevSubBlock:=CurSubBlock;
  129. CurSubBlock:=Ptr(CurBlock,CurSubBlock^.Next);
  130. end;
  131. end;
  132. LastBlock:=CurBlock;
  133. CurBlock:=CurBlockP^.NextBlockSeg;
  134. until CurBlock=HeapList;
  135. end;
  136. function SysGlobalBlockGetMem(Size: Word): FarPointer;
  137. var
  138. LastBlock: Word;
  139. begin
  140. Size:=(Size+3) and $fffc;
  141. result:=TryBlockGetMem(Size,LastBlock);
  142. if result<>nil then
  143. exit;
  144. if not NewHeapBlock(LastBlock) then
  145. begin
  146. { NewHeapBlock can only return false if ReturnNilIfGrowHeapFails=true }
  147. result:=nil;
  148. exit;
  149. end;
  150. result:=TryBlockGetMem(Size,LastBlock);
  151. end;
  152. function SysGlobalGetMem(Size: ptruint): FarPointer;
  153. type
  154. PFarWord=^Word;far;
  155. var
  156. hglob: HGLOBAL;
  157. begin
  158. if (size+2)>=HeapLimit then
  159. begin
  160. hglob:=GlobalAlloc(HeapAllocFlags, Size);
  161. if hglob=0 then
  162. if ReturnNilIfGrowHeapFails then
  163. begin
  164. result:=nil;
  165. exit;
  166. end
  167. else
  168. HandleError(203);
  169. result:=GlobalLock(hglob);
  170. if result=nil then
  171. HandleError(204);
  172. end
  173. else
  174. begin
  175. result:=SysGlobalBlockGetMem(Size+2);
  176. PFarWord(result)^:=Size;
  177. Inc(result,2);
  178. end;
  179. end;
  180. procedure TryBlockFreeMem(Addr: FarPointer; ASize: Word);
  181. var
  182. CurBlock: Word;
  183. CurBlockP: PGlobalHeapBlockHeader;
  184. CurSubBlock, PrevSubBlock: PFreeSubBlock;
  185. begin
  186. ASize:=(ASize+3) and $fffc;
  187. CurBlock:=Seg(Addr^);
  188. CurBlockP:=Ptr(CurBlock,0);
  189. if (Ofs(Addr^)<SizeOf(TGlobalHeapBlockHeader)) or ((Ofs(Addr^) and 3)<>0) or
  190. (CurBlockP^.ID<>GlobalHeapBlockID) then
  191. HandleError(204);
  192. if CurBlockP^.TotalFreeSpaceInBlock=0 then
  193. begin
  194. CurBlockP^.FirstFreeOfs:=Ofs(Addr^);
  195. with PFreeSubBlock(Addr)^ do
  196. begin
  197. Next:=0;
  198. Size:=ASize;
  199. end;
  200. end
  201. else if Ofs(Addr^)<CurBlockP^.FirstFreeOfs then
  202. begin
  203. if (Ofs(Addr^)+ASize)>CurBlockP^.FirstFreeOfs then
  204. HandleError(204)
  205. else if (Ofs(Addr^)+ASize)=CurBlockP^.FirstFreeOfs then
  206. begin
  207. PFreeSubBlock(Addr)^.Next:=PFreeSubBlock(Ptr(CurBlock,CurBlockP^.FirstFreeOfs))^.Next;
  208. PFreeSubBlock(Addr)^.Size:=ASize+PFreeSubBlock(Ptr(CurBlock,CurBlockP^.FirstFreeOfs))^.Size;
  209. end
  210. else
  211. begin
  212. PFreeSubBlock(Addr)^.Next:=CurBlockP^.FirstFreeOfs;
  213. PFreeSubBlock(Addr)^.Size:=ASize;
  214. end;
  215. CurBlockP^.FirstFreeOfs:=Ofs(Addr^);
  216. end
  217. else
  218. begin
  219. PrevSubBlock:=nil;
  220. CurSubBlock:=Ptr(CurBlock,CurBlockP^.FirstFreeOfs);
  221. while (Ofs(CurSubBlock^)<>0) and (Ofs(CurSubBlock^)<Ofs(Addr^)) do
  222. begin
  223. PrevSubBlock:=CurSubBlock;
  224. CurSubBlock:=Ptr(CurBlock,CurSubBlock^.Next);
  225. end;
  226. if PrevSubBlock=nil then
  227. HandleError(204);
  228. { merge with previous free block? }
  229. if Ofs(PrevSubBlock^)+PrevSubBlock^.Size=Ofs(Addr^) then
  230. begin
  231. Inc(PrevSubBlock^.Size,ASize);
  232. { merge with next as well? }
  233. if (Ofs(CurSubBlock^)<>0) and ((Ofs(PrevSubBlock^)+PrevSubBlock^.Size)=Ofs(CurSubBlock^)) then
  234. begin
  235. Inc(PrevSubBlock^.Size,CurSubBlock^.Size);
  236. PrevSubBlock^.Next:=CurSubBlock^.Next;
  237. end;
  238. end
  239. else
  240. begin
  241. PrevSubBlock^.Next:=Ofs(Addr^);
  242. if (Ofs(CurSubBlock^)<>0) and ((Ofs(Addr^)+ASize)=Ofs(CurSubBlock^)) then
  243. with PFreeSubBlock(Addr)^ do
  244. begin
  245. Next:=CurSubBlock^.Next;
  246. Size:=ASize+CurSubBlock^.Size;
  247. end
  248. else
  249. with PFreeSubBlock(Addr)^ do
  250. begin
  251. Next:=Ofs(CurSubBlock^);
  252. Size:=ASize;
  253. end;
  254. end;
  255. end;
  256. Inc(CurBlockP^.TotalFreeSpaceInBlock,ASize);
  257. end;
  258. function SysGlobalFreeMem(Addr: FarPointer): ptruint;
  259. type
  260. PFarWord=^Word;far;
  261. var
  262. hglob: HGLOBAL;
  263. begin
  264. if Addr<>nil then
  265. begin
  266. if Ofs(Addr^)=0 then
  267. begin
  268. hglob:=HGLOBAL(GlobalHandle(Seg(Addr^)));
  269. if hglob=0 then
  270. HandleError(204);
  271. result:=GlobalSize(hglob);
  272. if GlobalUnlock(hglob) then
  273. HandleError(204);
  274. if GlobalFree(hglob)<>0 then
  275. HandleError(204);
  276. end
  277. else
  278. begin
  279. Dec(Addr, 2);
  280. result:=PFarWord(Addr)^;
  281. TryBlockFreeMem(Addr, result+2);
  282. end;
  283. end
  284. else
  285. result:=0;
  286. end;
  287. function SysGlobalFreeMemSize(Addr: FarPointer; Size: Ptruint): ptruint;
  288. begin
  289. result:=SysGlobalFreeMem(addr);
  290. end;
  291. function SysGlobalAllocMem(size: ptruint): FarPointer;
  292. var
  293. hglob: HGLOBAL;
  294. begin
  295. if (size+2)>=HeapLimit then
  296. begin
  297. hglob:=GlobalAlloc(HeapAllocFlags or GMEM_ZEROINIT, Size);
  298. if hglob=0 then
  299. if ReturnNilIfGrowHeapFails then
  300. begin
  301. result:=nil;
  302. exit;
  303. end
  304. else
  305. HandleError(203);
  306. result:=GlobalLock(hglob);
  307. if result=nil then
  308. HandleError(204);
  309. end
  310. else
  311. begin
  312. result:=SysGlobalGetMem(size);
  313. FillChar(result^,size,0);
  314. end;
  315. end;
  316. function SysGlobalMemSize(p: FarPointer): ptruint;
  317. type
  318. PFarWord=^Word;far;
  319. var
  320. hglob: HGLOBAL;
  321. begin
  322. if Ofs(p^)=0 then
  323. begin
  324. hglob:=HGLOBAL(GlobalHandle(Seg(p^)));
  325. if hglob=0 then
  326. HandleError(204);
  327. result:=GlobalSize(hglob);
  328. end
  329. else
  330. begin
  331. Dec(p,2);
  332. result:=PFarWord(p)^;
  333. end;
  334. end;
  335. function SysGlobalReAllocMem(var p: FarPointer; size: ptruint):FarPointer;
  336. var
  337. hglob: HGLOBAL;
  338. begin
  339. if size=0 then
  340. begin
  341. SysGlobalFreeMem(p);
  342. result := nil;
  343. end
  344. else if p=nil then
  345. result := SysGlobalAllocMem(size)
  346. else
  347. if Ofs(p^)=0 then
  348. begin
  349. hglob:=HGLOBAL(GlobalHandle(Seg(p^)));
  350. if hglob=0 then
  351. HandleError(204);
  352. if GlobalUnlock(hglob) then
  353. HandleError(204);
  354. hglob:=GlobalReAlloc(hglob,size,HeapAllocFlags or GMEM_ZEROINIT);
  355. if hglob=0 then
  356. if ReturnNilIfGrowHeapFails then
  357. begin
  358. result:=nil;
  359. p:=nil;
  360. exit;
  361. end
  362. else
  363. HandleError(203);
  364. result:=GlobalLock(hglob);
  365. if result=nil then
  366. HandleError(204);
  367. end
  368. else
  369. begin
  370. { todo: do it in a more optimal way? }
  371. result:=SysGlobalAllocMem(size);
  372. Move(p^,result^,SysGlobalMemSize(p));
  373. SysGlobalFreeMem(p);
  374. end;
  375. p := result;
  376. end;
  377. function MemAvail: LongInt;
  378. var
  379. CurBlock: Word;
  380. CurBlockP: PGlobalHeapBlockHeader;
  381. CurSubBlock: PFreeSubBlock;
  382. begin
  383. result:=GetFreeSpace(0);
  384. CurBlock:=HeapList;
  385. if CurBlock=0 then
  386. exit;
  387. repeat
  388. CurBlockP:=Ptr(CurBlock,0);
  389. CurSubBlock:=Ptr(CurBlock,CurBlockP^.FirstFreeOfs);
  390. while Ofs(CurSubBlock^)<>0 do
  391. begin
  392. if CurSubBlock^.Size>2 then
  393. Inc(result,CurSubBlock^.Size-2);
  394. CurSubBlock:=Ptr(CurBlock,CurSubBlock^.Next);
  395. end;
  396. CurBlock:=CurBlockP^.NextBlockSeg;
  397. until CurBlock=HeapList;
  398. end;
  399. function MaxAvail: LongInt;
  400. var
  401. CurBlock: Word;
  402. CurBlockP: PGlobalHeapBlockHeader;
  403. CurSubBlock: PFreeSubBlock;
  404. begin
  405. result:=GlobalCompact(0);
  406. if result>(65536-SizeOf(TGlobalHeapBlockHeader)-2) then
  407. exit;
  408. CurBlock:=HeapList;
  409. if CurBlock=0 then
  410. exit;
  411. repeat
  412. CurBlockP:=Ptr(CurBlock,0);
  413. if CurBlockP^.TotalFreeSpaceInBlock>(result+2) then
  414. begin
  415. CurSubBlock:=Ptr(CurBlock,CurBlockP^.FirstFreeOfs);
  416. while Ofs(CurSubBlock^)<>0 do
  417. begin
  418. if CurSubBlock^.Size>(result+2) then
  419. result:=CurSubBlock^.Size-2;
  420. CurSubBlock:=Ptr(CurBlock,CurSubBlock^.Next);
  421. end;
  422. end;
  423. CurBlock:=CurBlockP^.NextBlockSeg;
  424. until CurBlock=HeapList;
  425. end;
  426. const
  427. GlobalHeapMemoryManager: TMemoryManager = (
  428. NeedLock: false; // Obsolete
  429. GetMem: @SysGlobalGetMem;
  430. FreeMem: @SysGlobalFreeMem;
  431. FreeMemSize: @SysGlobalFreeMemSize;
  432. AllocMem: @SysGlobalAllocMem;
  433. ReAllocMem: @SysGlobalReAllocMem;
  434. MemSize: @SysGlobalMemSize;
  435. InitThread: nil;
  436. DoneThread: nil;
  437. RelocateHeap: nil;
  438. GetHeapStatus: nil;
  439. GetFPCHeapStatus: nil;
  440. );