NT4/private/windows/media/winmm/task.c
2020-09-30 17:12:29 +02:00

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;
}