358 lines
10 KiB
C
358 lines
10 KiB
C
|
/******************************************************************************
|
||
|
|
||
|
Copyright (C) Microsoft Corporation 1985-1990. All rights reserved.
|
||
|
|
||
|
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 <winmmi.h>
|
||
|
|
||
|
#define MM_TASK_STACK_SIZE 0x200
|
||
|
|
||
|
/*
|
||
|
* Private structure type passed from mmTaskCreate to mmStartTask
|
||
|
*/
|
||
|
|
||
|
typedef struct {
|
||
|
HANDLE TerminationEvent;
|
||
|
DWORD 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 <f mmGetCurrentTask>.
|
||
|
*
|
||
|
* @xref mmTaskSignal mmTaskCreate
|
||
|
*
|
||
|
* @comm WARNING : For predictable results, must only be called from a
|
||
|
* task created with <f mmTaskCreate>.
|
||
|
*
|
||
|
*************************************************************************/
|
||
|
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 <f mmGetCurrentTask>.
|
||
|
*
|
||
|
* @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 <f mmTaskCreate>.
|
||
|
*
|
||
|
*************************************************************************/
|
||
|
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 <f Yield> or the undocumented Kernel yield
|
||
|
* function to yield within a task created with <f mmTaskCreate>.
|
||
|
*
|
||
|
*************************************************************************/
|
||
|
VOID APIENTRY mmTaskYield(VOID) {
|
||
|
Yield();
|
||
|
}
|
||
|
|
||
|
/*************************************************************************
|
||
|
*
|
||
|
* @doc DDK MMSYSTEM TASK
|
||
|
*
|
||
|
* @api HTASK | mmGetCurrentTask | This function returns the
|
||
|
* handle of the currently executing task created with
|
||
|
* <f mmTaskCreate>.
|
||
|
*
|
||
|
* @rdesc Returns a task handle. For predictable results and future
|
||
|
* compatibility, use this function rather than <f GetCurrentTask>
|
||
|
* to get the task handle of a task created with <f mmTaskCreate>.
|
||
|
*
|
||
|
* @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 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;
|
||
|
|
||
|
}
|