Skip to content

Commit 574b597

Browse files
committed
gh-110697: test_os TimerfdTests uses selectors
Replace select.epoll() with selectors.DefaultSelector to support FreeBSD 14 and newer. * Merge common code between test_timerfd_epoll() test_timerfd_ns_epoll(). * Replace "_ = func()" with "func()". * Add TimerfdTests.read_count_signaled() method.
1 parent 2c472a8 commit 574b597

File tree

1 file changed

+67
-102
lines changed

1 file changed

+67
-102
lines changed

Lib/test/test_os.py

Lines changed: 67 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import os
1515
import pickle
1616
import select
17+
import selectors
1718
import shutil
1819
import signal
1920
import socket
@@ -3940,6 +3941,11 @@ def timerfd_create(self, *args, **kwargs):
39403941
self.addCleanup(os.close, fd)
39413942
return fd
39423943

3944+
def read_count_signaled(self, fd):
3945+
# read 8 bytes
3946+
data = os.read(fd, 8)
3947+
return int.from_bytes(data, byteorder=sys.byteorder)
3948+
39433949
def test_timerfd_initval(self):
39443950
fd = self.timerfd_create(time.CLOCK_REALTIME)
39453951

@@ -3962,25 +3968,22 @@ def test_timerfd_initval(self):
39623968
self.assertAlmostEqual(next_expiration, initial_expiration, places=3)
39633969

39643970
def test_timerfd_non_blocking(self):
3965-
size = 8 # read 8 bytes
39663971
fd = self.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
39673972

39683973
# 0.1 second later
39693974
initial_expiration = 0.1
3970-
_, _ = os.timerfd_settime(fd, initial=initial_expiration, interval=0)
3975+
os.timerfd_settime(fd, initial=initial_expiration, interval=0)
39713976

39723977
# read() raises OSError with errno is EAGAIN for non-blocking timer.
39733978
with self.assertRaises(OSError) as ctx:
3974-
_ = os.read(fd, size)
3979+
self.read_count_signaled(fd)
39753980
self.assertEqual(ctx.exception.errno, errno.EAGAIN)
39763981

39773982
# Wait more than 0.1 seconds
39783983
time.sleep(initial_expiration + 0.1)
39793984

39803985
# confirm if timerfd is readable and read() returns 1 as bytes.
3981-
n = os.read(fd, size)
3982-
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
3983-
self.assertEqual(count_signaled, 1)
3986+
self.assertEqual(self.read_count_signaled(fd), 1)
39843987

39853988
def test_timerfd_negative(self):
39863989
one_sec_in_nsec = 10**9
@@ -3995,25 +3998,24 @@ def test_timerfd_negative(self):
39953998
for flags in test_flags:
39963999
with self.subTest(flags=flags, initial=initial, interval=interval):
39974000
with self.assertRaises(OSError) as context:
3998-
_, _ = os.timerfd_settime(fd, flags=flags, initial=initial, interval=interval)
4001+
os.timerfd_settime(fd, flags=flags, initial=initial, interval=interval)
39994002
self.assertEqual(context.exception.errno, errno.EINVAL)
40004003

40014004
with self.assertRaises(OSError) as context:
40024005
initial_ns = int( one_sec_in_nsec * initial )
40034006
interval_ns = int( one_sec_in_nsec * interval )
4004-
_, _ = os.timerfd_settime_ns(fd, flags=flags, initial=initial_ns, interval=interval_ns)
4007+
os.timerfd_settime_ns(fd, flags=flags, initial=initial_ns, interval=interval_ns)
40054008
self.assertEqual(context.exception.errno, errno.EINVAL)
40064009

40074010
def test_timerfd_interval(self):
4008-
size = 8 # read 8 bytes
40094011
fd = self.timerfd_create(time.CLOCK_REALTIME)
40104012

40114013
# 1 second
40124014
initial_expiration = 1
40134015
# 0.5 second
40144016
interval = 0.5
40154017

4016-
_, _ = os.timerfd_settime(fd, initial=initial_expiration, interval=interval)
4018+
os.timerfd_settime(fd, initial=initial_expiration, interval=interval)
40174019

40184020
# timerfd_gettime
40194021
next_expiration, interval2 = os.timerfd_gettime(fd)
@@ -4023,22 +4025,17 @@ def test_timerfd_interval(self):
40234025
count = 3
40244026
t = time.perf_counter()
40254027
for _ in range(count):
4026-
n = os.read(fd, size)
4027-
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
4028-
self.assertEqual(count_signaled, 1)
4028+
self.assertEqual(self.read_count_signaled(fd), 1)
40294029
t = time.perf_counter() - t
40304030

