Skip to content

Commit a51f181

Browse files
committed
Move windows specific code to its own file
1 parent 6d35de3 commit a51f181

File tree

3 files changed

+108
-98
lines changed

3 files changed

+108
-98
lines changed

Lib/test/libregrtest/main.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717
INTERRUPTED, CHILD_ERROR,
1818
PROGRESS_MIN_TIME, format_test_result)
1919
from test.libregrtest.setup import setup_tests
20-
from test.libregrtest.utils import (
21-
removepy, count, format_duration, printlist, WindowsLoadTracker)
20+
from test.libregrtest.utils import removepy, count, format_duration, printlist
2221
from test import support
2322
try:
2423
import gc
@@ -536,8 +535,12 @@ def _main(self, tests, kwargs):
536535

537536
self.getloadavg = None
538537
if hasattr(os, 'getloadavg'):
539-
self.getloadavg = lambda: os.getloadavg()[0]
538+
def getloadavg1m():
539+
return os.getloadavg()[0]
540+
self.getloadavg = getloadavg1m
540541
elif sys.platform == 'win32' and (self.ns.slaveargs is None):
542+
from test.libregrtest.win_utils import WindowsLoadTracker
543+
541544
load_tracker = WindowsLoadTracker()
542545
self.getloadavg = load_tracker.getloadavg
543546

Lib/test/libregrtest/utils.py

Lines changed: 1 addition & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
import os
1+
import os.path
22
import math
33
import textwrap
4-
import subprocess
5-
import sys
6-
from test import support
74

85

96
def format_duration(seconds):
@@ -57,94 +54,3 @@ def printlist(x, width=70, indent=4, file=None):
5754
print(textwrap.fill(' '.join(str(elt) for elt in sorted(x)), width,
5855
initial_indent=blanks, subsequent_indent=blanks),
5956
file=file)
60-
61-
BUFSIZE = 8192
62-
LOAD_FACTOR_1 = 0.9200444146293232478931553241
63-
SAMPLING_INTERVAL = 5
64-
COUNTER_NAME = r'\System\Processor Queue Length'
65-
66-
"""
67-
This class asynchronously interacts with the `typeperf` command to read
68-
the system load on Windows. Mulitprocessing and threads can't be used
69-
here because they interfere with the test suite's cases for those
70-
modules.
71-
"""
72-
class WindowsLoadTracker():
73-
def __init__(self):
74-
self.load = 0.0
75-
self.start()
76-
77-
def start(self):
78-
import _winapi
79-
import msvcrt
80-
import uuid
81-
82-
# Create a named pipe which allows for asynchronous IO in Windows
83-
pipe_name = r'\\.\pipe\typeperf_output_' + str(uuid.uuid4())
84-
85-
open_mode = _winapi.PIPE_ACCESS_INBOUND
86-
open_mode |= _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE
87-
open_mode |= _winapi.FILE_FLAG_OVERLAPPED
88-
89-
# This is the read end of the pipe, where we will be grabbing output
90-
self.pipe = _winapi.CreateNamedPipe(
91-
pipe_name, open_mode, _winapi.PIPE_WAIT,
92-
1, BUFSIZE, BUFSIZE, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL
93-
)
94-
# The write end of the pipe which is passed to the created process
95-
pipe_write_end = _winapi.CreateFile(
96-
pipe_name, _winapi.GENERIC_WRITE, 0, _winapi.NULL,
97-
_winapi.OPEN_EXISTING, 0x80000000, _winapi.NULL
98-
)
99-
# Open up the handle as a python file object so we can pass it to
100-
# subprocess
101-
command_stdout = msvcrt.open_osfhandle(pipe_write_end, 0)
102-
103-
# Connect to the read end of the pipe in overlap/async mode
104-
overlap = _winapi.ConnectNamedPipe(self.pipe, overlapped=True)
105-
overlap.GetOverlappedResult(True)
106-
107-
# Spawn off the load monitor
108-
command = ['typeperf', COUNTER_NAME, '-si', str(SAMPLING_INTERVAL)]
109-
self.p = subprocess.Popen(command, stdout=command_stdout, cwd=support.SAVEDCWD)
110-
111-
# Close our copy of the write end of the pipe
112-
os.close(command_stdout)
113-
114-
def read_output(self):
115-
import _winapi
116-
117-
overlapped, _ = _winapi.ReadFile(self.pipe, BUFSIZE, True)
118-
bytes_read, res = overlapped.GetOverlappedResult(False)
119-
if res != 0:
120-
return
121-
122-
return overlapped.getbuffer().decode()
123-
124-
def getloadavg(self):
125-
typeperf_output = self.read_output()
126-
# Nothing to update, just return the current load
127-
if not typeperf_output:
128-
return self.load
129-
130-
# Process the backlog of load values
131-
for line in typeperf_output.splitlines():
132-
# typeperf outputs in a CSV format like this:
133-
# "07/19/2018 01:32:26.605","3.000000"
134-
toks = line.split(',')
135-
# Ignore blank lines and the initial header
136-
if line.strip() == '' or (COUNTER_NAME in line) or len(toks) != 2:
137-
continue
138-
139-
load = float(toks[1].replace('"', ''))
140-
# We use an exponentially weighted moving average, imitating the
141-
# load calculation on Unix systems.
142-
# https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation
143-
new_load = self.load * LOAD_FACTOR_1 + load * (1.0 - LOAD_FACTOR_1)
144-
self.load = new_load
145-
146-
return self.load
147-
148-
def __del__(self):
149-
self.p.kill()
150-
self.p.wait()

