windows-nt/Source/XPSP1/NT/enduser/netmeeting/as/cpi32/sch.cpp
2020-09-26 16:20:57 +08:00

494 lines
12 KiB
C++

#include "precomp.h"
//
// SCH.CPP
// Scheduler
//
// Copyright(c) Microsoft Corporation 1997-
//
#define MLZ_FILE_ZONE ZONE_CORE
//
//
// SCH_Init - see sch.h
//
//
BOOL SCH_Init(void)
{
BOOL rc = FALSE;
DebugEntry(SCH_Init);
ASSERT(!g_schEvent);
ASSERT(!g_schThreadID);
ASSERT(!g_schMessageOutstanding);
//
// Create g_schEvent with:
// - default security descriptor
// - auto-reset (resets when a thread is unblocked)
// - initially signalled
//
g_schEvent = CreateEvent( NULL, FALSE, TRUE, SCH_EVENT_NAME );
if (g_schEvent == NULL)
{
ERROR_OUT(( "Failed to create g_schEvent"));
DC_QUIT;
}
InitializeCriticalSection(&g_schCriticalSection);
g_schCurrentMode = SCH_MODE_ASLEEP;
// lonchanc: do not start the scheduler as default
// SCHSetMode(SCH_MODE_NORMAL);
if (!DCS_StartThread(SCH_PacingProcessor))
{
ERROR_OUT(( "Failed to create SCH_PacingProcessor thread"));
DC_QUIT;
}
ASSERT(g_schThreadID);
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(SCH_Init, rc);
return(rc);
}
//
//
// SCH_Term - see sch.h
//
//
void SCH_Term(void)
{
DebugEntry(SCH_Term);
//
// This code needs to work even if SCH_Init hasn't been called or
// failed in the middle.
//
if (g_schEvent)
{
if (g_schThreadID)
{
//
// The scheduler thread exits its main loop when it spots that
// g_schTerminating is TRUE. So all we have to do is ensure
// that it runs its loop at least once more... It will clear g_schTerm-
// inated just before exiting.
//
g_schTerminating = TRUE;
SCH_ContinueScheduling(SCH_MODE_NORMAL);
while (g_schTerminating)
{
Sleep(0);
}
ASSERT(!g_schThreadID);
TRACE_OUT(("sch thread terminated"));
//
// Make sure we clear the message outstanding variable when
// our thread exits.
//
g_schMessageOutstanding = FALSE;
}
DeleteCriticalSection(&g_schCriticalSection);
CloseHandle(g_schEvent);
g_schEvent = NULL;
}
DebugExitVOID(SCH_Term);
}
//
//
// SCH_ContinueScheduling - see sch.h
//
//
void SCH_ContinueScheduling(UINT schedulingMode)
{
DebugEntry(SCH_ContinueScheduling);
ASSERT( ((schedulingMode == SCH_MODE_NORMAL) ||
(schedulingMode == SCH_MODE_TURBO)));
EnterCriticalSection(&g_schCriticalSection); // lonchanc: need crit sect protection
if (g_schCurrentMode == SCH_MODE_TURBO)
{
if (schedulingMode == SCH_MODE_TURBO)
{
SCHSetMode(schedulingMode);
}
DC_QUIT;
}
if (schedulingMode != g_schCurrentMode)
{
SCHSetMode(schedulingMode);
}
DC_EXIT_POINT:
g_schStayAwake = TRUE;
LeaveCriticalSection(&g_schCriticalSection); // lonchanc: need crit sect protection
DebugExitVOID(SCH_ContinueScheduling);
}
//
//
// SCH_SchedulingMessageProcessed - see sch.h
//
//
void SCH_SchedulingMessageProcessed()
{
DebugEntry(SCH_SchedulingMessageProcessed);
g_schMessageOutstanding = FALSE;
DebugExitVOID(SCH_SchedulingMessageProcessed);
}
//
// Name: SCH_PacingProcessor
//
// Purpose: The main function executed by the scheduling thread.
//
// Returns: Zero.
//
// Params: syncObject - object to pass back to SetEvent
//
// Operation: The thread enters a main loop which continues while the
// scheduler is initialized.
//
// The thread sets its priority to TIME_CRITICAL in order
// that it runs as soon as possible when ready.
//
// The thread waits on an event (g_schEvent) with a timeout that
// is set according to the current scheduler mode.
//
// The thread runs due to either:
// - the timeout expiring, which is the normal periodic
// scheduler behavior, or
// - g_schEvent being signalled, which is how the scheduler is
// woken from ASLEEP mode.
//
// The thread then posts a scheduler message the the Share Core
// (if there is not one already outstanding) and loops back
// to wait on g_schEvent.
//
// Changes in the scheduler mode are caused by calls to
// SCH_ContinueScheduling updating variables accessed in this
// routine, or by calculations made within the main loop of
// this routine (e.g. TURBO mode timeout).
//
//
DWORD WINAPI SCH_PacingProcessor(LPVOID hEventWait)
{
UINT rc = 0;
DWORD rcWait;
UINT timeoutPeriod;
DebugEntry(SCH_PacingProcessor);
//
// Give ourselves the highest possible priority (within our process
// priority class) to ensure that we run regularly to keep the
// scheduling messages flowing.
//
if (!SetThreadPriority( GetCurrentThread(),
THREAD_PRIORITY_TIME_CRITICAL ))
{
WARNING_OUT(( "SetThreadPriority failed"));
}
timeoutPeriod = g_schTimeoutPeriod;
g_schThreadID = GetCurrentThreadId();
//
// Let the caller continue
//
SetEvent((HANDLE)hEventWait);
//
// Keep looping until the scheduler terminates.
//
while (!g_schTerminating)
{
//
// Wait on g_schEvent with a timeout value that is set according
// to the current scheduling mode.
//
// When we are active (NORMAL/TURBO scheduling) the timeout
// period is a fraction of a second, so the normal behavior is
// for this call to timeout, rather than be signalled.
//
rcWait = WaitForSingleObject(g_schEvent, timeoutPeriod);
EnterCriticalSection(&g_schCriticalSection);
if (g_schMessageOutstanding)
{
//
// We must ensure that we post at least one scheduling message
// before we can attempt to sleep - so force schStayAwake to
// TRUE to keep us awake until we do post another message.
//
TRACE_OUT(( "Don't post message - one outstanding"));
g_schStayAwake = TRUE;
}
//
// If g_schEvent was signalled, then enter NORMAL scheduling mode.
//
if (rcWait == WAIT_OBJECT_0)
{
SCHSetMode(SCH_MODE_NORMAL);
}
else if (!g_schStayAwake)
{
TRACE_OUT(( "Sleep!"));
SCHSetMode(SCH_MODE_ASLEEP);
}
else if ( (g_schCurrentMode == SCH_MODE_TURBO) &&
((GetTickCount() - g_schLastTurboModeSwitch) >
SCH_TURBO_MODE_DURATION) )
{
//
// Switch from turbo state back to normal state.
//
SCHSetMode(SCH_MODE_NORMAL);
}
//
// Post the scheduling message - but only if there is not one
// already outstanding.
//
if (!g_schMessageOutstanding && !g_schTerminating)
{
SCHPostSchedulingMessage();
g_schStayAwake = FALSE;
}
timeoutPeriod = g_schTimeoutPeriod;
LeaveCriticalSection(&g_schCriticalSection);
}
g_schThreadID = 0;
g_schTerminating = FALSE;
DebugExitDWORD(SCH_PacingProcessor, rc);
return(rc);
}
//
// Name: SCHPostSchedulingMessage
//
// Purpose: Posts the scheduling message to the main Share Core window.
//
// Returns: Nothing.
//
// Params: None.
//
//
void SCHPostSchedulingMessage(void)
{
DebugEntry(SCHPostSchedulingMessage);
if (PostMessage( g_asMainWindow, DCS_PERIODIC_SCHEDULE_MSG, 0, 0 ))
{
g_schMessageOutstanding = TRUE;
}
DebugExitVOID(SCHPostSchedulingMessage);
}
//
// Name: SCHSetMode
//
// Purpose: Sets the current scheduler mode - and wakes the scheduler
// thread if necessary.
//
// Returns: Nothing.
//
// Params: newMode
//
//
void SCHSetMode(UINT newMode)
{
DebugEntry(SCHSetMode);
ASSERT( ((newMode == SCH_MODE_ASLEEP) ||
(newMode == SCH_MODE_NORMAL) ||
(newMode == SCH_MODE_TURBO) ));
EnterCriticalSection(&g_schCriticalSection);
TRACE_OUT(( "Switching from state %u -> %u", g_schCurrentMode, newMode));
if (newMode == SCH_MODE_TURBO)
{
g_schLastTurboModeSwitch = GetTickCount();
}
if (g_schCurrentMode == SCH_MODE_ASLEEP)
{
//
// Wake up the scheduler.
//
TRACE_OUT(( "Waking up scheduler - SetEvent"));
if (!SetEvent(g_schEvent))
{
ERROR_OUT(( "Failed SetEvent(%#x)", g_schEvent));
}
}
g_schCurrentMode = newMode;
g_schTimeoutPeriod = (newMode == SCH_MODE_ASLEEP) ? INFINITE :
((newMode == SCH_MODE_NORMAL) ? SCH_PERIOD_NORMAL :
SCH_PERIOD_TURBO);
LeaveCriticalSection(&g_schCriticalSection);
DebugExitVOID(SCHSetMode);
}
//
// DCS_StartThread(...)
//
// See ut.h
//
// DESCRIPTION:
// ============
// Start a new thread.
//
// PARAMETERS:
// ===========
// entryFunction : A pointer to the thread entry point.
// timeout : timeout in milliseconds
//
// RETURNS:
// ========
// Nothing.
//
//
BOOL DCS_StartThread
(
LPTHREAD_START_ROUTINE entryFunction
)
{
BOOL rc = FALSE;
HANDLE hndArray[2];
DWORD tid;
DWORD dwrc;
DebugEntry(DCS_StartThread);
//
// The event handle ( hndArray[0] ) is initialized in the call to CreateEvent,
// but in the case where that fails, we would try to CloseHandle on
// a garbage hndArray[1]. So we have to initialize the ThreadHandle
//
hndArray[1] = 0;
//
// Create event - initially non-signalled; manual control.
//
hndArray[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
if (hndArray[0] == 0)
{
ERROR_OUT(("Failed to create event: sys rc %lu", GetLastError()));
DC_QUIT;
}
TRACE_OUT(("Event 0x%08x created - now create thread", hndArray[0]));
//
// Start a new thread to run the DC-Share core task.
// Use C runtime (which calls CreateThread) to avoid memory leaks.
//
hndArray[1] = CreateThread(NULL, 0, entryFunction, (LPVOID)hndArray[0],
0, &tid);
if (hndArray[1] == 0)
{
//
// Failed!
//
ERROR_OUT(("Failed to create thread: sys rc %lu", GetLastError()));
DC_QUIT;
}
TRACE_OUT(("Thread 0x%08x created - now wait signal", hndArray[1]));
//
// Wait for thread exit or event to be set.
//
dwrc = WaitForMultipleObjects(2, hndArray, FALSE, INFINITE);
switch (dwrc)
{
case WAIT_OBJECT_0:
//
// Event triggered - thread initialised OK.
//
TRACE_OUT(("event signalled"));
rc = TRUE;
break;
case WAIT_OBJECT_0 + 1:
ERROR_OUT(("Thread exited with rc"));
break;
case WAIT_TIMEOUT:
TRACE_OUT(("Wait timeout"));
break;
default:
TRACE_OUT(("Wait returned %d", dwrc));
break;
}
DC_EXIT_POINT:
//
// Destroy event object.
//
if (hndArray[0] != 0)
{
TRACE_OUT(("Destroy event object"));
CloseHandle(hndArray[0]);
}
//
// Destroy thread handle object.
//
if (hndArray[1] != 0)
{
TRACE_OUT(("Destroy thread handle object"));
CloseHandle(hndArray[1]);
}
DebugExitBOOL(DCS_StartThread, rc);
return(rc);
}