xoscar 0.6.2__cp39-cp39-macosx_10_9_x86_64.whl → 0.7.0rc1__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.
- xoscar/_utils.cpython-39-darwin.so +0 -0
- xoscar/_utils.pyx +2 -1
- xoscar/backends/indigen/__main__.py +19 -0
- xoscar/backends/indigen/fate_sharing.py +221 -0
- xoscar/backends/indigen/pool.py +200 -264
- xoscar/backends/indigen/shared_memory.py +548 -0
- xoscar/backends/message.cpython-39-darwin.so +0 -0
- xoscar/backends/pool.py +4 -14
- xoscar/backends/test/pool.py +13 -24
- xoscar/collective/xoscar_pygloo.cpython-39-darwin.so +0 -0
- xoscar/context.cpython-39-darwin.so +0 -0
- xoscar/core.cpython-39-darwin.so +0 -0
- xoscar/serialization/core.cpython-39-darwin.so +0 -0
- xoscar/virtualenv/core.py +4 -0
- xoscar/virtualenv/uv.py +4 -1
- {xoscar-0.6.2.dist-info → xoscar-0.7.0rc1.dist-info}/METADATA +3 -1
- {xoscar-0.6.2.dist-info → xoscar-0.7.0rc1.dist-info}/RECORD +19 -16
- {xoscar-0.6.2.dist-info → xoscar-0.7.0rc1.dist-info}/WHEEL +1 -0
- {xoscar-0.6.2.dist-info → xoscar-0.7.0rc1.dist-info}/top_level.txt +0 -0
|
Binary file
|
xoscar/_utils.pyx
CHANGED
|
@@ -213,7 +213,8 @@ def create_actor_ref(*args, **kwargs):
|
|
|
213
213
|
if kwargs:
|
|
214
214
|
raise ValueError('Only `address` or `uid` keywords are supported')
|
|
215
215
|
|
|
216
|
-
|
|
216
|
+
# args: [address, uid], or [address, uid, proxy_addresses]
|
|
217
|
+
if 2 <= len(args) <= 3 and isinstance(args[0], (str, bytes)):
|
|
217
218
|
if address:
|
|
218
219
|
raise ValueError('address has been specified')
|
|
219
220
|
address = to_str(args[0])
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
if __name__ == "__main__":
|
|
2
|
+
import click
|
|
3
|
+
|
|
4
|
+
@click.group(
|
|
5
|
+
invoke_without_command=True,
|
|
6
|
+
name="xoscar",
|
|
7
|
+
help="Xoscar command-line interface.",
|
|
8
|
+
)
|
|
9
|
+
def main():
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
@main.command("start_sub_pool", help="Start a sub pool.")
|
|
13
|
+
@click.option("shm_name", "-sn", type=str, help="Shared memory name.")
|
|
14
|
+
def start_sub_pool(shm_name):
|
|
15
|
+
from xoscar.backends.indigen.pool import MainActorPool
|
|
16
|
+
|
|
17
|
+
MainActorPool._start_sub_pool_in_child(shm_name)
|
|
18
|
+
|
|
19
|
+
main()
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Copyright 2017 The Ray Authors.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http:#www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import asyncio
|
|
16
|
+
import logging
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
19
|
+
|
|
20
|
+
import psutil
|
|
21
|
+
|
|
22
|
+
# Linux can bind child processes' lifetimes to that of their parents via prctl.
|
|
23
|
+
# prctl support is detected dynamically once, and assumed thereafter.
|
|
24
|
+
linux_prctl = None
|
|
25
|
+
|
|
26
|
+
# Windows can bind processes' lifetimes to that of kernel-level "job objects".
|
|
27
|
+
# We keep a global job object to tie its lifetime to that of our own process.
|
|
28
|
+
win32_job = None
|
|
29
|
+
win32_AssignProcessToJobObject = None
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def detect_fate_sharing_support_win32():
|
|
35
|
+
global win32_job, win32_AssignProcessToJobObject
|
|
36
|
+
if win32_job is None and sys.platform == "win32":
|
|
37
|
+
import ctypes
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
from ctypes.wintypes import BOOL, DWORD, HANDLE, LPCWSTR, LPVOID
|
|
41
|
+
|
|
42
|
+
kernel32 = ctypes.WinDLL("kernel32")
|
|
43
|
+
kernel32.CreateJobObjectW.argtypes = (LPVOID, LPCWSTR)
|
|
44
|
+
kernel32.CreateJobObjectW.restype = HANDLE
|
|
45
|
+
sijo_argtypes = (HANDLE, ctypes.c_int, LPVOID, DWORD)
|
|
46
|
+
kernel32.SetInformationJobObject.argtypes = sijo_argtypes
|
|
47
|
+
kernel32.SetInformationJobObject.restype = BOOL
|
|
48
|
+
kernel32.AssignProcessToJobObject.argtypes = (HANDLE, HANDLE)
|
|
49
|
+
kernel32.AssignProcessToJobObject.restype = BOOL
|
|
50
|
+
kernel32.IsDebuggerPresent.argtypes = ()
|
|
51
|
+
kernel32.IsDebuggerPresent.restype = BOOL
|
|
52
|
+
except (AttributeError, TypeError, ImportError):
|
|
53
|
+
kernel32 = None
|
|
54
|
+
job = kernel32.CreateJobObjectW(None, None) if kernel32 else None
|
|
55
|
+
job = subprocess.Handle(job) if job else job
|
|
56
|
+
if job:
|
|
57
|
+
from ctypes.wintypes import DWORD, LARGE_INTEGER, ULARGE_INTEGER
|
|
58
|
+
|
|
59
|
+
class JOBOBJECT_BASIC_LIMIT_INFORMATION(ctypes.Structure):
|
|
60
|
+
_fields_ = [
|
|
61
|
+
("PerProcessUserTimeLimit", LARGE_INTEGER),
|
|
62
|
+
("PerJobUserTimeLimit", LARGE_INTEGER),
|
|
63
|
+
("LimitFlags", DWORD),
|
|
64
|
+
("MinimumWorkingSetSize", ctypes.c_size_t),
|
|
65
|
+
("MaximumWorkingSetSize", ctypes.c_size_t),
|
|
66
|
+
("ActiveProcessLimit", DWORD),
|
|
67
|
+
("Affinity", ctypes.c_size_t),
|
|
68
|
+
("PriorityClass", DWORD),
|
|
69
|
+
("SchedulingClass", DWORD),
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
class IO_COUNTERS(ctypes.Structure):
|
|
73
|
+
_fields_ = [
|
|
74
|
+
("ReadOperationCount", ULARGE_INTEGER),
|
|
75
|
+
("WriteOperationCount", ULARGE_INTEGER),
|
|
76
|
+
("OtherOperationCount", ULARGE_INTEGER),
|
|
77
|
+
("ReadTransferCount", ULARGE_INTEGER),
|
|
78
|
+
("WriteTransferCount", ULARGE_INTEGER),
|
|
79
|
+
("OtherTransferCount", ULARGE_INTEGER),
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
class JOBOBJECT_EXTENDED_LIMIT_INFORMATION(ctypes.Structure):
|
|
83
|
+
_fields_ = [
|
|
84
|
+
("BasicLimitInformation", JOBOBJECT_BASIC_LIMIT_INFORMATION),
|
|
85
|
+
("IoInfo", IO_COUNTERS),
|
|
86
|
+
("ProcessMemoryLimit", ctypes.c_size_t),
|
|
87
|
+
("JobMemoryLimit", ctypes.c_size_t),
|
|
88
|
+
("PeakProcessMemoryUsed", ctypes.c_size_t),
|
|
89
|
+
("PeakJobMemoryUsed", ctypes.c_size_t),
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
debug = kernel32.IsDebuggerPresent()
|
|
93
|
+
|
|
94
|
+
# Defined in <WinNT.h>; also available here:
|
|
95
|
+
# https://docs.microsoft.com/en-us/windows/win32/api/jobapi2/nf-jobapi2-setinformationjobobject
|
|
96
|
+
JobObjectExtendedLimitInformation = 9
|
|
97
|
+
JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800
|
|
98
|
+
JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION = 0x00000400
|
|
99
|
+
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000
|
|
100
|
+
buf = JOBOBJECT_EXTENDED_LIMIT_INFORMATION()
|
|
101
|
+
buf.BasicLimitInformation.LimitFlags = (
|
|
102
|
+
(0 if debug else JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE)
|
|
103
|
+
| JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION
|
|
104
|
+
| JOB_OBJECT_LIMIT_BREAKAWAY_OK
|
|
105
|
+
)
|
|
106
|
+
infoclass = JobObjectExtendedLimitInformation
|
|
107
|
+
if not kernel32.SetInformationJobObject(
|
|
108
|
+
job, infoclass, ctypes.byref(buf), ctypes.sizeof(buf)
|
|
109
|
+
):
|
|
110
|
+
job = None
|
|
111
|
+
win32_AssignProcessToJobObject = (
|
|
112
|
+
kernel32.AssignProcessToJobObject if kernel32 is not None else False
|
|
113
|
+
)
|
|
114
|
+
win32_job = job if job else False
|
|
115
|
+
return bool(win32_job)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def detect_fate_sharing_support_linux():
|
|
119
|
+
global linux_prctl
|
|
120
|
+
if linux_prctl is None and sys.platform.startswith("linux"):
|
|
121
|
+
try:
|
|
122
|
+
from ctypes import CDLL, c_int, c_ulong
|
|
123
|
+
|
|
124
|
+
prctl = CDLL(None).prctl
|
|
125
|
+
prctl.restype = c_int
|
|
126
|
+
prctl.argtypes = [c_int, c_ulong, c_ulong, c_ulong, c_ulong]
|
|
127
|
+
except (AttributeError, TypeError):
|
|
128
|
+
prctl = None
|
|
129
|
+
linux_prctl = prctl if prctl else False
|
|
130
|
+
return bool(linux_prctl)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def detect_fate_sharing_support():
|
|
134
|
+
result = None
|
|
135
|
+
if sys.platform == "win32":
|
|
136
|
+
result = detect_fate_sharing_support_win32()
|
|
137
|
+
elif sys.platform.startswith("linux"):
|
|
138
|
+
result = detect_fate_sharing_support_linux()
|
|
139
|
+
return result
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
if detect_fate_sharing_support():
|
|
143
|
+
logger.info("Using kernel-level fate-sharing.")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def set_kill_on_parent_death_linux():
|
|
147
|
+
"""Ensures this process dies if its parent dies (fate-sharing).
|
|
148
|
+
|
|
149
|
+
Linux-only. Must be called in preexec_fn (i.e. by the child).
|
|
150
|
+
"""
|
|
151
|
+
if detect_fate_sharing_support_linux():
|
|
152
|
+
import signal
|
|
153
|
+
|
|
154
|
+
PR_SET_PDEATHSIG = 1
|
|
155
|
+
if linux_prctl(PR_SET_PDEATHSIG, signal.SIGKILL, 0, 0, 0) != 0:
|
|
156
|
+
import ctypes
|
|
157
|
+
|
|
158
|
+
raise OSError(ctypes.get_errno(), "prctl(PR_SET_PDEATHSIG) failed")
|
|
159
|
+
else:
|
|
160
|
+
assert False, "PR_SET_PDEATHSIG used despite being unavailable"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def set_kill_child_on_death_win32(child_proc):
|
|
164
|
+
"""Ensures the child process dies if this process dies (fate-sharing).
|
|
165
|
+
|
|
166
|
+
Windows-only. Must be called by the parent, after spawning the child.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
child_proc: The subprocess.Popen or subprocess.Handle object.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
if isinstance(child_proc, subprocess.Popen):
|
|
173
|
+
child_proc = child_proc._handle
|
|
174
|
+
assert isinstance(child_proc, subprocess.Handle)
|
|
175
|
+
|
|
176
|
+
if detect_fate_sharing_support_win32():
|
|
177
|
+
if not win32_AssignProcessToJobObject(win32_job, int(child_proc)):
|
|
178
|
+
import ctypes
|
|
179
|
+
|
|
180
|
+
raise OSError(ctypes.get_last_error(), "AssignProcessToJobObject() failed")
|
|
181
|
+
else:
|
|
182
|
+
assert False, "AssignProcessToJobObject used despite being unavailable"
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def preexec_fn():
|
|
186
|
+
import signal
|
|
187
|
+
|
|
188
|
+
signal.pthread_sigmask(signal.SIG_BLOCK, {signal.SIGINT})
|
|
189
|
+
if sys.platform.startswith("linux"):
|
|
190
|
+
set_kill_on_parent_death_linux()
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
async def create_subprocess_exec(*args, **kwargs):
|
|
194
|
+
win32 = sys.platform == "win32"
|
|
195
|
+
# With Windows fate-sharing, we need special care:
|
|
196
|
+
# The process must be added to the job before it is allowed to execute.
|
|
197
|
+
# Otherwise, there's a race condition: the process might spawn children
|
|
198
|
+
# before the process itself is assigned to the job.
|
|
199
|
+
# After that point, its children will not be added to the job anymore.
|
|
200
|
+
CREATE_SUSPENDED = 0x00000004 # from Windows headers
|
|
201
|
+
creationflags = CREATE_SUSPENDED if win32 else 0
|
|
202
|
+
if win32 and detect_fate_sharing_support_win32():
|
|
203
|
+
creationflags |= subprocess.CREATE_NEW_PROCESS_GROUP
|
|
204
|
+
# CREATE_NEW_PROCESS_GROUP is used to send Ctrl+C on Windows:
|
|
205
|
+
# https://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal
|
|
206
|
+
process: asyncio.subprocess.Process = await asyncio.create_subprocess_exec(
|
|
207
|
+
*args,
|
|
208
|
+
**kwargs,
|
|
209
|
+
preexec_fn=preexec_fn if not win32 else None,
|
|
210
|
+
creationflags=creationflags,
|
|
211
|
+
)
|
|
212
|
+
if win32:
|
|
213
|
+
proc = process._transport._proc
|
|
214
|
+
try:
|
|
215
|
+
set_kill_child_on_death_win32(proc)
|
|
216
|
+
psutil.Process(process.pid).resume()
|
|
217
|
+
except (psutil.Error, OSError):
|
|
218
|
+
logger.exception("Resume process failed, kill %s.", process.pid)
|
|
219
|
+
process.kill()
|
|
220
|
+
raise
|
|
221
|
+
return process
|