xoscar 0.4.1__cp39-cp39-macosx_10_9_x86_64.whl → 0.4.2__cp39-cp39-macosx_10_9_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of xoscar might be problematic. Click here for more details.

Binary file
@@ -17,8 +17,9 @@ from __future__ import annotations
17
17
 
18
18
  import asyncio
19
19
  import concurrent.futures as futures
20
+ import logging
20
21
  import weakref
21
- from typing import Any, Callable, Coroutine, Dict, Type
22
+ from typing import Any, Callable, Coroutine, Dict, Optional, Type
22
23
  from urllib.parse import urlparse
23
24
 
24
25
  from ...errors import ServerClosed
@@ -29,13 +30,15 @@ from .errors import ChannelClosed
29
30
 
30
31
  DEFAULT_DUMMY_ADDRESS = "dummy://0"
31
32
 
33
+ logger = logging.getLogger(__name__)
34
+
32
35
 
33
36
  class DummyChannel(Channel):
34
37
  """
35
38
  Channel for communications in same process.
36
39
  """
37
40
 
38
- __slots__ = "_in_queue", "_out_queue", "_closed"
41
+ __slots__ = "__weakref__", "_in_queue", "_out_queue", "_closed"
39
42
 
40
43
  name = "dummy"
41
44
 
@@ -100,8 +103,8 @@ class DummyServer(Server):
100
103
  _address_to_instances: weakref.WeakValueDictionary[str, "DummyServer"] = (
101
104
  weakref.WeakValueDictionary()
102
105
  )
103
- _channels: list[ChannelType]
104
- _tasks: list[asyncio.Task]
106
+ _channels: weakref.WeakSet[Channel]
107
+ _tasks: set[asyncio.Task]
105
108
  scheme: str | None = "dummy"
106
109
 
107
110
  def __init__(
@@ -111,8 +114,8 @@ class DummyServer(Server):
111
114
  ):
112
115
  super().__init__(address, channel_handler)
113
116
  self._closed = asyncio.Event()
114
- self._channels = []
115
- self._tasks = []
117
+ self._channels = weakref.WeakSet()
118
+ self._tasks = set()
116
119
 
117
120
  @classmethod
118
121
  def get_instance(cls, address: str):
@@ -178,7 +181,7 @@ class DummyServer(Server):
178
181
  f"{type(self).__name__} got unexpected "
179
182
  f'arguments: {",".join(kwargs)}'
180
183
  )
181
- self._channels.append(channel)
184
+ self._channels.add(channel)
182
185
  await self.channel_handler(channel)
183
186
 
184
187
  @implements(Server.stop)
@@ -203,6 +206,7 @@ class DummyClient(Client):
203
206
  self, local_address: str | None, dest_address: str | None, channel: Channel
204
207
  ):
205
208
  super().__init__(local_address, dest_address, channel)
209
+ self._task: Optional[asyncio.Task] = None
206
210
 
207
211
  @staticmethod
208
212
  @implements(Client.connect)
@@ -232,11 +236,25 @@ class DummyClient(Client):
232
236
  task = asyncio.create_task(conn_coro)
233
237
  client = DummyClient(local_address, dest_address, client_channel)
234
238
  client._task = task
235
- server._tasks.append(task)
239
+ server._tasks.add(task)
240
+
241
+ def _discard(t):
242
+ server._tasks.discard(t)
243
+ logger.info("Channel exit: %s", server_channel.info)
244
+
245
+ task.add_done_callback(_discard)
236
246
  return client
237
247
 
238
248
  @implements(Client.close)
239
249
  async def close(self):
240
250
  await super().close()
241
- self._task.cancel()
242
- self._task = None
251
+ if self._task is not None:
252
+ task_loop = self._task.get_loop()
253
+ if task_loop is not None:
254
+ if not task_loop.is_running():
255
+ logger.warning(
256
+ "Dummy channel cancel task on a stopped loop, dest address: %s.",
257
+ self.dest_address,
258
+ )
259
+ self._task.cancel()
260
+ self._task = None
@@ -113,7 +113,7 @@ class SocketChannel(Channel):
113
113
  class _BaseSocketServer(Server, metaclass=ABCMeta):
114
114
  __slots__ = "_aio_server", "_channels"
115
115
 
116
- _channels: list[ChannelType]
116
+ _channels: set[Channel]
117
117
 
