Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

444 lines
13 KiB

  1. /*
  2. * memory utility functions
  3. *
  4. * global heap functions - allocate and free many small
  5. * pieces of memory by calling global alloc for large pieces
  6. * and breaking them up.
  7. *
  8. * We get memory allocations in units of ALLOCSIZE and hand out blocks
  9. * in units of BLKSIZE. Each allocation has a bitmap (segmap) with one
  10. * bit per block to track the blocks in the allocation that have been
  11. * handed out. All the allocations together are referred to as the heap.
  12. * The bitmap maps the entire allocation, so the first thing done is to
  13. * set the bits to say that the header (including the bitmap itself) has
  14. * already gone. Each allocation contains a count of the number of free
  15. * blocks left in it. This allows us to avoid searching allocations that
  16. * cannot possibly have enough room.
  17. *
  18. * Whenever we hand out some blocks we store the HGLOBAL of that allocation
  19. * immediately before the bit we hand out. This means that the HGLOBAL
  20. * gets stored in a lot of places, but we can always find it from the
  21. * pointer that the caller has. (Obviously we add a handle size to the
  22. * bytes asked for to allow for this). For historical reasons HGLOBALs are
  23. * often referred to as seg handles. The caller knows about the handle to
  24. * the whole heap. Only we know about these other handles.
  25. * All allocations are kept locked.
  26. *
  27. * Requests for more than MAXGALLOC bytes are passed on to GlobalAlloc and
  28. * so have none of this. Likewise when they are freed.
  29. *
  30. * The allocations are chained up so that we can look for free space in all
  31. * of them - BUT to keep speed
  32. * 1. we keep track of the number of free blocks in an allocation and
  33. * only look at the bitmap if it might win.
  34. * 2. When we fail to find free space and so get a new allocation, we chain
  35. * it on the front, so we will then normally allocate from this new first block.
  36. * We only * look further down the chain when the first block fails us.
  37. *
  38. * Multithread safe. An allocation contains a critical section, so
  39. * multiple simultaneous calls to gmem_get and gmem_free will be
  40. * protected.
  41. *
  42. * gmem_freeall should not be called until all other users have finished
  43. * with the heap.
  44. */
  45. #include <windows.h>
  46. #include <memory.h>
  47. #include "gutils.h"
  48. #include "gutilsrc.h" /* for string id */
  49. extern HANDLE hLibInst;
  50. /*
  51. * out-of-memory is not something we regard as normal.
  52. * - if we cannot allocate memory - we put up an abort-retry-ignore
  53. * error, and only return from the function if the user selects ignore.
  54. */
  55. int gmem_panic(void);
  56. /* ensure BLKSIZE is multiple of sizeof(DWORD) */
  57. #define BLKSIZE 16 /* block size in bytes to hand out */
  58. #define ALLOCSIZE 32768 /* allocation size in bytes to get */
  59. #define NBLKS (ALLOCSIZE / BLKSIZE) /* blocks per alloc */
  60. #define MAPSIZE (NBLKS / 8) /* bytes of bitmap needed */
  61. #define MAPLONGS (MAPSIZE / sizeof(DWORD)) /* DWORDS of bitmap needed */
  62. /* Macro to convert a request in bytes to a (rounded up) number of blocks */
  63. #define TO_BLKS(x) (((x) + BLKSIZE - 1) / BLKSIZE)
  64. typedef struct seghdr {
  65. HANDLE hseg; /* The HGLOBAL of this allocation */
  66. CRITICAL_SECTION critsec; /* Critsec for this allocation */
  67. struct seghdr FAR * pnext; /* Next allocation */
  68. long nblocks; /* num free blocks left in this alloc */
  69. DWORD segmap[MAPLONGS]; /* The bitmap */
  70. /* The available storage in an allocation follows immediately */
  71. } SEGHDR, FAR * SEGHDRP;
  72. /* Anything above this size, we alloc directly from global
  73. This must be smaller than ALLOCSIZE - sizeof(SEGHDR) - sizeof(HANDLE)
  74. */
  75. #define MAXGALLOC 20000
  76. /*
  77. * init heap - create first segment.
  78. Return the locked HGLOBAL of the new, initialised heap or NULL if it fails.
  79. */
  80. HANDLE APIENTRY
  81. gmem_init(void)
  82. {
  83. HANDLE hNew;
  84. SEGHDRP hp;
  85. /* Try to allocate. If fails, call gmem_panic.
  86. If user says IGNORE, return NULL, else go round again.
  87. */
  88. do {
  89. hNew = GlobalAlloc(GHND, ALLOCSIZE);/* moveable and Zero-init */
  90. if (hNew == NULL) {
  91. if (gmem_panic() == IDIGNORE) {
  92. return(NULL);
  93. }
  94. }
  95. } while (hNew == NULL);
  96. /* Lock it - or return NULL (unexpected) if it won't */
  97. hp = (SEGHDRP) GlobalLock(hNew);
  98. if (hp == NULL) {
  99. GlobalFree(hNew);
  100. return(NULL);
  101. }
  102. hp->hseg = hNew;
  103. InitializeCriticalSection(&hp->critsec);
  104. hp->pnext = NULL;
  105. gbit_init(hp->segmap, NBLKS);
  106. gbit_alloc(hp->segmap, 1, TO_BLKS(sizeof(SEGHDR)));
  107. hp->nblocks = NBLKS - TO_BLKS(sizeof(SEGHDR));
  108. return(hNew);
  109. } /* gmem_init */
  110. LONG gmemTime = 0; /* time used in musec */
  111. LONG gmemTot = 0; /* number of calls */
  112. LONG APIENTRY gmem_time(void)
  113. { return MulDiv(gmemTime, 1, gmemTot);
  114. }
  115. #ifdef TIMING
  116. LPSTR APIENTRY gmem_get_internal(HANDLE hHeap, int len);
  117. LPSTR APIENTRY
  118. gmem_get(HANDLE hHeap, int len)
  119. {
  120. LPSTR Ret;
  121. LARGE_INTEGER time1, time2, freq;
  122. LONG t1, t2;
  123. QueryPerformanceFrequency(&freq);
  124. if (gmemTot==0) {
  125. char msg[80];
  126. LONG temp = freq.LowPart;
  127. wsprintf(msg, "QPF gave %d", temp);
  128. Trace_Error(NULL, msg, FALSE);
  129. }
  130. ++gmemTot;
  131. QueryPerformanceCounter(&time1);
  132. Ret = gmem_get_internal(hHeap, len);
  133. QueryPerformanceCounter(&time2);
  134. t1 = time1.LowPart;
  135. t2 = time2.LowPart;
  136. gmemTime += t2-t1;
  137. return Ret;
  138. }
  139. #else
  140. /* cause gmem_get_internal to actually be the real gmem_get */
  141. #define gmem_get_internal gmem_get
  142. #endif
  143. /* Return an LPSTR pointing to room for len bytes. Try allocatng
  144. initially from hHeap, but reserve the right to get it from elsewhere.
  145. Return NULL if it fails.
  146. */
  147. LPSTR APIENTRY
  148. gmem_get_internal(HANDLE hHeap, int len)
  149. {
  150. SEGHDRP chainp;
  151. HANDLE hNew;
  152. SEGHDRP hp;
  153. LPSTR chp;
  154. long nblks;
  155. long start;
  156. long nfound;
  157. chp = NULL; /* eliminate spurious compiler warning - generate worse code. */
  158. //{ char msg[80];
  159. // wsprintf(msg, "gmem_get %d bytes", len);
  160. // Trace_File(msg);
  161. //}
  162. /* Zero bytes? Address zero is an adequate place! */
  163. if (len < 1) {
  164. return(NULL);
  165. }
  166. /* The heap is always locked (in gmem_init).
  167. Lock it again to get the pointer then we can safely unlock it.
  168. */
  169. chainp = (SEGHDRP) GlobalLock(hHeap);
  170. GlobalUnlock(hHeap);
  171. /*
  172. * Too big to be worth allocing from heap? - get from globalalloc.
  173. */
  174. if (len > MAXGALLOC) {
  175. /* Try to allocate. If fails, call gmem_panic.
  176. If user says IGNORE, return NULL, else go round again.
  177. */
  178. do {
  179. hNew = GlobalAlloc(GHND, len);
  180. if (hNew == NULL) {
  181. if (gmem_panic() == IDIGNORE) {
  182. return(NULL);
  183. }
  184. }
  185. } while (hNew == NULL);
  186. chp = GlobalLock(hNew);
  187. if (chp == NULL) {
  188. GlobalFree(hNew);
  189. return(NULL);
  190. }
  191. //{ char msg[80];
  192. // wsprintf(msg, " gmem_get direct address ==> %8x", chp);
  193. // Trace_File(msg);
  194. //}
  195. return(chp);
  196. }
  197. /*
  198. * get critical section during all access to the heap itself
  199. */
  200. EnterCriticalSection(&chainp->critsec);
  201. nblks = TO_BLKS(len + sizeof(HANDLE));
  202. for (hp = chainp; hp !=NULL; hp = hp->pnext) {
  203. if (hp->nblocks >= nblks) {
  204. nfound = gbit_findfree(hp->segmap, nblks,NBLKS, &start);
  205. if (nfound >= nblks) {
  206. gbit_alloc(hp->segmap, start, nblks);
  207. hp->nblocks -= nblks;
  208. /* convert blocknr to pointer
  209. * store seg handle in block
  210. * Prepare to return pointer to just after handle.
  211. */
  212. chp = (LPSTR) hp;
  213. chp = &chp[ (start-1) * BLKSIZE];
  214. * ( (HANDLE FAR *) chp) = hp->hseg;
  215. chp += sizeof(HANDLE);
  216. break;
  217. }
  218. }
  219. }
  220. if (hp == NULL) {
  221. // Trace_File("<gmen-get new block>");
  222. /* Try to allocate. If fails, call gmem_panic.
  223. If user says IGNORE, return NULL, else go round again.
  224. */
  225. do {
  226. hNew = GlobalAlloc(GHND, ALLOCSIZE);
  227. if (hNew == NULL) {
  228. if (gmem_panic() == IDIGNORE) {
  229. LeaveCriticalSection(&chainp->critsec);
  230. return(NULL);
  231. }
  232. }
  233. } while (hNew == NULL);
  234. hp = (SEGHDRP) GlobalLock(hNew);
  235. if (hp == NULL) {
  236. LeaveCriticalSection(&chainp->critsec);
  237. GlobalFree(hNew);
  238. return(NULL);
  239. }
  240. hp->pnext = chainp->pnext;
  241. hp->hseg = hNew;
  242. chainp->pnext = hp;
  243. gbit_init(hp->segmap, NBLKS);
  244. gbit_alloc(hp->segmap, 1, TO_BLKS(sizeof(SEGHDR)));
  245. hp->nblocks = NBLKS - TO_BLKS(sizeof(SEGHDR));
  246. nfound = gbit_findfree(hp->segmap, nblks, NBLKS, &start);
  247. if (nfound >= nblks) {
  248. gbit_alloc(hp->segmap, start, nblks);
  249. hp->nblocks -= nblks;
  250. /* convert block nr to pointer */
  251. chp = (LPSTR) hp;
  252. chp = &chp[ (start-1) * BLKSIZE];
  253. /* add a handle into the block and skip past */
  254. * ( (HANDLE FAR *) chp) = hp->hseg;
  255. chp += sizeof(HANDLE);
  256. }
  257. }
  258. /* ASSERT - by now we MUST have found a block. chp cannot be garbage.
  259. This requires that MAXGALLOC is not too large.
  260. */
  261. //{ char msg[80];
  262. // wsprintf(msg, " gmem_get suballoc address ==> %8x\n", chp);
  263. // Trace_File(msg);
  264. //}
  265. LeaveCriticalSection(&chainp->critsec);
  266. memset(chp, 0, len); /* We ask for ZEROINIT memory, but it could have
  267. been already affected by gmem_get; use; gmem_free
  268. */
  269. return(chp);
  270. } /* gmem_get */
  271. void APIENTRY
  272. gmem_free(HANDLE hHeap, LPSTR ptr, int len)
  273. {
  274. SEGHDRP chainp;
  275. SEGHDRP hp;
  276. HANDLE hmem;
  277. long nblks, blknr;
  278. LPSTR chp;
  279. //{ char msg[80];
  280. // wsprintf(msg, " gmem_free address ==> %8x, len %d \n", ptr, len);
  281. // Trace_File(msg);
  282. //}
  283. if (len < 1) {
  284. return;
  285. }
  286. /* In Windiff, things are run on different threads and Exit can result
  287. in a general cleanup. It is possible that the creation of stuff is
  288. in an in-between state at this point. The dogma is that when we
  289. allocate a new structure and tie it into a List or whatever that
  290. will need to be freed later:
  291. EITHER all pointers within the allocated structure are made NULL
  292. before it is chained in
  293. OR the caller of Gmem services undertakes not to try to free any
  294. garbage pointers that are not yet quite built
  295. For this reason, if ptr is NULL, we go home peacefully.
  296. */
  297. if (ptr==NULL) return;
  298. /*
  299. * allocs greater than MAXGALLOC were too big to be worth
  300. * allocing from the heap - they will have been allocated
  301. * directly from globalalloc
  302. */
  303. if (len > MAXGALLOC) {
  304. hmem = GlobalHandle( (LPSTR) ptr);
  305. GlobalUnlock(hmem);
  306. GlobalFree(hmem);
  307. return;
  308. }
  309. chainp = (SEGHDRP) GlobalLock(hHeap);
  310. EnterCriticalSection(&chainp->critsec);
  311. /* just before the ptr we gave the user, is the handle to
  312. * the block.
  313. */
  314. chp = (LPSTR) ptr;
  315. chp -= sizeof(HANDLE);
  316. hmem = * ((HANDLE FAR *) chp);
  317. hp = (SEGHDRP) GlobalLock(hmem);
  318. nblks = TO_BLKS(len + sizeof(HANDLE));
  319. /* convert ptr to block nr */
  320. blknr = TO_BLKS( (unsigned) (chp - (LPSTR) hp) ) + 1;
  321. gbit_free(hp->segmap, blknr, nblks);
  322. hp->nblocks += nblks;
  323. GlobalUnlock(hmem);
  324. LeaveCriticalSection(&chainp->critsec);
  325. GlobalUnlock(hHeap);
  326. }
  327. void APIENTRY
  328. gmem_freeall(HANDLE hHeap)
  329. {
  330. SEGHDRP chainp;
  331. HANDLE hSeg;
  332. chainp = (SEGHDRP) GlobalLock(hHeap);
  333. /* this segment is always locked - so we need to unlock
  334. * it here as well as below
  335. */
  336. GlobalUnlock(hHeap);
  337. /* finished with the critical section -
  338. * caller must ensure that at this point there is no
  339. * longer any contention
  340. */
  341. DeleteCriticalSection(&chainp->critsec);
  342. while (chainp != NULL) {
  343. hSeg = chainp->hseg;
  344. chainp = chainp->pnext;
  345. GlobalUnlock(hSeg);
  346. GlobalFree(hSeg);
  347. }
  348. }
  349. /*
  350. * a memory allocation attempt has failed. return IDIGNORE to ignore the
  351. * error and return NULL to the caller, and IDRETRY to retry the allocation
  352. * attempt.
  353. */
  354. int
  355. gmem_panic(void)
  356. {
  357. int code;
  358. TCHAR szBuff1[MAX_PATH];
  359. TCHAR szBuff2[MAX_PATH];
  360. LoadString(hLibInst,
  361. IDS_MEMORY_ALLOC_FAIL,
  362. szBuff1,
  363. sizeof(szBuff1)/sizeof(szBuff1[0]));
  364. LoadString(hLibInst,
  365. IDS_OUT_OF_MEMORY,
  366. szBuff2,
  367. sizeof(szBuff2)/sizeof(szBuff2[0]));
  368. code = MessageBox(NULL, szBuff1, szBuff2,
  369. MB_ICONSTOP|MB_ABORTRETRYIGNORE);
  370. if (code == IDABORT) {
  371. /* abort this whole process */
  372. ExitProcess(1);
  373. } else {
  374. return(code);
  375. }
  376. return 0;
  377. }