494 lines
12 KiB
C++
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);
|
|
|
|
}
|
|
|
|
|
|
|