mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			105 lines
		
	
	
	
		
			3.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			105 lines
		
	
	
	
		
			3.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import _winapi
 | |
| import msvcrt
 | |
| import os
 | |
| import subprocess
 | |
| import uuid
 | |
| from test import support
 | |
| 
 | |
| 
 | |
| # Max size of asynchronous reads
 | |
| BUFSIZE = 8192
 | |
| # Exponential damping factor (see below)
 | |
| LOAD_FACTOR_1 = 0.9200444146293232478931553241
 | |
| # Seconds per measurement
 | |
| SAMPLING_INTERVAL = 5
 | |
| COUNTER_NAME = r'\System\Processor Queue Length'
 | |
| 
 | |
| 
 | |
| class WindowsLoadTracker():
 | |
|     """
 | |
|     This class asynchronously interacts with the `typeperf` command to read
 | |
|     the system load on Windows. Mulitprocessing and threads can't be used
 | |
|     here because they interfere with the test suite's cases for those
 | |
|     modules.
 | |
|     """
 | |
| 
 | |
|     def __init__(self):
 | |
|         self.load = 0.0
 | |
|         self.start()
 | |
| 
 | |
|     def start(self):
 | |
|         # Create a named pipe which allows for asynchronous IO in Windows
 | |
|         pipe_name =  r'\\.\pipe\typeperf_output_' + str(uuid.uuid4())
 | |
| 
 | |
|         open_mode =  _winapi.PIPE_ACCESS_INBOUND
 | |
|         open_mode |= _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE
 | |
|         open_mode |= _winapi.FILE_FLAG_OVERLAPPED
 | |
| 
 | |
|         # This is the read end of the pipe, where we will be grabbing output
 | |
|         self.pipe = _winapi.CreateNamedPipe(
 | |
|             pipe_name, open_mode, _winapi.PIPE_WAIT,
 | |
|             1, BUFSIZE, BUFSIZE, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL
 | |
|         )
 | |
|         # The write end of the pipe which is passed to the created process
 | |
|         pipe_write_end = _winapi.CreateFile(
 | |
|             pipe_name, _winapi.GENERIC_WRITE, 0, _winapi.NULL,
 | |
|             _winapi.OPEN_EXISTING, 0, _winapi.NULL
 | |
|         )
 | |
|         # Open up the handle as a python file object so we can pass it to
 | |
|         # subprocess
 | |
|         command_stdout = msvcrt.open_osfhandle(pipe_write_end, 0)
 | |
| 
 | |
|         # Connect to the read end of the pipe in overlap/async mode
 | |
|         overlap = _winapi.ConnectNamedPipe(self.pipe, overlapped=True)
 | |
|         overlap.GetOverlappedResult(True)
 | |
| 
 | |
|         # Spawn off the load monitor
 | |
|         command = ['typeperf', COUNTER_NAME, '-si', str(SAMPLING_INTERVAL)]
 | |
|         self.p = subprocess.Popen(command, stdout=command_stdout, cwd=support.SAVEDCWD)
 | |
| 
 | |
|         # Close our copy of the write end of the pipe
 | |
|         os.close(command_stdout)
 | |
| 
 | |
|     def close(self):
 | |
|         if self.p is None:
 | |
|             return
 | |
|         self.p.kill()
 | |
|         self.p.wait()
 | |
|         self.p = None
 | |
| 
 | |
|     def __del__(self):
 | |
|         self.close()
 | |
| 
 | |
|     def read_output(self):
 | |
|         import _winapi
 | |
| 
 | |
|         overlapped, _ = _winapi.ReadFile(self.pipe, BUFSIZE, True)
 | |
|         bytes_read, res = overlapped.GetOverlappedResult(False)
 | |
|         if res != 0:
 | |
|             return
 | |
| 
 | |
|         return overlapped.getbuffer().decode()
 | |
| 
 | |
|     def getloadavg(self):
 | |
|         typeperf_output = self.read_output()
 | |
|         # Nothing to update, just return the current load
 | |
|         if not typeperf_output:
 | |
|             return self.load
 | |
| 
 | |
|         # Process the backlog of load values
 | |
|         for line in typeperf_output.splitlines():
 | |
|             # typeperf outputs in a CSV format like this:
 | |
|             # "07/19/2018 01:32:26.605","3.000000"
 | |
|             toks = line.split(',')
 | |
|             # Ignore blank lines and the initial header
 | |
|             if line.strip() == '' or (COUNTER_NAME in line) or len(toks) != 2:
 | |
|                 continue
 | |
| 
 | |
|             load = float(toks[1].replace('"', ''))
 | |
|             # 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
 | |
|             new_load = self.load * LOAD_FACTOR_1 + load * (1.0 - LOAD_FACTOR_1)
 | |
|             self.load = new_load
 | |
| 
 | |
|         return self.load
 | 
