// This routine calculates the next timeout interval for the various // activity timers. This routine doesn't sort timers, on the assumption // that a given system will have relatively few inactivity timers. DWORD GetNextInactivityTimeout(DWORD dwElapsed) { DWORD dwTimeout = INFINITE; DWORD dwIndex; PACTIVITY_TIMER pat; PMLOCK(); for(dwIndex = 0; (pat = gppActivityTimers[dwIndex]) != NULL; dwIndex++) { DWORD dwTimeLeft = pat->dwTimeLeft; if(dwTimeLeft != INFINITE) { // subtract elapsed time if(dwTimeLeft < dwElapsed) { dwTimeLeft = 0; } else { dwTimeLeft -= dwElapsed; } // update the timeout value if(dwTimeout == INFINITE || dwTimeLeft < dwTimeout) { dwTimeout = dwTimeLeft; } // update the timer pat->dwTimeLeft = dwTimeLeft; } } PMUNLOCK(); return dwTimeout; }
// This routine initializes the list of activity timers. It returns ERROR_SUCCESS // if successful or a Win32 error code otherwise. DWORD ActivityTimerInitList(VOID) { DWORD dwStatus; HKEY hk = NULL; TCHAR szSources[1024]; DWORD dwNumTimers = 0; PPACTIVITY_TIMER ppatList = NULL; #ifndef SHIP_BUILD SETFNAME(_T("ActivityTimerInitList")); #endif wsprintf(szSources, _T("%s\\ActivityTimers"), PWRMGR_REG_KEY); dwStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szSources, 0, 0, &hk); if(dwStatus == ERROR_SUCCESS) { // figure out how many values are associated with the key dwStatus = RegQueryInfoKey(hk, NULL, NULL, NULL, &dwNumTimers, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } else { // no timers configured in the registry dwNumTimers = 0; dwStatus = ERROR_SUCCESS; } // if there are any values, allocate an array to hold them if(dwStatus == ERROR_SUCCESS) { ppatList = (PPACTIVITY_TIMER) PmAlloc((dwNumTimers + 1) * sizeof(PACTIVITY_TIMER)); if(ppatList == NULL) { PMLOGMSG(ZONE_WARN, (_T("%s: couldn't allocate %d timers\r\n"), pszFname, dwNumTimers)); dwStatus = ERROR_NOT_ENOUGH_MEMORY; } else { memset(ppatList, 0, (dwNumTimers + 1) * sizeof(PACTIVITY_TIMER)); ppatList[dwNumTimers] = NULL; } } // read list of timers if(dwStatus == ERROR_SUCCESS && dwNumTimers != 0) { DWORD dwIndex = 0; do { TCHAR szName[256]; DWORD cbValueName = dim(szName); dwStatus = RegEnumKeyEx(hk, dwIndex, szName, &cbValueName, NULL, NULL, NULL, NULL); if(dwStatus == ERROR_SUCCESS) { HKEY hkSubKey = NULL; // open the subkey dwStatus = RegOpenKeyEx(hk, szName, 0, 0, &hkSubKey); if(dwStatus == ERROR_SUCCESS) { DWORD dwSize, dwType, dwTimeout; LPTSTR pszValueName; // read the timeout, expressed in seconds dwSize = sizeof(dwTimeout); pszValueName = _T("Timeout"); dwStatus = RegQueryValueEx(hkSubKey, pszValueName, NULL, &dwType, (LPBYTE) &dwTimeout, &dwSize); if(dwStatus == ERROR_SUCCESS) { if(dwType != REG_DWORD || dwTimeout > MAXTIMERINTERVAL) { PMLOGMSG(ZONE_WARN, (_T("%s: RegQueryValueEx('%s'\'%s') or returned invalid type %d or invalid value %d\r\n"), pszFname, szName, pszValueName, dwType, dwTimeout)); dwStatus = ERROR_INVALID_DATA; } // convert timeout to milliseconds dwTimeout *= 1000; } else { // no timeout in seconds, try milliseconds dwSize = sizeof(dwTimeout); pszValueName = _T("TimeoutMs"); dwStatus = RegQueryValueEx(hkSubKey, pszValueName, NULL, &dwType, (LPBYTE) &dwTimeout, &dwSize); if(dwStatus != ERROR_SUCCESS || dwType != REG_DWORD || dwTimeout > (MAXTIMERINTERVAL * 1000)) { PMLOGMSG(ZONE_WARN, (_T("%s: RegQueryValueEx('%s'\'%s') failed %d (or returned invalid type %d or invalid value %d)\r\n"), pszFname, szName, pszValueName, dwStatus, dwType, dwTimeout)); dwStatus = ERROR_INVALID_DATA; } } if(dwStatus == ERROR_SUCCESS) { // get wake sources dwSize = sizeof(szSources); pszValueName = _T("WakeSources"); dwStatus = RegQueryValueEx(hkSubKey, pszValueName, NULL, &dwType, (LPBYTE) szSources, &dwSize); if(dwStatus != ERROR_SUCCESS) { // no wake sources szSources[0] = 0; szSources[1] = 0; dwStatus = ERROR_SUCCESS; } else if(dwType != REG_MULTI_SZ) { PMLOGMSG(ZONE_WARN, (_T("%s: invalid type %d for '%s'\'%s'\r\n"), pszFname, dwType, szName, pszValueName)); dwStatus = ERROR_INVALID_DATATYPE; } else { szSources[dim(szSources) -1] = szSources[dim(szSources) -2] = 0; // Terminate MultiSZ } } // did we get the parameters? if(dwStatus == ERROR_SUCCESS) { ppatList[dwIndex] = ActivityTimerCreate(szName, dwTimeout, szSources); if(ppatList[dwIndex] == NULL) { dwStatus = ERROR_NOT_ENOUGH_MEMORY; } } } // release the registry key RegCloseKey(hkSubKey); } // update the index dwIndex++; } while(dwStatus == ERROR_SUCCESS && dwIndex < dwNumTimers); // did we read all items ok? if(dwStatus == ERROR_NO_MORE_ITEMS) { dwStatus = ERROR_SUCCESS; } // terminate the list with a NULL ppatList[dwIndex] = NULL; } // did we succeed? if(dwStatus == ERROR_SUCCESS) { PMLOCK(); gppActivityTimers = ppatList; PMUNLOCK(); } else { DWORD dwIndex; if(ppatList != NULL) { for(dwIndex = 0; dwIndex < dwNumTimers; dwIndex++) { if(ppatList[dwIndex] != NULL) { ActivityTimerDestroy(ppatList[dwIndex]); } } PmFree(ppatList); } } // release resources if(hk != NULL) RegCloseKey(hk); PMLOGMSG(dwStatus != ERROR_SUCCESS && ZONE_WARN, (_T("%s: returning %d\r\n"), pszFname, dwStatus)); return dwStatus; }
// this thread handles activity timer events DWORD WINAPI ActivityTimersThreadProc(LPVOID lpvParam) { DWORD dwStatus, dwNumEvents, dwWaitInterval; HANDLE hevReady = (HANDLE) lpvParam; HANDLE hEvents[MAXIMUM_WAIT_OBJECTS]; BOOL fDone = FALSE; HANDLE hevDummy = NULL; INT iPriority; const DWORD cdwTimerBaseIndex = 2; #ifndef SHIP_BUILD SETFNAME(_T("ActivityTimersThreadProc")); #endif PMLOGMSG(ZONE_INIT, (_T("+%s: thread 0x%08x\r\n"), pszFname, GetCurrentThreadId())); // set the thread priority if(!GetPMThreadPriority(_T("TimerPriority256"), &iPriority)) { iPriority = DEF_ACTIVITY_TIMER_THREAD_PRIORITY; } CeSetThreadPriority(GetCurrentThread(), iPriority); // initialize the list of activity timers if(ActivityTimerInitList() != ERROR_SUCCESS) { PMLOGMSG(ZONE_WARN, (_T("%s: ActivityTimerInitList() failed\r\n"), pszFname)); goto done; } // create a dummy event that's never signaled hevDummy = CreateEvent(NULL, FALSE, FALSE, NULL); if(hevDummy == NULL) { PMLOGMSG(ZONE_WARN, (_T("%s: Couldn't create dummy event\r\n"), pszFname)); goto done; } // set up the list of events dwNumEvents = 0; hEvents[dwNumEvents++] = ghevPmShutdown; hEvents[dwNumEvents++] = ghevTimerResume; PMLOCK(); if(gppActivityTimers[0] == NULL) { // no activity timers defined PmFree(gppActivityTimers); gppActivityTimers = NULL; } else { // copy activity timer events into the event list while(dwNumEvents < dim(hEvents) && gppActivityTimers[dwNumEvents - cdwTimerBaseIndex] != NULL) { hEvents[dwNumEvents] = gppActivityTimers[dwNumEvents - cdwTimerBaseIndex]->hevReset; dwNumEvents++; } } PMUNLOCK(); // we're up and running SetEvent(hevReady); // are there actually any timers to wait on? if(dwNumEvents <= cdwTimerBaseIndex) { // no timers defined, so we don't need this thread to wait on them. PMLOGMSG(ZONE_INIT || ZONE_WARN, (_T("%s: no activity timers defined, exiting\r\n"), pszFname)); Sleep(1000); // don't want PM initialization to fail when we exit goto done; } // wait for these events to get signaled PMLOGMSG(ZONE_TIMERS, (_T("%s: entering wait loop, %d timers total\r\n"), pszFname, dwNumEvents - cdwTimerBaseIndex)); dwWaitInterval = 0; while(!fDone) { DWORD dwTimeout = GetNextInactivityTimeout(dwWaitInterval); DWORD dwWaitStart = GetTickCount(); PMLOGMSG(ZONE_TIMERS, (_T("%s: waiting %u (0x%08x) ms for next event, wait interval was %d\r\n"), pszFname, dwTimeout, dwTimeout, dwWaitInterval)); dwStatus = WaitForMultipleObjects(dwNumEvents, hEvents, FALSE, dwTimeout); dwWaitInterval = GetTickCount() - dwWaitStart; // figure out what caused the wakeup if(dwStatus == (WAIT_OBJECT_0 + 0)) { PMLOGMSG(ZONE_WARN, (_T("%s: shutdown event set\r\n"), pszFname)); fDone = TRUE; } else if(dwStatus == (WAIT_OBJECT_0 + 1)) { DWORD dwIndex; PACTIVITY_TIMER pat; // we've resumed, so re-enable all activity timers that can be reset PMLOGMSG(ZONE_TIMERS, (_T("%s: resume event set\r\n"), pszFname)); PMLOCK(); for(dwIndex = 0; (pat = gppActivityTimers[dwIndex]) != NULL; dwIndex++) { DWORD dwEventIndex = dwIndex + cdwTimerBaseIndex; if(hEvents[dwEventIndex] == hevDummy) { hEvents[dwEventIndex] = pat->hevReset; } pat->dwTimeLeft = pat->dwTimeout + dwWaitInterval; } PMUNLOCK(); } else if(dwStatus == WAIT_TIMEOUT) { DWORD dwIndex; PACTIVITY_TIMER pat; // figure out which event(s) timed out PMLOCK(); for(dwIndex = 0; (pat = gppActivityTimers[dwIndex]) != NULL; dwIndex++) { if(pat->dwTimeLeft <= dwWaitInterval && pat->dwTimeLeft != INFINITE) { // has the timer really expired? if(WaitForSingleObject(pat->hevReset, 0) == WAIT_OBJECT_0) { // The timer was reset while we weren't looking at it, so we'll look // at it again later. Calculate the new timeout, compensating for the update // that will occur in GetNextInactivityTimeout(). PMLOGMSG(ZONE_TIMERS, (_T("%s: timer '%s' reset after timeout\r\n"), pszFname, pat->pszName)); pat->dwTimeLeft = pat->dwTimeout + dwWaitInterval; pat->dwResetCount++; } else { // the timer has really expired, update events appropriately PMLOGMSG(ZONE_TIMERS, (_T("%s: timer '%s' has expired\r\n"), pszFname, pat->pszName)); ResetEvent(pat->hevActive); SetEvent(pat->hevInactive); // start looking at the reset event for this timer again hEvents[dwIndex + cdwTimerBaseIndex] = pat->hevReset; // update counts pat->dwTimeLeft = INFINITE; pat->dwExpiredCount++; } } } PMUNLOCK(); } else if(dwStatus > (WAIT_OBJECT_0 + 0) && dwStatus < (WAIT_OBJECT_0 + dwNumEvents)) { PACTIVITY_TIMER pat; DWORD dwEventIndex = dwStatus - WAIT_OBJECT_0; PMLOCK(); // get a pointer to the timer pat = gppActivityTimers[dwEventIndex - cdwTimerBaseIndex]; // handle its events DEBUGCHK(pat != NULL); if(pat->dwTimeout == 0) { // we're not using the event, so ignore it pat->dwTimeLeft = INFINITE; } else { PMLOGMSG(ZONE_TIMERS, (_T("%s: timer '%s' reset\r\n"), pszFname, pat->pszName)); // set events appropriately ResetEvent(pat->hevInactive); SetEvent(pat->hevActive); // don't look at this event again until it's about ready to time out hEvents[dwEventIndex] = hevDummy; // update time left on the timer, compensating for the update // that will occur in GetNextInactivityTimeout(). pat->dwTimeLeft = pat->dwTimeout + dwWaitInterval; } pat->dwResetCount++; PMUNLOCK(); } else { PMLOGMSG(ZONE_WARN, (_T("%s: WaitForMultipleObjects() returned %d, status is %d\r\n"), pszFname, dwStatus, GetLastError())); fDone = TRUE; } } done: // release resources if(hevDummy != NULL) CloseHandle(hevDummy); PMLOCK(); if(gppActivityTimers != NULL) { DWORD dwIndex = 0; while(gppActivityTimers[dwIndex] != NULL) { ActivityTimerDestroy(gppActivityTimers[dwIndex]); dwIndex++; } PmFree(gppActivityTimers); gppActivityTimers = NULL; } PMUNLOCK(); PMLOGMSG(ZONE_INIT | ZONE_WARN, (_T("-%s: exiting\r\n"), pszFname)); return 0; }
EXTERN_C DWORD WINAPI PlatformSetSystemPowerState (LPCTSTR pszName, BOOL fForce, BOOL fInternal) { DWORD dwStatus = ERROR_SUCCESS; PSYSTEM_POWER_STATE pNewSystemPowerState = NULL; PDEVICE_POWER_RESTRICTION pNewCeilingDx = NULL; BOOL fDoTransition = FALSE; INT iPreSuspendPriority = 0; static BOOL fFirstCall = TRUE; SETFNAME (_T ("PlatformSetSystemPowerState")); // Read system power state variables and construct new lists: if (gfFileSystemsAvailable) PmUpdateSystemPowerStatesIfChanged (); dwStatus = RegReadSystemPowerState (pszName, &pNewSystemPowerState, &pNewCeilingDx); // Did we get registry information about the new power state? if (dwStatus == ERROR_SUCCESS) { BOOL fSuspendSystem = FALSE; static BOOL fWantStartupScreen = FALSE; DWORD dwNewStateFlags = pNewSystemPowerState->dwFlags; // Assume we will update the system power state: fDoTransition = TRUE; // Are we going to suspend the system as a whole? if ((dwNewStateFlags & (POWER_STATE_SUSPEND | POWER_STATE_OFF | POWER_STATE_CRITICAL | POWER_STATE_RESET)) != 0) { fSuspendSystem = TRUE; } // A "critical" suspend might mean we have totally lost battery power and need // to suspend really quickly. Depending on the platform, OEMs may be able // to bypass driver notification entirely and rely on xxx_PowerDown() notifications // to suspend gracefully. Or they may be able to implement a critical suspend // kernel ioctl. This sample implementation is very generic and simply sets the // POWER_FORCE flag, which is not used. if (dwNewStateFlags & (POWER_STATE_CRITICAL | POWER_STATE_OFF | POWER_STATE_RESET)) { fForce = TRUE; } // If everything seems OK, do the set operation: if (fDoTransition) { POWER_BROADCAST_BUFFER pbb; PDEVICE_LIST pdl; BOOL fResumeSystem = FALSE; // Send out system power state change notifications: pbb.Message = PBT_TRANSITION; pbb.Flags = pNewSystemPowerState->dwFlags; pbb.Length = _tcslen (pNewSystemPowerState->pszName) + 1; // Char count not byte count for now if (pbb.Length > MAX_PATH) { // Truncate the system power state name -- note, we actually have MAX_PATH + 1 // characters available. pbb.Length = MAX_PATH; } _tcsncpy_s (pbb.SystemPowerState, _countof (pbb.SystemPowerState), pNewSystemPowerState->pszName, pbb.Length); pbb.Length *= sizeof (pbb.SystemPowerState[0]); // Convert to byte count GenerateNotifications ((PPOWER_BROADCAST) & pbb); // Is GWES ready? if (!gfGwesReady) { if (WaitForAPIReady (SH_GDI, 0) == WAIT_OBJECT_0) { gfGwesReady = TRUE; } } // Are we suspending? if (fSuspendSystem && gpfnGwesPowerDown != NULL) { // Start the process of suspending GWES: if (gfGwesReady) { fWantStartupScreen = gpfnGwesPowerDown (); } } // Update global system state variables: PMLOCK (); PSYSTEM_POWER_STATE pOldSystemPowerState = gpSystemPowerState; PDEVICE_POWER_RESTRICTION pOldCeilingDx = gpCeilingDx; if (gpSystemPowerState != NULL && (gpSystemPowerState-> dwFlags & (POWER_STATE_SUSPEND | POWER_STATE_OFF | POWER_STATE_CRITICAL)) != 0) { // We are exiting a suspended state: fResumeSystem = TRUE; } gpSystemPowerState = pNewSystemPowerState; gpCeilingDx = pNewCeilingDx; PMUNLOCK (); // Are we suspending, resuming, or neither? if (fSuspendSystem) { INT iCurrentPriority; // We're suspending: update all devices other than block devices, // in case any of them need to access the registry or write files. PMLOGMSG (ZONE_PLATFORM || ZONE_RESUME, (_T ("%s: suspending - notifying non-block drivers\r\n"), pszFname)); for (pdl = gpDeviceLists; pdl != NULL; pdl = pdl->pNext) { if (*pdl->pGuid != idBlockDevices) { UpdateClassDeviceStates (pdl); } } // Notify the kernel that we are about to suspend. This gives the // kernel an opportunity to clear wake source flags before we initiate // the suspend process. If we don't do this and a wake source interrupt // occurs between the time we call PowerOffSystem() and the time // OEMPowerOff() is invoked, it is hard for the kernel to know whether or // not to suspend. PMLOGMSG (ZONE_PLATFORM || ZONE_RESUME, (_T ("%s: calling KernelIoControl(IOCTL_HAL_PRESUSPEND)\r\n"), pszFname)); KernelIoControl (IOCTL_HAL_PRESUSPEND, NULL, 0, NULL, 0, NULL); iCurrentPriority = CeGetThreadPriority (GetCurrentThread ()); DEBUGCHK (iCurrentPriority != THREAD_PRIORITY_ERROR_RETURN); if (iCurrentPriority != THREAD_PRIORITY_ERROR_RETURN) { CeSetThreadPriority (GetCurrentThread (), giPreSuspendPriority); Sleep (0); CeSetThreadPriority (GetCurrentThread (), iCurrentPriority); } // Notify file systems that their block drivers will soon go away. // After making this call, this thread is the only one that can access // the file system (including registry and device drivers) without // blocking. Unfortunately, this API takes and holds the file system // critical section, so other threads attempting to access the registry // or files may cause priority inversions. To avoid priority problem // that may starve the Power Manager, we may raise our own priority to a // high level. Do this if giSuspendPriority is non-zero. if (giSuspendPriority != 0) { iPreSuspendPriority = CeGetThreadPriority (GetCurrentThread ()); DEBUGCHK (iPreSuspendPriority != THREAD_PRIORITY_ERROR_RETURN); PMLOGMSG (ZONE_PLATFORM, (_T ("%s: suspending: raising thread priority for 0x%08x from %d to %d\r\n"), pszFname, GetCurrentThreadId (), iPreSuspendPriority, giSuspendPriority)); CeSetThreadPriority (GetCurrentThread (), giSuspendPriority); } // Discard code pages from drivers. This is a diagnostic tool to // forcibly expose paging-related bugs that could cause apparently // random system crashes or hangs. Optionally, OEMs can disable this // for production systems to speed up resume times. We have to call // PageOutMode before FileSys Shutdown. Otherwise, it cause dead lock // between filesystem and loader. if (gfPageOutAllModules) { ForcePageout (); } if (g_pSysRegistryAccess) g_pSysRegistryAccess->EnterLock (); gfFileSystemsAvailable = FALSE; if ((dwNewStateFlags & POWER_STATE_RESET) != 0) { // Is this to be a cold boot? if (_tcscmp (pszName, _T ("coldreboot")) == 0) { SetCleanRebootFlag (); } } FileSystemPowerFunction (FSNOTIFY_POWER_OFF); // Update block device power states: PMLOGMSG (ZONE_PLATFORM || ZONE_RESUME, (_T ("%s: suspending - notifying block drivers\r\n"), pszFname)); pdl = GetDeviceListFromClass (&idBlockDevices); if (pdl != NULL) { UpdateClassDeviceStates (pdl); } // Handle resets and shutdowns here, after flushing files. Since Windows CE does // not define a standard mechanism for handling shutdown (via POWER_STATE_OFF), // OEMs will need to fill in the appropriate code here. Similarly, if an OEM does // not support IOCTL_HAL_REBOOT, they should not support POWER_STATE_RESET. if ((dwNewStateFlags & POWER_STATE_RESET) != 0) { // Should not return from this call, but if we do just suspend the system: KernelLibIoControl ((HANDLE) KMOD_OAL, IOCTL_HAL_REBOOT, NULL, 0, NULL, 0, NULL); RETAILMSG (TRUE, (_T ("PM: PlatformSetSystemPowerState: KernelIoControl(IOCTL_HAL_REBOOT) returned!\r\n"))); DEBUGCHK (FALSE); // Break into the debugger. } } else if (fResumeSystem) { // We're waking up from a resume -- update block device power states // so we can access the registry and/or files. PMLOGMSG (ZONE_PLATFORM || ZONE_RESUME, (_T ("%s: resuming - notifying block drivers\r\n"), pszFname)); pdl = GetDeviceListFromClass (&idBlockDevices); if (pdl != NULL) { UpdateClassDeviceStates (pdl); } // Notify file systems that their block drivers are back. FileSystemPowerFunction (FSNOTIFY_POWER_ON); gfFileSystemsAvailable = TRUE; if (g_pSysRegistryAccess) g_pSysRegistryAccess->LeaveLock (); // Update all devices other than block devices: PMLOGMSG (ZONE_PLATFORM || ZONE_RESUME, (_T ("%s: resuming - notifying block drivers\r\n"), pszFname)); for (pdl = gpDeviceLists; pdl != NULL; pdl = pdl->pNext) { if (*pdl->pGuid != idBlockDevices) { UpdateClassDeviceStates (pdl); } } // Tell GWES to wake up: if (gpfnGwesPowerUp != NULL && gfGwesReady) { gpfnGwesPowerUp (fWantStartupScreen); fWantStartupScreen = FALSE; } // Send out resume notification: pbb.Message = PBT_RESUME; pbb.Flags = 0; pbb.Length = 0; pbb.SystemPowerState[0] = 0; GenerateNotifications ((PPOWER_BROADCAST) & pbb); } else { // Update all devices without any particular ordering: UpdateAllDeviceStates (); } // Release the old state information: SystemPowerStateDestroy (pOldSystemPowerState); while (pOldCeilingDx != NULL) { PDEVICE_POWER_RESTRICTION pdpr = pOldCeilingDx->pNext; PowerRestrictionDestroy (pOldCeilingDx); pOldCeilingDx = pdpr; } // Are we suspending? if (fSuspendSystem) { // Set a flag to notify the resume thread that this was a controlled suspend. gfSystemSuspended = TRUE; PMLOGMSG (ZONE_PLATFORM || ZONE_RESUME, (_T ("%s: calling PowerOffSystem()\r\n"), pszFname)); PowerOffSystem (); // Sets a flag in the kernel for the scheduler. Sleep (0); // Force the scheduler to run. PMLOGMSG (ZONE_PLATFORM || ZONE_RESUME, (_T ("%s: back from PowerOffSystem()\r\n"), pszFname)); // Clear the suspend flag: gfSystemSuspended = FALSE; } } else { // Release the unused new state information: SystemPowerStateDestroy (pNewSystemPowerState); while (pNewCeilingDx != NULL) { PDEVICE_POWER_RESTRICTION pdpr = pNewCeilingDx->pNext; PowerRestrictionDestroy (pNewCeilingDx); pNewCeilingDx = pdpr; } } } // Restore our priority if we updated it during a suspend transition: if (giSuspendPriority != 0 && iPreSuspendPriority != 0) { PMLOGMSG (ZONE_PLATFORM, (_T ("%s: restoring thread priority to %d\r\n"), pszFname, iPreSuspendPriority)); CeSetThreadPriority (GetCurrentThread (), iPreSuspendPriority); } return dwStatus; }