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.

401 lines
11 KiB

  1. /* Copyright (c) 1998-1999 Microsoft Corporation */
  2. /* @doc DMusic16
  3. *
  4. * @module Alloc.c - Memory allocation routines |
  5. *
  6. * This module provides memory allocation routines for DMusic16.DLL. It allows the MIDI input and
  7. * output modules to allocated and free <c EVENT> structures.
  8. *
  9. * The allocated recognizes two types of events by size. If an event is create with 4 or less bytes
  10. * of data, then it is allocated as a channel message. Channel message events are allocated one
  11. * page at a time and kept in a free list.
  12. *
  13. * If the event size is greater than 4 bytes, then the event is a system exclusive message (or long
  14. * data in the legacy API nomenclature). These events are allocated individually, one per page.
  15. *
  16. * All allocated memory is preceded with a <c SEGHDR>, which is used to identify the size and type
  17. * of the segment and to keep it in a list. Since all events will be accessed at event time (in
  18. * either a MIDI input callback or a timeSetEvent callback), all memory is automatically page
  19. * locked.
  20. *
  21. * @globalv WORD | gsSegList |Selector of first segment in allocated list
  22. * @globalv LPEVENT | glpFreeEventList | List of free 4-byte events
  23. * @globalv LPEVENT | glpFreeBigEventList | List of free 4-byte events
  24. */
  25. #include <windows.h>
  26. #include <mmsystem.h>
  27. #include <memory.h>
  28. #include "dmusic16.h"
  29. #include "debug.h"
  30. STATIC WORD gsSegList;
  31. STATIC LPEVENT glpFreeEventList;
  32. STATIC LPEVENT glpFreeBigEventList;
  33. /* Given a far pointer, get its selector.
  34. */
  35. #define SEL_OF(lp) (WORD)((((DWORD)lp) >> 16) & 0xffff)
  36. /* Given a far event pointer, get the far pointer to its segment headear.
  37. */
  38. #define SEGHDR_OF(lp) ((LPSEGHDR)(((DWORD)lp) & 0xffff0000l))
  39. STATIC BOOL RefillEventList(VOID);
  40. STATIC LPSEGHDR AllocSeg(WORD cbSeg);
  41. STATIC VOID FreeBigEvents(VOID);
  42. STATIC VOID FreeSeg(LPSEGHDR lpSeg);
  43. /* @func Called at DLL LibInit
  44. *
  45. * @comm
  46. * Initializes all free lists to empty.
  47. *
  48. */
  49. VOID PASCAL
  50. AllocOnLoad(VOID)
  51. {
  52. gsSegList = 0;
  53. glpFreeEventList = NULL;
  54. glpFreeBigEventList = NULL;
  55. }
  56. /* @func Called at DLL LibExit
  57. *
  58. * @comm
  59. * Unlock and free all of the memory allocated.
  60. *
  61. * AllocOnUnload jettisons all memory the allocator has ever allocated.
  62. * It assumes that all pointers to events will no longer ever be touched (i.e. all callbacks must
  63. * have already been disabled by this point).
  64. */
  65. VOID PASCAL
  66. AllocOnExit(VOID)
  67. {
  68. WORD sSel;
  69. WORD sSelNext;
  70. LPSEGHDR lpSeg;
  71. sSel = gsSegList;
  72. while (sSel)
  73. {
  74. lpSeg = (LPSEGHDR)(((DWORD)sSel) << 16);
  75. sSelNext = lpSeg->selNext;
  76. FreeSeg(lpSeg);
  77. sSel = sSelNext;
  78. }
  79. /* This just invalidated both free lists as well as the segment list
  80. */
  81. gsSegList = 0;
  82. glpFreeEventList = NULL;
  83. glpFreeBigEventList = NULL;
  84. }
  85. /* @func Allocate an event of a given size
  86. *
  87. * @rdesc Returns a far pointer to the event or NULL if memory could not be allocated.
  88. *
  89. * @comm
  90. *
  91. * This function is not callable at interrupt time.
  92. *
  93. * This function is called to allocate a single event. The event will be allocated from
  94. * page-locked memory and filled with the given event data.
  95. *
  96. * Events are classified as normal events, which contain channel messages, and big events,
  97. * which contain SysEx data. The two are distinguished by their size: any event containing
  98. * a DWORD of data or less is a normal event.
  99. *
  100. * Since channel messages comprise most of the MIDI stream, allocation of these events is optimized.
  101. * A segment is allocated containing approximately one page worth (4k) of 4-byte events. These
  102. * events are doled out of a free pool, which only occasionally needs to be refilled from system
  103. * memory.
  104. *
  105. * Big events are allocated on an as-needed basis. When they have been free'd by a call to FreeEvent,
  106. * they are placed on a special free list. This list is used to find memory for future big events,
  107. * and is occasionally free'd back to Windows on a call to AllocEvent in order to minimize the
  108. * amount of page-locked memory in use.
  109. */
  110. LPEVENT PASCAL
  111. AllocEvent(
  112. DWORD msTime, /* @parm The absolute time based on timeGetTime() of the event */
  113. QUADWORD rtTime, /* @parm The absolute time based on the IRferenceClock in 100ns units */
  114. WORD cbEvent) /* @parm The number of bytes of event data in pbData */
  115. {
  116. LPEVENT lpEvent;
  117. LPEVENT lpEventPrev;
  118. LPEVENT lpEventCurr;
  119. LPSEGHDR lpSeg;
  120. /* Check for big event first (Sysex)
  121. */
  122. if (cbEvent > sizeof(DWORD))
  123. {
  124. /* First see if we have an event that will work already
  125. */
  126. lpEventPrev = NULL;
  127. lpEventCurr = glpFreeBigEventList;
  128. while (lpEventCurr)
  129. {
  130. if (SEGHDR_OF(lpEventCurr)->cbSeg >= sizeof(EVENT) + cbEvent)
  131. {
  132. break;
  133. }
  134. lpEventPrev = lpEventCurr;
  135. lpEventCurr = lpEventCurr->lpNext;
  136. }
  137. if (lpEventCurr)
  138. {
  139. /* Remove this event from the list and use it
  140. */
  141. if (lpEventPrev)
  142. {
  143. lpEventPrev->lpNext = lpEventCurr->lpNext;
  144. }
  145. else
  146. {
  147. glpFreeBigEventList = lpEventCurr->lpNext;
  148. }
  149. lpEventCurr->lpNext = NULL;
  150. }
  151. else
  152. {
  153. /* Nope, need to allocate one
  154. */
  155. lpSeg = AllocSeg(sizeof(EVENT) + cbEvent);
  156. if (NULL == lpSeg)
  157. {
  158. return NULL;
  159. }
  160. lpEventCurr = (LPEVENT)(lpSeg + 1);
  161. }
  162. lpEventCurr->msTime = msTime;
  163. lpEventCurr->rtTime = rtTime;
  164. lpEventCurr->wFlags = 0;
  165. lpEventCurr->cbEvent = cbEvent;
  166. return lpEventCurr;
  167. }
  168. /* BUGBUG How often???
  169. */
  170. FreeBigEvents();
  171. /* Normal event. Pull it off the free list (refill if needed) and fill it in.
  172. */
  173. if (NULL == glpFreeEventList)
  174. {
  175. if (!RefillEventList())
  176. {
  177. return NULL;
  178. }
  179. }
  180. lpEvent = glpFreeEventList;
  181. glpFreeEventList = lpEvent->lpNext;
  182. lpEvent->msTime = msTime;
  183. lpEvent->rtTime = rtTime;
  184. lpEvent->wFlags = 0;
  185. lpEvent->cbEvent = cbEvent;
  186. return lpEvent;
  187. }
  188. /* @func Free an event back to its appropriate free list
  189. *
  190. * @comm
  191. *
  192. * FreeEvent makes no system calls; it simply places the given event back on the correct
  193. * free list. If the event needs to be actually free'd, that will be done at a later time
  194. * in user mode.
  195. */
  196. VOID PASCAL
  197. FreeEvent(
  198. LPEVENT lpEvent) /* @parm The event to free */
  199. {
  200. LPSEGHDR lpSeg;
  201. lpSeg = SEGHDR_OF(lpEvent);
  202. if (lpSeg->wFlags & SEG_F_4BYTE_EVENTS)
  203. {
  204. lpEvent->lpNext = glpFreeEventList;
  205. glpFreeEventList = lpEvent;
  206. }
  207. else
  208. {
  209. lpEvent->lpNext = glpFreeBigEventList;
  210. glpFreeBigEventList = lpEvent;
  211. }
  212. }
  213. /* @func Refill the free list of normal events
  214. *
  215. * @rdesc Returns TRUE if the list was refilled or FALSE if there was no memory.
  216. *
  217. * @comm
  218. *
  219. * This routine is not callable from interrupt time.
  220. *
  221. * Allocate one page-sized segment of normal events and add them to the free list.
  222. *
  223. */
  224. STATIC BOOL
  225. RefillEventList(VOID)
  226. {
  227. LPSEGHDR lpSeg;
  228. LPEVENT lpEvent;
  229. UINT cbEvent;
  230. UINT idx;
  231. cbEvent = sizeof(EVENT) + sizeof(DWORD);
  232. lpSeg = AllocSeg(C_PER_SEG * cbEvent);
  233. if (NULL == lpSeg)
  234. {
  235. return FALSE;
  236. }
  237. lpSeg->wFlags = SEG_F_4BYTE_EVENTS;
  238. /* Put the events into the free pool
  239. */
  240. lpEvent = (LPEVENT)(lpSeg + 1);
  241. for (idx = C_PER_SEG - 1; idx; --idx)
  242. {
  243. lpEvent->lpNext = (LPEVENT)(((LPBYTE)lpEvent) + cbEvent);
  244. lpEvent = lpEvent->lpNext;
  245. }
  246. lpEvent->lpNext = glpFreeEventList;
  247. glpFreeEventList = (LPEVENT)(lpSeg + 1);
  248. return TRUE;
  249. }
  250. /* @func Free all big events
  251. *
  252. * @comm
  253. *
  254. * This function is not callable at interrupt time.
  255. *
  256. * This function frees all big events on the free big event list. Free big events are those
  257. * with event data sizes of more than one DWORD; they are allocated one event per segment
  258. * as needed rather than being pooled like channel messages.
  259. *
  260. * This function is called every now and then as a side effect of AllocEvent in order to
  261. * free up the page-locked memory associated with completed big events.
  262. *
  263. */
  264. STATIC VOID
  265. FreeBigEvents(VOID)
  266. {
  267. LPEVENT lpEvent;
  268. LPEVENT lpEventNext;
  269. LPSEGHDR lpSeg;
  270. lpEvent = glpFreeBigEventList;
  271. while (lpEvent)
  272. {
  273. lpEventNext = lpEvent->lpNext;
  274. lpSeg = SEGHDR_OF(lpEvent);
  275. FreeSeg(lpSeg);
  276. lpEvent = lpEventNext;
  277. }
  278. glpFreeBigEventList = NULL;
  279. }
  280. /* @func Allocate a segment and put it into the list of allocated segments.
  281. *
  282. * @rdesc A far pointer to the segment header or NULL if the memory could not be allocated.
  283. *
  284. * @comm
  285. *
  286. * This function is not callable at interrupt time.
  287. *
  288. * This is the lowest-level allocation routine which actually calls Windows to allocate the memory.
  289. * The caller is responsible for carving the memory into one or more events.
  290. *
  291. * The data area of the segment will be filled with zeroes.
  292. *
  293. * Since events are accessed at interrupt time (timeSetEvent callback), the memory is allocated and
  294. * page locked.
  295. *
  296. * This routine also inserts the segment into the global list of allocated segments for cleanup.
  297. */
  298. STATIC LPSEGHDR
  299. AllocSeg(
  300. WORD cbSeg) /* @parm The size of data needed in the segment, excluding the segment header */
  301. {
  302. HANDLE hSeg;
  303. WORD sSegHdr;
  304. LPSEGHDR lpSeg;
  305. /* Allocate and page-lock a segment
  306. * NOTE: GPTR contains zero-init
  307. */
  308. cbSeg += sizeof(SEGHDR);
  309. hSeg = GlobalAlloc(GPTR | GMEM_SHARE, cbSeg);
  310. if (0 == hSeg)
  311. {
  312. return NULL;
  313. }
  314. lpSeg = (LPSEGHDR)GlobalLock(hSeg);
  315. if (NULL == lpSeg)
  316. {
  317. GlobalFree(sSegHdr);
  318. return NULL;
  319. }
  320. sSegHdr = SEL_OF(lpSeg);
  321. if (!GlobalSmartPageLock(sSegHdr))
  322. {
  323. GlobalUnlock(sSegHdr);
  324. GlobalFree(sSegHdr);
  325. return NULL;
  326. }
  327. lpSeg->hSeg = hSeg;
  328. lpSeg->cbSeg = cbSeg;
  329. lpSeg->selNext = gsSegList;
  330. gsSegList = sSegHdr;
  331. return lpSeg;
  332. }
  333. /* @func Free a segment back to Windows
  334. *
  335. * @comm
  336. *
  337. * This function is not callable at interrupt time.
  338. *
  339. * Just unlock the segment and free it. The calling cleanup code is assumed to have removed
  340. * the segment from the global list of allocated segments.
  341. *
  342. */
  343. STATIC VOID FreeSeg(
  344. LPSEGHDR lpSeg) /* @parm The segment to free */
  345. {
  346. WORD sSel = SEL_OF(lpSeg);
  347. HANDLE hSeg;
  348. hSeg = lpSeg->hSeg;
  349. GlobalSmartPageUnlock(sSel);
  350. GlobalUnlock(hSeg);
  351. GlobalFree(hSeg);
  352. }