40314031
total_time = initial_expiration + interval * (count - 1)
40324032
self.assertGreater(t, total_time)
40334033

40344034
# wait 3.5 time of interval
40354035
time.sleep( (count+0.5) * interval)
4036-
n = os.read(fd, size)
4037-
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
4038-
self.assertEqual(count_signaled, count)
4036+
self.assertEqual(self.read_count_signaled(fd), count)
40394037

40404038
def test_timerfd_TFD_TIMER_ABSTIME(self):
4041-
size = 8 # read 8 bytes
40424039
fd = self.timerfd_create(time.CLOCK_REALTIME)
40434040

40444041
now = time.clock_gettime(time.CLOCK_REALTIME)
@@ -4049,7 +4046,7 @@ def test_timerfd_TFD_TIMER_ABSTIME(self):
40494046
# not interval timer
40504047
interval = 0
40514048

4052-
_, _ = os.timerfd_settime(fd, flags=os.TFD_TIMER_ABSTIME, initial=initial_expiration, interval=interval)
4049+
os.timerfd_settime(fd, flags=os.TFD_TIMER_ABSTIME, initial=initial_expiration, interval=interval)
40534050

40544051
# timerfd_gettime
40554052
# Note: timerfd_gettime returns relative values even if TFD_TIMER_ABSTIME is specified.
@@ -4058,15 +4055,13 @@ def test_timerfd_TFD_TIMER_ABSTIME(self):
40584055
self.assertAlmostEqual(next_expiration, offset, places=3)
40594056

40604057
t = time.perf_counter()
4061-
n = os.read(fd, size)
4062-
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
4058+
count_signaled = self.read_count_signaled(fd)
40634059
t = time.perf_counter() - t
40644060
self.assertEqual(count_signaled, 1)
40654061

40664062
self.assertGreater(t, offset - self.CLOCK_RES)
40674063

40684064
def test_timerfd_select(self):
4069-
size = 8 # read 8 bytes
40704065
fd = self.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
40714066

40724067
rfd, wfd, xfd = select.select([fd], [fd], [fd], 0)
@@ -4077,56 +4072,74 @@ def test_timerfd_select(self):
40774072
# every 0.125 second
40784073
interval = 0.125
40794074

4080-
_, _ = os.timerfd_settime(fd, initial=initial_expiration, interval=interval)
4075+
os.timerfd_settime(fd, initial=initial_expiration, interval=interval)
40814076

40824077
count = 3
40834078
t = time.perf_counter()
40844079
for _ in range(count):
40854080
rfd, wfd, xfd = select.select([fd], [fd], [fd], initial_expiration + interval)
40864081
self.assertEqual((rfd, wfd, xfd), ([fd], [], []))
4087-
n = os.read(fd, size)
4088-
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
4089-
self.assertEqual(count_signaled, 1)
4082+
self.assertEqual(self.read_count_signaled(fd), 1)
40904083
t = time.perf_counter() - t
40914084

40924085
total_time = initial_expiration + interval * (count - 1)
40934086
self.assertGreater(t, total_time)
40944087

4095-
def test_timerfd_epoll(self):
4096-
size = 8 # read 8 bytes
4088+
def check_timerfd_poll(self, nanoseconds):
40974089
fd = self.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
40984090

4099-
ep = select.epoll()
4100-
ep.register(fd, select.EPOLLIN)
4101-
self.addCleanup(ep.close)
4091+
selector = selectors.DefaultSelector()
4092+
selector.register(fd, selectors.EVENT_READ)
4093+
self.addCleanup(selector.close)
41024094

4095+
sec_to_nsec = 10 ** 9
41034096
# 0.25 second
4104-
initial_expiration = 0.25
4097+
initial_expiration_ns = sec_to_nsec // 4
41054098
# every 0.125 second
4106-
interval = 0.125
4099+
interval_ns = sec_to_nsec // 8
41074100

4108-
_, _ = os.timerfd_settime(fd, initial=initial_expiration, interval=interval)
4101+
if nanoseconds:
4102+
os.timerfd_settime_ns(fd,
4103+
initial=initial_expiration_ns,
4104+
interval=interval_ns)
4105+
else:
4106+
os.timerfd_settime(fd,
4107+
initial=initial_expiration_ns / sec_to_nsec,
4108+
interval=interval_ns / sec_to_nsec)
41094109

