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