118
118
  def __init__(
119
119
  self,
@@ -124,7 +124,7 @@ class _BaseSocketServer(Server, metaclass=ABCMeta):
124
124
  super().__init__(address, channel_handler)
125
125
  # asyncio.Server
126
126
  self._aio_server = aio_server
127
- self._channels = []
127
+ self._channels = set()
128
128
 
129
129
  @implements(Server.start)
130
130
  async def start(self):
@@ -170,9 +170,16 @@ class _BaseSocketServer(Server, metaclass=ABCMeta):
170
170
  dest_address=dest_address,
171
171
  channel_type=self.channel_type,
172
172
  )
173
- self._channels.append(channel)
173
+ self._channels.add(channel)
174
174
  # handle over channel to some handlers
175
- await self.channel_handler(channel)
175
+ try:
176
+ await self.channel_handler(channel)
177
+ finally:
178
+ if not channel.closed:
179
+ await channel.close()
180
+ # Remove channel if channel exit
181
+ self._channels.discard(channel)
182
+ logger.debug("Channel exit: %s", channel.info)
176
183
 
177
184
  @implements(Server.stop)
178
185
  async def stop(self):
@@ -185,6 +192,7 @@ class _BaseSocketServer(Server, metaclass=ABCMeta):
185
192
  await asyncio.gather(
186
193
  *(channel.close() for channel in self._channels if not channel.closed)
187
194
  )
195
+ self._channels.clear()
188
196
 
189
197
  @property
190
198
  @implements(Server.stopped)
@@ -368,7 +368,7 @@ class UCXServer(Server):
368
368
  scheme = "ucx"
369
369
 
370
370
  _ucp_listener: "ucp.Listener" # type: ignore
371
- _channels: List[UCXChannel]
371
+ _channels: set[UCXChannel]
372
372
 