Lib/test/libregrtest/win_utils.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import subprocess
2+
import sys
3+
import os
4+
from test import support
5+
6+
7+
# Max size of asynchronous reads
8+
BUFSIZE = 8192
9+
# Exponential damping factor (see below)
10+
LOAD_FACTOR_1 = 0.9200444146293232478931553241
11+
# Seconds per measurement
12+
SAMPLING_INTERVAL = 5
13+
COUNTER_NAME = r'\System\Processor Queue Length'
14+
15+
16+
class WindowsLoadTracker():
17+
"""
18+
This class asynchronously interacts with the `typeperf` command to read
19+
the system load on Windows. Mulitprocessing and threads can't be used
20+
here because they interfere with the test suite's cases for those
21+
modules.
22+
"""
23+
24+
def __init__(self):
25+
self.load = 0.0
26+
self.start()
27+
28+
def start(self):
29+
import _winapi
30+
import msvcrt
31+
import uuid
32+
33+
# Create a named pipe which allows for asynchronous IO in Windows
34+
pipe_name = r'\\.\pipe\typeperf_output_' + str(uuid.uuid4())
35+
36+
open_mode = _winapi.PIPE_ACCESS_INBOUND
37+
open_mode |= _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE
38+
open_mode |= _winapi.FILE_FLAG_OVERLAPPED
39+
40+
# This is the read end of the pipe, where we will be grabbing output
41+
self.pipe = _winapi.CreateNamedPipe(
42+
pipe_name, open_mode, _winapi.PIPE_WAIT,
43+
1, BUFSIZE, BUFSIZE, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL
44+
)
45+
# The write end of the pipe which is passed to the created process
46+
pipe_write_end = _winapi.CreateFile(
47+
pipe_name, _winapi.GENERIC_WRITE, 0, _winapi.NULL,
48+
_winapi.OPEN_EXISTING, 0, _winapi.NULL
49+
)
50+
# Open up the handle as a python file object so we can pass it to
51+
# subprocess
52+
command_stdout = msvcrt.open_osfhandle(pipe_write_end, 0)
53+
54+
# Connect to the read end of the pipe in overlap/async mode
55+
overlap = _winapi.ConnectNamedPipe(self.pipe, overlapped=True)
56+
overlap.GetOverlappedResult(True)
57+
58+
# Spawn off the load monitor
59+
command = ['typeperf', COUNTER_NAME, '-si', str(SAMPLING_INTERVAL)]
60+
self.p = subprocess.Popen(command, stdout=command_stdout, cwd=support.SAVEDCWD)
61+
62+
# Close our copy of the write end of the pipe
63+
os.close(command_stdout)
64+
65+
def __del__(self):
66+
self.p.kill()
67+
self.p.wait()
68+
69+
def read_output(self):
70+
import _winapi
71+
72+
overlapped, _ = _winapi.ReadFile(self.pipe, BUFSIZE, True)
73+
bytes_read, res = overlapped.GetOverlappedResult(False)
74+
if res != 0:
75+
return
76+
77+
return overlapped.getbuffer().decode()
78+
79+
def getloadavg(self):
80+
typeperf_output = self.read_output()
81+
# Nothing to update, just return the current load
82+
if not typeperf_output:
83+
return self.load
84+
85+
# Process the backlog of load values
86+
for line in typeperf_output.splitlines():
87+
# typeperf outputs in a CSV format like this:
88+
# "07/19/2018 01:32:26.605","3.000000"
89+
toks = line.split(',')
90+
# Ignore blank lines and the initial header
91+
if line.strip() == '' or (COUNTER_NAME in line) or len(toks) != 2:
92+
continue
93+
94+
load = float(toks[1].replace('"', ''))
95+
# We use an exponentially weighted moving average, imitating the
96+
# load calculation on Unix systems.
97+
# https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation
98+
new_load = self.load * LOAD_FACTOR_1 + load * (1.0 - LOAD_FACTOR_1)
99+
self.load = new_load
100+
101+
return self.load

0 commit comments

Comments
 (0)