Prompt48 commited on
Commit
c714546
·
verified ·
1 Parent(s): cfa7007

Upload edit\Qwen3-TTS-test\.venv\Lib\site-packages\torch\multiprocessing\spawn.py with huggingface_hub

Browse files
edit//Qwen3-TTS-test//.venv//Lib//site-packages//torch//multiprocessing//spawn.py ADDED
@@ -0,0 +1,340 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ import logging
3
+ import multiprocessing
4
+ import multiprocessing.connection
5
+ import os
6
+ import pickle
7
+ import signal
8
+ import sys
9
+ import tempfile
10
+ import time
11
+ import warnings
12
+ from concurrent.futures import as_completed, ThreadPoolExecutor
13
+ from typing import Optional
14
+
15
+ from . import _prctl_pr_set_pdeathsig # type: ignore[attr-defined]
16
+
17
+
18
+ ENV_VAR_PARALLEL_START = "TORCH_MP_PARALLEL_START"
19
+
20
+ log = logging.getLogger(__name__)
21
+
22
+ __all__ = [
23
+ "ProcessContext",
24
+ "ProcessException",
25
+ "ProcessExitedException",
26
+ "ProcessRaisedException",
27
+ "spawn",
28
+ "SpawnContext",
29
+ "start_processes",
30
+ ]
31
+
32
+
33
+ class ProcessException(Exception):
34
+ __slots__ = ["error_index", "error_pid"]
35
+
36
+ def __init__(self, msg: str, error_index: int, pid: int):
37
+ super().__init__(msg)
38
+ self.msg = msg
39
+ self.error_index = error_index
40
+ self.pid = pid
41
+
42
+ def __reduce__(self):
43
+ return type(self), (self.msg, self.error_index, self.pid)
44
+
45
+
46
+ class ProcessRaisedException(ProcessException):
47
+ """Exception raised when a process failed due to an exception raised by the code."""
48
+
49
+ def __init__(
50
+ self,
51
+ msg: str,
52
+ error_index: int,
53
+ error_pid: int,
54
+ ):
55
+ super().__init__(msg, error_index, error_pid)
56
+
57
+
58
+ class ProcessExitedException(ProcessException):
59
+ """Exception raised when a process failed due to signal or exited with a specific code."""
60
+
61
+ __slots__ = ["exit_code"]
62
+
63
+ def __init__(
64
+ self,
65
+ msg: str,
66
+ error_index: int,
67
+ error_pid: int,
68
+ exit_code: int,
69
+ signal_name: Optional[str] = None,
70
+ ):
71
+ super().__init__(msg, error_index, error_pid)
72
+ self.exit_code = exit_code
73
+ self.signal_name = signal_name
74
+
75
+ def __reduce__(self):
76
+ return (
77
+ type(self),
78
+ (self.msg, self.error_index, self.pid, self.exit_code, self.signal_name),
79
+ )
80
+
81
+
82
+ def _wrap(fn, i, args, error_file):
83
+ # prctl(2) is a Linux specific system call.
84
+ # On other systems the following function call has no effect.
85
+ # This is set to ensure that non-daemonic child processes can
86
+ # terminate if their parent terminates before they do.
87
+ _prctl_pr_set_pdeathsig(signal.SIGINT)
88
+
89
+ try:
90
+ fn(i, *args)
91
+ except KeyboardInterrupt:
92
+ pass # SIGINT; Killed by parent, do nothing
93
+ except Exception:
94
+ # Propagate exception to parent process, keeping original traceback
95
+ import traceback
96
+
97
+ with open(error_file, "wb") as fh:
98
+ pickle.dump(traceback.format_exc(), fh)
99
+ sys.exit(1)
100
+
101
+
102
+ class ProcessContext:
103
+ def __init__(self, processes, error_files):
104
+ self.error_files = error_files
105
+ self.processes = processes
106
+ self.sentinels = {
107
+ process.sentinel: index for index, process in enumerate(processes)
108
+ }
109
+
110
+ def pids(self):
111
+ return [int(process.pid) for process in self.processes]
112
+
113
+ def _join_procs_with_timeout(self, timeout: float):
114
+ """Attempt to join all processes with a shared timeout."""
115
+ end = time.monotonic() + timeout
116
+ for process in self.processes:
117
+ time_to_wait = max(0, end - time.monotonic())
118
+ process.join(time_to_wait)
119
+
120
+ def join(
121
+ self, timeout: Optional[float] = None, grace_period: Optional[float] = None
122
+ ):
123
+ r"""Join one or more processes within spawn context.
124
+
125
+ Attempt to join one or more processes in this spawn context.
126
+ If one of them exited with a non-zero exit status, this function
127
+ kills the remaining processes (optionally with a grace period)
128
+ and raises an exception with the cause of the first process exiting.
129
+
130
+ Returns ``True`` if all processes have been joined successfully,
131
+ ``False`` if there are more processes that need to be joined.
132
+
133
+ Args:
134
+ timeout (float): Wait this long (in seconds) before giving up on waiting.
135
+ grace_period (float): When any processes fail, wait this long (in seconds)
136
+ for others to shutdown gracefully before terminating them. If they
137
+ still don't exit, wait another grace period before killing them.
138
+ """
139
+ # Ensure this function can be called even when we're done.
140
+ if len(self.sentinels) == 0:
141
+ return True
142
+
143
+ # Wait for any process to fail or all of them to succeed.
144
+ ready = multiprocessing.connection.wait(
145
+ self.sentinels.keys(),
146
+ timeout=timeout,
147
+ )
148
+
149
+ error_index = None
150
+ for sentinel in ready:
151
+ index = self.sentinels.pop(sentinel)
152
+ process = self.processes[index]
153
+ process.join()
154
+ if process.exitcode != 0:
155
+ error_index = index
156
+ break
157
+
158
+ # Return if there was no error.
159
+ if error_index is None:
160
+ # Return whether or not all processes have been joined.
161
+ return len(self.sentinels) == 0
162
+ # An error occurred. Clean-up all processes before returning.
163
+ # First, allow a grace period for processes to shutdown themselves.
164
+ if grace_period is not None:
165
+ self._join_procs_with_timeout(grace_period)
166
+ # Then, terminate processes that are still alive. Try SIGTERM first.
167
+ for process in self.processes:
168
+ if process.is_alive():
169
+ log.warning("Terminating process %s via signal SIGTERM", process.pid)
170
+ process.terminate()
171
+
172
+ # Try SIGKILL if the process isn't going down after another grace_period.
173
+ # The reason is related to python signal handling is limited
174
+ # to main thread and if that is in c/c++ land and stuck it won't
175
+ # to handle it. We have seen processes getting stuck not handling
176
+ # SIGTERM for the above reason.
177
+ self._join_procs_with_timeout(30 if grace_period is None else grace_period)
178
+ for process in self.processes:
179
+ if process.is_alive():
180
+ log.warning(
181
+ "Unable to shutdown process %s via SIGTERM , forcefully exiting via SIGKILL",
182
+ process.pid,
183
+ )
184
+ process.kill()
185
+ process.join()
186
+
187
+ # The file will only be created if the process crashed.
188
+ failed_process = self.processes[error_index]
189
+ if not os.access(self.error_files[error_index], os.R_OK):
190
+ exitcode = self.processes[error_index].exitcode
191
+ if exitcode < 0:
192
+ try:
193
+ name = signal.Signals(-exitcode).name
194
+ except ValueError:
195
+ name = f"<Unknown signal {-exitcode}>"
196
+ raise ProcessExitedException(
197
+ "process %d terminated with signal %s" % (error_index, name),
198
+ error_index=error_index,
199
+ error_pid=failed_process.pid,
200
+ exit_code=exitcode,
201
+ signal_name=name,
202
+ )
203
+ else:
204
+ raise ProcessExitedException(
205
+ "process %d terminated with exit code %d" % (error_index, exitcode),
206
+ error_index=error_index,
207
+ error_pid=failed_process.pid,
208
+ exit_code=exitcode,
209
+ )
210
+
211
+ with open(self.error_files[error_index], "rb") as fh:
212
+ original_trace = pickle.load(fh)
213
+ msg = "\n\n-- Process %d terminated with the following error:\n" % error_index
214
+ msg += original_trace
215
+ raise ProcessRaisedException(msg, error_index, failed_process.pid)
216
+
217
+
218
+ class SpawnContext(ProcessContext):
219
+ def __init__(self, processes, error_files):
220
+ warnings.warn("SpawnContext is renamed to ProcessContext since 1.4 release.")
221
+ super().__init__(processes, error_files)
222
+
223
+
224
+ # Note: [start_processes]
225
+ # mp.start_processes handles both start_method='spawn' and 'fork'. It's supposed to be a
226
+ # more generalized API than mp.spawn. Currently we only document mp.spawn as it's the
227
+ # CUDA compatible start_method. However, in environments like Ipython notebooks, 'fork'
228
+ # works better than 'spawn'. Every helper function we created for mp.spawn is indeed
229
+ # general enough, and backends like XLA can reuse them in Colab notebooks as well.
230
+ # Currently we only add this API first, we can consider adding it to documentation as
231
+ # needed in the future.
232
+ def start_processes(
233
+ fn,
234
+ args=(),
235
+ nprocs=1,
236
+ join=True,
237
+ daemon=False,
238
+ start_method="spawn",
239
+ ):
240
+ # To speed up performance in certain cases (see https://github.com/pytorch/pytorch/issues/133010),
241
+ # this func will start processes in parallel if start_method is 'forkserver'.
242
+ # Please opt in to this perf optimization by setting env var (TORCH_MP_PARALLEL_START) to 1.
243
+ # todo: investigate why spawn does not work with threadpool and raises SIGINT
244
+ if (
245
+ start_method == "forkserver"
246
+ and os.environ.get(ENV_VAR_PARALLEL_START, "0") == "1"
247
+ ):
248
+ log.info("Starting processes in parallel.")
249
+ start_parallel = True
250
+ else:
251
+ # Set env var TORCH_MP_PARALLEL_START to 0 to disable parallel start
252
+ start_parallel = False
253
+
254
+ mp = multiprocessing.get_context(start_method)
255
+ error_files = [None] * nprocs
256
+ processes = [None] * nprocs
257
+
258
+ def start_process(i):
259
+ # Each process is assigned a file to write tracebacks to. We
260
+ # use the file being non-empty to indicate an exception
261
+ # occurred (vs an expected shutdown). Note: this previously
262
+ # used a multiprocessing.Queue but that can be prone to
263
+ # deadlocks, so we went with a simpler solution for a one-shot
264
+ # message between processes.
265
+ tf = tempfile.NamedTemporaryFile(
266
+ prefix="pytorch-errorfile-", suffix=".pickle", delete=False
267
+ )
268
+ tf.close()
269
+ os.unlink(tf.name)
270
+ process = mp.Process(
271
+ target=_wrap,
272
+ args=(fn, i, args, tf.name),
273
+ daemon=daemon,
274
+ )
275
+ process.start()
276
+ return i, process, tf.name
277
+
278
+ if not start_parallel:
279
+ for i in range(nprocs):
280
+ idx, process, tf_name = start_process(i)
281
+ error_files[idx] = tf_name
282
+ processes[idx] = process
283
+ else:
284
+ with ThreadPoolExecutor(max_workers=nprocs) as executor:
285
+ futures = [executor.submit(start_process, i) for i in range(nprocs)]
286
+ for fut in as_completed(futures):
287
+ idx, process, tf_name = fut.result()
288
+ # idx and process rank needs to be the same.
289
+ error_files[idx] = tf_name
290
+ processes[idx] = process
291
+ context = ProcessContext(processes, error_files)
292
+ if not join:
293
+ return context
294
+
295
+ # Loop on join until it returns True or raises an exception.
296
+ while not context.join():
297
+ pass
298
+
299
+
300
+ def spawn(fn, args=(), nprocs=1, join=True, daemon=False, start_method="spawn"):
301
+ r"""Spawns ``nprocs`` processes that run ``fn`` with ``args``.
302
+
303
+ If one of the processes exits with a non-zero exit status, the
304
+ remaining processes are killed and an exception is raised with the
305
+ cause of termination. In the case an exception was caught in the
306
+ child process, it is forwarded and its traceback is included in
307
+ the exception raised in the parent process.
308
+
309
+ Args:
310
+ fn (function): Function is called as the entrypoint of the
311
+ spawned process. This function must be defined at the top
312
+ level of a module so it can be pickled and spawned. This
313
+ is a requirement imposed by multiprocessing.
314
+
315
+ The function is called as ``fn(i, *args)``, where ``i`` is
316
+ the process index and ``args`` is the passed through tuple
317
+ of arguments.
318
+
319
+ args (tuple): Arguments passed to ``fn``.
320
+ nprocs (int): Number of processes to spawn.
321
+ join (bool): Perform a blocking join on all processes.
322
+ daemon (bool): The spawned processes' daemon flag. If set to True,
323
+ daemonic processes will be created.
324
+ start_method (str): (deprecated) this method will always use ``spawn``
325
+ as the start method. To use a different start method
326
+ use ``start_processes()``.
327
+
328
+ Returns:
329
+ None if ``join`` is ``True``,
330
+ :class:`~ProcessContext` if ``join`` is ``False``
331
+
332
+ """
333
+ if start_method != "spawn":
334
+ msg = (
335
+ f"This method only supports start_method=spawn (got: {start_method}).\n"
336
+ "To use a different start_method use:\n\t\t"
337
+ " torch.multiprocessing.start_processes(...)"
338
+ )
339
+ warnings.warn(msg, FutureWarning, stacklevel=2)
340
+ return start_processes(fn, args, nprocs, join, daemon, start_method="spawn")