Skip to content

Commit cd40e6f

Browse files
authored
Bring back custom Event and fix improper usage of _wakeup_timer_in_thread (#39)
* fix import logic again to address #38 * fix thread test import Event * fix thread test import Event * commit potential fix? * replace asyncio.sleep with self.sleep * readd get_event_loop_policy() * readd loop kwargs until we figure out what to do * fix server test import * fix erroneous loops because im illiterate * Add changes by @lqhuang
1 parent 5ef6c48 commit cd40e6f

File tree

5 files changed

+36
-18
lines changed

5 files changed

+36
-18
lines changed

mode/services.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from types import TracebackType
99
from typing import (
1010
Any,
11-
AsyncContextManager,
1211
AsyncIterator,
1312
Awaitable,
1413
Callable,
@@ -41,6 +40,7 @@
4140
from .utils.tracebacks import format_task_stack
4241
from .utils.trees import Node
4342
from .utils.types.trees import NodeT
43+
from .utils.typing import AsyncContextManager
4444

4545
__all__ = ["ServiceBase", "Service", "Diag", "task", "timer", "crontab"]
4646

@@ -567,16 +567,16 @@ def __init__(
567567
super().__init__(loop=self._loop)
568568

569569
def _new_started_event(self) -> Event:
570-
return Event()
570+
return Event(loop=self.loop)
571571

572572
def _new_stopped_event(self) -> Event:
573-
return Event()
573+
return Event(loop=self.loop)
574574

575575
def _new_shutdown_event(self) -> Event:
576-
return Event()
576+
return Event(loop=self.loop)
577577

578578
def _new_crashed_event(self) -> Event:
579-
return Event()
579+
return Event(loop=self.loop)
580580

581581
async def transition_with(
582582
self, flag: str, fut: Awaitable, *args: Any, **kwargs: Any

mode/threads.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import sys
1111
import threading
1212
import traceback
13-
from asyncio.locks import Event
1413
from time import monotonic
1514
from typing import (
1615
Any,
@@ -26,6 +25,7 @@
2625

2726
from .services import Service
2827
from .utils.futures import maybe_async, maybe_set_exception, maybe_set_result, notify
28+
from .utils.locks import Event
2929

3030
__all__ = [
3131
"QueuedMethod",
@@ -104,16 +104,18 @@ def __init__(
104104
**kwargs: Any,
105105
) -> None:
106106
# cannot share loop between threads, so create a new one
107+
assert asyncio.get_event_loop_policy().get_event_loop()
107108
if executor is not None:
108109
raise NotImplementedError("executor argument no longer supported")
109110
self.parent_loop = loop or asyncio.get_event_loop_policy().get_event_loop()
110111
self.thread_loop = (
111112
thread_loop or asyncio.get_event_loop_policy().new_event_loop()
112113
)
113-
self._thread_started = Event()
114+
self._thread_started = Event(loop=self.parent_loop)
114115
if Worker is not None:
115116
self.Worker = Worker
116117
super().__init__(loop=self.thread_loop, **kwargs)
118+
assert self._shutdown.loop is self.parent_loop
117119

118120
async def on_thread_started(self) -> None:
119121
...
@@ -148,7 +150,7 @@ async def on_thread_stop(self) -> None:
148150
# thread calls _shutdown.set(), parent calls _shutdown.wait()
149151

150152
def _new_shutdown_event(self) -> Event:
151-
return Event()
153+
return Event(loop=self.parent_loop)
152154

153155
async def maybe_start(self) -> bool:
154156
if not self._thread_started.is_set():
@@ -179,13 +181,12 @@ async def start(self) -> None:
179181

180182
async def _keepalive2(self) -> None:
181183
while not self.should_stop:
182-
await self.sleep(1.1)
184+
await self.sleep(2.0)
183185
if self.last_wakeup_at:
184186
if monotonic() - self.last_wakeup_at > 3.0:
185187
self.log.error("Thread keepalive is not responding...")
186-
asyncio.run_coroutine_threadsafe(
187-
self._wakeup_timer_in_thread(), self.thread_loop
188-
)
188+
await asyncio.sleep(0.0) # for unittest to invoke `call_soon`
189+
await self._wakeup_timer_in_thread()
189190

190191
async def _wakeup_timer_in_thread(self) -> None:
191192
self.last_wakeup_at = monotonic()
@@ -323,7 +324,7 @@ class MethodQueue(Service):
323324
def __init__(self, num_workers: int = 2, **kwargs: Any) -> None:
324325
super().__init__(**kwargs)
325326
self._queue = asyncio.Queue()
326-
self._queue_ready = Event()
327+
self._queue_ready = Event(loop=self.loop)
327328
self.num_workers = num_workers
328329
self._workers = []
329330

mode/utils/queues.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
import asyncio
33
import math
44
import typing
5-
from asyncio.locks import Event
65
from collections import deque
76
from typing import Any, Callable, List, Set, TypeVar, cast, no_type_check
87
from weakref import WeakSet
98

9+
from .locks import Event
1010
from .objects import cached_property
1111
from .typing import Deque
1212

@@ -60,8 +60,8 @@ def __init__(
6060
loop: asyncio.AbstractEventLoop = None
6161
) -> None:
6262
self.loop = loop
63-
self._resume = Event()
64-
self._suspend = Event()
63+
self._resume = Event(loop=self.loop)
64+
self._suspend = Event(loop=self.loop)
6565
if initially_suspended:
6666
self._suspend.set()
6767
self._queues = WeakSet()

t/functional/test_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import asyncio
22
import logging
3-
from asyncio.locks import Event
43
from typing import AsyncContextManager, ContextManager
54
from unittest.mock import Mock
65

76
import pytest
87

98
import mode
9+
from mode.utils.locks import Event
1010

1111

1212
class X(mode.Service):

t/unit/test_threads.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import asyncio
22
import sys
33
import threading
4-
from asyncio.locks import Event
54
from unittest.mock import ANY, Mock, patch
65

76
if sys.version_info < (3, 8):
@@ -13,6 +12,7 @@
1312

1413
from mode.threads import MethodQueue, QueueServiceThread, ServiceThread, WorkerThread
1514
from mode.utils.futures import done_future
15+
from mode.utils.locks import Event
1616

1717

1818
class test_WorkerThread:
@@ -263,6 +263,23 @@ async def test_crash(self, *, thread):
263263
await asyncio.sleep(0.1) # wait for call_soon_threadsafe
264264
thread._thread_running.set_exception.assert_called_with(exc)
265265

266+
@pytest.mark.asyncio
267+
async def test__wakeup_timer_in_thread(self, *, thread, event_loop):
268+
thread.add_future = Mock(name="thread.add_future")
269+
thread._wakeup_timer_in_thread = AsyncMock()
270+
thread._stopped.is_set = Mock(return_value=False)
271+
thread._crashed.is_set = Mock(return_value=False)
272+
thread.sleep = AsyncMock()
273+
274+
def cb():
275+
thread._stopped.is_set.return_value = True
276+
assert thread.should_stop
277+
278+
event_loop.call_soon(cb)
279+
await thread._keepalive2()
280+
281+
thread._wakeup_timer_in_thread.assert_awaited()
282+
266283

267284
class test_MethodQueue:
268285
@pytest.mark.asyncio

0 commit comments

Comments
 (0)