1
1
import asyncio
2
- from concurrent .futures import ThreadPoolExecutor
3
- from queue import Queue
4
- from typing import Any , Self , TYPE_CHECKING
5
-
6
- if TYPE_CHECKING :
7
- from collections .abc import Awaitable
2
+ from typing import Any , Self
8
3
4
+ from bookmarkmgr .asyncio import ForgivingTaskGroup
9
5
from bookmarkmgr .cronet ._cronet import ffi , lib
10
6
from bookmarkmgr .cronet .errors import NotContextManagerError
11
7
from bookmarkmgr .cronet .types import Executor , Runnable
@@ -16,15 +12,21 @@ def _executor_execute(executor: Executor, runnable: Runnable) -> None:
16
12
manager : ExecutorManager = ffi .from_handle (
17
13
lib .Cronet_Executor_GetClientContext (executor ),
18
14
)
19
- manager .enqueue_runnable (runnable )
15
+ manager .submit_runnable (runnable )
16
+
17
+
18
+ def _process_runnable (runnable : Runnable ) -> None :
19
+ try :
20
+ lib .Cronet_Runnable_Run (runnable )
21
+ finally :
22
+ lib .Cronet_Runnable_Destroy (runnable )
20
23
21
24
22
25
class ExecutorManager :
23
26
def __init__ (self ) -> None :
24
- self ._handle = ffi .new_handle (self )
25
- self ._queue : Queue [Runnable | None ] = Queue ()
26
- self ._worker : Awaitable [None ] | None = None
27
27
self ._executor : Executor | None = None
28
+ self ._handle = ffi .new_handle (self )
29
+ self ._task_group : ForgivingTaskGroup | None = None
28
30
29
31
async def __aenter__ (self ) -> Self :
30
32
if self ._executor is None :
@@ -33,10 +35,11 @@ async def __aenter__(self) -> Self:
33
35
)
34
36
lib .Cronet_Executor_SetClientContext (self ._executor , self ._handle )
35
37
36
- self ._processing_allowed = True
38
+ if self ._task_group is None :
39
+ self ._task_group = ForgivingTaskGroup ()
40
+ await self ._task_group .__aenter__ ()
37
41
38
- if self ._worker is None :
39
- self ._worker = asyncio .create_task (self ._spawn_worker_thread ())
42
+ self ._processing_allowed = True
40
43
41
44
return self
42
45
@@ -46,43 +49,36 @@ async def __aexit__(
46
49
* args : Any , # noqa: PYI036
47
50
** kwargs : Any ,
48
51
) -> None :
49
- self .shutdown ( process_pending = exc_type is None )
52
+ self ._processing_allowed = False
50
53
51
54
try :
52
- if self ._worker is not None :
53
- await self ._worker
55
+ if self ._task_group is not None :
56
+ await self ._task_group .__aexit__ (exc_type , * args , ** kwargs )
57
+ self ._task_group = None
54
58
finally :
55
59
if self ._executor is not None :
56
60
lib .Cronet_Executor_Destroy (self ._executor )
57
61
self ._executor = None
58
62
59
- self ._worker = None
63
+ def submit_runnable (self , runnable : Runnable ) -> None :
64
+ if self ._task_group is None :
65
+ message = "ExecutorManager has not been entered"
66
+ raise RuntimeError (message )
60
67
61
- async def _spawn_worker_thread (self ) -> None :
62
- with ThreadPoolExecutor () as pool :
63
- await asyncio .get_running_loop ().run_in_executor (
64
- pool ,
65
- self ._worker_loop ,
66
- )
68
+ if not self ._processing_allowed :
69
+ lib .Cronet_Runnable_Destroy (runnable )
70
+ return
67
71
68
- def _worker_loop (self ) -> None :
69
- while (runnable := self ._queue .get ()) is not None :
70
- try :
71
- if self ._processing_allowed :
72
- lib .Cronet_Runnable_Run (runnable )
73
- finally :
74
- lib .Cronet_Runnable_Destroy (runnable )
75
-
76
- def enqueue_runnable (self , runnable : Runnable ) -> None :
77
- self ._queue .put_nowait (runnable )
72
+ self ._task_group .create_task (
73
+ asyncio .to_thread (
74
+ _process_runnable ,
75
+ runnable ,
76
+ ),
77
+ )
78
78
79
79
@property
80
80
def executor (self ) -> Executor :
81
81
if self ._executor is None :
82
82
raise NotContextManagerError
83
83
84
84
return self ._executor
85
-
86
- def shutdown (self , * , process_pending : bool = True ) -> None :
87
- self ._processing_allowed = process_pending
88
- self ._queue .put_nowait (None )
0 commit comments