You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

221 lines
7.9 KiB
Python

import os
import pytest
on_windows = os.name == "nt"
# Mark all the tests in this file as being windows-only
pytestmark = pytest.mark.skipif(not on_windows, reason="windows only")
from .._core.tests.tutil import slow
import trio
from .. import _core
from .. import _timeouts
if on_windows:
from .._core._windows_cffi import ffi, kernel32
from .._wait_for_object import (
WaitForSingleObject,
WaitForMultipleObjects_sync,
)
async def test_WaitForMultipleObjects_sync():
# This does a series of tests where we set/close the handle before
# initiating the waiting for it.
#
# Note that closing the handle (not signaling) will cause the
# *initiation* of a wait to return immediately. But closing a handle
# that is already being waited on will not stop whatever is waiting
# for it.
# One handle
handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
kernel32.SetEvent(handle1)
WaitForMultipleObjects_sync(handle1)
kernel32.CloseHandle(handle1)
print("test_WaitForMultipleObjects_sync one OK")
# Two handles, signal first
handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
handle2 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
kernel32.SetEvent(handle1)
WaitForMultipleObjects_sync(handle1, handle2)
kernel32.CloseHandle(handle1)
kernel32.CloseHandle(handle2)
print("test_WaitForMultipleObjects_sync set first OK")
# Two handles, signal second
handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
handle2 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
kernel32.SetEvent(handle2)
WaitForMultipleObjects_sync(handle1, handle2)
kernel32.CloseHandle(handle1)
kernel32.CloseHandle(handle2)
print("test_WaitForMultipleObjects_sync set second OK")
# Two handles, close first
handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
handle2 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
kernel32.CloseHandle(handle1)
with pytest.raises(OSError):
WaitForMultipleObjects_sync(handle1, handle2)
kernel32.CloseHandle(handle2)
print("test_WaitForMultipleObjects_sync close first OK")
# Two handles, close second
handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
handle2 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
kernel32.CloseHandle(handle2)
with pytest.raises(OSError):
WaitForMultipleObjects_sync(handle1, handle2)
kernel32.CloseHandle(handle1)
print("test_WaitForMultipleObjects_sync close second OK")
@slow
async def test_WaitForMultipleObjects_sync_slow():
# This does a series of test in which the main thread sync-waits for
# handles, while we spawn a thread to set the handles after a short while.
TIMEOUT = 0.3
# One handle
handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
t0 = _core.current_time()
async with _core.open_nursery() as nursery:
nursery.start_soon(
trio.to_thread.run_sync, WaitForMultipleObjects_sync, handle1
)
await _timeouts.sleep(TIMEOUT)
# If we would comment the line below, the above thread will be stuck,
# and Trio won't exit this scope
kernel32.SetEvent(handle1)
t1 = _core.current_time()
assert TIMEOUT <= (t1 - t0) < 2.0 * TIMEOUT
kernel32.CloseHandle(handle1)
print("test_WaitForMultipleObjects_sync_slow one OK")
# Two handles, signal first
handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
handle2 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
t0 = _core.current_time()
async with _core.open_nursery() as nursery:
nursery.start_soon(
trio.to_thread.run_sync, WaitForMultipleObjects_sync, handle1, handle2
)
await _timeouts.sleep(TIMEOUT)
kernel32.SetEvent(handle1)
t1 = _core.current_time()
assert TIMEOUT <= (t1 - t0) < 2.0 * TIMEOUT
kernel32.CloseHandle(handle1)
kernel32.CloseHandle(handle2)
print("test_WaitForMultipleObjects_sync_slow thread-set first OK")
# Two handles, signal second
handle1 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
handle2 = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
t0 = _core.current_time()
async with _core.open_nursery() as nursery:
nursery.start_soon(
trio.to_thread.run_sync, WaitForMultipleObjects_sync, handle1, handle2
)
await _timeouts.sleep(TIMEOUT)
kernel32.SetEvent(handle2)
t1 = _core.current_time()
assert TIMEOUT <= (t1 - t0) < 2.0 * TIMEOUT
kernel32.CloseHandle(handle1)
kernel32.CloseHandle(handle2)
print("test_WaitForMultipleObjects_sync_slow thread-set second OK")
async def test_WaitForSingleObject():
# This does a series of test for setting/closing the handle before
# initiating the wait.
# Test already set
handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
kernel32.SetEvent(handle)
await WaitForSingleObject(handle) # should return at once
kernel32.CloseHandle(handle)
print("test_WaitForSingleObject already set OK")
# Test already set, as int
handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
handle_int = int(ffi.cast("intptr_t", handle))
kernel32.SetEvent(handle)
await WaitForSingleObject(handle_int) # should return at once
kernel32.CloseHandle(handle)
print("test_WaitForSingleObject already set OK")
# Test already closed
handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
kernel32.CloseHandle(handle)
with pytest.raises(OSError):
await WaitForSingleObject(handle) # should return at once
print("test_WaitForSingleObject already closed OK")
# Not a handle
with pytest.raises(TypeError):
await WaitForSingleObject("not a handle") # Wrong type
# with pytest.raises(OSError):
# await WaitForSingleObject(99) # If you're unlucky, it actually IS a handle :(
print("test_WaitForSingleObject not a handle OK")
@slow
async def test_WaitForSingleObject_slow():
# This does a series of test for setting the handle in another task,
# and cancelling the wait task.
# Set the timeout used in the tests. We test the waiting time against
# the timeout with a certain margin.
TIMEOUT = 0.3
async def signal_soon_async(handle):
await _timeouts.sleep(TIMEOUT)
kernel32.SetEvent(handle)
# Test handle is SET after TIMEOUT in separate coroutine
handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
t0 = _core.current_time()
async with _core.open_nursery() as nursery:
nursery.start_soon(WaitForSingleObject, handle)
nursery.start_soon(signal_soon_async, handle)
kernel32.CloseHandle(handle)
t1 = _core.current_time()
assert TIMEOUT <= (t1 - t0) < 2.0 * TIMEOUT
print("test_WaitForSingleObject_slow set from task OK")
# Test handle is SET after TIMEOUT in separate coroutine, as int
handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
handle_int = int(ffi.cast("intptr_t", handle))
t0 = _core.current_time()
async with _core.open_nursery() as nursery:
nursery.start_soon(WaitForSingleObject, handle_int)
nursery.start_soon(signal_soon_async, handle)
kernel32.CloseHandle(handle)
t1 = _core.current_time()
assert TIMEOUT <= (t1 - t0) < 2.0 * TIMEOUT
print("test_WaitForSingleObject_slow set from task as int OK")
# Test handle is CLOSED after 1 sec - NOPE see comment above
# Test cancellation
handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
t0 = _core.current_time()
with _timeouts.move_on_after(TIMEOUT):
await WaitForSingleObject(handle)
kernel32.CloseHandle(handle)
t1 = _core.current_time()
assert TIMEOUT <= (t1 - t0) < 2.0 * TIMEOUT
print("test_WaitForSingleObject_slow cancellation OK")