zmc
2023-08-08 e792e9a60d958b93aef96050644f369feb25d61b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
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")