seconds per measurement exponential damping factor to compute exponentially weighted moving average on 1 minute 60 seconds initialize the load using the arithmetic mean of the first nvalue values of the processor queue length this class asynchronously reads the performance counters to calculate the system load on windows a raw thread is used here to prevent interference with the test suite s cases for the threading module preflight test for access to the performance data permissionerror will be raised if not allowed localize module access to prevent shutdown errors run until signaled to stop notify stopped localize module access to prevent shutdown errors get the system object perfdatablock wchar signature4 8 dwowd littleendian 4 dword version 4 dword revision 4 dword totalbytelength 4 dword headerlength 24 byte offset perfobjecttype dword totalbytelength dword definitionlength dword headerlength find the processor queue length counter index44 perfcounterdefinition dword bytelength dword counternametitleindex 7 dwords28 bytes dword counteroffset the counter is known to be perfcounterrawcount dword we use an exponentially weighted moving average imitating the load calculation on unix systems https en wikipedia orgwikiloadcomputingunixstyleloadcalculation https en wikipedia orgwikimovingaverageexponentialmovingaverage localize module access to prevent shutdown errors tell the update thread to quit wait for the update thread to signal done cleanup events seconds per measurement exponential damping factor to compute exponentially weighted moving average on 1 minute 60 seconds initialize the load using the arithmetic mean of the first nvalue values of the processor queue length this class asynchronously reads the performance counters to calculate the system load on windows a raw thread is used here to prevent interference with the test suite s cases for the threading module pre flight test for access to the performance data permissionerror will be raised if not allowed localize module access to prevent shutdown errors run until signaled to stop notify stopped localize module access to prevent shutdown errors get the system object perf_data_block wchar signature 4 8 dwowd littleendian 4 dword version 4 dword revision 4 dword totalbytelength 4 dword headerlength 24 byte offset perf_object_type dword totalbytelength dword definitionlength dword headerlength find the processor queue length counter index 44 perf_counter_definition dword bytelength dword counternametitleindex 7 dwords 28 bytes dword counteroffset the counter is known to be perf_counter_rawcount dword we use an exponentially weighted moving average imitating the load calculation on unix systems https en wikipedia org wiki load_ computing unix style_load_calculation https en wikipedia org wiki moving_average exponential_moving_average localize module access to prevent shutdown errors tell the update thread to quit wait for the update thread to signal done cleanup events
import _overlapped
import _thread
import _winapi
import math
import struct
import winreg



SAMPLING_INTERVAL = 1


LOAD_FACTOR_1 = 1 / math.exp(SAMPLING_INTERVAL / 60)


NVALUE = 5


class WindowsLoadTracker():
    

    def __init__(self):
        
        
        winreg.QueryInfoKey(winreg.HKEY_PERFORMANCE_DATA)

        self._values = []
        self._load = None
        self._running = _overlapped.CreateEvent(None, True, False, None)
        self._stopped = _overlapped.CreateEvent(None, True, False, None)

        _thread.start_new_thread(self._update_load, (), {})

    def _update_load(self,
                    
                     _wait=_winapi.WaitForSingleObject,
                     _signal=_overlapped.SetEvent):
        
        while _wait(self._running, 1000):
            self._calculate_load()
        
        _signal(self._stopped)

    def _calculate_load(self,
                        
                        _query=winreg.QueryValueEx,
                        _hkey=winreg.HKEY_PERFORMANCE_DATA,
                        _unpack=struct.unpack_from):
        
        data, _ = _query(_hkey, '2')
        
        
        
        
        
        
        
        
        
        obj_start, = _unpack('L', data, 24)
        
        
        
        
        
        
        data_start, defn_start = _unpack('4xLL', data, obj_start)
        data_base = obj_start + data_start
        defn_base = obj_start + defn_start
        
        while defn_base < data_base:
            
            
            
            
            
            
            size, idx, offset = _unpack('LL28xL', data, defn_base)
            defn_base += size
            if idx == 44:
                counter_offset = data_base + offset
                
                processor_queue_length, = _unpack('L', data, counter_offset)
                break
        else:
            return

        
        
        
        
        if self._load is not None:
            self._load = (self._load * LOAD_FACTOR_1
                            + processor_queue_length  * (1.0 - LOAD_FACTOR_1))
        elif len(self._values) < NVALUE:
            self._values.append(processor_queue_length)
        else:
            self._load = sum(self._values) / len(self._values)

    def close(self, kill=True):
        self.__del__()
        return

    def __del__(self,
                
                _wait=_winapi.WaitForSingleObject,
                _close=_winapi.CloseHandle,
                _signal=_overlapped.SetEvent):
        if self._running is not None:
            
            _signal(self._running)
            
            _wait(self._stopped, -1)
            
            _close(self._running)
            _close(self._stopped)
            self._running = self._stopped = None

    def getloadavg(self):
        return self._load