373
373
  def __init__(
374
374
  self,
@@ -381,7 +381,7 @@ class UCXServer(Server):
381
381
  self.host = host
382
382
  self.port = port
383
383
  self._ucp_listener = ucp_listener
384
- self._channels = []
384
+ self._channels = set()
385
385
  self._closed = asyncio.Event()
386
386
 
387
387
  @classproperty
@@ -469,9 +469,16 @@ class UCXServer(Server):
469
469
  channel = UCXChannel(
470
470
  ucp_endpoint, local_address=local_address, dest_address=dest_address
471
471
  )
472
- self._channels.append(channel)
472
+ self._channels.add(channel)
473
473
  # handle over channel to some handlers
474
- await self.channel_handler(channel)
474
+ try:
475
+ await self.channel_handler(channel)
476
+ finally:
477
+ if not channel.closed:
478
+ await channel.close()
479
+ # Remove channel if channel exit
480
+ self._channels.discard(channel)
481
+ logger.debug("Channel exit: %s", channel.info)
475
482
 
476
483
  @implements(Server.stop)
477
484
  async def stop(self):
@@ -480,7 +487,7 @@ class UCXServer(Server):
480
487
  await asyncio.gather(
481
488
  *(channel.close() for channel in self._channels if not channel.closed)
482
489
  )
483
- self._channels = []
490
+ self._channels.clear()
484
491
  self._ucp_listener = None
485
492
  self._closed.set()
486
493
 
xoscar/backends/core.py CHANGED
@@ -18,6 +18,8 @@ from __future__ import annotations
18
18
  import asyncio
19
19
  import copy
20
20
  import logging
21
+ import threading
22
+ import weakref
21
23
  from typing import Type, Union
22
24
 
23
25
  from .._utils import Timer
@@ -31,8 +33,8 @@ ResultMessageType = Union[ResultMessage, ErrorMessage]
31
33
  logger = logging.getLogger(__name__)
32
34
 
33
35
 
34
- class ActorCaller:
35
- __slots__ = "_client_to_message_futures", "_clients", "_profiling_data"
36
+ class ActorCallerThreadLocal:
37
+ __slots__ = ("_client_to_message_futures", "_clients", "_profiling_data")
36
38
 
37
39
  _client_to_message_futures: dict[Client, dict[bytes, asyncio.Future]]
38
40
  _clients: dict[Client, asyncio.Task]
@@ -193,6 +195,7 @@ class ActorCaller:
193
195
  return await self.call_with_client(client, message, wait)
194
196
 
195
197
  async def stop(self):
198
+ logger.debug("Actor caller stop.")
196
199
  try:
197
200
  await asyncio.gather(*[client.close() for client in self._clients])
198
201
  except (ConnectionError, ServerClosed):
@@ -202,3 +205,37 @@ class ActorCaller:
202
205
  def cancel_tasks(self):
203
206
  # cancel listening for all clients
204
207
  _ = [task.cancel() for task in self._clients.values()]
208
+
209
+
210
+ class ActorCaller:
211
+ __slots__ = "_thread_local"
212
+
213
+ class _RefHolder:
214
+ pass
215
+
216
+ _close_loop = asyncio.new_event_loop()
217
+ _close_thread = threading.Thread(target=_close_loop.run_forever, daemon=True)
218
+ _close_thread.start()
219
+
220
+ def __init__(self):
221
+ self._thread_local = threading.local()
222
+
223
+ def __getattr__(self, item):
224
+ try:
225
+ actor_caller = self._thread_local.actor_caller
226
+ except AttributeError:
227
+ thread_info = str(threading.current_thread())
228
+ logger.debug("Creating a new actor caller for thread: %s", thread_info)
229
+ actor_caller = self._thread_local.actor_caller = ActorCallerThreadLocal()
230
+ ref = self._thread_local.ref = ActorCaller._RefHolder()
231
+ # If the thread exit, we clean the related actor callers and channels.
232
+
233
+ def _cleanup():
234
+ asyncio.run_coroutine_threadsafe(actor_caller.stop(), self._close_loop)
235
+ logger.debug(
236
+ "Clean up the actor caller due to thread exit: %s", thread_info
237
+ )
238
+
239
+ weakref.finalize(ref, _cleanup)
240
+
241
+ return getattr(actor_caller, item)
Binary file
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xoscar
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Summary: Python actor framework for heterogeneous computing.
5
5
  Home-page: http://github.com/xorbitsai/xoscar
6
6
  Author: Qin Xuye
@@ -1,7 +1,7 @@
1
- xoscar-0.4.1.dist-info/RECORD,,
2
- xoscar-0.4.1.dist-info/WHEEL,sha256=FoxzhRRMXcm8mSmqIxEPOmfhajsNIlMLdYtHdyz-ZKQ,109
3
- xoscar-0.4.1.dist-info/top_level.txt,sha256=vYlqqY4Nys8Thm1hePIuUv8eQePdULVWMmt7lXtX_ZA,21
4
- xoscar-0.4.1.dist-info/METADATA,sha256=s1flP4I8DrL3z4TI3mPEAZpBJTlNTM9UwVVmRNXB1-Y,9042
1
+ xoscar-0.4.2.dist-info/RECORD,,
2
+ xoscar-0.4.2.dist-info/WHEEL,sha256=FoxzhRRMXcm8mSmqIxEPOmfhajsNIlMLdYtHdyz-ZKQ,109
3
+ xoscar-0.4.2.dist-info/top_level.txt,sha256=vYlqqY4Nys8Thm1hePIuUv8eQePdULVWMmt7lXtX_ZA,21
4
+ xoscar-0.4.2.dist-info/METADATA,sha256=86wcGCexxpSltidUVDP0KShGLsodQR5lck9OpOWgHIc,9042
5
5
  xoscar/_utils.pyx,sha256=UR1FtYXAYKIdEWR9HulEpMbSOrkQWi6xGz63d4IQmG0,7059
6
6
  xoscar/backend.py,sha256=is436OPkZfSpQXaoqTRVta5eoye_pp45RFgCstAk2hU,1850
7
7
  xoscar/core.pxd,sha256=4lBq8J0kjcXcsGuvN7Kv4xcL5liHwTTFWlqyK7XAEnw,1280
@@ -12,18 +12,18 @@ xoscar/nvutils.py,sha256=qmW4mKLU0WB2yCs198ccQOgLL02zB7Fsa-AotO3NOmg,20412
12
12
  xoscar/constants.py,sha256=QHHSREw6uWBBjQDCFqlNfTvBZgniJPGy42KSIsR8Fqw,787
13
13
  xoscar/__init__.py,sha256=0zX8kKaio3ZIrlzB79WybcravMJw1OxPWjDspTgJFyQ,1608
14
14
  xoscar/api.py,sha256=3hztPoOxg8A_mlhWyWgVP7FMXG0PATA1TP4Rbaj7A-g,13327
15
- xoscar/core.cpython-39-darwin.so,sha256=Jdl9NelbQoqEAc-cL8_sLe1ywnQcYrSuRMdYpc-2o0Q,448264
15
+ xoscar/core.cpython-39-darwin.so,sha256=AmnkUdJv6-zKNVe0VrXZ8WeDSJP3tC_s4v4heq7Fz7Y,448264
16
16
  xoscar/utils.py,sha256=jUw6OICZUPBbmS1b3GE4vLctJf6fCKXrYtLtBuK-Oqc,16483
17
17
  xoscar/debug.py,sha256=9Z8SgE2WaKYQcyDo-5-DxEJQ533v7kWjrvCd28pSx3E,5069
18
18
  xoscar/libcpp.pxd,sha256=DJqBxLFOKL4iRr9Kale5UH3rbvPRD1x5bTSOPHFpz9I,1147
19
19
  xoscar/context.pyx,sha256=8CdgPnWcE9eOp3N600WgDQ03MCi8P73eUOGcfV7Zksg,10942
20
20
  xoscar/errors.py,sha256=wBlQOKsXf0Fc4skN39tDie0YZT-VIAuLNRgoDl2pZcA,1241
21
- xoscar/_utils.cpython-39-darwin.so,sha256=Fr0PrhRCQ6PvlPkueT74lRCnJQ6FgdtUfHPcoW9aEWU,160824
21
+ xoscar/_utils.cpython-39-darwin.so,sha256=DfpsecCvGEj-VmwuFx3jDhst2n85JC-j2ZP2yRtK_m0,160824
22
22
  xoscar/core.pyx,sha256=Aqc2i8Fetsd5wRAPF4kL0ddnBZn3E2HRNCvup79BbQc,21730
23
23
  xoscar/driver.py,sha256=498fowtJr6b3FE8FIOA_Tc1Vwx88nfZw7p0FxrML0h4,1372
24
24
  xoscar/profiling.py,sha256=BC5OF0HzSaXv8V7w-y-B8r5gV5DgxHFoTEIF6jCMioQ,8015
25
25
  xoscar/_utils.pxd,sha256=5KYAL3jfPdejsHnrGGT2s--ZUX5SXznQWpHVSno429k,1157
26
- xoscar/context.cpython-39-darwin.so,sha256=UkkEl3HqCkezVnFrHDoupgbN6N_CNaE_53sSfxounCY,200448
26
+ xoscar/context.cpython-39-darwin.so,sha256=yel10nYAEY6PysHn5DLdcyl156MS2qQXjGZdt-N9Jso,200448
27
27
  xoscar/metrics/__init__.py,sha256=9Badi7rxYikGm2dQiNCrj9GgMRBxwuR3JaEKcFZmfak,705
28
28
  xoscar/metrics/api.py,sha256=BBlMIFvVAGVfrtpeJ1YlH9Tqhy9OzGavwvGyeHcQ0Tk,8856
29
29
  xoscar/metrics/backends/__init__.py,sha256=h_JgzSqV5lP6vQ6XX_17kE4IY4BRnvKta_7VLQAL1ms,581
@@ -43,16 +43,16 @@ xoscar/serialization/pyfury.py,sha256=sifOnVMYoS82PzZEkzkfxesmMHei23k5UAUUKUyoOY
43
43
  xoscar/serialization/core.pxd,sha256=k4RoJgX5E5LGs4jdCQ7vvcn26MabXbrWoWhkO49X6YI,985
44
44
  xoscar/serialization/__init__.py,sha256=5Y_C3cYbQJIZ09LRjeCf-jrkLma7mfN8I5bznHrdsbg,846
45
45
  xoscar/serialization/numpy.py,sha256=5Kem87CvpJmzUMp3QHk4WeHU30FoQWTJJP2SwIcaQG0,2919
46
- xoscar/serialization/core.cpython-39-darwin.so,sha256=yA1neOIhTms_IGsCPKK57JaiyQeC2j-6OWUJMUuEe-g,408080
46
+ xoscar/serialization/core.cpython-39-darwin.so,sha256=OBeZI8npBa5M46HtWHAE1FtxqMe0URdNSv4NCc-TbUI,408080
47
47
  xoscar/serialization/cuda.py,sha256=iFUEnN4SiquBIhyieyOrfw3TnKnW-tU_vYgqOxO_DrA,3758
48
48
  xoscar/serialization/scipy.py,sha256=yOEi0NB8cqQ6e2UnCZ1w006RsB7T725tIL-DM_hNcsU,2482
49
49
  xoscar/serialization/aio.py,sha256=5DySPgDxU43ec7_5Ct44-Oqt7YNSJBfuf8VdQgQlChA,4731
50
50
  xoscar/serialization/core.pyx,sha256=bjR-zXGm9qersk7kYPzpjpMIxDl_Auur4BCubRfKmfA,29626
51
- xoscar/backends/message.cpython-39-darwin.so,sha256=XsJRs2s6D5KBvPLB6uMS-aWvCrRouDPWigKdO2LpOcQ,366752
51
+ xoscar/backends/message.cpython-39-darwin.so,sha256=rmxdgx6nD60Hfm3vUCrpr-C5fMjTvz2USHzIgpmcT9o,366752
52
52
  xoscar/backends/config.py,sha256=EG26f0GwX_f4dAhwTW77RBjiK9h8R_3JrD-rBF1bAq8,4984
53
53
  xoscar/backends/allocate_strategy.py,sha256=tC1Nbq2tJohahUwd-zoRYHEDX65wyuX8tmeY45uWj_w,4845
54
54
  xoscar/backends/__init__.py,sha256=VHEBQcUWM5bj027W8EUf9PiJUAP7JoMrRw3Tsvy5ySw,643
55
- xoscar/backends/core.py,sha256=YcXVrMrTUjnrnH-RhrtUyxnCbwon8miq2UigCQv-y_Q,8039
55
+ xoscar/backends/core.py,sha256=GlhWdwl6gWBBwHynV4UWqOXVKDS3OMfAtOLJQHNqV-8,9294
56
56
  xoscar/backends/context.py,sha256=Vr_PibRxYCDQ_gYK7r-BOlw9TXw8VQbFsVTH7K7mHPk,15470
57
57
  xoscar/backends/router.py,sha256=mhSvM5KVfV882jricVcpyxAqHEvhS4zL6ivczC6fOTE,7746
58
58
  xoscar/backends/message.pyx,sha256=uyzilPc_7SqNwGUL4U-Zbfqku8bfZyRW_Lt_S3I_LEU,17930
@@ -64,13 +64,13 @@ xoscar/backends/indigen/pool.py,sha256=mWYkOP4VVoUsXFgfpwruPuWblF6Waan5vxit8B-9_
64
64
  xoscar/backends/test/backend.py,sha256=nv9WFhH5Bbq4Q1HB9yfpciZBaeHT4IQAtzugBWESrUY,1263
65
65
  xoscar/backends/test/__init__.py,sha256=j2ZfD6prD9WjUxRUDC7Eq5Z7N7TkL6fFr59oNyc_vY4,682
66
66
  xoscar/backends/test/pool.py,sha256=TW4X6J-92Pti66103poQBNDBznX6CBD3RLOc_zixjTo,7257
67
- xoscar/backends/communication/ucx.py,sha256=7GAKIzlbxy-NpaDE9VLPde90tKxeU8lVZH8c_3ByuZ0,19703
67
+ xoscar/backends/communication/ucx.py,sha256=_Dp9Ld2MWIa1txSGMnmfYwJDT0esxS-GOd2FQ4BdHiM,19960
68
68
  xoscar/backends/communication/__init__.py,sha256=tB05BlK63iWQnfJgRzKt4mFKRtmWUki5hUGSZQwAotc,1050
69
69
  xoscar/backends/communication/core.py,sha256=sJeE3foRIqVPXldzYpFKHDSsabfAIFBU4JuXY4OyklY,2130
70
70
  xoscar/backends/communication/utils.py,sha256=AmovE-hmWLXNCPwHafYuaRjOk8m42BUyT3XBqfXQRVI,3664
71
71
  xoscar/backends/communication/errors.py,sha256=V3CdBe2xX9Rwv32f2dH2Msc84yaUhlyerZ42-739o1Q,723
72
- xoscar/backends/communication/socket.py,sha256=ZSDqS_Z9-Oxs2Q2fqWUuUJ9hNf4w8lNJ8gUv5r6Ji_Y,13691
73
- xoscar/backends/communication/dummy.py,sha256=gaKPNiN4x2aGZV3IGaaa8eaweBVjRh8B19jU1B5t2yw,7798
72
+ xoscar/backends/communication/socket.py,sha256=R9jF_Iy3HTSPXkBGrws5ckK-EjYnaPBSTdMqMZYqxqc,13972
73
+ xoscar/backends/communication/dummy.py,sha256=sKBqoFkftm6ynV3p5UNGI4VYFGlrOHJgJlqj35PGAgk,8479
74
74
  xoscar/backends/communication/base.py,sha256=0P4Tr35GSWpRp394e9jVWUUoKKa-gIk177eYPw1BnSU,7421
75
75
  xoscar/aio/__init__.py,sha256=kViDKR_kJe59VQViHITKEfBcIgN4ZJblUyd8zl0E3ZI,675
76
76
  xoscar/aio/file.py,sha256=PBtkLp-Q7XtYl-zk00s18TtgIrkNr60J3Itf66ctO1o,1486
File without changes