41104110
count = 3
4111-
t = time.perf_counter()
4111+
if nanoseconds:
4112+
t = time.perf_counter_ns()
4113+
else:
4114+
t = time.perf_counter()
41124115
for i in range(count):
4113-
timeout_margin = interval
4116+
timeout_margin_ns = interval_ns
41144117
if i == 0:
4115-
timeout = initial_expiration + interval + timeout_margin
4118+
timeout_ns = initial_expiration_ns + interval_ns + timeout_margin_ns
41164119
else:
4117-
timeout = interval + timeout_margin
4118-
# epoll timeout is in seconds.
4119-
events = ep.poll(timeout)
4120-
self.assertEqual(events, [(fd, select.EPOLLIN)])
4121-
n = os.read(fd, size)
4122-
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
4123-
self.assertEqual(count_signaled, 1)
4120+
timeout_ns = interval_ns + timeout_margin_ns
41244121

4125-
t = time.perf_counter() - t
4122+
ready = selector.select(timeout_ns / sec_to_nsec)
4123+
self.assertEqual(len(ready), 1, ready)
4124+
event = ready[0][1]
4125+
self.assertEqual(event, selectors.EVENT_READ)
41264126

4127-
total_time = initial_expiration + interval * (count - 1)
4128-
self.assertGreater(t, total_time)
4129-
ep.unregister(fd)
4127+
self.assertEqual(self.read_count_signaled(fd), 1)
4128+
4129+
total_time = initial_expiration_ns + interval_ns * (count - 1)
4130+
if nanoseconds:
4131+
dt = time.perf_counter_ns() - t
4132+
self.assertGreater(dt, total_time)
4133+
else:
4134+
dt = time.perf_counter() - t
4135+
self.assertGreater(dt, total_time / sec_to_nsec)
4136+
selector.unregister(fd)
4137+
4138+
def test_timerfd_poll(self):
4139+
self.check_timerfd_poll(False)
4140+
4141+
def test_timerfd_ns_poll(self):
4142+
self.check_timerfd_poll(True)
41304143

41314144
def test_timerfd_ns_initval(self):
41324145
one_sec_in_nsec = 10**9
@@ -4153,7 +4166,6 @@ def test_timerfd_ns_initval(self):
41534166
self.assertAlmostEqual(next_expiration_ns, initial_expiration_ns, delta=limit_error)
41544167

41554168
def test_timerfd_ns_interval(self):
4156-
size = 8 # read 8 bytes
41574169
one_sec_in_nsec = 10**9
41584170
limit_error = one_sec_in_nsec // 10**3
41594171
fd = self.timerfd_create(time.CLOCK_REALTIME)
@@ -4163,7 +4175,7 @@ def test_timerfd_ns_interval(self):
41634175
# every 0.5 second
41644176
interval_ns = one_sec_in_nsec // 2
41654177

4166-
_, _ = os.timerfd_settime_ns(fd, initial=initial_expiration_ns, interval=interval_ns)
4178+
os.timerfd_settime_ns(fd, initial=initial_expiration_ns, interval=interval_ns)
41674179

41684180
# timerfd_gettime
41694181
next_expiration_ns, interval_ns2 = os.timerfd_gettime_ns(fd)
@@ -4173,23 +4185,18 @@ def test_timerfd_ns_interval(self):
41734185
count = 3
41744186
t = time.perf_counter_ns()
41754187
for _ in range(count):
4176-
n = os.read(fd, size)
4177-
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
4178-
self.assertEqual(count_signaled, 1)
4188+
self.assertEqual(self.read_count_signaled(fd), 1)
41794189
t = time.perf_counter_ns() - t
41804190

41814191
total_time_ns = initial_expiration_ns + interval_ns * (count - 1)
41824192
self.assertGreater(t, total_time_ns)
41834193

41844194
# wait 3.5 time of interval
41854195
time.sleep( (count+0.5) * interval_ns / one_sec_in_nsec)
4186-
n = os.read(fd, size)
4187-
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
4188-
self.assertEqual(count_signaled, count)
4196+
self.assertEqual(self.read_count_signaled(fd), count)
41894197

41904198

