text: /*++
  
  Copyright (C) 1996-2001 Microsoft Corporation
  
  Module Name:
  
      THROTTLE.CPP
  
  Abstract:
  
          see throttle.h
  
  History:
  
  	24-Oct-200   ivanbrug    created.
  
  
  --*/
  
  #include "precomp.h"
  #include <throttle.h>
  #include <arrtempl.h>
  
  #include <wbemint.h>
  
  typedef NTSTATUS (NTAPI * fnNtQuerySystemInformation )(
      IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
      OUT PVOID SystemInformation,
      IN ULONG SystemInformationLength,
      OUT PULONG ReturnLength OPTIONAL
      );    
  
  fnNtQuerySystemInformation MyNtQuerySystemInformation = NULL;
  
  HRESULT POLARITY
  Throttle(DWORD dwFlags,
           DWORD IdleMSec,         // in MilliSeconds
           DWORD IoIdleBytePerSec, // in BytesPerSec
           DWORD SleepLoop,        // in MilliSeconds
           DWORD MaxWait)          // in MilliSeconds
  //
  //  This function will wait until one of this two/three things have happened:
  //  - the system has been idle for at least IdleMSec milliseconds
  //       (no user input within IdleMSec milliseconds)
  //  - the Number of bytes per seconds passed through 
  //      the IO system is below a threashold
  //  - the MaxWait time has elapsed
  //  
  //  Reasonable Params are 
  //  3000-5000 millisecond with THROTTLE_USER
  //  300.000 - 500.000 bytes/second with THROTTLE_IO
  //  200 - 500 milliseconds for SleepLoop 
  //  several minutes  for MaxWait
  //
  //  remarks: 
  //  - the function will SUCCEDED(Throttle), no matter 
  //  - if the MaxWait has been reached or if the Idle conditions 
  //    have been met the function will fail in any other case
  //  - the function is 'precise' within the range if a System Tick 
  //    (15ms on professional)
  //  - in the case of an IO throttling, there will always be a Sleep(150)
  //
  {
      //
      // init static and globals
      //
  
      if (!MyNtQuerySystemInformation)
      {
          HMODULE hDll = GetModuleHandleW(L"ntdll.dll");
          if (hDll){
              MyNtQuerySystemInformation = (fnNtQuerySystemInformation)GetProcAddress(hDll,"NtQuerySystemInformation"); 
  			if ( MyNtQuerySystemInformation == NULL )
  			{
  				return WBEM_E_FAILED;
  			}
          } else {
              return WBEM_E_FAILED;
          }
      }
  
      static DWORD TimeInc = 0;
      if (!TimeInc)
      {
          BOOL  bIsValid;
          DWORD dwAdj;
          
          if (!GetSystemTimeAdjustment(&dwAdj,&TimeInc,&bIsValid))
          {
              return WBEM_E_FAILED;
          }
      }
  
      static DWORD PageSize = 0;
      if (!PageSize)
      {
          SYSTEM_INFO  SysInfo;
          GetSystemInfo(&SysInfo);
          PageSize = SysInfo.dwPageSize; 
      }
      //
      // param validation
      //
      if ((dwFlags & ~THROTTLE_ALLOWED_FLAGS) ||
          (0 == SleepLoop))
          return WBEM_E_INVALID_PARAMETER;
  
      DWORD nTimes = MaxWait/SleepLoop;
      // user input structures
      LASTINPUTINFO LInInfo;
      LInInfo.cbSize = sizeof(LASTINPUTINFO);
      DWORD i;
      DWORD Idle100ns = 10000*IdleMSec; // conversion from 1ms to 100ns
      // io throttling
      SYSTEM_PERFORMANCE_INFORMATION SPI[2];
      BOOL bFirstIOSampleDone = FALSE;
      DWORD dwWhich = 0;
      DWORD cbIO = 1+IoIdleBytePerSec;
      DWORD cbIOOld = 0;
      // boolean logic
      BOOL  bCnd1 = FALSE;
      BOOL  bCnd2 = FALSE;
      // registry stuff for wmisvc to force exit from this function
      HKEY hKey = NULL;
      LONG lRes;
      DWORD dwType;
      DWORD dwLen = sizeof(DWORD);
      DWORD dwVal;
      
      lRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
      	               HOME_REG_PATH,
      	               0,
      	               KEY_READ,
      	               &hKey);
      if (ERROR_SUCCESS != lRes)
      	return WBEM_E_FAILED;
      CRegCloseMe cm_(hKey);
      
      if (dwFlags & THROTTLE_IO)
      {
          NTSTATUS Status;
      	Status = MyNtQuerySystemInformation(SystemPerformanceInformation,
                                            &SPI[dwWhich],
             	                              sizeof(SYSTEM_PERFORMANCE_INFORMATION),
                 	                          0);        
          if (0 != Status)
          {
              return WBEM_E_FAILED;
          }
          dwWhich = (dwWhich+1)%2;
          Sleep(150);
      }
  
  
      for (i=0;i<nTimes;i++)
      {
          //
          // check if someone is telling us to stop waiting
          //
          lRes = RegQueryValueEx(hKey,
                               DO_THROTTLE,
                               0,
                               &dwType,                             
                               (BYTE*)&dwVal,
                               &dwLen);
          if(ERROR_SUCCESS == lRes &&
            0 == dwVal)
            return THROTTLE_FORCE_EXIT;
          	
          //
          //    check the user-input idleness
          //
          if (dwFlags & THROTTLE_USER)
          {
  	        if (!GetLastInputInfo(&LInInfo))
  	            return WBEM_E_FAILED;
  	        DWORD Now = GetTickCount();
  	        if (Now < LInInfo.dwTime)
  	        {
  	            continue; // one of the 49.7 days events
  	        }
  	        DWORD LastInput100ns = (Now - LInInfo.dwTime)*TimeInc;
  	        if (LastInput100ns >= Idle100ns)
  	        {
                  if (0 == (dwFlags & ~THROTTLE_USER)) {
                      return THROTTLE_USER_IDLE;
                  } else {
                      bCnd1 = TRUE;
                  };	            
  	        }
          }
          //
          // avoid checking the second condition 
          // if the first is FALSE
          //
          if (((dwFlags & (THROTTLE_IO|THROTTLE_USER)) == (THROTTLE_IO|THROTTLE_USER)) &&
              !bCnd1)
          {
              goto sleep_label;
          }
          //
          //  check the io idleness
          //
          if (dwFlags & THROTTLE_IO)
          {
  	        NTSTATUS Status;
      	    Status = MyNtQuerySystemInformation(SystemPerformanceInformation,
          	                                  &SPI[dwWhich],
              	                              sizeof(SYSTEM_PERFORMANCE_INFORMATION),
                  	                          0);
              if (0 == Status){
              
                  cbIOOld = cbIO;
                  cbIO = (DWORD)((SPI[dwWhich].IoReadTransferCount.QuadPart   - SPI[(dwWhich-1)%2].IoReadTransferCount.QuadPart) +
                                 (SPI[dwWhich].IoWriteTransferCount.QuadPart  - SPI[(dwWhich-1)%2].IoWriteTransferCount.QuadPart) +
                                 (SPI[dwWhich].IoOtherTransferCount.QuadPart  - SPI[(dwWhich-1)%2].IoOtherTransferCount.QuadPart) +
                                 ((SPI[dwWhich].PageReadCount         - SPI[(dwWhich-1)%2].PageReadCount) +
                                  (SPI[dwWhich].CacheReadCount        - SPI[(dwWhich-1)%2].CacheReadCount) +
                                  (SPI[dwWhich].DirtyPagesWriteCount  - SPI[(dwWhich-1)%2].DirtyPagesWriteCount) +
                                  (SPI[dwWhich].MappedPagesWriteCount - SPI[(dwWhich-1)%2].MappedPagesWriteCount)) * PageSize);
  
                  cbIO = (cbIO * 1000)/SleepLoop;  
  
                  //DBG_PRINTFA((pBuff,"%d - ",cbIO));
                  
                  cbIO = (cbIOOld+cbIO)/2;
                  dwWhich = (dwWhich+1)%2;
                  
                  //DBG_PRINTFA((pBuff,"%d < %d\n",cbIO,IoIdleBytePerSec));
                  
                  if (cbIO < IoIdleBytePerSec)
                  {
                      if (0 == (dwFlags & ~THROTTLE_IO)) {
                           return THROTTLE_IO_IDLE;
                       } else {
                           bCnd2 = TRUE;
                       };
                  }
              }
              else
              {
                  return WBEM_E_FAILED;
              }
          }
          //
          //  check the combined condition
          //
          if (dwFlags & (THROTTLE_IO|THROTTLE_USER))
          {
              if (bCnd1 && bCnd2) 
              {
                  return (THROTTLE_IO_IDLE|THROTTLE_USER_IDLE);
              } 
              else 
              {
                  bCnd1 = FALSE;
                  bCnd2 = FALSE;
              }
          }   
          
  sleep_label:        
          Sleep(SleepLoop);
      }   
      
      return THROTTLE_MAX_WAIT;
  
  }
  