/****************************************************************************** Copyright (c) 1985-1998 Microsoft Corporation Title: task.c - support for task creation and blocking Version: 1.00 Date: 05-Mar-1990 Author: ROBWI ------------------------------------------------------------------------------ Change log: DATE REV DESCRIPTION ----------- ----- ----------------------------------------------------------- 05-MAR-1990 ROBWI First Version - APIs and structures 18-APR-1990 ROBWI Ported from Resman to mmsystem 25-JUN-1990 ROBWI Added mmTaskYield 07-JUL-1991 CJP Modified to work with new stack switcher code SD Ported to NT RCBS Added NT function - Modelled on threads and PostThreadMessage : HTASK is thread id (DWORD) *****************************************************************************/ #define MMNOTIMER #define MMNOSEQ #define MMNOWAVE #define MMNOMIDI #define MMNOJOY #define MMNOSOUND #define MMNOMCI #define NOMIDIDEV #define NOWAVEDEV #define NOTIMERDEV #define NOJOYDEV #define NOMCIDEV #define NOSEQDEV #include #define MM_TASK_STACK_SIZE 0x200 /* * Private structure type passed from mmTaskCreate to mmStartTask */ typedef struct { HANDLE TerminationEvent; DWORD_PTR dwInst; LPTHREAD_START_ROUTINE lpfn; } MM_THREAD_START_DATA; /* * Task start stub */ STATICFN DWORD mmStartTask(LPVOID lpThreadParameter); /************************************************************************* * * @doc DDK MMSYSTEM TASK * * @api VOID | mmTaskBlock | This function blocks the current * task context if its event count is 0. * * @parm HANDLE | hTask | Task handle of the current task. For predictable * results, get the task handle from . * * @xref mmTaskSignal mmTaskCreate * * @comm WARNING : For predictable results, must only be called from a * task created with . * *************************************************************************/ VOID APIENTRY mmTaskBlock(DWORD h) { MSG msg; /* * Loop until we get the message we want */ for (;;) { /* * Retrieve any message for task */ GetMessage(&msg, NULL, 0, 0); /* * If the message is for a window dispatch it */ if (msg.hwnd != NULL) { DispatchMessage(&msg); } else { /* * WM_USER is the signal message */ if (msg.message == WM_USER) { break; } } } return; } /************************************************************************* * * @doc DDK MMSYSTEM TASK * * @api BOOL | mmTaskSignal | This function signals the specified * task, incrementing its event count and unblocking * it. * * @parm HTASK | hTask | Task handle. For predictable results, get the * task handle from . * * @rdesc Returns TRUE if the signal was sent, else FALSE if the message * queue was full. * * @xref mmTaskBlock mmTaskCreate * * @comm Must be callable at interrupt time! WARNING : For * predictable results, must only be called from a task * created with . * *************************************************************************/ BOOL APIENTRY mmTaskSignal(DWORD h) { #ifdef DBG BOOL fErr; dprintf2(("Signalling Thread %x", (ULONG)h)); fErr = PostThreadMessage((DWORD)h, WM_USER, 0, 0); if (!fErr) { dprintf1(("Error %d signalling Thread %x", GetLastError(), (ULONG)h)); } return(fErr); #else return PostThreadMessage((DWORD)h, WM_USER, 0, 0); #endif } /************************************************************************* * * @doc DDK MMSYSTEM TASK * * @api VOID | mmTaskYield | This function causes the current task * to yield. * * @comm For predictable results and future compatibility, use this * function rather than or the undocumented Kernel yield * function to yield within a task created with . * *************************************************************************/ VOID APIENTRY mmTaskYield(VOID) { Yield(); } /************************************************************************* * * @doc DDK MMSYSTEM TASK * * @api HTASK | mmGetCurrentTask | This function returns the * handle of the currently executing task created with * . * * @rdesc Returns a task handle. For predictable results and future * compatibility, use this function rather than * to get the task handle of a task created with . * * @xref mmTaskCreate * *************************************************************************/ DWORD APIENTRY mmGetCurrentTask(VOID) { return (DWORD)GetCurrentThreadId(); } /*************************************************************************** * * @doc DDK MMSYSTEM TASK * * @api UINT | mmTaskCreate | This function creates a new task. * * @parm LPTASKCALLBACK | lpfn | Points to a program supplied * function and represents the starting address of the new * task. * * @parm HANDLE * | lph | Points to the variable that receives the * task handle (NOT the task identifier). This is used by * systems that wish to use the handle to wait for task * termination. If lph is 0 the thread handle is closed here * * @parm DWORD | dwStack | Specifies the size of the stack to be * provided to the task. * * @parm DWORD | dwInst | DWORD of instance data to pass to the task * routine. * * @rdesc Returns zero if the function is successful. Otherwise it * returns an error value which may be one of the following: * * @flag TASKERR_NOTASKSUPPORT | Task support is not available. * @flag TASKERR_OUTOFMEMORY | Not enough memory to create task. * * @comm When a mmsystem task is created, the system will make a far * call to the program-supplied function whose address is * specified by the lpfn parameter. This function may include * local variables and may call other functions as long as * the stack has sufficient space. * * The task terminates when it returns. * * @xref mmTaskSignal mmTaskBlock * ***************************************************************************/ UINT APIENTRY mmTaskCreate(LPTASKCALLBACK lpfn, HANDLE * lph, DWORD_PTR dwInst) { DWORD ThreadId; HANDLE ThreadHandle; HANDLE TerminationEvent; MM_THREAD_START_DATA *ThreadData; /* * Create a block to pass stuff to our new thread */ ThreadData = (MM_THREAD_START_DATA *)LocalAlloc(LPTR, sizeof(*ThreadData)); if (ThreadData == NULL) { return TASKERR_OUTOFMEMORY; } ThreadData->dwInst = dwInst; ThreadData->lpfn = (LPTHREAD_START_ROUTINE)lpfn; /* * We create an event which will be set when the thread terminates * The initial state is NOT signalled. This means that the handle * can be waited on immediately. */ if (lph) { ThreadData->TerminationEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (ThreadData->TerminationEvent == NULL) { LocalFree(ThreadData); return TASKERR_OUTOFMEMORY; } } /* * The new thread will free ThreadData - copy of Termination Event handle */ TerminationEvent = ThreadData->TerminationEvent; /* * create another thread so that we can run the stream outside of * the context of the app. */ ThreadHandle = CreateThread(NULL, MM_TASK_STACK_SIZE, mmStartTask, (LPVOID)ThreadData, 0, &ThreadId); if (ThreadHandle) { if (lph) { *lph = TerminationEvent; } CloseHandle(ThreadHandle); dprintf2(("Created task with thread id %x", ThreadId)); return 0; } else { if (lph) { CloseHandle(ThreadData->TerminationEvent); } LocalFree(ThreadData); return TASKERR_OUTOFMEMORY; } } /*************************************************************************** * * @doc DDK MMSYSTEM TASK * * @api DWORD | mmStartTask | This function is a stub for a new task. * * @parm LPVOID | lpThreadParameter | Points to the data for the * thread. In our case this is an MM_THREAD_START_DATA * packet. * * @rdesc Returns the return code of the thread routine passed. * * @comm When a mmsystem task is created, this routine will always be * the entry point for it. It calls the routine the application * wanted then sets an event for termination. The reason for this * is that we often want to wait for the thread to terminate inside * winmm's DLL init routine which deadlock if you wait for threads to * really terminate. On the other hand we don't want the thread to * be executing other DLL's code at the point when we say we're * finished because that DLL may be unloading. * * The task terminates when it returns. * * @xref mmTaskSignal mmTaskBlock * ***************************************************************************/ STATICFN DWORD mmStartTask(LPVOID lpThreadParameter) { MM_THREAD_START_DATA ThreadData; DWORD ThreadReturn; /* * Take a copy of the input data and free the allocated memory */ ThreadData = *(MM_THREAD_START_DATA *)lpThreadParameter; LocalFree(lpThreadParameter); /* * Call the real thread */ ThreadReturn = (*ThreadData.lpfn)((PVOID)ThreadData.dwInst); /* * The real thread is now finshed so set its event */ if (ThreadData.TerminationEvent) { SetEvent(ThreadData.TerminationEvent); } /* * Return the return code the thread wanted to return */ return ThreadReturn; }