RecyclerMM.pas 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916
  1. // RecyclerMM
  2. {: Egg<p>
  3. Recycling Memory Manager (aka RMM).<p>
  4. Provides high-speed allocation/release of highly-aligned memory
  5. via a segregated storage algorithm (for small and medium blocks)
  6. and a virtual heap (large blocks).<br>
  7. Supports Shared Memory (like ShareMem, but no DLL required).<p>
  8. Copyright 2005 - Creative IT / Eric Grange<br>
  9. Default licensing is GPL, use under MPL can be granted (on request, for free)
  10. for users/companies "supporting" Open Source (purely subjective decision by us)<p>
  11. Implementation Notes:<ul>
  12. <li>Shared Memory support is implemented through the creation of a Local
  13. Atom and a (never visible) window, which allow main EXE/DLLs modules
  14. to be aware of each other's RecyclerMM support and thus, reuse a single
  15. manager instance (which may be one from the main exe, or the one of
  16. the statically linked DLLs, depending on initialization order).
  17. <li>Small blocks chunks and batches are allocated at the top of the address
  18. space, large blocks at the bottom.
  19. <li>Use of the Delphi 7 SP1 is *NOT* recommended, not because it won't work,
  20. but because bugs introduced in the SP1 register allocator will generate
  21. sub-optimal code. If you really need SP1, apply the patch first then
  22. manually revert DCC32.EXE and DCC70.DLL to their original state
  23. </ul><p>
  24. <b>History : </b><font size=-1><ul>
  25. <li>19/04/05 - EG - Work on version 2.0 started
  26. <li>28/10/03 - EG - Creation of Version 1.0
  27. </ul></font>
  28. }
  29. unit RecyclerMM;
  30. interface
  31. {$OPTIMIZATION ON}
  32. {$STACKFRAMES OFF}
  33. {$WRITEABLECONST OFF}
  34. {$BOOLEVAL OFF}
  35. {$ifdef VER150} // of course it's "unsafe", so no warnings plz
  36. {$WARN UNSAFE_CODE OFF}
  37. {$WARN UNSAFE_TYPE OFF}
  38. {$endif}
  39. // No debug info, so the debugger won't step through memory management code
  40. {.$D-}
  41. uses Windows;
  42. // if set the RecyclerMM will automatically bind itself as default memory manager
  43. {$define AUTO_BIND}
  44. // If set, RecyclerMM will automatically locate and share memory with other
  45. // RecyclerMMs in DLL modules (same functionality as Borland's ShareMem unit).
  46. // Sharing will only happen with compatible RMMs.
  47. {.$define SHARE_MEM}
  48. // If set the (possible) BPLs won't be patched, only the jump table will
  49. {.$define NO_BPL_PATCHING}
  50. // If set SSE code for Move16/Clear16 will be allowed
  51. // Mileage on the efficiency of SSE over the FPU-based transfer may vary,
  52. // you may want to test it and figure out which is best in your case. Typically,
  53. // the FPU approach will be good for lots of small or scattered blocks on AMD
  54. // CPUs, while SSE shines on large blocks with a P4
  55. {$define ALLOW_SSE}
  56. // If set and exception will be explicitly raised if your code attempts
  57. // to release a block that isn't allocated. By default, RMM only detects
  58. // that issue reliably for large blocks and signals the issue to the Borland RTL,
  59. // which may then raise an exception. But in some circumstances, the RTL will
  60. // just ignore the issue. When the option is active, RMM will accurately detect
  61. // this issue all the time, and trigger an exception itself.
  62. // Doing so incurs a performance penalty on block release, and should preferably
  63. // only be used for testing or if memory integrity is of primary importance
  64. {$define RAISE_EXCEPTION_ON_INVALID_RELEASE}
  65. // Delayed release affects the management of the pool of free memory retained
  66. // by the manager. In delayed mode, 1/4th of the freed blocks are returned
  67. // to the OS every 250 ms, with by default up to 64 MB of memory whose release
  68. // is delayed. In non-delayed mode, the pool is fully retained all the time
  69. // (but the default pool size is much smaller at 8 MB)
  70. // This can improve performance as blocks will linger a bit before being
  71. // returned to the OS, but can temporarily increase memory consumption
  72. // (should be harmless most of the time, as the memory is still usable
  73. // by your application, just not usable for other applications within
  74. // a small delay).
  75. // This option is automatically turned off in DLLs
  76. {$define ALLOW_DELAYED_RELEASE}
  77. // If set usage of memory mapped files for large blocks will be allowed,
  78. // this can have a significant performance impact on frequently reallocated
  79. // large blocks, as it bypasses most of the copy on reallocation.
  80. // However, as it stresses the file system, it may exhibit performance side
  81. // effects if the application allocates a very large number of large blocks.
  82. // Note that memorymapping will only be used for reallocated blocks, there is
  83. // thus no penalty for statically allocated large blocks.
  84. {$define ALLOW_MEMORYMAPPED_LARGE_BLOCKS}
  85. // If set RMMUsageSnapShot functions will be available.
  86. // These functions allow generating a diagnostic and memory map report
  87. {.$define ALLOW_USAGE_SNAPSHOT}
  88. // Selection of the locking mechanim
  89. // If set, Windows CriticalSections will be used, otherwise it will be SpinLocks
  90. {.$define USE_CRITICAL_SECTIONS}
  91. // compile error when incompatible options have been selected
  92. {$ifdef SHARE_MEM}
  93. {$ifdef ALLOW_DELAYED_RELEASE}
  94. Error : you cannot combine ALLOW_DELAYED_RELEASE and SHARE_MEM (yet)
  95. {$endif}
  96. {$endif}
  97. const
  98. // Ratio for ReallocDownSizing (4 = downsizing will happen if only 1/4 used)
  99. cSMBReallocDownSizing = 4;
  100. cLGBReallocDownSizing = 4;
  101. // Ratio for upsizing (1 = allocate only what's needed, 2 = allocate twice the
  102. // needed space, etc. Must be >= 1.0 or things will go banana )
  103. cReallocUpSizing = 1.1;
  104. cReallocUpSizing256 = Word(Round(cReallocUpSizing*256)); // what's actualy used internally
  105. cReallocUpSizingLimit = Cardinal(1 shl 31) div cReallocUpSizing256;
  106. cReallocUpSizingLGBLimit= Round((1 shl 30)/cReallocUpSizing);
  107. cReallocUpSizingSMBPad = 48;
  108. cReallocMinSize = 64;
  109. // Size and Index limits for SMBs
  110. cSMBMaxSizeIndex = 49;
  111. cSMBSizes : packed array [0..cSMBMaxSizeIndex] of Word = (
  112. // 52 values from an exponential curve manually adjusted to "look nice" :)
  113. 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224,
  114. 240, 256, 288, 320, 384, 432, 496, 576, 656, 752, 864, 976, 1120,
  115. 1280, 1472, 1712, 2032, 2256, 2512, 2848, 3264, 3840, 4352, 4672, 5024,
  116. 5456, 5952, 6544, 7264, 8176, 9344, 10912, 13088, 16368, 21824, 32752 );
  117. // Maximum Size (bytes) of blocks managed by SMBs (max 64kB)
  118. cSMBMaxSize = 32752;
  119. // Size of chunks to retrieve from the OS
  120. cOSChunkSize = 1024*1024; // 640 kB should be enough for everyboy, no?
  121. cOSChunkRandomOffset = 4096; // max size of random offset
  122. cOSChunkItemSize = 65536;
  123. cOSChunkBlockCount = (cOSChunkSize-cOSChunkRandomOffset) div cOSChunkItemSize;
  124. // Amount of memory who's delayed release of the next seconds is tolerated
  125. cOSDelayedAllowedMemoryLatency = 8*1024*1024; // 8 MB
  126. cOSDelayedAllowedChunksLatency = cOSDelayedAllowedMemoryLatency div cOSChunkSize;
  127. {$ifdef ALLOW_MEMORYMAPPED_LARGE_BLOCKS}
  128. // minimal size of LGB before memory mapping mode is allowed to kick in
  129. cMemoryMappedLargeBlocksMinSize = 1024*1024;
  130. {$endif}
  131. type
  132. // TRMMStatus
  133. //
  134. TRMMStatus = (rmmsUnallocated, rmmsAllocated, rmmsReserved,
  135. rmmsSysAllocated, rmmsSysReserved);
  136. // TRMMMemoryMap
  137. //
  138. {: Describes a 64 kB range of the RMM memory use.<p>
  139. This structure isn't used by RMM itself, it's used to report the status
  140. of the memory allocation in RMMUsageSnapShot. }
  141. TRMMMemoryMap = packed record
  142. StartAddr : Pointer; // Start of address range
  143. Length : Cardinal; // Length of address range (bytes)
  144. AllocatedUserSize : Cardinal; // Bytes in range allocated by user
  145. Status : TRMMStatus; // Status of address range
  146. end;
  147. PRMMMemoryMap = ^TRMMMemoryMap;
  148. // TRMMSMBStat
  149. //
  150. TRMMSMBStat = packed record
  151. BlockSize : Cardinal;
  152. AllocatedBlocks : Cardinal;
  153. AllocatedUserSize : Cardinal;
  154. end;
  155. PRMMSMBStat = ^TRMMSMBStat;
  156. const
  157. // As a constant, we make our mem map big enough to support 3GB addressing
  158. // If you really need the extra 64 kB and know that your code will never be
  159. // run in /3GB mode then you can reduce this to 32767.
  160. // The actual limit of the memmap is in vMemMapUpper.
  161. cMemMapUpperMax = 49151;
  162. // Now the two we chose between to use as the actual limit within the array
  163. cMemMapUpper2GB = 32767;
  164. cMemMapUpper3GB = 49151;
  165. type
  166. // TRMMUsageBench
  167. //
  168. TRMMUsageBench = packed record
  169. TotalTime : Int64; // in CPU ticks!
  170. NbCalls : Cardinal;
  171. end;
  172. {$ifdef ALLOW_USAGE_SNAPSHOT}
  173. // TRMMUsageSnapShot
  174. //
  175. {: RMM usage diagnostic snapshot, returned by RMMUsageSnapShot. }
  176. TRMMUsageSnapShot = packed record
  177. // RMM Stats
  178. TotalVirtualAllocated : Cardinal;
  179. AllocatedBlocks : Cardinal;
  180. AllocatedUserSize : Cardinal;
  181. // Virtual Memory Stats
  182. TotalVMSpace : Cardinal;
  183. SystemAllocatedVM : Cardinal;
  184. SystemReservedVM : Cardinal;
  185. LargestFreeVM : Cardinal;
  186. // Map
  187. NbMapItems : Cardinal;
  188. Map : packed array [0..cMemMapUpperMax] of TRMMMemoryMap;
  189. SMBStats : packed array [0..cSMBMaxSizeIndex] of TRMMSMBStat;
  190. // Usage
  191. BenchRGetMem : TRMMUsageBench;
  192. BenchRReallocMem : TRMMUsageBench;
  193. BenchRFreeMem : TRMMUsageBench;
  194. end;
  195. PRMMUsageSnapShot = ^TRMMUsageSnapShot;
  196. {$endif}
  197. {: Fast 16 bytes-based move.<p>
  198. Copies blocks of 16 bytes only, ie. Count is rounded up to the nearest
  199. multiple of 16. Overlapping source/destination are not handled. }
  200. var Move16 : procedure (const Source; var Dest; Count: Integer); register;
  201. {: Fills an area whose size is a multiple of 16-bytes with zeros.<p>
  202. Count is rounded up to the nearest multiple of 16 }
  203. var MemClear16 : procedure (const Buffer; Count: Integer); register;
  204. // Direct access functions - only for single .EXE with no RMM DLLs
  205. function RGetMem(Size : Integer) : Pointer;
  206. function RAllocMem(Size : Cardinal) : Pointer;
  207. function RFreeMem(P : Pointer) : Integer;
  208. function RReallocMem(P : Pointer; Size : Cardinal) : Pointer;
  209. function RAllocated(const P : Pointer) : Boolean;
  210. {: True if P points to the beginning of an allocated block.<p> }
  211. var Allocated : function (const P : Pointer) : Boolean;
  212. {: Generates a memory map of RMM memory usage.<p>
  213. While the map is generated, all RMM activity is freezed. }
  214. {$ifdef ALLOW_USAGE_SNAPSHOT}
  215. function RMMUsageSnapShot : TRMMUsageSnapShot; overload;
  216. procedure RMMUsageSnapShot(var result : TRMMUsageSnapShot); overload;
  217. {$endif}
  218. procedure BindRMM;
  219. procedure UnBindRMM;
  220. function RMMActive : Boolean;
  221. procedure InitializeRMM;
  222. procedure FinalizeRMM;
  223. function RunningIn3GBMode : Boolean;
  224. var
  225. // Number of entries in memmap array
  226. vMemMapUpper : Cardinal;
  227. // Virtual memory limit (used for SECURE_MEMMAP)
  228. vVirtualLimit : Cardinal;
  229. const
  230. // Unused, this is just to have it in clear in the DCU
  231. cRecyclerMMCopyright = 'RecyclerMM - ©2005 Creative IT';
  232. // ------------------------------------------------------------------
  233. // ------------------------------------------------------------------
  234. // ------------------------------------------------------------------
  235. implementation
  236. // ------------------------------------------------------------------
  237. // ------------------------------------------------------------------
  238. // ------------------------------------------------------------------
  239. const
  240. cMAX_PATH = 512;
  241. cBAADFOOD = $BAADF00D;
  242. type
  243. PPointer = ^Pointer;
  244. TPointerArrayMap = packed array [0..cMemMapUpperMax] of Pointer;
  245. TWordArray = packed array [0..MaxInt shr 2] of Word;
  246. PWordArray = ^TWordArray;
  247. TCardinalArray = packed array [0..MaxInt shr 3] of Cardinal;
  248. PCardinalArray = ^TCardinalArray;
  249. PSMBManager = ^TSMBManager;
  250. POSChunk = ^TOSChunk;
  251. {$ifdef USE_CRITICAL_SECTIONS}
  252. TCSLock = TRTLCriticalSection;
  253. {$else}
  254. TCSLock = LongBool;
  255. {$endif}
  256. PCSLock = ^TCSLock;
  257. TMemoryRange = packed record
  258. Start : Pointer;
  259. Length : Cardinal;
  260. end;
  261. // TSMBLinkedList
  262. //
  263. TSMBLinkedList = packed record
  264. First, Last : PSMBManager;
  265. end;
  266. // TSMBInfo
  267. //
  268. {: SmallBlock management info for a given size.<p> }
  269. TSMBInfo = packed record
  270. CSLock : TCSLock;
  271. FreeSMBs : TSMBLinkedList;
  272. FullSMBs : TSMBLinkedList;
  273. Size : Cardinal;
  274. BlocksPerSMB : Cardinal;
  275. DownSizingSize : Cardinal;
  276. end;
  277. PSMBInfo = ^TSMBInfo;
  278. // TSMBManager
  279. //
  280. {: Manages a Small Blocks chunk.<p>
  281. Small blocks manage many user blocks of constant (BlockSize) size,
  282. which are allocated/freed in a stack-like fashion. }
  283. TSMBManager = packed record
  284. SMBInfo : PSMBInfo; // pointer to the SMBInfo (size related)
  285. NbFreeBlocks : Cardinal;
  286. FirstFreedBlock : Pointer;
  287. Next, Prev : PSMBManager; // pointer to the next/prev managers
  288. MaxNbFreeBlocks : Cardinal;
  289. BlockSize : Cardinal; // Size of blocks in SMB
  290. BlockStart : Pointer; // base address for SMB blocks
  291. DownSizingSize : Cardinal;
  292. NextNonAllocatedBlkID : Pointer;
  293. Padding : packed array [1..6] of Cardinal;
  294. end;
  295. // TLGBManager
  296. //
  297. {: Manages a Large Block.<p>
  298. LGBs each manage a single user-allocated block. They are allowed to
  299. reserve address space (to improve the chances of in-place growth). }
  300. PLGBManager = ^TLGBManager;
  301. TLGBManager = record
  302. BlockSize : Cardinal; // Total allocated size for the block
  303. DataStart : Pointer; // Start of user data
  304. DataSize : Cardinal; // Size requested by the user
  305. MaxDataSize : Cardinal; // Maximum size without reallocation
  306. Next, Prev : PLGBManager;
  307. hFile, hMapping : Cardinal;// handles for memory mapping
  308. end;
  309. // TOSChunk
  310. //
  311. {: A range of heap-managed SMB or small LGB space }
  312. TOSChunk = packed record
  313. Prev, Next : POSChunk;
  314. FreeBlocks : Integer;
  315. Full : LongBool;
  316. FirstBlock : Cardinal;
  317. Manager : packed array [0..cOSChunkBlockCount-1] of TSMBManager;
  318. end;
  319. // TSharedMemoryManager
  320. //
  321. {: Extends TMemoryManager to accomodate RMM functions.<p>
  322. This structure is what RMMs cross-refer when sharing memory. }
  323. TSharedMemoryManager = record
  324. MemoryManager : TMemoryManager;
  325. Allocated : function(const P : Pointer) : Boolean;
  326. {$ifdef ALLOW_USAGE_SNAPSHOT}
  327. RMMUsageSnapShot : function : TRMMUsageSnapShot;
  328. {$endif}
  329. end;
  330. PSharedMemoryManager = ^TSharedMemoryManager;
  331. var
  332. // Only the lower 2 or 3 GB are accessible to an application under Win32,
  333. // that's a maximum of 32768 or 49152 blocks which are all mapped by a 128/192 kB array
  334. vRunningIn3GBMode : Boolean;
  335. {$ifdef ALLOW_DELAYED_RELEASE}
  336. // ID of the cleanup thread
  337. vCleanupThreadID : Cardinal;
  338. vCleanupThreadHnd : Cardinal;
  339. vCleanupThreadEvent : Cardinal;
  340. {$endif}
  341. vMemoryMap : TPointerArrayMap;
  342. // Binding variables
  343. vOldMemoryManager : TMemoryManager;
  344. vRMMBound : Integer;
  345. {$ifdef ALLOW_SSE}
  346. vSSESupported : Integer;
  347. {$endif}
  348. // Shared memory variables
  349. vSharedMemoryManager : TSharedMemoryManager;
  350. {$ifdef SHARE_MEM}
  351. vSharedMemory_Data : HWND;
  352. vSharedMemory_DataName : ShortString = '########-RecyclerMM-100'#0;
  353. vSharedMemory_InUse : Boolean;
  354. {$endif}
  355. // Chunks pool
  356. vOSChunksLock : TCSLock;
  357. vOSChunksFirst : POSChunk;
  358. vOSChunksFirstFull : POSChunk;
  359. {$ifdef ALLOW_DELAYED_RELEASE}
  360. vOSChunkNbEntirelyFree : Integer;
  361. {$endif}
  362. // SMB information array by size class (index)
  363. vSMBs : array [0..cSMBMaxSizeIndex] of TSMBInfo;
  364. vSMBSizeToPSMBInfo : packed array [0..(cSMBMaxSize-1) shr 4] of Byte;
  365. // Large blocks are just chained
  366. vLGBManagers : PLGBManager;
  367. vLGBLock : TCSLock;
  368. // Temporary path for memorymapped temp files
  369. vTemporaryFilesPath : array [0..cMAX_PATH] of Char;
  370. // RunningIn3GBMode
  371. //
  372. function RunningIn3GBMode : Boolean;
  373. begin
  374. Result:=vRunningIn3GBMode;
  375. end;
  376. // SwitchToThread logic
  377. //
  378. var vSwitchToThread : procedure; stdcall;
  379. procedure Win9xSwitchToThread; stdcall;
  380. begin
  381. Sleep(0);
  382. end;
  383. procedure InitializeSwitchToThread;
  384. var
  385. hLib : Cardinal;
  386. begin
  387. hLib:=LoadLibrary('Kernel32.dll');
  388. vSwitchToThread:=GetProcAddress(hLib, 'SwitchToThread');
  389. FreeLibrary(hLib);
  390. if not Assigned(vSwitchToThread) then
  391. vSwitchToThread:=@Win9xSwitchToThread;
  392. end;
  393. // RaiseInvalidPtrError
  394. //
  395. procedure RaiseInvalidPtrError;
  396. begin
  397. RunError(204); // Invalid pointer operation
  398. end;
  399. // InitializeCSLock
  400. //
  401. procedure InitializeCSLock(var csLock : TCSLock);
  402. begin
  403. {$ifdef USE_CRITICAL_SECTIONS}
  404. InitializeCriticalSection(csLock);
  405. {$else}
  406. csLock:=False;
  407. {$endif}
  408. end;
  409. // DeleteCSLock
  410. //
  411. procedure DeleteCSLock(var csLock : TCSLock);
  412. begin
  413. {$ifdef USE_CRITICAL_SECTIONS}
  414. DeleteCriticalSection(csLock);
  415. {$endif}
  416. end;
  417. // LockCmpxchg
  418. //
  419. function LockCmpXchg(compareVal, newVal : Byte; anAddress : PByte) : Byte;
  420. // AL = compareVal, dl = newVal, ecx = anAddress
  421. asm
  422. lock cmpxchg [ecx], dl
  423. end;
  424. // CSLockEnter
  425. //
  426. procedure CSLockEnter(var csLock : TCSLock);
  427. {$ifdef USE_CRITICAL_SECTIONS}
  428. begin
  429. if IsMultiThread then
  430. EnterCriticalSection(csLock); //}
  431. {$else}
  432. begin
  433. if IsMultiThread then begin
  434. while LockCmpxchg(0, 1, @csLock)<>0 do begin
  435. vSwitchToThread;
  436. if LockCmpxchg(0, 1, @csLock)=0 then
  437. Break;
  438. Windows.Sleep(10);
  439. end;
  440. end; // }
  441. {asm
  442. cmp byte ptr [IsMultiThread], 0
  443. jz @@LockDone
  444. mov ecx, eax
  445. xor eax, eax
  446. mov dl, 1
  447. lock cmpxchg [ecx], dl
  448. jz @@LockDone
  449. push ebx
  450. mov ebx, ecx
  451. call [vSwitchToThread]
  452. @@LockLoop:
  453. xor eax, eax
  454. mov dl, 1
  455. lock cmpxchg [ebx], dl
  456. jz @@LockEntered
  457. push 10
  458. call Windows.Sleep
  459. jmp @@LockLoop
  460. @@LockEntered:
  461. pop ebx
  462. @@LockDone: //}
  463. {$endif}
  464. end;
  465. // CSLockTryEnter
  466. //
  467. function CSLockTryEnter(var csLock : TCSLock) : Boolean;
  468. begin
  469. {$ifdef USE_CRITICAL_SECTIONS}
  470. Result:=(not IsMultiThread) or (TryEnterCriticalSection(csLock));
  471. {$else}
  472. Result:=(not IsMultiThread) or (LockCmpxchg(0, 1, @csLock)=0);
  473. {$endif}
  474. end;
  475. // CSLockLeave
  476. //
  477. procedure CSLockLeave(var csLock : TCSLock);
  478. begin
  479. {$ifdef USE_CRITICAL_SECTIONS}
  480. if IsMultiThread then
  481. LeaveCriticalSection(csLock);
  482. {$else}
  483. csLock:=False;
  484. {$endif}
  485. end;
  486. // MMRandom
  487. //
  488. var vRandomLast : Cardinal;
  489. function MMRandom : Integer;
  490. begin
  491. vRandomLast:=(3877*vRandomLast+29573) mod 139968;
  492. Result:=vRandomLast xor (vRandomLast shr 8);
  493. end;
  494. // UpdateMemoryMap
  495. //
  496. procedure UpdateMemoryMap(baseAddr : Pointer; size : Cardinal; manager : Pointer);
  497. var
  498. i : Cardinal;
  499. begin
  500. for i:=(Cardinal(baseAddr) shr 16) to ((Cardinal(baseAddr)+size-1) shr 16) do
  501. vMemoryMap[i]:=manager;
  502. end;
  503. // CreateTemporaryFileAndMapping
  504. // returns False if failed
  505. function CreateTemporaryFile(var hFile : Cardinal) : Boolean;
  506. var
  507. tempFileName : array [0..cMAX_PATH] of Char;
  508. begin
  509. GetTempFileName(@vTemporaryFilesPath[0], 'RMM', 0, @tempFileName[0]);
  510. hFile:=Windows.CreateFile(@tempFileName[0], GENERIC_READ or GENERIC_WRITE,
  511. 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY or FILE_FLAG_DELETE_ON_CLOSE, 0);
  512. Result:=(hFile<>INVALID_HANDLE_VALUE);
  513. end;
  514. // SetTemporaryFileSizeAndRemap
  515. // returns pointer to mapped space, nil if failed
  516. function SetTemporaryFileSizeAndMap(const hFile, newSize : Cardinal;
  517. var hMapping : Cardinal) : Pointer;
  518. begin
  519. Result:=nil;
  520. SetFilePointer(hFile, newSize, nil, FILE_BEGIN);
  521. if SetEndOfFile(hFile) then begin
  522. hMapping:=CreateFileMapping(hFile, nil, PAGE_READWRITE, 0, 0, nil);
  523. if (hMapping<>0) and (hMapping<>ERROR_ALREADY_EXISTS) then begin
  524. Result:=MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, newSize);
  525. if Result=nil then begin
  526. CloseHandle(hMapping);
  527. hMapping:=0;
  528. end;
  529. end;
  530. end;
  531. end;
  532. // CutOutChunk
  533. //
  534. procedure CutOutChunk(chunk : POSChunk);
  535. begin
  536. if chunk.Full then begin
  537. // cut from full
  538. if chunk.Prev=nil then
  539. vOSChunksFirstFull:=chunk.Next
  540. else chunk.Prev.Next:=chunk.Next;
  541. if chunk.Next<>nil then
  542. chunk.Next.Prev:=chunk.Prev;
  543. end else begin
  544. // cut from non-full
  545. if chunk.Prev=nil then
  546. vOSChunksFirst:=chunk.Next
  547. else chunk.Prev.Next:=chunk.Next;
  548. if chunk.Next<>nil then
  549. chunk.Next.Prev:=chunk.Prev;
  550. end;
  551. end;
  552. // DestroyChunk
  553. //
  554. procedure DestroyChunk(chunk : POSChunk);
  555. begin
  556. if chunk.Full then RunError(103);
  557. if chunk.FreeBlocks<>cOSChunkBlockCount then RunError(104);
  558. chunk:=Pointer(Cardinal(chunk) and $FFFF0000);
  559. UpdateMemoryMap(chunk, cOSChunkSize, nil);
  560. VirtualFree(chunk, 0, MEM_RELEASE);
  561. end;
  562. // RMMAllocChunkItem
  563. //
  564. function RMMAllocChunkItem : PSMBManager;
  565. var
  566. chunk : POSChunk;
  567. chunkRandomOffset : Cardinal;
  568. i : Integer;
  569. alignedAddress : Cardinal;
  570. begin
  571. CSLockEnter(vOSChunksLock);
  572. chunk:=vOSChunksFirst;
  573. if chunk=nil then begin
  574. // all chunks full, allocate a new one
  575. chunk:=VirtualAlloc(nil, cOSChunkSize, MEM_COMMIT, PAGE_READWRITE);
  576. if chunk=nil then begin
  577. Result:=nil;
  578. Exit;
  579. end;
  580. // randomize chunk location by up to 4 kB
  581. chunkRandomOffset:=(((cOSChunkRandomOffset div 16)-1) and MMRandom)*16;
  582. while chunkRandomOffset+SizeOf(TOSChunk)>4096 do
  583. Dec(chunkRandomOffset, 16);
  584. // update MM and initialize chunk structure
  585. UpdateMemoryMap(chunk, cOSChunkSize, Pointer(Cardinal(chunk)+chunkRandomOffset));
  586. Inc(Cardinal(chunk), chunkRandomOffset);
  587. chunk.FreeBlocks:=cOSChunkBlockCount;
  588. alignedAddress:=(Cardinal(chunk)+SizeOf(TOSChunk)+15) and $FFFFFFF0;
  589. chunk.FirstBlock:=alignedAddress;
  590. for i:=0 to cOSChunkBlockCount-1 do
  591. chunk.Manager[i].BlockStart:=nil;
  592. // place in linked list
  593. chunk.Prev:=nil;
  594. chunk.Next:=vOSChunksFirst;
  595. if vOSChunksFirst<>nil then
  596. vOSChunksFirst.Prev:=chunk;
  597. vOSChunksFirst:=chunk;
  598. end;
  599. i:=0;
  600. while chunk.Manager[i].BlockStart<>nil do Inc(i);
  601. Result:[email protected][i];
  602. Result.BlockStart:=Pointer(chunk.FirstBlock+Cardinal(i)*cOSChunkItemSize);
  603. Dec(chunk.FreeBlocks);
  604. // if we filled this one up, move it to the full chunks
  605. if chunk.FreeBlocks=0 then begin
  606. // cut from non-full
  607. CutOutChunk(chunk);
  608. // paste to full
  609. chunk.Full:=True;
  610. chunk.Next:=vOSChunksFirstFull;
  611. if vOSChunksFirstFull<>nil then
  612. vOSChunksFirstFull.Prev:=chunk;
  613. vOSChunksFirstFull:=chunk;
  614. chunk.Prev:=nil;
  615. end;
  616. CSLockLeave(vOSChunksLock);
  617. end;
  618. // RMMVirtualFreeChunkItem
  619. //
  620. procedure RMMVirtualFreeChunkItem(p : Pointer);
  621. var
  622. i : Integer;
  623. chunk : POSChunk;
  624. begin
  625. CSLockEnter(vOSChunksLock);
  626. // identify the chunk for the pointer
  627. chunk:=POSChunk(vMemoryMap[Cardinal(p) shr 16]);
  628. if (Cardinal(chunk) and 1)<>0 then
  629. RaiseInvalidPtrError;
  630. chunk:=POSChunk(Cardinal(Chunk) and $FFFFFFF0);
  631. // release
  632. i:=(Cardinal(p)-chunk.FirstBlock) div cOSChunkItemSize;
  633. chunk.Manager[i].BlockStart:=nil;
  634. Inc(chunk.FreeBlocks);
  635. if chunk.Full then begin
  636. // we're no longer full, cut from full
  637. CutOutChunk(chunk);
  638. // paste to non-full
  639. chunk.Full:=False;
  640. chunk.Next:=vOSChunksFirst;
  641. if vOSChunksFirst<>nil then
  642. vOSChunksFirst.Prev:=chunk;
  643. vOSChunksFirst:=chunk;
  644. chunk.Prev:=nil;
  645. end;
  646. // if completely freed and not the only chunk, cleanup
  647. if (chunk.FreeBlocks=cOSChunkBlockCount) then begin
  648. {$ifndef ALLOW_DELAYED_RELEASE}
  649. if (chunk.Prev<>nil) or (chunk.Next<>nil) then begin
  650. CutOutChunk(chunk);
  651. DestroyChunk(chunk);
  652. end;
  653. {$else}
  654. Inc(vOSChunkNbEntirelyFree);
  655. if vOSChunkNbEntirelyFree>cOSDelayedAllowedChunksLatency then begin
  656. if vCleanupThreadID<>0 then
  657. SetEvent(vCleanupThreadEvent);
  658. end;
  659. {$endif}
  660. end;
  661. CSLockLeave(vOSChunksLock);
  662. end;
  663. // RMMVirtualAlloc
  664. //
  665. function RMMVirtualAlloc(const blkSize : Cardinal) : Pointer;
  666. begin
  667. Result:=VirtualAlloc(nil, blkSize, MEM_COMMIT+MEM_TOP_DOWN, PAGE_READWRITE);
  668. end;
  669. // RMMVirtualFree
  670. //
  671. procedure RMMVirtualFree(p : Pointer; const blkSize : Cardinal);
  672. begin
  673. VirtualFree(p, 0, MEM_RELEASE);
  674. end;
  675. // ComputeLGBBlockSize
  676. //
  677. function ComputeLGBBlockSize(dataSize : Cardinal) : Cardinal;
  678. var
  679. baseOffset : Cardinal;
  680. begin
  681. baseOffset:=(SizeOf(TLGBManager)+15) and $FFFFFFF0;
  682. Result:=((dataSize+baseOffset+$FFFF) and $FFFF0000);
  683. end;
  684. // ComputeLGBDataStart
  685. //
  686. function ComputeLGBDataStart(p : Pointer; blockSize, dataSize : Cardinal) : Pointer;
  687. var
  688. baseOffset, margin, randomOffset, test : Cardinal;
  689. begin
  690. baseOffset:=(SizeOf(TLGBManager)+15) and $FFFFFFF0;
  691. margin:=(blockSize-baseOffset-dataSize);
  692. if margin>$2000 then margin:=$2000; // 8 kB max
  693. test:=0;
  694. repeat
  695. randomOffset:=test;
  696. test:=(test shl 1)+16;
  697. until test>margin;
  698. randomOffset:=randomOffset and MMRandom;
  699. Result:=Pointer(Cardinal(P)+baseOffset+randomOffset);
  700. end;
  701. // AllocateLGB
  702. //
  703. function AllocateLGB(Size : Cardinal) : Pointer;
  704. var
  705. blkSize : Cardinal;
  706. lgbManager : PLGBManager;
  707. begin
  708. blkSize:=ComputeLGBBlockSize(Size);
  709. // Spawn manager, allocate block
  710. lgbManager:=RMMVirtualAlloc(blkSize);
  711. if lgbManager=nil then
  712. Result:=nil
  713. else begin
  714. lgbManager.hFile:=0;
  715. lgbManager.DataSize:=Size;
  716. lgbManager.BlockSize:=blkSize;
  717. lgbManager.DataStart:=ComputeLGBDataStart(lgbManager, blkSize, Size);
  718. lgbManager.MaxDataSize:=blkSize-(Cardinal(lgbManager.DataStart)-Cardinal(lgbManager));
  719. // Add in the LGB linked list
  720. CSLockEnter(vLGBLock);
  721. if vLGBManagers<>nil then
  722. vLGBManagers.Prev:=lgbManager;
  723. lgbManager.Next:=vLGBManagers;
  724. lgbManager.Prev:=nil;
  725. vLGBManagers:=lgbManager;
  726. CSLockLeave(vLGBLock);
  727. UpdateMemoryMap(lgbManager, lgbManager.BlockSize, Pointer(Cardinal(lgbManager)+1));
  728. Result:=lgbManager.DataStart;
  729. end;
  730. end;
  731. // ReleaseLGB
  732. //
  733. procedure ReleaseLGB(aManager : PLGBManager);
  734. var
  735. manager : TLGBManager; // local copy
  736. begin
  737. UpdateMemoryMap(aManager, aManager.BlockSize, nil);
  738. manager:=aManager^;
  739. // Remove from LGB linked list
  740. CSLockEnter(vLGBLock);
  741. if manager.Prev=nil then
  742. vLGBManagers:=manager.Next
  743. else manager.Prev.Next:=manager.Next;
  744. if manager.Next<>nil then
  745. manager.Next.Prev:=manager.Prev;
  746. CSLockLeave(vLGBLock);
  747. // Free block
  748. {$ifdef ALLOW_MEMORYMAPPED_LARGE_BLOCKS}
  749. if aManager.hFile<>0 then begin
  750. UnmapViewOfFile(aManager);
  751. CloseHandle(manager.hMapping);
  752. CloseHandle(manager.hFile);
  753. end else RMMVirtualFree(aManager, manager.BlockSize);
  754. {$else}
  755. RMMVirtualFree(aManager, manager.BlockSize);
  756. {$endif}
  757. end;
  758. // ReallocateLGB
  759. //
  760. function ReallocateLGB(oldManager : PLGBManager; newSize : Cardinal) : PLGBManager;
  761. var
  762. blkSize, oldDataOffset, copySize : Cardinal;
  763. newManager : PLGBManager;
  764. hFile, hMapping : Cardinal;
  765. needDataTransfer : Boolean;
  766. begin
  767. if (newSize>oldManager.DataSize) and (newSize<cReallocUpSizingLGBLimit) then
  768. newSize:=Round(newSize*cReallocUpSizing);
  769. blkSize:=ComputeLGBBlockSize(newSize);
  770. hFile:=oldManager.hFile;
  771. hMapping:=oldManager.hMapping;
  772. newManager:=nil;
  773. needDataTransfer:=True;
  774. CSLockEnter(vLGBLock);
  775. {$ifdef ALLOW_MEMORYMAPPED_LARGE_BLOCKS}
  776. if hFile<>0 then begin
  777. oldDataOffset:=Cardinal(oldManager.DataStart)-Cardinal(oldManager);
  778. UpdateMemoryMap(oldManager, oldManager.BlockSize, nil);
  779. UnmapViewOfFile(oldManager);
  780. CloseHandle(hMapping);
  781. newManager:=SetTemporaryFileSizeAndMap(hFile, blkSize, hMapping);
  782. if newManager<>nil then begin
  783. newManager.DataStart:=Pointer(Cardinal(newManager)+oldDataOffset);
  784. needDataTransfer:=False;
  785. end;
  786. end;
  787. {$endif}
  788. if hFile=0 then begin
  789. {$ifdef ALLOW_MEMORYMAPPED_LARGE_BLOCKS}
  790. if newSize>cMemoryMappedLargeBlocksMinSize then begin
  791. // promote to memory-mapped
  792. if CreateTemporaryFile(hFile) then begin
  793. newManager:=SetTemporaryFileSizeAndMap(hFile, blkSize, hMapping);
  794. if newManager=nil then begin
  795. CloseHandle(hFile);
  796. hFile:=0;
  797. end;
  798. end else hFile:=0;
  799. end;
  800. {$endif}
  801. if newManager=nil then
  802. newManager:=RMMVirtualAlloc(blkSize);
  803. newManager.Next:=oldManager.Next;
  804. newManager.Prev:=oldManager.Prev;
  805. end;
  806. if newManager<>nil then begin
  807. if newManager.Prev<>nil then
  808. newManager.Prev.Next:=newManager
  809. else vLGBManagers:=newManager;
  810. if newManager.Next<>nil then
  811. newManager.Next.Prev:=newManager;
  812. end;
  813. CSLockLeave(vLGBLock);
  814. if Assigned(newManager) then begin
  815. if needDataTransfer then begin
  816. newManager.DataStart:=ComputeLGBDataStart(newManager, blkSize, newSize);
  817. copySize:=oldManager.DataSize;
  818. if copySize>newSize then
  819. copySize:=newSize;
  820. Move16(oldManager.DataStart^, newManager.DataStart^, copySize);
  821. UpdateMemoryMap(oldManager, oldManager.BlockSize, nil);
  822. RMMVirtualFree(oldManager, oldManager.BlockSize);
  823. end;
  824. newManager.hFile:=hFile;
  825. newManager.hMapping:=hMapping;
  826. newManager.BlockSize:=blkSize;
  827. newManager.DataSize:=newSize;
  828. newManager.MaxDataSize:=blkSize-(Cardinal(newManager.DataStart)-Cardinal(newManager));
  829. UpdateMemoryMap(newManager, blkSize, Pointer(Cardinal(newManager)+1));
  830. Result:=newManager;
  831. end else Result:=nil;
  832. end;
  833. // Move16SSE
  834. //
  835. procedure Move16SSE(const Source; var Dest; Count: Integer); register;
  836. // eax : Source
  837. // edx : Dest
  838. // ecx : Count
  839. asm
  840. or ecx, ecx
  841. jz @@End
  842. @@Copy:
  843. add ecx, 15 // round up ecx (Count) to 16
  844. and cl, $F0
  845. lea eax, [eax+ecx]
  846. lea edx, [edx+ecx]
  847. neg ecx
  848. test ecx, 16
  849. jz @@Batch32
  850. db $0F,$6F,$24,$08 /// movq mm4, [eax+ecx]
  851. db $0F,$6F,$6C,$08,$08 /// movq mm5, [eax+ecx+8]
  852. db $0F,$7F,$24,$0A /// movq [edx+ecx], mm4
  853. db $0F,$7F,$6C,$0A,$08 /// movq [edx+ecx+8], mm5
  854. add ecx, 16
  855. jnz @@Batch32
  856. emms
  857. ret
  858. @@Batch32:
  859. cmp ecx, -2*1024 // beyond 2 kb, use uncached transfer
  860. jl @@HugeLoop
  861. @@Loop:
  862. db $0F,$6F,$04,$08 /// movq mm0, [eax+ecx]
  863. db $0F,$6F,$4C,$08,$08 /// movq mm1, [eax+ecx+8]
  864. db $0F,$6F,$54,$08,$10 /// movq mm2, [eax+ecx+16]
  865. db $0F,$6F,$5C,$08,$18 /// movq mm3, [eax+ecx+24]
  866. db $0F,$7F,$04,$0A /// movq [edx+ecx], mm0
  867. db $0F,$7F,$4C,$0A,$08 /// movq [edx+ecx+8], mm1
  868. db $0F,$7F,$54,$0A,$10 /// movq [edx+ecx+16], mm2
  869. db $0F,$7F,$5C,$0A,$18 /// movq [edx+ecx+24], mm3
  870. add ecx, 32
  871. jnz @@Loop
  872. emms
  873. ret
  874. @@HugeLoop:
  875. db $0F,$6F,$04,$08 /// movq mm0, [eax+ecx]
  876. db $0F,$6F,$4C,$08,$08 /// movq mm1, [eax+ecx+8]
  877. db $0F,$6F,$54,$08,$10 /// movq mm2, [eax+ecx+16]
  878. db $0F,$6F,$5C,$08,$18 /// movq mm3, [eax+ecx+24]
  879. db $0F,$E7,$04,$0A /// movntq [edx+ecx], mm0
  880. db $0F,$E7,$4C,$0A,$08 /// movntq [edx+ecx+8], mm1
  881. db $0F,$E7,$54,$0A,$10 /// movntq [edx+ecx+16], mm2
  882. db $0F,$E7,$5C,$0A,$18 /// movntq [edx+ecx+24], mm3
  883. add ecx, 32
  884. jnz @@HugeLoop
  885. emms
  886. @@End:
  887. end;
  888. // MemClear16SSE
  889. //
  890. procedure MemClear16SSE(var Buffer; Count: Integer); register;
  891. asm
  892. or edx, edx
  893. jz @@End
  894. @@Copy:
  895. // round to 16
  896. add edx, 15
  897. and dl, $F0
  898. lea eax, [eax+edx]
  899. db $0F,$EF,$C0 /// pxor mm0, mm0
  900. neg edx
  901. test edx, 16
  902. jz @@Loop
  903. db $0F,$E7,$04,$10 /// movntq [eax+edx], mm0
  904. db $0F,$E7,$44,$10,$08 /// movntq [eax+edx+8], mm0
  905. add edx, 16
  906. jz @@End
  907. @@Loop:
  908. db $0F,$E7,$04,$10 /// movntq [eax+edx], mm0
  909. db $0F,$E7,$44,$10,$08 /// movntq [eax+edx+8], mm0
  910. db $0F,$E7,$44,$10,$10 /// movntq [eax+edx+16], mm0
  911. db $0F,$E7,$44,$10,$18 /// movntq [eax+edx+24], mm0
  912. add edx, 32
  913. jnz @@Loop
  914. @@End:
  915. db $0F,$77 /// emms
  916. end;
  917. // MemClear16RTL
  918. //
  919. procedure MemClear16RTL(var Buffer; Count: Integer);
  920. begin
  921. FillChar(Buffer, Count, 0);
  922. end;
  923. // SMBLinkedListInsertFirst
  924. //
  925. procedure SMBLinkedListInsertFirst(var linkedList : TSMBLinkedList; item : PSMBManager);
  926. begin
  927. if linkedList.First=nil then begin
  928. linkedList.First:=item;
  929. linkedList.Last:=item;
  930. item.Next:=nil;
  931. end else begin
  932. item.Next:=linkedList.First;
  933. linkedList.First.Prev:=item;
  934. linkedList.First:=item;
  935. end;
  936. item.Prev:=nil;
  937. end;
  938. // SMBLinkedListInsertLast
  939. //
  940. procedure SMBLinkedListInsertLast(var linkedList : TSMBLinkedList; item : PSMBManager);
  941. begin
  942. if linkedList.First=nil then begin
  943. linkedList.First:=item;
  944. linkedList.Last:=item;
  945. item.Prev:=nil;
  946. end else begin
  947. item.Prev:=linkedList.Last;
  948. linkedList.Last.Next:=item;
  949. linkedList.Last:=item;
  950. end;
  951. item.Next:=nil;
  952. end;
  953. // SMBLinkedListCut
  954. //
  955. procedure SMBLinkedListCut(var linkedList : TSMBLinkedList; item : PSMBManager);
  956. begin
  957. if item.Prev=nil then
  958. linkedList.First:=item.Next
  959. else item.Prev.Next:=item.Next;
  960. if item.Next=nil then
  961. linkedList.Last:=item.Prev
  962. else item.Next.Prev:=item.Prev;
  963. end;
  964. // AllocateSMB
  965. //
  966. function AllocateSMB(smbInfo : PSMBInfo) : PSMBManager;
  967. var
  968. n : Cardinal;
  969. begin
  970. // Determine ChunkSize
  971. n:=smbInfo.BlocksPerSMB;
  972. Result:=RMMAllocChunkItem;
  973. if Result=nil then Exit;
  974. Result.MaxNbFreeBlocks:=n;
  975. Result.NbFreeBlocks:=n;
  976. Result.SMBInfo:=smbInfo;
  977. Result.BlockSize:=smbInfo.Size;
  978. Result.DownSizingSize:=smbInfo.DownSizingSize;
  979. // prepare block offsets stack
  980. Result.NextNonAllocatedBlkID:=Result.BlockStart;
  981. Result.FirstFreedBlock:=nil;
  982. SMBLinkedListInsertFirst(smbInfo.FreeSMBs, Result);
  983. end;
  984. // ReleaseSMB
  985. //
  986. procedure ReleaseSMB(manager : PSMBManager);
  987. begin
  988. if manager.NbFreeBlocks>0 then
  989. SMBLinkedListCut(manager.SMBInfo.FreeSMBs, manager)
  990. else SMBLinkedListCut(manager.SMBInfo.FullSMBs, manager);
  991. RMMVirtualFreeChunkItem(manager.BlockStart);
  992. end;
  993. // RGetMem
  994. //
  995. function RGetMem(Size: Integer): Pointer;
  996. procedure MoveManagerToFull(manager : PSMBManager);
  997. begin
  998. SMBLinkedListCut(manager.SMBInfo.FreeSMBs, manager);
  999. SMBLinkedListInsertFirst(manager.SMBInfo.FullSMBs, manager);
  1000. end;
  1001. function AllocFromNewManager(smbInfo : PSMBInfo) : Pointer;
  1002. var
  1003. manager : PSMBManager;
  1004. begin
  1005. manager:=AllocateSMB(smbInfo);
  1006. if manager=nil then begin
  1007. Result:=nil;
  1008. end else begin
  1009. Dec(manager.NbFreeBlocks);
  1010. Result:=manager.NextNonAllocatedBlkID;
  1011. Inc(Cardinal(manager.NextNonAllocatedBlkID), manager.BlockSize);
  1012. end;
  1013. CSLockLeave(smbInfo.CSLock);
  1014. end;
  1015. var
  1016. manager : PSMBManager;
  1017. smbInfo : PSMBInfo;
  1018. begin
  1019. if Size>cSMBMaxSize then begin
  1020. // Large blocks
  1021. Result:=AllocateLGB(Size);
  1022. end else begin
  1023. // Small Blocks logic
  1024. smbInfo:=@vSMBs[vSMBSizeToPSMBInfo[(Size-1) shr 4]];
  1025. CSLockEnter(smbInfo.CSLock);
  1026. manager:=smbInfo.FreeSMBs.First;
  1027. if manager=nil then begin
  1028. // allocate block from new manager
  1029. Result:=AllocFromNewManager(smbInfo);
  1030. end else begin
  1031. // allocate from existing manager
  1032. if manager.NbFreeBlocks=1 then
  1033. MoveManagerToFull(manager);
  1034. Dec(manager.NbFreeBlocks);
  1035. Result:=manager.FirstFreedBlock;
  1036. if Result<>nil then
  1037. manager.FirstFreedBlock:=PPointer(Result)^
  1038. else begin
  1039. Result:=manager.NextNonAllocatedBlkID;
  1040. Inc(Cardinal(manager.NextNonAllocatedBlkID), manager.BlockSize);
  1041. end;
  1042. CSLockLeave(manager.SMBInfo.CSLock);
  1043. end;
  1044. {$ifdef RAISE_EXCEPTION_ON_INVALID_RELEASE}
  1045. PDouble(Result)^:=0;
  1046. {$endif}
  1047. end;
  1048. end;
  1049. // FreeSMBBlockAndLeaveLock
  1050. //
  1051. procedure FreeSMBBlockAndLeaveLock(manager : PSMBManager; p : Pointer);
  1052. procedure MoveToNonFullAndLeaveLock(manager : PSMBManager);
  1053. var
  1054. smbInfo : PSMBInfo;
  1055. begin
  1056. smbInfo:=manager.SMBInfo;
  1057. SMBLinkedListCut(smbInfo.FullSMBs, manager);
  1058. SMBLinkedListInsertFirst(smbInfo.FreeSMBs, manager);
  1059. CSLockLeave(smbInfo.CSLock);
  1060. end;
  1061. var
  1062. n : Cardinal;
  1063. smbInfo : PSMBInfo;
  1064. begin
  1065. PPointer(P)^:=manager.FirstFreedBlock;
  1066. manager.FirstFreedBlock:=P;
  1067. {$ifdef RAISE_EXCEPTION_ON_INVALID_RELEASE}
  1068. PCardinalArray(P)[1]:=cBAADFOOD;
  1069. {$endif}
  1070. n:=manager.NbFreeBlocks;
  1071. Inc(manager.NbFreeBlocks);
  1072. if n=0 then begin
  1073. MoveToNonFullAndLeaveLock(manager);
  1074. end else if (n+1=manager.MaxNbFreeBlocks) then begin
  1075. smbInfo:=manager.SMBInfo;
  1076. ReleaseSMB(manager);
  1077. CSLockLeave(smbInfo.CSLock);
  1078. end else CSLockLeave(manager.SMBInfo.CSLock);
  1079. end;
  1080. // SMBManagerFromChunkAndPointer
  1081. //
  1082. function SMBManagerFromChunkAndPointer(chunk : POSChunk; P : Cardinal) : PSMBManager;
  1083. var
  1084. i : Cardinal;
  1085. begin
  1086. // identify chunk item in the chunk, this will be our manager
  1087. i:=(P-chunk.FirstBlock) div cOSChunkItemSize;
  1088. Result:[email protected][i];
  1089. {$ifdef RAISE_EXCEPTION_ON_INVALID_RELEASE}
  1090. if Result.BlockStart=nil then
  1091. Result:=nil;
  1092. {$endif}
  1093. end;
  1094. // RFreeMem
  1095. //
  1096. function RFreeMem(P : Pointer) : Integer;
  1097. var
  1098. manager : PSMBManager;
  1099. lgm : PLGBManager;
  1100. begin
  1101. lgm:=vMemoryMap[Cardinal(P) shr 16];
  1102. if (Cardinal(lgm) and 1)=0 then begin
  1103. // Small block release logic, this is a chunk
  1104. manager:=SMBManagerFromChunkAndPointer(POSChunk(lgm), Cardinal(P));
  1105. if manager<>nil then begin
  1106. {$ifdef RAISE_EXCEPTION_ON_INVALID_RELEASE}
  1107. if PCardinalArray(P)[1]=cBAADFOOD then begin
  1108. Result:=-1;
  1109. Exit;
  1110. end;
  1111. {$endif}
  1112. CSLockEnter(manager.SMBInfo.CSLock);
  1113. FreeSMBBlockAndLeaveLock(manager, P);
  1114. end else begin
  1115. // not found = invalid free
  1116. Result:=-1;
  1117. Exit;
  1118. end;
  1119. end else begin
  1120. lgm:=PLGBManager(Cardinal(lgm) and $FFFFFFF0);
  1121. if (lgm<>nil) and (P=lgm.DataStart) then begin
  1122. // Large block
  1123. ReleaseLGB(lgm);
  1124. end else begin
  1125. Result:=-1;
  1126. Exit;
  1127. end;
  1128. end;
  1129. Result:=0;
  1130. end;
  1131. // ReallocTransferSMB
  1132. //
  1133. function ReallocTransferSMB(P: Pointer; Size : Cardinal; manager : PSMBManager) : Pointer;
  1134. var
  1135. copySize : Cardinal;
  1136. begin
  1137. if Size<cReallocUpSizingLimit then
  1138. Result:=RGetMem(cReallocUpSizingSMBPad+((Size*cReallocUpSizing256) shr 8))
  1139. else Result:=RGetMem(Size);
  1140. if Result<>nil then begin
  1141. copySize:=manager.BlockSize;
  1142. if copySize>Size then copySize:=Size;
  1143. Move16(P^, Result^, copySize);
  1144. CSLockEnter(manager.SMBInfo.CSLock);
  1145. FreeSMBBlockAndLeaveLock(manager, P);
  1146. end;
  1147. end;
  1148. // ReallocTransferLGB
  1149. //
  1150. function ReallocTransferLGB(p : Pointer; size : Cardinal; lgm : PLGBManager) : Pointer;
  1151. begin
  1152. if Size>cSMBMaxSize then begin
  1153. // LGB to LGB
  1154. lgm:=ReallocateLGB(lgm, size);
  1155. if lgm<>nil then
  1156. Result:=lgm.DataStart
  1157. else result:=nil;
  1158. end else begin
  1159. // transition from LGB to SMB
  1160. Result:=RGetMem(size);
  1161. if Result<>nil then begin
  1162. if size>lgm.DataSize then
  1163. size:=lgm.DataSize;
  1164. Move16(p^, Result^, size);
  1165. ReleaseLGB(lgm);
  1166. end;
  1167. end;
  1168. end;
  1169. // RReallocMem
  1170. //
  1171. function RReallocMem(P : Pointer; Size : Cardinal) : Pointer;
  1172. var
  1173. manager : PSMBManager;
  1174. lgm : PLGBManager;
  1175. begin
  1176. lgm:=vMemoryMap[Cardinal(P) shr 16];
  1177. if (Cardinal(lgm) and 1)=0 then begin
  1178. // Reallocating a SMB
  1179. {$ifdef RAISE_EXCEPTION_ON_INVALID_RELEASE}
  1180. if PCardinalArray(P)[1]=cBAADFOOD then begin
  1181. Result:=nil;
  1182. Exit;
  1183. end;
  1184. {$endif}
  1185. manager:=SMBManagerFromChunkAndPointer(POSChunk(lgm), Cardinal(P));
  1186. if manager<>nil then begin
  1187. if (Size<=manager.BlockSize) and (Size>=manager.DownSizingSize) then
  1188. Result:=P
  1189. else Result:=ReallocTransferSMB(P, Size, manager);
  1190. end else begin
  1191. Result:=nil;
  1192. end;
  1193. end else begin
  1194. lgm:=PLGBManager(Cardinal(lgm) and $FFFFFFF0);
  1195. if (lgm<>nil) and (P=lgm.DataStart) then begin
  1196. // Reallocating a LGB
  1197. if (Size<=lgm.MaxDataSize) and (Size>=lgm.MaxDataSize div cLGBReallocDownSizing) then begin
  1198. lgm.DataSize:=Size;
  1199. Result:=P;
  1200. end else Result:=ReallocTransferLGB(P, Size, lgm);
  1201. end else begin
  1202. Result:=nil;
  1203. end;
  1204. end;
  1205. end;
  1206. // RAllocMem
  1207. //
  1208. function RAllocMem(Size : Cardinal) : Pointer; register;
  1209. asm
  1210. push ebx
  1211. cmp eax, 0
  1212. jg @@Alloc
  1213. xor eax, eax
  1214. jmp @@End
  1215. @@Alloc:
  1216. mov ebx, eax
  1217. call RGetMem // Result:=RGetMem(Size);
  1218. cmp ebx, 64*1024 // Blocks larger than 64kB are automatically initialized to zero
  1219. jg @@End
  1220. mov edx, ebx
  1221. mov ebx, eax
  1222. call [MemClear16] // MemClear16(Result^, Size);
  1223. mov eax, ebx
  1224. @@End:
  1225. pop ebx
  1226. end;
  1227. // RAllocated
  1228. //
  1229. function RAllocated(const P : Pointer) : Boolean;
  1230. {var
  1231. blkID : Cardinal;
  1232. manager : PSMBManager;
  1233. locP : Pointer;}
  1234. begin
  1235. { locP:=P;
  1236. if locP=nil then }
  1237. Result:=False
  1238. { else begin
  1239. manager:=vMemoryMap[Cardinal(locP) shr 16];
  1240. if Assigned(manager) then begin
  1241. if manager.SMBInfo.CSLock<>nil then begin
  1242. blkID:=Cardinal(P)-Cardinal(manager.BlockStart);
  1243. Result:=(blkID mod manager.blockSize=0);
  1244. end else Result:=(PLGBManager(manager).DataStart=locP);
  1245. end else Result:=False;
  1246. end; }
  1247. end;
  1248. // InitializeRMM
  1249. //
  1250. procedure InitializeRMM;
  1251. var
  1252. i, j, k : Integer;
  1253. smbInfo : PSMBInfo;
  1254. begin
  1255. InitializeCSLock(vLGBLock);
  1256. for i:=Low(vSMBs) to High(vSMBs) do begin
  1257. smbInfo:=@vSMBs[i];
  1258. InitializeCSLock(smbInfo.CSLock);
  1259. smbInfo.Size:=cSMBSizes[i];
  1260. smbInfo.BlocksPerSMB:=cOSChunkItemSize div smbInfo.Size;
  1261. if smbInfo.Size>cReallocMinSize then
  1262. smbInfo.DownSizingSize:=smbInfo.Size div cSMBReallocDownSizing
  1263. else smbInfo.DownSizingSize:=0;
  1264. end;
  1265. j:=0;
  1266. for i:=0 to cSMBMaxSizeIndex do begin
  1267. k:=cSMBSizes[i];
  1268. while j<k do begin
  1269. vSMBSizeToPSMBInfo[j shr 4]:=i;
  1270. Inc(j, 16);
  1271. end;
  1272. end;
  1273. InitializeCSLock(vOSChunksLock);
  1274. end;
  1275. // FinalizeRMM
  1276. //
  1277. procedure FinalizeRMM;
  1278. var
  1279. i : Integer;
  1280. chunk : POSChunk;
  1281. begin
  1282. // release LGBs
  1283. DeleteCSLock(vLGBLock);
  1284. while vLGBManagers<>nil do
  1285. ReleaseLGB(vLGBManagers);
  1286. // release SMBs
  1287. for i:=Low(vSMBs) to High(vSMBs) do begin
  1288. while vSMBs[i].FreeSMBs.First<>nil do
  1289. ReleaseSMB(vSMBs[i].FreeSMBs.First);
  1290. while vSMBs[i].FullSMBs.First<>nil do
  1291. ReleaseSMB(vSMBs[i].FullSMBs.First);
  1292. DeleteCSLock(vSMBs[i].CSLock);
  1293. end;
  1294. // release OS chunks
  1295. if vOSChunksFirstFull<>nil then
  1296. RunError(153); // "CRC Error in Data"
  1297. if vOSChunksFirst<>nil then begin
  1298. while vOSChunksFirst<>nil do begin
  1299. chunk:=vOSChunksFirst;
  1300. CutOutChunk(chunk);
  1301. DestroyChunk(chunk);
  1302. end;
  1303. end;
  1304. DeleteCSLock(vOSChunksLock);
  1305. end;
  1306. // LockRMM
  1307. //
  1308. procedure LockRMM;
  1309. var
  1310. i : Integer;
  1311. begin
  1312. CSLockEnter(vLGBLock);
  1313. for i:=Low(vSMBs) to High(vSMBs) do
  1314. CSLockEnter(vSMBs[i].CSLock);
  1315. end;
  1316. // UnLockRMM
  1317. //
  1318. procedure UnLockRMM;
  1319. var
  1320. i : Integer;
  1321. begin
  1322. for i:=High(vSMBs) downto Low(vSMBs) do
  1323. CSLockLeave(vSMBs[i].CSLock);
  1324. CSLockLeave(vLGBLock);
  1325. end;
  1326. //
  1327. // Cleanup thread
  1328. //
  1329. {$ifdef ALLOW_DELAYED_RELEASE}
  1330. function CleanupThreadProc(parameter : Pointer) : Integer; stdcall;
  1331. var
  1332. chunk, chunkNext : POSChunk;
  1333. cleanupChunkChain : POSChunk;
  1334. begin
  1335. repeat
  1336. cleanupChunkChain:=nil;
  1337. // Clean up one empty chunk
  1338. if vOSChunkNbEntirelyFree>1 then begin
  1339. CSLockEnter(vOSChunksLock);
  1340. chunk:=vOSChunksFirst;
  1341. if chunk<>nil then chunk:=chunk.Next;
  1342. while chunk<>nil do begin
  1343. chunkNext:=chunk.Next;
  1344. if (chunk.FreeBlocks=cOSChunkBlockCount) then begin
  1345. CutOutChunk(chunk);
  1346. chunk.Next:=cleanupChunkChain;
  1347. cleanupChunkChain:=chunk;
  1348. Dec(vOSChunkNbEntirelyFree);
  1349. if vOSChunkNbEntirelyFree<cOSDelayedAllowedChunksLatency then Break;
  1350. end;
  1351. chunk:=chunkNext;
  1352. end;
  1353. CSLockLeave(vOSChunksLock);
  1354. end;
  1355. // Destroy them out of the lock
  1356. while cleanupChunkChain<>nil do begin
  1357. chunk:=cleanupChunkChain;
  1358. cleanupChunkChain:=chunk.Next;
  1359. DestroyChunk(chunk);
  1360. end;
  1361. WaitForSingleObject(vCleanupThreadEvent, 250);
  1362. until vCleanupThreadID=0;
  1363. Result:=0;
  1364. end;
  1365. procedure StartCleanupThread;
  1366. begin
  1367. if (vCleanupThreadID=0) and (not IsLibrary) then begin
  1368. vCleanupThreadEvent:=CreateEvent(nil, False, False, nil);
  1369. vCleanupThreadHnd:=CreateThread(nil, 16*1024, @CleanupThreadProc, nil,
  1370. 0, vCleanupThreadID);
  1371. // SetThreadPriority(vCleanupThreadHnd, THREAD_PRIORITY_ABOVE_NORMAL);
  1372. end;
  1373. end;
  1374. procedure StopCleanupThread;
  1375. begin
  1376. if vCleanupThreadID<>0 then begin
  1377. vCleanupThreadID:=0;
  1378. SetEvent(vCleanupThreadEvent);
  1379. WaitForSingleObject(vCleanupThreadHnd, INFINITE);
  1380. CloseHandle(vCleanupThreadHnd);
  1381. CloseHandle(vCleanupThreadEvent);
  1382. end;
  1383. end;
  1384. {$endif}
  1385. // BindRMM
  1386. //
  1387. procedure BindRMM;
  1388. {$ifdef SHARE_MEM}
  1389. procedure PrepareDataName;
  1390. const
  1391. cIntToHex : ShortString = '0123456789ABCDEF';
  1392. var
  1393. i : Integer;
  1394. h : Cardinal;
  1395. begin
  1396. // name generation must NOT use any dynamic stuff (for obvious reasons)
  1397. h:=GetCurrentProcessID;
  1398. for i:=0 to 7 do
  1399. vSharedMemory_DataName[i+1]:=cIntToHex[1+((h shr (i*4)) and $F)];
  1400. end;
  1401. {$endif}
  1402. var
  1403. smm : PSharedMemoryManager;
  1404. hwnd : Integer;
  1405. begin
  1406. Inc(vRMMBound);
  1407. if vRMMBound=1 then begin
  1408. {$ifdef SHARE_MEM}
  1409. PrepareDataName;
  1410. hwnd:=FindWindow('STATIC', PChar(@vSharedMemory_DataName[1]));
  1411. {$else}
  1412. hwnd:=0;
  1413. {$endif}
  1414. smm:=@vSharedMemoryManager;
  1415. if hwnd=0 then begin
  1416. // defined SharedMemoryManager fields
  1417. smm.MemoryManager.GetMem:=@RGetMem;
  1418. smm.MemoryManager.FreeMem:=@RFreeMem;
  1419. smm.MemoryManager.ReallocMem:=@RReallocMem;
  1420. smm.Allocated:=@Allocated;
  1421. {$ifdef ALLOW_USAGE_SNAPSHOT}
  1422. smm.RMMUsageSnapShot:=@RMMUsageSnapShot;
  1423. {$endif}
  1424. // Setup structure data for shared memory
  1425. {$ifdef SHARE_MEM}
  1426. vSharedMemory_Data:=CreateWindow('STATIC',
  1427. PChar(@vSharedMemory_DataName[1]),
  1428. WS_POPUP,
  1429. 0, 0, 0, 0,
  1430. 0, 0, GetCurrentProcessID, nil);
  1431. SetWindowLong(vSharedMemory_Data, GWL_USERDATA,
  1432. LongWord(@vSharedMemoryManager));
  1433. vSharedMemory_InUse:=False;
  1434. {$endif}
  1435. InitializeRMM;
  1436. {$ifdef ALLOW_DELAYED_RELEASE}
  1437. StartCleanupThread;
  1438. {$endif}
  1439. end else begin
  1440. {$ifdef SHARE_MEM}
  1441. // we're in a DLL and a RMM has been setup by the application
  1442. smm:=PSharedMemoryManager(GetWindowLong(hwnd, GWL_USERDATA));
  1443. vSharedMemory_InUse:=True;
  1444. {$endif}
  1445. end;
  1446. // replace standard MemoryManager
  1447. GetMemoryManager(vOldMemoryManager);
  1448. SetMemoryManager(smm.MemoryManager);
  1449. // Redirect SysUtils's AllocMem
  1450. Allocated:[email protected];
  1451. {$ifdef ALLOW_USAGE_SNAPSHOT}
  1452. vRMMUsageSnapShotPatch:=RedirectPatch(@RMMUsageSnapShot, @smm.RMMUsageSnapShot);
  1453. {$endif}
  1454. end;
  1455. end;
  1456. // UnBindRMM
  1457. //
  1458. procedure UnBindRMM;
  1459. begin
  1460. Dec(vRMMBound);
  1461. if vRMMBound=0 then begin
  1462. SetMemoryManager(vOldMemoryManager);
  1463. {$ifdef SHARE_MEM}
  1464. if not vSharedMemory_InUse then
  1465. DestroyWindow(vSharedMemory_Data);
  1466. {$endif}
  1467. FinalizeRMM;
  1468. end else if vRMMBound<0 then
  1469. RunError(210); // Object not initialized
  1470. end;
  1471. // RMMActive
  1472. //
  1473. function RMMActive : Boolean;
  1474. begin
  1475. Result:=(vRMMBound>0);
  1476. end;
  1477. {$ifdef ALLOW_USAGE_SNAPSHOT}
  1478. // RMMUsageSnapShot (func)
  1479. //
  1480. function RMMUsageSnapShot : TRMMUsageSnapShot;
  1481. begin
  1482. RMMUsageSnapShot(Result);
  1483. end;
  1484. // RMMUsageSnapShot (proc)
  1485. //
  1486. procedure RMMUsageSnapShot(var result : TRMMUsageSnapShot); overload;
  1487. // computes userSize and nbBlocks contribution for overlapping situations
  1488. procedure AddOverlapStat(start, blockStart, blockEnd, blockSize : Cardinal;
  1489. var userSize, nbBlocks : Cardinal);
  1490. var
  1491. startEnd : Cardinal;
  1492. begin
  1493. if blockSize=0 then Exit;
  1494. startEnd:=start+(1 shl 16);
  1495. if (blockStart>=startEnd) or (blockEnd<=start) then Exit;
  1496. if (blockStart>=start) and (blockStart<startEnd) then
  1497. Inc(nbBlocks);
  1498. blockEnd:=blockStart+blockSize;
  1499. if blockEnd<=start then Exit;
  1500. if blockEnd<=startEnd then
  1501. if blockStart>=start then
  1502. Inc(userSize, blockSize)
  1503. else Inc(userSize, blockEnd-start)
  1504. else if blockStart>=start then
  1505. Inc(userSize, startEnd-blockStart)
  1506. else Inc(userSize, 1 shl 16);
  1507. end;
  1508. var
  1509. i, j, k, kp : Cardinal;
  1510. userSize, nbBlocks, blkSize, totalUserSize : Cardinal;
  1511. psmb : PSMBManager;
  1512. plgb : PLGBManager;
  1513. mapEntry : PRMMMemoryMap;
  1514. mbi : TMemoryBasicInformation;
  1515. begin
  1516. Assert(vRMMBound>0);
  1517. // we're not allowed to use any kind of dynamic allocation here
  1518. LockRMM;
  1519. try
  1520. Result.BenchRGetMem:=vBenchRGetMem;
  1521. Result.BenchRReallocMem:=vBenchRReallocMem;
  1522. Result.BenchRFreeMem:=vBenchRFreeMem;
  1523. Result.NbMapItems:=vMemMapUpper+1;
  1524. Result.TotalVirtualAllocated:=vTotalVirtualAllocated;
  1525. nbBlocks:=0;
  1526. totalUserSize:=0;
  1527. // Build the memory map
  1528. // first go through the memory map
  1529. for i:=0 to vMemMapUpper do begin
  1530. mapEntry:[email protected][i];
  1531. mapEntry.StartAddr:=Pointer(i shl 16);
  1532. mapEntry.Length:=1 shl 16;
  1533. psmb:=vMemoryMap[i];
  1534. if psmb=nil then begin
  1535. // 64kb area not allocated by RMM (but maybe reserved as chunkbatch)
  1536. mapEntry.AllocatedUserSize:=0;
  1537. mapEntry.Status:=rmmsUnallocated;
  1538. end else if psmb.Signature=cSMBSignature then begin
  1539. // 64kb area used by an SMB
  1540. userSize:=0;
  1541. for k:=0 to psmb.MaxNbFreeBlocks-1 do begin
  1542. blkSize:=psmb.BlockSize;
  1543. if blkSize>0 then begin
  1544. Inc(userSize, blkSize);
  1545. Inc(nbBlocks);
  1546. end;
  1547. end;
  1548. Inc(totalUserSize, userSize);
  1549. mapEntry.AllocatedUserSize:=userSize;
  1550. if userSize>0 then
  1551. mapEntry.Status:=rmmsAllocated
  1552. else mapEntry.Status:=rmmsReserved;
  1553. end else if psmb.Signature=cLGBSignature then begin
  1554. // 64kb area used by an LGB
  1555. plgb:=PLGBManager(psmb);
  1556. k:=(i shl 16)-Cardinal(plgb.BlockStart);
  1557. if k=0 then begin
  1558. Inc(totalUserSize, plgb.DataSize);
  1559. Inc(nbBlocks);
  1560. end;
  1561. if k<plgb.DataSize then
  1562. mapEntry.AllocatedUserSize:=1 shl 16
  1563. else if k+(1 shl 16)<plgb.DataSize then
  1564. mapEntry.AllocatedUserSize:=plgb.DataSize-k
  1565. else mapEntry.AllocatedUserSize:=0;
  1566. mapEntry.Status:=rmmsAllocated;
  1567. end;
  1568. end;
  1569. Result.AllocatedBlocks:=nbBlocks;
  1570. Result.AllocatedUserSize:=totalUserSize;
  1571. // Collect VM space stats
  1572. Result.TotalVMSpace:=(vMemMapUpper+1) shl 16;
  1573. Result.SystemAllocatedVM:=0;
  1574. Result.SystemReservedVM:=0;
  1575. k:=0; kp:=0;
  1576. // Make a pass through the unallocated chunks and ask about their status
  1577. for i:=0 to vMemMapUpper do begin
  1578. mapEntry:[email protected][i];
  1579. if mapEntry.Status=rmmsUnallocated then begin
  1580. VirtualQuery(Pointer(i shl 16), mbi, SizeOf(mbi));
  1581. if mbi.State=MEM_COMMIT then begin
  1582. mapEntry.Status:=rmmsSysAllocated;
  1583. Inc(Result.SystemAllocatedVM, 1 shl 16);
  1584. end else if mbi.State=MEM_RESERVE then begin
  1585. mapEntry.Status:=rmmsSysReserved;
  1586. Inc(Result.SystemReservedVM, 1 shl 16);
  1587. end;
  1588. end;
  1589. if mapEntry.Status<>rmmsUnallocated then begin
  1590. if i-k>kp then kp:=i-k;
  1591. k:=i+1;
  1592. end;
  1593. end;
  1594. if vMemMapUpper+1 - k > kp then kp:=vMemMapUpper - k;
  1595. Result.LargestFreeVM:=kp shl 16;
  1596. // Build SMBStats
  1597. for i:=Low(vSMBs) to High(vSMBs) do begin
  1598. nbBlocks:=0;
  1599. userSize:=0;
  1600. k:=0;
  1601. psmb:=vSMBs[i].First;
  1602. while Assigned(psmb) do begin
  1603. Inc(nbBlocks, psmb.MaxNbFreeBlocks-psmb.NbFreeBlocks);
  1604. for j:=0 to psmb.MaxNbFreeBlocks-1 do
  1605. Inc(userSize, psmb.BlockSize);
  1606. Inc(k, cSMBChunkSize);
  1607. psmb:=psmb.Next;
  1608. end;
  1609. with Result.SMBStats[i] do begin
  1610. BlockSize:=SMBIndexToSize(i);
  1611. AllocatedBlocks:=nbBlocks;
  1612. AllocatedUserSize:=userSize;
  1613. TotalVirtualAllocated:=k;
  1614. end;
  1615. end;
  1616. finally
  1617. UnLockRMM;
  1618. end;
  1619. end;
  1620. {$endif} // ALLOW_USAGE_SNAPSHOT
  1621. // ------------------------------------------------------------------
  1622. // ------------------------------------------------------------------
  1623. // ------------------------------------------------------------------
  1624. var
  1625. vMemStatus : TMEMORYSTATUS;
  1626. initialization
  1627. // ------------------------------------------------------------------
  1628. // ------------------------------------------------------------------
  1629. // ------------------------------------------------------------------
  1630. if IsMemoryManagerSet then
  1631. RunError(208); // Overlay manager not installed
  1632. // detect if in 3GB mode
  1633. vMemStatus.dwLength:=32;
  1634. GlobalMemoryStatus(vMemStatus);
  1635. vRunningIn3GBMode:=(vMemStatus.dwTotalVirtual>$80000000);
  1636. if vRunningIn3GBMode then
  1637. vMemMapUpper:=cMemMapUpper3GB
  1638. else vMemMapUpper:=cMemMapUpper2GB;
  1639. vVirtualLimit:=vMemStatus.dwTotalVirtual;
  1640. // SwitchToThread available on NT only, so dynamic linking required
  1641. InitializeSwitchToThread;
  1642. {$ifdef ALLOW_SSE}
  1643. try
  1644. // detect SSE capable CPU
  1645. asm
  1646. push ebx
  1647. pushfd
  1648. pop eax
  1649. mov edx, eax
  1650. xor edx, $200000
  1651. push eax
  1652. popfd
  1653. pushfd
  1654. pop eax
  1655. cmp eax, edx
  1656. jz @@Exit // CPUID not supported
  1657. mov eax, 0
  1658. db $0F,$A2 /// cpuid
  1659. jz @@Exit // features not supported
  1660. mov eax, 1
  1661. db $0F,$A2 /// cpuid
  1662. test edx, (1 shl 25) // SSE support?
  1663. setnz al
  1664. mov byte ptr [vSSESupported], al
  1665. @@Exit:
  1666. pop ebx
  1667. end;
  1668. except
  1669. // trap for old/exotics CPUs
  1670. vSSESupported:=0;
  1671. end;
  1672. if vSSESupported<>0 then begin
  1673. MemClear16:=@MemClear16SSE;
  1674. Move16:=@Move16SSE;
  1675. end else begin
  1676. MemClear16:=@MemClear16RTL;
  1677. Move16:[email protected];
  1678. end;
  1679. {$else}
  1680. MemClear16:=@MemClear16FPU;
  1681. Move16:=@Move16FPU;
  1682. {$endif}
  1683. {$ifdef ALLOW_MEMORYMAPPED_LARGE_BLOCKS}
  1684. GetTempPath(cMAX_PATH, @vTemporaryFilesPath[0]);
  1685. {$endif}
  1686. {$ifdef AUTO_BIND}
  1687. BindRMM;
  1688. {$endif}
  1689. finalization
  1690. {$ifdef ALLOW_DELAYED_RELEASE}
  1691. StopCleanupThread;
  1692. {$endif}
  1693. {$ifdef AUTO_BIND}
  1694. UnBindRMM;
  1695. {$endif}
  1696. end.