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.

357 lines
10 KiB

  1. /******************************************************************************
  2. Copyright (c) 1985-1998 Microsoft Corporation
  3. Title: task.c - support for task creation and blocking
  4. Version: 1.00
  5. Date: 05-Mar-1990
  6. Author: ROBWI
  7. ------------------------------------------------------------------------------
  8. Change log:
  9. DATE REV DESCRIPTION
  10. ----------- ----- -----------------------------------------------------------
  11. 05-MAR-1990 ROBWI First Version - APIs and structures
  12. 18-APR-1990 ROBWI Ported from Resman to mmsystem
  13. 25-JUN-1990 ROBWI Added mmTaskYield
  14. 07-JUL-1991 CJP Modified to work with new stack switcher code
  15. SD Ported to NT
  16. RCBS Added NT function - Modelled on threads and
  17. PostThreadMessage :
  18. HTASK is thread id (DWORD)
  19. *****************************************************************************/
  20. #define MMNOTIMER
  21. #define MMNOSEQ
  22. #define MMNOWAVE
  23. #define MMNOMIDI
  24. #define MMNOJOY
  25. #define MMNOSOUND
  26. #define MMNOMCI
  27. #define NOMIDIDEV
  28. #define NOWAVEDEV
  29. #define NOTIMERDEV
  30. #define NOJOYDEV
  31. #define NOMCIDEV
  32. #define NOSEQDEV
  33. #include <winmmi.h>
  34. #define MM_TASK_STACK_SIZE 0x200
  35. /*
  36. * Private structure type passed from mmTaskCreate to mmStartTask
  37. */
  38. typedef struct {
  39. HANDLE TerminationEvent;
  40. DWORD_PTR dwInst;
  41. LPTHREAD_START_ROUTINE lpfn;
  42. } MM_THREAD_START_DATA;
  43. /*
  44. * Task start stub
  45. */
  46. STATICFN DWORD mmStartTask(LPVOID lpThreadParameter);
  47. /*************************************************************************
  48. *
  49. * @doc DDK MMSYSTEM TASK
  50. *
  51. * @api VOID | mmTaskBlock | This function blocks the current
  52. * task context if its event count is 0.
  53. *
  54. * @parm HANDLE | hTask | Task handle of the current task. For predictable
  55. * results, get the task handle from <f mmGetCurrentTask>.
  56. *
  57. * @xref mmTaskSignal mmTaskCreate
  58. *
  59. * @comm WARNING : For predictable results, must only be called from a
  60. * task created with <f mmTaskCreate>.
  61. *
  62. *************************************************************************/
  63. VOID APIENTRY mmTaskBlock(DWORD h)
  64. {
  65. MSG msg;
  66. /*
  67. * Loop until we get the message we want
  68. */
  69. for (;;) {
  70. /*
  71. * Retrieve any message for task
  72. */
  73. GetMessage(&msg, NULL, 0, 0);
  74. /*
  75. * If the message is for a window dispatch it
  76. */
  77. if (msg.hwnd != NULL) {
  78. DispatchMessage(&msg);
  79. } else {
  80. /*
  81. * WM_USER is the signal message
  82. */
  83. if (msg.message == WM_USER) {
  84. break;
  85. }
  86. }
  87. }
  88. return;
  89. }
  90. /*************************************************************************
  91. *
  92. * @doc DDK MMSYSTEM TASK
  93. *
  94. * @api BOOL | mmTaskSignal | This function signals the specified
  95. * task, incrementing its event count and unblocking
  96. * it.
  97. *
  98. * @parm HTASK | hTask | Task handle. For predictable results, get the
  99. * task handle from <f mmGetCurrentTask>.
  100. *
  101. * @rdesc Returns TRUE if the signal was sent, else FALSE if the message
  102. * queue was full.
  103. *
  104. * @xref mmTaskBlock mmTaskCreate
  105. *
  106. * @comm Must be callable at interrupt time! WARNING : For
  107. * predictable results, must only be called from a task
  108. * created with <f mmTaskCreate>.
  109. *
  110. *************************************************************************/
  111. BOOL APIENTRY mmTaskSignal(DWORD h)
  112. {
  113. #ifdef DBG
  114. BOOL fErr;
  115. dprintf2(("Signalling Thread %x", (ULONG)h));
  116. fErr = PostThreadMessage((DWORD)h, WM_USER, 0, 0);
  117. if (!fErr) {
  118. dprintf1(("Error %d signalling Thread %x", GetLastError(), (ULONG)h));
  119. }
  120. return(fErr);
  121. #else
  122. return PostThreadMessage((DWORD)h, WM_USER, 0, 0);
  123. #endif
  124. }
  125. /*************************************************************************
  126. *
  127. * @doc DDK MMSYSTEM TASK
  128. *
  129. * @api VOID | mmTaskYield | This function causes the current task
  130. * to yield.
  131. *
  132. * @comm For predictable results and future compatibility, use this
  133. * function rather than <f Yield> or the undocumented Kernel yield
  134. * function to yield within a task created with <f mmTaskCreate>.
  135. *
  136. *************************************************************************/
  137. VOID APIENTRY mmTaskYield(VOID) {
  138. Yield();
  139. }
  140. /*************************************************************************
  141. *
  142. * @doc DDK MMSYSTEM TASK
  143. *
  144. * @api HTASK | mmGetCurrentTask | This function returns the
  145. * handle of the currently executing task created with
  146. * <f mmTaskCreate>.
  147. *
  148. * @rdesc Returns a task handle. For predictable results and future
  149. * compatibility, use this function rather than <f GetCurrentTask>
  150. * to get the task handle of a task created with <f mmTaskCreate>.
  151. *
  152. * @xref mmTaskCreate
  153. *
  154. *************************************************************************/
  155. DWORD APIENTRY mmGetCurrentTask(VOID) {
  156. return (DWORD)GetCurrentThreadId();
  157. }
  158. /***************************************************************************
  159. *
  160. * @doc DDK MMSYSTEM TASK
  161. *
  162. * @api UINT | mmTaskCreate | This function creates a new task.
  163. *
  164. * @parm LPTASKCALLBACK | lpfn | Points to a program supplied
  165. * function and represents the starting address of the new
  166. * task.
  167. *
  168. * @parm HANDLE * | lph | Points to the variable that receives the
  169. * task handle (NOT the task identifier). This is used by
  170. * systems that wish to use the handle to wait for task
  171. * termination. If lph is 0 the thread handle is closed here
  172. *
  173. * @parm DWORD | dwStack | Specifies the size of the stack to be
  174. * provided to the task.
  175. *
  176. * @parm DWORD | dwInst | DWORD of instance data to pass to the task
  177. * routine.
  178. *
  179. * @rdesc Returns zero if the function is successful. Otherwise it
  180. * returns an error value which may be one of the following:
  181. *
  182. * @flag TASKERR_NOTASKSUPPORT | Task support is not available.
  183. * @flag TASKERR_OUTOFMEMORY | Not enough memory to create task.
  184. *
  185. * @comm When a mmsystem task is created, the system will make a far
  186. * call to the program-supplied function whose address is
  187. * specified by the lpfn parameter. This function may include
  188. * local variables and may call other functions as long as
  189. * the stack has sufficient space.
  190. *
  191. * The task terminates when it returns.
  192. *
  193. * @xref mmTaskSignal mmTaskBlock
  194. *
  195. ***************************************************************************/
  196. UINT APIENTRY mmTaskCreate(LPTASKCALLBACK lpfn, HANDLE * lph, DWORD_PTR dwInst)
  197. {
  198. DWORD ThreadId;
  199. HANDLE ThreadHandle;
  200. HANDLE TerminationEvent;
  201. MM_THREAD_START_DATA *ThreadData;
  202. /*
  203. * Create a block to pass stuff to our new thread
  204. */
  205. ThreadData = (MM_THREAD_START_DATA *)LocalAlloc(LPTR, sizeof(*ThreadData));
  206. if (ThreadData == NULL) {
  207. return TASKERR_OUTOFMEMORY;
  208. }
  209. ThreadData->dwInst = dwInst;
  210. ThreadData->lpfn = (LPTHREAD_START_ROUTINE)lpfn;
  211. /*
  212. * We create an event which will be set when the thread terminates
  213. * The initial state is NOT signalled. This means that the handle
  214. * can be waited on immediately.
  215. */
  216. if (lph) {
  217. ThreadData->TerminationEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  218. if (ThreadData->TerminationEvent == NULL) {
  219. LocalFree(ThreadData);
  220. return TASKERR_OUTOFMEMORY;
  221. }
  222. }
  223. /*
  224. * The new thread will free ThreadData - copy of Termination Event handle
  225. */
  226. TerminationEvent = ThreadData->TerminationEvent;
  227. /*
  228. * create another thread so that we can run the stream outside of
  229. * the context of the app.
  230. */
  231. ThreadHandle = CreateThread(NULL,
  232. MM_TASK_STACK_SIZE,
  233. mmStartTask,
  234. (LPVOID)ThreadData,
  235. 0,
  236. &ThreadId);
  237. if (ThreadHandle) {
  238. if (lph) {
  239. *lph = TerminationEvent;
  240. }
  241. CloseHandle(ThreadHandle);
  242. dprintf2(("Created task with thread id %x", ThreadId));
  243. return 0;
  244. } else {
  245. if (lph) {
  246. CloseHandle(ThreadData->TerminationEvent);
  247. }
  248. LocalFree(ThreadData);
  249. return TASKERR_OUTOFMEMORY;
  250. }
  251. }
  252. /***************************************************************************
  253. *
  254. * @doc DDK MMSYSTEM TASK
  255. *
  256. * @api DWORD | mmStartTask | This function is a stub for a new task.
  257. *
  258. * @parm LPVOID | lpThreadParameter | Points to the data for the
  259. * thread. In our case this is an MM_THREAD_START_DATA
  260. * packet.
  261. *
  262. * @rdesc Returns the return code of the thread routine passed.
  263. *
  264. * @comm When a mmsystem task is created, this routine will always be
  265. * the entry point for it. It calls the routine the application
  266. * wanted then sets an event for termination. The reason for this
  267. * is that we often want to wait for the thread to terminate inside
  268. * winmm's DLL init routine which deadlock if you wait for threads to
  269. * really terminate. On the other hand we don't want the thread to
  270. * be executing other DLL's code at the point when we say we're
  271. * finished because that DLL may be unloading.
  272. *
  273. * The task terminates when it returns.
  274. *
  275. * @xref mmTaskSignal mmTaskBlock
  276. *
  277. ***************************************************************************/
  278. STATICFN DWORD mmStartTask(LPVOID lpThreadParameter)
  279. {
  280. MM_THREAD_START_DATA ThreadData;
  281. DWORD ThreadReturn;
  282. /*
  283. * Take a copy of the input data and free the allocated memory
  284. */
  285. ThreadData = *(MM_THREAD_START_DATA *)lpThreadParameter;
  286. LocalFree(lpThreadParameter);
  287. /*
  288. * Call the real thread
  289. */
  290. ThreadReturn = (*ThreadData.lpfn)((PVOID)ThreadData.dwInst);
  291. /*
  292. * The real thread is now finshed so set its event
  293. */
  294. if (ThreadData.TerminationEvent) {
  295. SetEvent(ThreadData.TerminationEvent);
  296. }
  297. /*
  298. * Return the return code the thread wanted to return
  299. */
  300. return ThreadReturn;
  301. }