41914199
def test_timerfd_ns_TFD_TIMER_ABSTIME(self):
4192-
size = 8 # read 8 bytes
41934200
one_sec_in_nsec = 10**9
41944201
limit_error = one_sec_in_nsec // 10**3
41954202
fd = self.timerfd_create(time.CLOCK_REALTIME)
@@ -4202,7 +4209,7 @@ def test_timerfd_ns_TFD_TIMER_ABSTIME(self):
42024209
# not interval timer
42034210
interval_ns = 0
42044211

4205-
_, _ = os.timerfd_settime_ns(fd, flags=os.TFD_TIMER_ABSTIME, initial=initial_expiration_ns, interval=interval_ns)
4212+
os.timerfd_settime_ns(fd, flags=os.TFD_TIMER_ABSTIME, initial=initial_expiration_ns, interval=interval_ns)
42064213

42074214
# timerfd_gettime
42084215
# Note: timerfd_gettime returns relative values even if TFD_TIMER_ABSTIME is specified.
@@ -4211,15 +4218,13 @@ def test_timerfd_ns_TFD_TIMER_ABSTIME(self):
42114218
self.assertLess(abs(next_expiration_ns - offset_ns), limit_error)
42124219

42134220
t = time.perf_counter_ns()
4214-
n = os.read(fd, size)
4215-
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
4221+
count_signaled = self.read_count_signaled(fd)
42164222
t = time.perf_counter_ns() - t
42174223
self.assertEqual(count_signaled, 1)
42184224

42194225
self.assertGreater(t, offset_ns - self.CLOCK_RES_NS)
42204226

42214227
def test_timerfd_ns_select(self):
4222-
size = 8 # read 8 bytes
42234228
one_sec_in_nsec = 10**9
42244229

42254230
fd = self.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
@@ -4232,59 +4237,19 @@ def test_timerfd_ns_select(self):
42324237
# every 0.125 second
42334238
interval_ns = one_sec_in_nsec // 8
42344239

4235-
_, _ = os.timerfd_settime_ns(fd, initial=initial_expiration_ns, interval=interval_ns)
4240+
os.timerfd_settime_ns(fd, initial=initial_expiration_ns, interval=interval_ns)
42364241

42374242
count = 3
42384243
t = time.perf_counter_ns()
42394244
for _ in range(count):
42404245
rfd, wfd, xfd = select.select([fd], [fd], [fd], (initial_expiration_ns + interval_ns) / 1e9 )
42414246
self.assertEqual((rfd, wfd, xfd), ([fd], [], []))
4242-
n = os.read(fd, size)
4243-
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
4244-
self.assertEqual(count_signaled, 1)
4247+
self.assertEqual(self.read_count_signaled(fd), 1)
42454248
t = time.perf_counter_ns() - t
42464249

42474250
total_time_ns = initial_expiration_ns + interval_ns * (count - 1)
42484251
self.assertGreater(t, total_time_ns)
42494252

4250-
def test_timerfd_ns_epoll(self):
4251-
size = 8 # read 8 bytes
4252-
one_sec_in_nsec = 10**9
4253-
fd = self.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
4254-
4255-
ep = select.epoll()
4256-
ep.register(fd, select.EPOLLIN)
4257-
self.addCleanup(ep.close)
4258-
4259-
# 0.25 second
4260-
initial_expiration_ns = one_sec_in_nsec // 4
4261-
# every 0.125 second
4262-
interval_ns = one_sec_in_nsec // 8
4263-
4264-
_, _ = os.timerfd_settime_ns(fd, initial=initial_expiration_ns, interval=interval_ns)
4265-
4266-
count = 3
4267-
t = time.perf_counter_ns()
4268-
for i in range(count):
4269-
timeout_margin_ns = interval_ns
4270-
if i == 0:
4271-
timeout_ns = initial_expiration_ns + interval_ns + timeout_margin_ns
4272-
else:
4273-
timeout_ns = interval_ns + timeout_margin_ns
4274-
4275-
# epoll timeout is in seconds.
4276-
events = ep.poll(timeout_ns / one_sec_in_nsec)
4277-
self.assertEqual(events, [(fd, select.EPOLLIN)])
4278-
n = os.read(fd, size)
4279-
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
4280-
self.assertEqual(count_signaled, 1)
4281-
4282-
t = time.perf_counter_ns() - t
4283-
4284-
total_time = initial_expiration_ns + interval_ns * (count - 1)
4285-
self.assertGreater(t, total_time)
4286-
ep.unregister(fd)
4287-
42884253
class OSErrorTests(unittest.TestCase):
42894254
def setUp(self):
42904255
class Str(str):

0 commit comments

Comments
 (0)