cp -rf py3.13/examples .
cp -rf py3.13/doc .
cp -f py3.13/index.html .
cp -rf py3.13/_multiprocess _multiprocess
cp -rf Python-3.14.0a1/Modules/_multiprocessing Modules/_multiprocess
cp -rf py3.13/multiprocess multiprocess
# ----------------------------------------------------------------------
$ diff Python-3.14.0a1/Modules/_multiprocessing/semaphore.c Modules/_multiprocess/semaphore.c 
10c10
< #include "multiprocessing.h"
---
> #include "multiprocess.h"
39,40c39,40
< module _multiprocessing
< class _multiprocessing.SemLock "SemLockObject *" "&_PyMp_SemLockType"
---
> module _multiprocess
> class _multiprocess.SemLock "SemLockObject *" "&_PyMp_SemLockType"
85c85
< _multiprocessing.SemLock.acquire
---
> _multiprocess.SemLock.acquire
94,95c94,95
< _multiprocessing_SemLock_acquire_impl(SemLockObject *self, int blocking,
<                                       PyObject *timeout_obj)
---
> _multiprocess_SemLock_acquire_impl(SemLockObject *self, int blocking,
>                                    PyObject *timeout_obj)
177c177
< _multiprocessing.SemLock.release
---
> _multiprocess.SemLock.release
183c183
< _multiprocessing_SemLock_release_impl(SemLockObject *self)
---
> _multiprocess_SemLock_release_impl(SemLockObject *self)
303c303
< _multiprocessing.SemLock.acquire
---
> _multiprocess.SemLock.acquire
312,313c312,313
< _multiprocessing_SemLock_acquire_impl(SemLockObject *self, int blocking,
<                                       PyObject *timeout_obj)
---
> _multiprocess_SemLock_acquire_impl(SemLockObject *self, int blocking,
>                                    PyObject *timeout_obj)
389c389
< _multiprocessing.SemLock.release
---
> _multiprocess.SemLock.release
395c395
< _multiprocessing_SemLock_release_impl(SemLockObject *self)
---
> _multiprocess_SemLock_release_impl(SemLockObject *self)
479c479
< _multiprocessing.SemLock.__new__
---
> _multiprocess.SemLock.__new__
490,491c490,491
< _multiprocessing_SemLock_impl(PyTypeObject *type, int kind, int value,
<                               int maxvalue, const char *name, int unlink)
---
> _multiprocess_SemLock_impl(PyTypeObject *type, int kind, int value,
>                            int maxvalue, const char *name, int unlink)
538c538
< _multiprocessing.SemLock._rebuild
---
> _multiprocess.SemLock._rebuild
549,551c549,551
< _multiprocessing_SemLock__rebuild_impl(PyTypeObject *type, SEM_HANDLE handle,
<                                        int kind, int maxvalue,
<                                        const char *name)
---
> _multiprocess_SemLock__rebuild_impl(PyTypeObject *type, SEM_HANDLE handle,
>                                     int kind, int maxvalue,
>                                     const char *name)
591c591
< _multiprocessing.SemLock._count
---
> _multiprocess.SemLock._count
597c597
< _multiprocessing_SemLock__count_impl(SemLockObject *self)
---
> _multiprocess_SemLock__count_impl(SemLockObject *self)
604c604
< _multiprocessing.SemLock._is_mine
---
> _multiprocess.SemLock._is_mine
610c610
< _multiprocessing_SemLock__is_mine_impl(SemLockObject *self)
---
> _multiprocess_SemLock__is_mine_impl(SemLockObject *self)
618c618
< _multiprocessing.SemLock._get_value
---
> _multiprocess.SemLock._get_value
624c624
< _multiprocessing_SemLock__get_value_impl(SemLockObject *self)
---
> _multiprocess_SemLock__get_value_impl(SemLockObject *self)
643c643
< _multiprocessing.SemLock._is_zero
---
> _multiprocess.SemLock._is_zero
649c649
< _multiprocessing_SemLock__is_zero_impl(SemLockObject *self)
---
> _multiprocess_SemLock__is_zero_impl(SemLockObject *self)
671c671
< _multiprocessing.SemLock._after_fork
---
> _multiprocess.SemLock._after_fork
677c677
< _multiprocessing_SemLock__after_fork_impl(SemLockObject *self)
---
> _multiprocess_SemLock__after_fork_impl(SemLockObject *self)
686c686
< _multiprocessing.SemLock.__enter__
---
> _multiprocess.SemLock.__enter__
692c692
< _multiprocessing_SemLock___enter___impl(SemLockObject *self)
---
> _multiprocess_SemLock___enter___impl(SemLockObject *self)
695c695
<     return _multiprocessing_SemLock_acquire_impl(self, 1, Py_None);
---
>     return _multiprocess_SemLock_acquire_impl(self, 1, Py_None);
700c700
< _multiprocessing.SemLock.__exit__
---
> _multiprocess.SemLock.__exit__
711,713c711,713
< _multiprocessing_SemLock___exit___impl(SemLockObject *self,
<                                        PyObject *exc_type,
<                                        PyObject *exc_value, PyObject *exc_tb)
---
> _multiprocess_SemLock___exit___impl(SemLockObject *self,
>                                     PyObject *exc_type,
>                                     PyObject *exc_value, PyObject *exc_tb)
716c716
<     return _multiprocessing_SemLock_release_impl(self);
---
>     return _multiprocess_SemLock_release_impl(self);
731,740c731,740
<     _MULTIPROCESSING_SEMLOCK_ACQUIRE_METHODDEF
<     _MULTIPROCESSING_SEMLOCK_RELEASE_METHODDEF
<     _MULTIPROCESSING_SEMLOCK___ENTER___METHODDEF
<     _MULTIPROCESSING_SEMLOCK___EXIT___METHODDEF
<     _MULTIPROCESSING_SEMLOCK__COUNT_METHODDEF
<     _MULTIPROCESSING_SEMLOCK__IS_MINE_METHODDEF
<     _MULTIPROCESSING_SEMLOCK__GET_VALUE_METHODDEF
<     _MULTIPROCESSING_SEMLOCK__IS_ZERO_METHODDEF
<     _MULTIPROCESSING_SEMLOCK__REBUILD_METHODDEF
<     _MULTIPROCESSING_SEMLOCK__AFTER_FORK_METHODDEF
---
>     _MULTIPROCESS_SEMLOCK_ACQUIRE_METHODDEF
>     _MULTIPROCESS_SEMLOCK_RELEASE_METHODDEF
>     _MULTIPROCESS_SEMLOCK___ENTER___METHODDEF
>     _MULTIPROCESS_SEMLOCK___EXIT___METHODDEF
>     _MULTIPROCESS_SEMLOCK__COUNT_METHODDEF
>     _MULTIPROCESS_SEMLOCK__IS_MINE_METHODDEF
>     _MULTIPROCESS_SEMLOCK__GET_VALUE_METHODDEF
>     _MULTIPROCESS_SEMLOCK__IS_ZERO_METHODDEF
>     _MULTIPROCESS_SEMLOCK__REBUILD_METHODDEF
>     _MULTIPROCESS_SEMLOCK__AFTER_FORK_METHODDEF
771c771
<     {Py_tp_new, _multiprocessing_SemLock},
---
>     {Py_tp_new, _multiprocess_SemLock},
779c779
<     .name = "_multiprocessing.SemLock",
---
>     .name = "_multiprocess.SemLock",
$ diff Python-3.14.0a1/Modules/_multiprocessing/multiprocessing.h Modules/_multiprocess/multiprocess.h 
1,2c1,2
< #ifndef MULTIPROCESSING_H
< #define MULTIPROCESSING_H
---
> #ifndef MULTIPROCESS_H
> #define MULTIPROCESS_H
104c104
< #endif /* MULTIPROCESSING_H */
---
> #endif /* MULTIPROCESS_H */
$ diff Python-3.14.0a1/Modules/_multiprocessing/multiprocessing.c Modules/_multiprocess/multiprocess.c
2c2
<  * Extension module used by multiprocessing package
---
>  * Extension module used by multiprocess package
4c4
<  * multiprocessing.c
---
>  * multiprocess.c
10c10
< #include "multiprocessing.h"
---
> #include "multiprocess.h"
30c30
< module _multiprocessing
---
> module _multiprocess
77c77
< _multiprocessing.closesocket
---
> _multiprocess.closesocket
85c85
< _multiprocessing_closesocket_impl(PyObject *module, HANDLE handle)
---
> _multiprocess_closesocket_impl(PyObject *module, HANDLE handle)
100c100
< _multiprocessing.recv
---
> _multiprocess.recv
109c109
< _multiprocessing_recv_impl(PyObject *module, HANDLE handle, int size)
---
> _multiprocess_recv_impl(PyObject *module, HANDLE handle, int size)
132c132
< _multiprocessing.send
---
> _multiprocess.send
141c141
< _multiprocessing_send_impl(PyObject *module, HANDLE handle, Py_buffer *buf)
---
> _multiprocess_send_impl(PyObject *module, HANDLE handle, Py_buffer *buf)
160c160
< _multiprocessing.sem_unlink
---
> _multiprocess.sem_unlink
168c168
< _multiprocessing_sem_unlink_impl(PyObject *module, const char *name)
---
> _multiprocess_sem_unlink_impl(PyObject *module, const char *name)
180,182c180,182
<     _MULTIPROCESSING_CLOSESOCKET_METHODDEF
<     _MULTIPROCESSING_RECV_METHODDEF
<     _MULTIPROCESSING_SEND_METHODDEF
---
>     _MULTIPROCESS_CLOSESOCKET_METHODDEF
>     _MULTIPROCESS_RECV_METHODDEF
>     _MULTIPROCESS_SEND_METHODDEF
185c185
<     _MULTIPROCESSING_SEM_UNLINK_METHODDEF
---
>     _MULTIPROCESS_SEM_UNLINK_METHODDEF
196c196
< multiprocessing_exec(PyObject *module)
---
> multiprocess_exec(PyObject *module)
277,278c277,278
< static PyModuleDef_Slot multiprocessing_slots[] = {
<     {Py_mod_exec, multiprocessing_exec},
---
> static PyModuleDef_Slot multiprocess_slots[] = {
>     {Py_mod_exec, multiprocess_exec},
284c284
< static struct PyModuleDef multiprocessing_module = {
---
> static struct PyModuleDef multiprocess_module = {
286c286
<     .m_name = "_multiprocessing",
---
>     .m_name = "_multiprocess",
289c289
<     .m_slots = multiprocessing_slots,
---
>     .m_slots = multiprocess_slots,
293c293
< PyInit__multiprocessing(void)
---
> PyInit__multiprocess(void)
295c295
<     return PyModuleDef_Init(&multiprocessing_module);
---
>     return PyModuleDef_Init(&multiprocess_module);
# ----------------------------------------------------------------------
diff Python-3.13.0rc1/Lib/multiprocessing/connection.py Python-3.14.0a1/Lib/multiprocessing/connection.py
13a14
> import itertools
18d18
< import time
20c20
< import itertools
---
> import time
42c42,44
< BUFSIZE = 8192
---
> # 64 KiB is the default PIPE buffer size of most POSIX platforms.
> BUFSIZE = 64 * 1024
> 
395c397,398
<             chunk = read(handle, remaining)
---
>             to_read = min(BUFSIZE, remaining)
>             chunk = read(handle, to_read)
diff Python-3.13.0rc1/Lib/multiprocessing/context.py Python-3.14.0a1/Lib/multiprocessing/context.py
170c170
<         from . import connection
---
>         from . import connection  # noqa: F401
262,268c262,267
<         if sys.platform == 'win32':
<             return ['spawn']
<         else:
<             methods = ['spawn', 'fork'] if sys.platform == 'darwin' else ['fork', 'spawn']
<             if reduction.HAVE_SEND_HANDLE:
<                 methods.append('forkserver')
<             return methods
---
>         default = self._default_context.get_start_method()
>         start_method_names = [default]
>         start_method_names.extend(
>             name for name in _concrete_contexts if name != default
>         )
>         return start_method_names
323,326c322,326
<     if sys.platform == 'darwin':
<         # bpo-33725: running arbitrary code after fork() is no longer reliable
<         # on macOS since macOS 10.14 (Mojave). Use spawn by default instead.
<         _default_context = DefaultContext(_concrete_contexts['spawn'])
---
>     # bpo-33725: running arbitrary code after fork() is no longer reliable
>     # on macOS since macOS 10.14 (Mojave). Use spawn by default instead.
>     # gh-84559: We changed everyones default to a thread safeish one in 3.14.
>     if reduction.HAVE_SEND_HANDLE and sys.platform != 'darwin':
>         _default_context = DefaultContext(_concrete_contexts['forkserver'])
328c328
<         _default_context = DefaultContext(_concrete_contexts['fork'])
---
>         _default_context = DefaultContext(_concrete_contexts['spawn'])
330c330
< else:
---
> else:  # Windows
diff Python-3.13.0rc1/Lib/multiprocessing/managers.py Python-3.14.0a1/Lib/multiprocessing/managers.py
1155,1158c1155,1158
<     '__add__', '__contains__', '__delitem__', '__getitem__', '__len__',
<     '__mul__', '__reversed__', '__rmul__', '__setitem__',
<     'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove',
<     'reverse', 'sort', '__imul__'
---
>     '__add__', '__contains__', '__delitem__', '__getitem__', '__imul__',
>     '__len__', '__mul__', '__reversed__', '__rmul__', '__setitem__',
>     'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop',
>     'remove', 'reverse', 'sort',
1172,1173c1172,1174
<     '__contains__', '__delitem__', '__getitem__', '__iter__', '__len__',
<     '__setitem__', 'clear', 'copy', 'get', 'items',
---
>     '__contains__', '__delitem__', '__getitem__', '__ior__', '__iter__',
>     '__len__', '__or__', '__reversed__', '__ror__',
>     '__setitem__', 'clear', 'copy', 'fromkeys', 'get', 'items',
1180c1181,1183
<     __class_getitem__ = classmethod(types.GenericAlias)
---
>     def __ior__(self, value):
>         self._callmethod('__ior__', (value,))
>         return self
1181a1185
>     __class_getitem__ = classmethod(types.GenericAlias)
diff Python-3.13.0rc1/Lib/multiprocessing/shared_memory.py Python-3.14.0a1/Lib/multiprocessing/shared_memory.py
542c542
<             raise ValueError(f"{value!r} not in this container")
---
>             raise ValueError("ShareableList.index(x): x not in list")
diff Python-3.13.0rc1/Lib/multiprocessing/util.py Python-3.14.0a1/Lib/multiprocessing/util.py
17c17
< from subprocess import _args_from_interpreter_flags
---
> from subprocess import _args_from_interpreter_flags  # noqa: F401
448,449c448
<             False, False, -1, None, None, None, -1, None,
<             subprocess._USE_VFORK)
---
>             False, False, -1, None, None, None, -1, None)
$ diff Python-3.13.0rc1/Lib/test/_test_multiprocessing.py Python-3.14.0a1/Lib/test/_test_multiprocessing.py
5556c5556
<     def test_get_all(self):
---
>     def test_get_all_start_methods(self):
5557a5558
>         self.assertIn('spawn', methods)
5559a5561,5565
>         elif sys.platform == 'darwin':
>             self.assertEqual(methods[0], 'spawn')  # The default is first.
>             # Whether these work or not, they remain available on macOS.
>             self.assertIn('fork', methods)
>             self.assertIn('forkserver', methods)
5561,5564c5567,5578
<             self.assertTrue(methods == ['fork', 'spawn'] or
<                             methods == ['spawn', 'fork'] or
<                             methods == ['fork', 'spawn', 'forkserver'] or
<                             methods == ['spawn', 'fork', 'forkserver'])
---
>             # POSIX
>             self.assertIn('fork', methods)
>             if other_methods := set(methods) - {'fork', 'spawn'}:
>                 # If there are more than those two, forkserver must be one.
>                 self.assertEqual({'forkserver'}, other_methods)
>             # The default is the first method in the list.
>             self.assertIn(methods[0], {'forkserver', 'spawn'},
>                           msg='3.14+ default must not be fork')
>             if methods[0] == 'spawn':
>                 # Confirm that the current default selection logic prefers
>                 # forkserver vs spawn when available.
>                 self.assertNotIn('forkserver', methods)
6092a6107,6122
>         obj += [7]
>         case.assertIsInstance(obj, multiprocessing.managers.ListProxy)
>         case.assertListEqual(list(obj), [5, 7])
>         obj *= 2
>         case.assertIsInstance(obj, multiprocessing.managers.ListProxy)
>         case.assertListEqual(list(obj), [5, 7, 5, 7])
>         double_obj = obj * 2
>         case.assertIsInstance(double_obj, list)
>         case.assertListEqual(list(double_obj), [5, 7, 5, 7, 5, 7, 5, 7])
>         double_obj = 2 * obj
>         case.assertIsInstance(double_obj, list)
>         case.assertListEqual(list(double_obj), [5, 7, 5, 7, 5, 7, 5, 7])
>         copied_obj = obj.copy()
>         case.assertIsInstance(copied_obj, list)
>         case.assertListEqual(list(copied_obj), [5, 7, 5, 7])
>         obj.extend(double_obj + copied_obj)
6097,6098c6127,6130
<         case.assertEqual(len(obj), 1)
<         case.assertEqual(obj.pop(0), 5)
---
>         case.assertEqual(len(obj), 16)
>         case.assertEqual(obj.pop(0), 7)
>         obj.clear()
>         case.assertEqual(len(obj), 0)
6117c6149,6171
<         case.assertTupleEqual(obj.popitem(), ('foo', 5))
---
>         obj |= {'bar': 6}
>         case.assertIsInstance(obj, multiprocessing.managers.DictProxy)
>         case.assertDictEqual(dict(obj), {'foo': 5, 'bar': 6})
>         x = reversed(obj)
>         case.assertIsInstance(x, type(iter([])))
>         case.assertListEqual(list(x), ['bar', 'foo'])
>         x = {'bar': 7, 'baz': 7} | obj
>         case.assertIsInstance(x, dict)
>         case.assertDictEqual(dict(x), {'foo': 5, 'bar': 6, 'baz': 7})
>         x = obj | {'bar': 7, 'baz': 7}
>         case.assertIsInstance(x, dict)
>         case.assertDictEqual(dict(x), {'foo': 5, 'bar': 7, 'baz': 7})
>         x = obj.fromkeys(['bar'], 6)
>         case.assertIsInstance(x, dict)
>         case.assertDictEqual(x, {'bar': 6})
>         x = obj.popitem()
>         case.assertIsInstance(x, tuple)
>         case.assertTupleEqual(x, ('bar', 6))
>         obj.setdefault('bar', 0)
>         obj.update({'bar': 7})
>         case.assertEqual(obj.pop('bar'), 7)
>         obj.clear()
>         case.assertEqual(len(obj), 0)

# ----------------------------------------------------------------------
diff Python-3.14.0a1/Modules/_multiprocessing/semaphore.c Python-3.14.0a2/Modules/_multiprocessing/semaphore.c
17a18
> // These match the values in Lib/multiprocessing/synchronize.py
diff Python-3.14.0a1/Modules/_multiprocessing/clinic/semaphore.c.h Python-3.14.0a2/Modules/_multiprocessing/clinic/semaphore.c.h
61c61,62
<     args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf);
---
>     args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
>             /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
166c167,168
<     args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf);
---
>     args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
>             /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
266c268,269
<     fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 5, 5, 0, argsbuf);
---
>     fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser,
>             /*minpos*/ 5, /*maxpos*/ 5, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
576c579
< /*[clinic end generated code: output=dea36482d23a355f input=a9049054013a1b77]*/
---
> /*[clinic end generated code: output=9023d3e48a24afd2 input=a9049054013a1b77]*/
diff Python-3.14.0a1/Lib/multiprocessing/forkserver.py Python-3.14.0a2/Lib/multiprocessing/forkserver.py
170a171,172
>         if sys_path is not None:
>             sys.path[:] = sys_path
diff Python-3.14.0a1/Lib/multiprocessing/managers.py Python-3.14.0a2/Lib/multiprocessing/managers.py
20a21
> import collections.abc
760a762,765
>     # Each instance gets a `_serial` number. Unlike `id(...)`, this number
>     # is never reused.
>     _next_serial = 1
> 
764,767c769,775
<             tls_idset = BaseProxy._address_to_local.get(token.address, None)
<             if tls_idset is None:
<                 tls_idset = util.ForkAwareLocal(), ProcessLocalSet()
<                 BaseProxy._address_to_local[token.address] = tls_idset
---
>             tls_serials = BaseProxy._address_to_local.get(token.address, None)
>             if tls_serials is None:
>                 tls_serials = util.ForkAwareLocal(), ProcessLocalSet()
>                 BaseProxy._address_to_local[token.address] = tls_serials
> 
>             self._serial = BaseProxy._next_serial
>             BaseProxy._next_serial += 1
771c779
<         self._tls = tls_idset[0]
---
>         self._tls = tls_serials[0]
773,774c781,782
<         # self._idset is used to record the identities of all shared
<         # objects for which the current process owns references and
---
>         # self._all_serials is a set used to record the identities of all
>         # shared objects for which the current process owns references and
776c784
<         self._idset = tls_idset[1]
---
>         self._all_serials = tls_serials[1]
859c867
<         self._idset.add(self._id)
---
>         self._all_serials.add(self._serial)
865,866c873,874
<             args=(self._token, self._authkey, state,
<                   self._tls, self._idset, self._Client),
---
>             args=(self._token, self._serial, self._authkey, state,
>                   self._tls, self._all_serials, self._Client),
871,872c879,880
<     def _decref(token, authkey, state, tls, idset, _Client):
<         idset.discard(token.id)
---
>     def _decref(token, serial, authkey, state, tls, idset, _Client):
>         idset.discard(serial)
1169a1178
> collections.abc.MutableSequence.register(BaseListProxy)
1171c1180
< _BaseDictProxy = MakeProxyType('DictProxy', (
---
> _BaseDictProxy = MakeProxyType('_BaseDictProxy', (
1186a1196,1197
> collections.abc.MutableMapping.register(_BaseDictProxy)
> 
diff Python-3.14.0a1/Lib/multiprocessing/synchronize.py Python-3.14.0a2/Lib/multiprocessing/synchronize.py
24,26c24
< # Try to import the mp.synchronize module cleanly, if it fails
< # raise ImportError for platforms lacking a working sem_open implementation.
< # See issue 3770
---
> # TODO: Do any platforms still lack a functioning sem_open?
29c27
< except (ImportError):
---
> except ImportError:
31,33c29
<                       " implementation, therefore, the required" +
<                       " synchronization primitives needed will not" +
<                       " function, see issue 3770.")
---
>                       " implementation. https://github.com/python/cpython/issues/48020.")
39c35,38
< RECURSIVE_MUTEX, SEMAPHORE = list(range(2))
---
> # These match the enum in Modules/_multiprocessing/semaphore.c
> RECURSIVE_MUTEX = 0
> SEMAPHORE = 1
> 
177c176
<             elif self._semlock._get_value() == 1:
---
>             elif not self._semlock._is_zero():
203c202
<             elif self._semlock._get_value() == 1:
---
>             elif not self._semlock._is_zero():
diff Python-3.14.0a1/Lib/multiprocessing/util.py Python-3.14.0a2/Lib/multiprocessing/util.py
441d440
<     import subprocess
diff Python-3.14.0a1/Lib/test/_test_multiprocessing.py Python-3.14.0a2/Lib/test/_test_multiprocessing.py 
14a15
> import importlib
18a20
> import collections.abc
21a24
> import shutil
23a27
> import tempfile
256a261,263
>     # If not empty, limit which start method suites run this class.
>     START_METHODS: set[str] = set()
>     start_method = None  # set by install_tests_in_module_dict()
1364a1372,1431
>     @staticmethod
>     def _acquire(lock, l=None):
>         lock.acquire()
>         if l is not None:
>             l.append(repr(lock))
> 
>     @staticmethod
>     def _acquire_event(lock, event):
>         lock.acquire()
>         event.set()
>         time.sleep(1.0)
> 
>     def test_repr_lock(self):
>         if self.TYPE != 'processes':
>             self.skipTest('test not appropriate for {}'.format(self.TYPE))
> 
>         lock = self.Lock()
>         self.assertEqual(f'<Lock(owner=None)>', repr(lock))
> 
>         lock.acquire()
>         self.assertEqual(f'<Lock(owner=MainProcess)>', repr(lock))
>         lock.release()
> 
>         tname = 'T1'
>         l = []
>         t = threading.Thread(target=self._acquire,
>                              args=(lock, l),
>                              name=tname)
>         t.start()
>         time.sleep(0.1)
>         self.assertEqual(f'<Lock(owner=MainProcess|{tname})>', l[0])
>         lock.release()
> 
>         t = threading.Thread(target=self._acquire,
>                              args=(lock,),
>                              name=tname)
>         t.start()
>         time.sleep(0.1)
>         self.assertEqual('<Lock(owner=SomeOtherThread)>', repr(lock))
>         lock.release()
> 
>         pname = 'P1'
>         l = multiprocessing.Manager().list()
>         p = self.Process(target=self._acquire,
>                          args=(lock, l),
>                          name=pname)
>         p.start()
>         p.join()
>         self.assertEqual(f'<Lock(owner={pname})>', l[0])
> 
>         lock = self.Lock()
>         event = self.Event()
>         p = self.Process(target=self._acquire_event,
>                          args=(lock, event),
>                          name='P2')
>         p.start()
>         event.wait()
>         self.assertEqual(f'<Lock(owner=SomeOtherProcess)>', repr(lock))
>         p.terminate()
> 
1371a1439,1500
>     @staticmethod
>     def _acquire_release(lock, timeout, l=None, n=1):
>         for _ in range(n):
>             lock.acquire()
>         if l is not None:
>             l.append(repr(lock))
>         time.sleep(timeout)
>         for _ in range(n):
>             lock.release()
> 
>     def test_repr_rlock(self):
>         if self.TYPE != 'processes':
>             self.skipTest('test not appropriate for {}'.format(self.TYPE))
> 
>         lock = self.RLock()
>         self.assertEqual('<RLock(None, 0)>', repr(lock))
> 
>         n = 3
>         for _ in range(n):
>             lock.acquire()
>         self.assertEqual(f'<RLock(MainProcess, {n})>', repr(lock))
>         for _ in range(n):
>             lock.release()
> 
>         t, l = [], []
>         for i in range(n):
>             t.append(threading.Thread(target=self._acquire_release,
>                                       args=(lock, 0.1, l, i+1),
>                                       name=f'T{i+1}'))
>             t[-1].start()
>         for t_ in t:
>             t_.join()
>         for i in range(n):
>             self.assertIn(f'<RLock(MainProcess|T{i+1}, {i+1})>', l)
> 
> 
>         t = threading.Thread(target=self._acquire_release,
>                                  args=(lock, 0.2),
>                                  name=f'T1')
>         t.start()
>         time.sleep(0.1)
>         self.assertEqual('<RLock(SomeOtherThread, nonzero)>', repr(lock))
>         time.sleep(0.2)
> 
>         pname = 'P1'
>         l = multiprocessing.Manager().list()
>         p = self.Process(target=self._acquire_release,
>                          args=(lock, 0.1, l),
>                          name=pname)
>         p.start()
>         p.join()
>         self.assertEqual(f'<RLock({pname}, 1)>', l[0])
> 
>         event = self.Event()
>         lock = self.RLock()
>         p = self.Process(target=self._acquire_event,
>                          args=(lock, event))
>         p.start()
>         event.wait()
>         self.assertEqual('<RLock(SomeOtherProcess, nonzero)>', repr(lock))
>         p.join()
> 
2333a2463,2479
>     def test_list_isinstance(self):
>         a = self.list()
>         self.assertIsInstance(a, collections.abc.MutableSequence)
> 
>         # MutableSequence also has __iter__, but we can iterate over
>         # ListProxy using __getitem__ instead. Adding __iter__ to ListProxy
>         # would change the behavior of a list modified during iteration.
>         mutable_sequence_methods = (
>             '__contains__', '__delitem__', '__getitem__', '__iadd__',
>             '__len__', '__reversed__', '__setitem__', 'append',
>             'clear', 'count', 'extend', 'index', 'insert', 'pop', 'remove',
>             'reverse',
>         )
>         for name in mutable_sequence_methods:
>             with self.subTest(name=name):
>                 self.assertTrue(callable(getattr(a, name)))
> 
2373a2520,2532
>     def test_dict_isinstance(self):
>         a = self.dict()
>         self.assertIsInstance(a, collections.abc.MutableMapping)
> 
>         mutable_mapping_methods = (
>             '__contains__', '__delitem__', '__eq__', '__getitem__', '__iter__',
>             '__len__', '__ne__', '__setitem__', 'clear', 'get', 'items',
>             'keys', 'pop', 'popitem', 'setdefault', 'update', 'values',
>         )
>         for name in mutable_mapping_methods:
>             with self.subTest(name=name):
>                 self.assertTrue(callable(getattr(a, name)))
> 
5763a5923,5924
>     @unittest.skipIf(sys.platform.startswith("netbsd"),
>                      "gh-125620: Skip on NetBSD due to long wait for SIGKILL process termination.")
6266a6428,6497
> class _TestSpawnedSysPath(BaseTestCase):
>     """Test that sys.path is setup in forkserver and spawn processes."""
> 
>     ALLOWED_TYPES = {'processes'}
>     # Not applicable to fork which inherits everything from the process as is.
>     START_METHODS = {"forkserver", "spawn"}
> 
>     def setUp(self):
>         self._orig_sys_path = list(sys.path)
>         self._temp_dir = tempfile.mkdtemp(prefix="test_sys_path-")
>         self._mod_name = "unique_test_mod"
>         module_path = os.path.join(self._temp_dir, f"{self._mod_name}.py")
>         with open(module_path, "w", encoding="utf-8") as mod:
>             mod.write("# A simple test module\n")
>         sys.path[:] = [p for p in sys.path if p]  # remove any existing ""s
>         sys.path.insert(0, self._temp_dir)
>         sys.path.insert(0, "")  # Replaced with an abspath in child.
>         self.assertIn(self.start_method, self.START_METHODS)
>         self._ctx = multiprocessing.get_context(self.start_method)
> 
>     def tearDown(self):
>         sys.path[:] = self._orig_sys_path
>         shutil.rmtree(self._temp_dir, ignore_errors=True)
> 
>     @staticmethod
>     def enq_imported_module_names(queue):
>         queue.put(tuple(sys.modules))
> 
>     def test_forkserver_preload_imports_sys_path(self):
>         if self._ctx.get_start_method() != "forkserver":
>             self.skipTest("forkserver specific test.")
>         self.assertNotIn(self._mod_name, sys.modules)
>         multiprocessing.forkserver._forkserver._stop()  # Must be fresh.
>         self._ctx.set_forkserver_preload(
>             ["test.test_multiprocessing_forkserver", self._mod_name])
>         q = self._ctx.Queue()
>         proc = self._ctx.Process(
>                 target=self.enq_imported_module_names, args=(q,))
>         proc.start()
>         proc.join()
>         child_imported_modules = q.get()
>         q.close()
>         self.assertIn(self._mod_name, child_imported_modules)
> 
>     @staticmethod
>     def enq_sys_path_and_import(queue, mod_name):
>         queue.put(sys.path)
>         try:
>             importlib.import_module(mod_name)
>         except ImportError as exc:
>             queue.put(exc)
>         else:
>             queue.put(None)
> 
>     def test_child_sys_path(self):
>         q = self._ctx.Queue()
>         proc = self._ctx.Process(
>                 target=self.enq_sys_path_and_import, args=(q, self._mod_name))
>         proc.start()
>         proc.join()
>         child_sys_path = q.get()
>         import_error = q.get()
>         q.close()
>         self.assertNotIn("", child_sys_path)  # replaced by an abspath
>         self.assertIn(self._temp_dir, child_sys_path)  # our addition
>         # ignore the first element, it is the absolute "" replacement
>         self.assertEqual(child_sys_path[1:], sys.path[1:])
>         self.assertIsNone(import_error, msg=f"child could not import {self._mod_name}")
> 
> 
6460a6692,6693
>             if base.START_METHODS and start_method not in base.START_METHODS:
>                 continue  # class not intended for this start method.
6473a6707
>                 Temp.start_method = start_method
# ----------------------------------------------------------------------
diff Python-3.14.0a2/Lib/multiprocessing/connection.py Python-3.14.0a3/Lib/multiprocessing/connection.py
183a184,187
>     def _detach(self):
>         """Stop managing the underlying file descriptor or handle."""
>         self._handle = None
> 
962c966
<         raise AuthenticationError('challenge too short: {len(message)} bytes')
---
>         raise AuthenticationError(f'challenge too short: {len(message)} bytes')
diff Python-3.14.0a2/Lib/multiprocessing/forkserver.py Python-3.14.0a3/Lib/multiprocessing/forkserver.py
11a12
> from . import AuthenticationError
27a29
> _AUTHKEY_LEN = 32  # <= PIPEBUF so it fits a single write to an empty pipe.
35a38
>         self._forkserver_authkey = None
61a65
>         self._forkserver_authkey = None
85a90
>         assert self._forkserver_authkey
95a101,112
>                 client.setblocking(True)
>                 wrapped_client = connection.Connection(client.fileno())
>                 # The other side of this exchange happens in the child as
>                 # implemented in main().
>                 try:
>                     connection.answer_challenge(
>                             wrapped_client, self._forkserver_authkey)
>                     connection.deliver_challenge(
>                             wrapped_client, self._forkserver_authkey)
>                 finally:
>                     wrapped_client._detach()
>                     del wrapped_client
122a140
>                 self._forkserver_authkey = None
133c151
<                 data = {x: y for x, y in data.items() if x in desired_keys}
---
>                 main_kws = {x: y for x, y in data.items() if x in desired_keys}
135c153
<                 data = {}
---
>                 main_kws = {}
146a165,166
>                 # A short lived pipe to initialize the forkserver authkey.
>                 authkey_r, authkey_w = os.pipe()
148c168,169
<                     fds_to_pass = [listener.fileno(), alive_r]
---
>                     fds_to_pass = [listener.fileno(), alive_r, authkey_r]
>                     main_kws['authkey_r'] = authkey_r
150c171
<                             data)
---
>                             main_kws)
156a178
>                     os.close(authkey_w)
159a182,189
>                     os.close(authkey_r)
>                 # Authenticate our control socket to prevent access from
>                 # processes we have not shared this key with.
>                 try:
>                     self._forkserver_authkey = os.urandom(_AUTHKEY_LEN)
>                     os.write(authkey_w, self._forkserver_authkey)
>                 finally:
>                     os.close(authkey_w)
168,169c198,209
< def main(listener_fd, alive_r, preload, main_path=None, sys_path=None):
<     '''Run forkserver.'''
---
> def main(listener_fd, alive_r, preload, main_path=None, sys_path=None,
>          *, authkey_r=None):
>     """Run forkserver."""
>     if authkey_r is not None:
>         try:
>             authkey = os.read(authkey_r, _AUTHKEY_LEN)
>             assert len(authkey) == _AUTHKEY_LEN, f'{len(authkey)} < {_AUTHKEY_LEN}'
>         finally:
>             os.close(authkey_r)
>     else:
>         authkey = b''
> 
260,261c300,317
<                         # Receive fds from client
<                         fds = reduction.recvfds(s, MAXFDS_TO_SEND + 1)
---
>                         try:
>                             if authkey:
>                                 wrapped_s = connection.Connection(s.fileno())
>                                 # The other side of this exchange happens in
>                                 # in connect_to_new_process().
>                                 try:
>                                     connection.deliver_challenge(
>                                             wrapped_s, authkey)
>                                     connection.answer_challenge(
>                                             wrapped_s, authkey)
>                                 finally:
>                                     wrapped_s._detach()
>                                     del wrapped_s
>                             # Receive fds from client
>                             fds = reduction.recvfds(s, MAXFDS_TO_SEND + 1)
>                         except (EOFError, BrokenPipeError, AuthenticationError):
>                             s.close()
>                             continue
diff Python-3.14.0a2/Lib/multiprocessing/reduction.py Python-3.14.0a3/Lib/multiprocessing/reduction.py
142,144d141
<     # On MacOSX we should acknowledge receipt of fds -- see Issue14669
<     ACKNOWLEDGE = sys.platform == 'darwin'
< 
150c147
<         if ACKNOWLEDGE and sock.recv(1) != b'A':
---
>         if sock.recv(1) != b'A':
161,162c158,162
<             if ACKNOWLEDGE:
<                 sock.send(b'A')
---
>             # We send/recv an Ack byte after the fds to work around an old
>             # macOS bug; it isn't clear if this is still required but it
>             # makes unit testing fd sending easier.
>             # See: https://github.com/python/cpython/issues/58874
>             sock.send(b'A')  # Acknowledge
diff Python-3.14.0a2/Lib/test/_test_multiprocessing.py Python-3.14.0a3/Lib/test/_test_multiprocessing.py 
849,850c849,850
<     @classmethod
<     def _sleep_and_set_event(self, evt, delay=0.0):
---
>     @staticmethod
>     def _sleep_and_set_event(evt, delay=0.0):
900a901,950
>     def test_forkserver_auth_is_enabled(self):
>         if self.TYPE == "threads":
>             self.skipTest(f"test not appropriate for {self.TYPE}")
>         if multiprocessing.get_start_method() != "forkserver":
>             self.skipTest("forkserver start method specific")
> 
>         forkserver = multiprocessing.forkserver._forkserver
>         forkserver.ensure_running()
>         self.assertTrue(forkserver._forkserver_pid)
>         authkey = forkserver._forkserver_authkey
>         self.assertTrue(authkey)
>         self.assertGreater(len(authkey), 15)
>         addr = forkserver._forkserver_address
>         self.assertTrue(addr)
> 
>         # Demonstrate that a raw auth handshake, as Client performs, does not
>         # raise an error.
>         client = multiprocessing.connection.Client(addr, authkey=authkey)
>         client.close()
> 
>         # That worked, now launch a quick process.
>         proc = self.Process(target=sys.exit)
>         proc.start()
>         proc.join()
>         self.assertEqual(proc.exitcode, 0)
> 
>     def test_forkserver_without_auth_fails(self):
>         if self.TYPE == "threads":
>             self.skipTest(f"test not appropriate for {self.TYPE}")
>         if multiprocessing.get_start_method() != "forkserver":
>             self.skipTest("forkserver start method specific")
> 
>         forkserver = multiprocessing.forkserver._forkserver
>         forkserver.ensure_running()
>         self.assertTrue(forkserver._forkserver_pid)
>         authkey_len = len(forkserver._forkserver_authkey)
>         with unittest.mock.patch.object(
>                 forkserver, '_forkserver_authkey', None):
>             # With an incorrect authkey we should get an auth rejection
>             # rather than the above protocol error.
>             forkserver._forkserver_authkey = b'T' * authkey_len
>             proc = self.Process(target=sys.exit)
>             with self.assertRaises(multiprocessing.AuthenticationError):
>                 proc.start()
>             del proc
> 
>         # authkey restored, launching processes should work again.
>         proc = self.Process(target=sys.exit)
>         proc.start()
>         proc.join()
# ----------------------------------------------------------------------
diff Python-3.14.0a3/Lib/multiprocessing/resource_tracker.py Python-3.14.0a4/Lib/multiprocessing/resource_tracker.py
157a158
>                 prev_sigmask = None
160c161
<                         signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS)
---
>                         prev_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS)
163,164c164,165
<                     if _HAVE_SIGMASK:
<                         signal.pthread_sigmask(signal.SIG_UNBLOCK, _IGNORED_SIGNALS)
---
>                     if prev_sigmask is not None:
>                         signal.pthread_sigmask(signal.SIG_SETMASK, prev_sigmask)
diff Python-3.14.0a3/Lib/test/_test_multiprocessing.py Python-3.14.0a4/Lib/test/_test_multiprocessing.py 
6047a6048,6068
>     @unittest.skipUnless(hasattr(signal, "pthread_sigmask"), "pthread_sigmask is not available")
>     def test_resource_tracker_blocked_signals(self):
>         #
>         # gh-127586: Check that resource_tracker does not override blocked signals of caller.
>         #
>         from multiprocessing.resource_tracker import ResourceTracker
>         orig_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, set())
>         signals = {signal.SIGTERM, signal.SIGINT, signal.SIGUSR1}
> 
>         try:
>             for sig in signals:
>                 signal.pthread_sigmask(signal.SIG_SETMASK, {sig})
>                 self.assertEqual(signal.pthread_sigmask(signal.SIG_BLOCK, set()), {sig})
>                 tracker = ResourceTracker()
>                 tracker.ensure_running()
>                 self.assertEqual(signal.pthread_sigmask(signal.SIG_BLOCK, set()), {sig})
>                 tracker._stop()
>         finally:
>             # restore sigmask to what it was before executing test
>             signal.pthread_sigmask(signal.SIG_SETMASK, orig_sigmask)
> 
# ----------------------------------------------------------------------
diff Python-3.14.0a4/Modules/_multiprocessing/semaphore.c Python-3.14.0a5/Modules/_multiprocessing/semaphore.c
30a31,32
> #define _SemLockObject_CAST(op) ((SemLockObject *)(op))
> 
579c581
< semlock_dealloc(SemLockObject* self)
---
> semlock_dealloc(PyObject *op)
580a583
>     SemLockObject *self = _SemLockObject_CAST(op);
721c724
< semlock_traverse(SemLockObject *s, visitproc visit, void *arg)
---
> semlock_traverse(PyObject *s, visitproc visit, void *arg)
diff Python-3.14.0a4/Modules/_multiprocessing/clinic/semaphore.c.h Python-3.14.0a5/Modules/_multiprocessing/clinic/semaphore.c.h
28c28
< _multiprocessing_SemLock_acquire(SemLockObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
---
> _multiprocessing_SemLock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
81c81
<     return_value = _multiprocessing_SemLock_acquire_impl(self, blocking, timeout_obj);
---
>     return_value = _multiprocessing_SemLock_acquire_impl((SemLockObject *)self, blocking, timeout_obj);
105c105
< _multiprocessing_SemLock_release(SemLockObject *self, PyObject *Py_UNUSED(ignored))
---
> _multiprocessing_SemLock_release(PyObject *self, PyObject *Py_UNUSED(ignored))
110c110
<     return_value = _multiprocessing_SemLock_release_impl(self);
---
>     return_value = _multiprocessing_SemLock_release_impl((SemLockObject *)self);
134c134
< _multiprocessing_SemLock_acquire(SemLockObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
---
> _multiprocessing_SemLock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
187c187
<     return_value = _multiprocessing_SemLock_acquire_impl(self, blocking, timeout_obj);
---
>     return_value = _multiprocessing_SemLock_acquire_impl((SemLockObject *)self, blocking, timeout_obj);
211c211
< _multiprocessing_SemLock_release(SemLockObject *self, PyObject *Py_UNUSED(ignored))
---
> _multiprocessing_SemLock_release(PyObject *self, PyObject *Py_UNUSED(ignored))
216c216
<     return_value = _multiprocessing_SemLock_release_impl(self);
---
>     return_value = _multiprocessing_SemLock_release_impl((SemLockObject *)self);
361c361
< _multiprocessing_SemLock__count(SemLockObject *self, PyObject *Py_UNUSED(ignored))
---
> _multiprocessing_SemLock__count(PyObject *self, PyObject *Py_UNUSED(ignored))
366c366
<     return_value = _multiprocessing_SemLock__count_impl(self);
---
>     return_value = _multiprocessing_SemLock__count_impl((SemLockObject *)self);
389c389
< _multiprocessing_SemLock__is_mine(SemLockObject *self, PyObject *Py_UNUSED(ignored))
---
> _multiprocessing_SemLock__is_mine(PyObject *self, PyObject *Py_UNUSED(ignored))
391c391
<     return _multiprocessing_SemLock__is_mine_impl(self);
---
>     return _multiprocessing_SemLock__is_mine_impl((SemLockObject *)self);
411c411
< _multiprocessing_SemLock__get_value(SemLockObject *self, PyObject *Py_UNUSED(ignored))
---
> _multiprocessing_SemLock__get_value(PyObject *self, PyObject *Py_UNUSED(ignored))
413c413
<     return _multiprocessing_SemLock__get_value_impl(self);
---
>     return _multiprocessing_SemLock__get_value_impl((SemLockObject *)self);
433c433
< _multiprocessing_SemLock__is_zero(SemLockObject *self, PyObject *Py_UNUSED(ignored))
---
> _multiprocessing_SemLock__is_zero(PyObject *self, PyObject *Py_UNUSED(ignored))
435c435
<     return _multiprocessing_SemLock__is_zero_impl(self);
---
>     return _multiprocessing_SemLock__is_zero_impl((SemLockObject *)self);
455c455
< _multiprocessing_SemLock__after_fork(SemLockObject *self, PyObject *Py_UNUSED(ignored))
---
> _multiprocessing_SemLock__after_fork(PyObject *self, PyObject *Py_UNUSED(ignored))
457c457
<     return _multiprocessing_SemLock__after_fork_impl(self);
---
>     return _multiprocessing_SemLock__after_fork_impl((SemLockObject *)self);
477c477
< _multiprocessing_SemLock___enter__(SemLockObject *self, PyObject *Py_UNUSED(ignored))
---
> _multiprocessing_SemLock___enter__(PyObject *self, PyObject *Py_UNUSED(ignored))
482c482
<     return_value = _multiprocessing_SemLock___enter___impl(self);
---
>     return_value = _multiprocessing_SemLock___enter___impl((SemLockObject *)self);
507c507
< _multiprocessing_SemLock___exit__(SemLockObject *self, PyObject *const *args, Py_ssize_t nargs)
---
> _multiprocessing_SemLock___exit__(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
531c531
<     return_value = _multiprocessing_SemLock___exit___impl(self, exc_type, exc_value, exc_tb);
---
>     return_value = _multiprocessing_SemLock___exit___impl((SemLockObject *)self, exc_type, exc_value, exc_tb);
579c579
< /*[clinic end generated code: output=9023d3e48a24afd2 input=a9049054013a1b77]*/
---
> /*[clinic end generated code: output=e28d0fdbfefd1235 input=a9049054013a1b77]*/
diff Python-3.14.0a4/Lib/multiprocessing/connection.py Python-3.14.0a5/Lib/multiprocessing/connection.py
856c856
< def _get_digest_name_and_payload(message: bytes) -> (str, bytes):
---
> def _get_digest_name_and_payload(message):  # type: (bytes) -> tuple[str, bytes]
diff Python-3.14.0a4/Lib/multiprocessing/forkserver.py Python-3.14.0a5/Lib/multiprocessing/forkserver.py
385,389c385,389
<     data = b''
<     length = SIGNED_STRUCT.size
<     while len(data) < length:
<         s = os.read(fd, length - len(data))
<         if not s:
---
>     data = bytearray(SIGNED_STRUCT.size)
>     unread = memoryview(data)
>     while unread:
>         count = os.readinto(fd, unread)
>         if count == 0:
391c391,392
<         data += s
---
>         unread = unread[count:]
> 
diff Python-3.14.0a4/Lib/multiprocessing/synchronize.py Python-3.14.0a5/Lib/multiprocessing/synchronize.py
362c362
<     def __repr__(self) -> str:
---
>     def __repr__(self):
diff Python-3.14.0a4/Lib/test/_test_multiprocessing.py Python-3.14.0a5/Lib/test/_test_multiprocessing.py 
322c322
<         self.assertTrue(not current.daemon)
---
>         self.assertFalse(current.daemon)
466c466
<         self.assertTrue(type(self.active_children()) is list)
---
>         self.assertIs(type(self.active_children()), list)
586,587c586,587
<         self.assertTrue(type(cpus) is int)
<         self.assertTrue(cpus >= 1)
---
>         self.assertIsInstance(cpus, int)
>         self.assertGreaterEqual(cpus, 1)
2385,2386c2385,2386
<         self.assertFalse(hasattr(arr4, 'get_lock'))
<         self.assertFalse(hasattr(arr4, 'get_obj'))
---
>         self.assertNotHasAttr(arr4, 'get_lock')
>         self.assertNotHasAttr(arr4, 'get_obj')
2391,2392c2391,2392
<         self.assertFalse(hasattr(arr5, 'get_lock'))
<         self.assertFalse(hasattr(arr5, 'get_obj'))
---
>         self.assertNotHasAttr(arr5, 'get_lock')
>         self.assertNotHasAttr(arr5, 'get_obj')
2465,2466c2465,2466
<         self.assertFalse(hasattr(arr4, 'get_lock'))
<         self.assertFalse(hasattr(arr4, 'get_obj'))
---
>         self.assertNotHasAttr(arr4, 'get_lock')
>         self.assertNotHasAttr(arr4, 'get_obj')
2471,2472c2471,2472
<         self.assertFalse(hasattr(arr5, 'get_lock'))
<         self.assertFalse(hasattr(arr5, 'get_obj'))
---
>         self.assertNotHasAttr(arr5, 'get_lock')
>         self.assertNotHasAttr(arr5, 'get_obj')
2660,2661c2660,2661
<         self.assertTrue(hasattr(n, 'name'))
<         self.assertTrue(not hasattr(n, 'job'))
---
>         self.assertHasAttr(n, 'name')
>         self.assertNotHasAttr(n, 'job')
4941,4942c4941
<             self.assertTrue(hasattr(mod, '__all__'), name)
< 
---
>             self.assertHasAttr(mod, '__all__', name)
4944,4947c4943
<                 self.assertTrue(
<                     hasattr(mod, attr),
<                     '%r does not have attribute %r' % (mod, attr)
<                     )
---
>                 self.assertHasAttr(mod, attr)
4960c4956
<         self.assertTrue(logger is not None)
---
>         self.assertIsNotNone(logger)
5756,5758c5752,5753
<                 self.assertTrue(type(ctx).__name__.lower().startswith(method))
<                 self.assertTrue(
<                     ctx.Process.__name__.lower().startswith(method))
---
>                 self.assertStartsWith(type(ctx).__name__.lower(), method)
>                 self.assertStartsWith(ctx.Process.__name__.lower(), method)
5959,5961c5954,5956
<                 self.assertTrue(issubclass(the_warn.category, UserWarning))
<                 self.assertTrue("resource_tracker: process died"
<                                 in str(the_warn.message))
---
>                 self.assertIsSubclass(the_warn.category, UserWarning)
>                 self.assertIn("resource_tracker: process died",
>                               str(the_warn.message))
6166,6167c6161,6162
<         self.assertFalse(
<             any(process.is_alive() for process in forked_processes))
---
>         for process in forked_processes:
>             self.assertFalse(process.is_alive(), process)
# ----------------------------------------------------------------------
diff Python-3.14.0a5/Modules/_multiprocessing/clinic/semaphore.c.h Python-3.14.0a6/Modules/_multiprocessing/clinic/semaphore.c.h
326c326
< _multiprocessing_SemLock__rebuild(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs)
---
> _multiprocessing_SemLock__rebuild(PyObject *type, PyObject *const *args, Py_ssize_t nargs)
338c338
<     return_value = _multiprocessing_SemLock__rebuild_impl(type, handle, kind, maxvalue, name);
---
>     return_value = _multiprocessing_SemLock__rebuild_impl((PyTypeObject *)type, handle, kind, maxvalue, name);
579c579
< /*[clinic end generated code: output=e28d0fdbfefd1235 input=a9049054013a1b77]*/
---
> /*[clinic end generated code: output=dddd8e989525f565 input=a9049054013a1b77]*/
diff Python-3.14.0a5/Lib/multiprocessing/managers.py Python-3.14.0a6/Lib/multiprocessing/managers.py
1197a1198,1227
> _BaseSetProxy = MakeProxyType("_BaseSetProxy", (
>     '__and__', '__class_getitem__', '__contains__', '__iand__', '__ior__',
>     '__isub__', '__iter__', '__ixor__', '__len__', '__or__', '__rand__',
>     '__ror__', '__rsub__', '__rxor__', '__sub__', '__xor__',
>     '__ge__', '__gt__', '__le__', '__lt__',
>     'add', 'clear', 'copy', 'difference', 'difference_update', 'discard',
>     'intersection', 'intersection_update', 'isdisjoint', 'issubset',
>     'issuperset', 'pop', 'remove', 'symmetric_difference',
>     'symmetric_difference_update', 'union', 'update',
> ))
> 
> class SetProxy(_BaseSetProxy):
>     def __ior__(self, value):
>         self._callmethod('__ior__', (value,))
>         return self
>     def __iand__(self, value):
>         self._callmethod('__iand__', (value,))
>         return self
>     def __ixor__(self, value):
>         self._callmethod('__ixor__', (value,))
>         return self
>     def __isub__(self, value):
>         self._callmethod('__isub__', (value,))
>         return self
> 
>     __class_getitem__ = classmethod(types.GenericAlias)
> 
> collections.abc.MutableMapping.register(_BaseSetProxy)
> 
> 
1247a1278
> SyncManager.register('set', set, SetProxy)
diff Python-3.14.0a5/Lib/test/_test_multiprocessing.py Python-3.14.0a6/Lib/test/_test_multiprocessing.py 
592c592,593
<         p = self.Process(target=time.sleep, args=(DELTA,))
---
>         event = self.Event()
>         p = self.Process(target=event.wait, args=())
595,597c596,601
<         p.daemon = True
<         p.start()
<         self.assertIn(p, self.active_children())
---
>         try:
>             p.daemon = True
>             p.start()
>             self.assertIn(p, self.active_children())
>         finally:
>             event.set()
1524,1527c1528,1529
< 
<         t = threading.Thread(target=self._acquire_release,
<                                  args=(lock, 0.2),
<                                  name=f'T1')
---
>         rlock = self.RLock()
>         t = threading.Thread(target=rlock.acquire)
1529,1531c1531,1532
<         time.sleep(0.1)
<         self.assertEqual('<RLock(SomeOtherThread, nonzero)>', repr(lock))
<         time.sleep(0.2)
---
>         t.join()
>         self.assertEqual('<RLock(SomeOtherThread, nonzero)>', repr(rlock))
1542,1545c1543,1544
<         event = self.Event()
<         lock = self.RLock()
<         p = self.Process(target=self._acquire_event,
<                          args=(lock, event))
---
>         rlock = self.RLock()
>         p = self.Process(target=self._acquire, args=(rlock,))
1547,1548d1545
<         event.wait()
<         self.assertEqual('<RLock(SomeOtherProcess, nonzero)>', repr(lock))
1549a1547
>         self.assertEqual('<RLock(SomeOtherProcess, nonzero)>', repr(rlock))
1631c1629
<         for i in range(10):
---
>         for _ in support.sleeping_retry(support.SHORT_TIMEOUT):
1637,1638c1635
<             time.sleep(DELTA)
<         time.sleep(DELTA)
---
> 
1660d1656
<         self.addCleanup(p.join)
1662,1665c1658,1660
<         p = threading.Thread(target=self.f, args=(cond, sleeping, woken))
<         p.daemon = True
<         p.start()
<         self.addCleanup(p.join)
---
>         t = threading.Thread(target=self.f, args=(cond, sleeping, woken))
>         t.daemon = True
>         t.start()
1672,1673c1667
<         time.sleep(DELTA)
<         self.assertReturnsIfImplemented(0, get_value, woken)
---
>         self.assertReachesEventually(lambda: get_value(woken), 0)
1681,1682c1675
<         time.sleep(DELTA)
<         self.assertReturnsIfImplemented(1, get_value, woken)
---
>         self.assertReachesEventually(lambda: get_value(woken), 1)
1690,1691c1683
<         time.sleep(DELTA)
<         self.assertReturnsIfImplemented(2, get_value, woken)
---
>         self.assertReachesEventually(lambda: get_value(woken), 2)
1695c1687,1689
<         p.join()
---
> 
>         threading_helper.join_thread(t)
>         join_process(p)
1702a1697
>         workers = []
1708c1703
<             self.addCleanup(p.join)
---
>             workers.append(p)
1714c1709
<             self.addCleanup(t.join)
---
>             workers.append(t)
1733c1728
<             self.addCleanup(p.join)
---
>             workers.append(p)
1738c1733
<             self.addCleanup(t.join)
---
>             workers.append(t)
1754c1749,1751
<         self.assertReachesEventually(lambda: get_value(woken), 6)
---
>         for i in range(6):
>             woken.acquire()
>         self.assertReturnsIfImplemented(0, get_value, woken)
1758a1756,1759
>         for w in workers:
>             # NOTE: join_process and join_thread are the same
>             threading_helper.join_thread(w)
> 
1764a1766
>         workers = []
1769c1771
<             self.addCleanup(p.join)
---
>             workers.append(p)
1774c1776
<             self.addCleanup(t.join)
---
>             workers.append(t)
1808a1811,1814
>         for w in workers:
>             # NOTE: join_process and join_thread are the same
>             threading_helper.join_thread(w)
> 
6443a6450,6593
>     @classmethod
>     def _test_set_operator_symbols(cls, obj):
>         case = unittest.TestCase()
>         obj.update(['a', 'b', 'c'])
>         case.assertEqual(len(obj), 3)
>         case.assertIn('a', obj)
>         case.assertNotIn('d', obj)
>         result = obj | {'d', 'e'}
>         case.assertSetEqual(result, {'a', 'b', 'c', 'd', 'e'})
>         result = {'d', 'e'} | obj
>         case.assertSetEqual(result, {'a', 'b', 'c', 'd', 'e'})
>         obj |= {'d', 'e'}
>         case.assertSetEqual(obj, {'a', 'b', 'c', 'd', 'e'})
>         case.assertIsInstance(obj, multiprocessing.managers.SetProxy)
> 
>         obj.clear()
>         obj.update(['a', 'b', 'c'])
>         result = {'a', 'b', 'd'} - obj
>         case.assertSetEqual(result, {'d'})
>         result = obj - {'a', 'b'}
>         case.assertSetEqual(result, {'c'})
>         obj -= {'a', 'b'}
>         case.assertSetEqual(obj, {'c'})
>         case.assertIsInstance(obj, multiprocessing.managers.SetProxy)
> 
>         obj.clear()
>         obj.update(['a', 'b', 'c'])
>         result = {'b', 'c', 'd'} ^ obj
>         case.assertSetEqual(result, {'a', 'd'})
>         result = obj ^ {'b', 'c', 'd'}
>         case.assertSetEqual(result, {'a', 'd'})
>         obj ^= {'b', 'c', 'd'}
>         case.assertSetEqual(obj, {'a', 'd'})
>         case.assertIsInstance(obj, multiprocessing.managers.SetProxy)
> 
>         obj.clear()
>         obj.update(['a', 'b', 'c'])
>         result = obj & {'b', 'c', 'd'}
>         case.assertSetEqual(result, {'b', 'c'})
>         result = {'b', 'c', 'd'} & obj
>         case.assertSetEqual(result, {'b', 'c'})
>         obj &= {'b', 'c', 'd'}
>         case.assertSetEqual(obj, {'b', 'c'})
>         case.assertIsInstance(obj, multiprocessing.managers.SetProxy)
> 
>         obj.clear()
>         obj.update(['a', 'b', 'c'])
>         case.assertSetEqual(set(obj), {'a', 'b', 'c'})
> 
>     @classmethod
>     def _test_set_operator_methods(cls, obj):
>         case = unittest.TestCase()
>         obj.add('d')
>         case.assertIn('d', obj)
> 
>         obj.clear()
>         obj.update(['a', 'b', 'c'])
>         copy_obj = obj.copy()
>         case.assertSetEqual(copy_obj, obj)
>         obj.remove('a')
>         case.assertNotIn('a', obj)
>         case.assertRaises(KeyError, obj.remove, 'a')
> 
>         obj.clear()
>         obj.update(['a'])
>         obj.discard('a')
>         case.assertNotIn('a', obj)
>         obj.discard('a')
>         case.assertNotIn('a', obj)
>         obj.update(['a'])
>         popped = obj.pop()
>         case.assertNotIn(popped, obj)
> 
>         obj.clear()
>         obj.update(['a', 'b', 'c'])
>         result = obj.intersection({'b', 'c', 'd'})
>         case.assertSetEqual(result, {'b', 'c'})
>         obj.intersection_update({'b', 'c', 'd'})
>         case.assertSetEqual(obj, {'b', 'c'})
> 
>         obj.clear()
>         obj.update(['a', 'b', 'c'])
>         result = obj.difference({'a', 'b'})
>         case.assertSetEqual(result, {'c'})
>         obj.difference_update({'a', 'b'})
>         case.assertSetEqual(obj, {'c'})
> 
>         obj.clear()
>         obj.update(['a', 'b', 'c'])
>         result = obj.symmetric_difference({'b', 'c', 'd'})
>         case.assertSetEqual(result, {'a', 'd'})
>         obj.symmetric_difference_update({'b', 'c', 'd'})
>         case.assertSetEqual(obj, {'a', 'd'})
> 
>     @classmethod
>     def _test_set_comparisons(cls, obj):
>         case = unittest.TestCase()
>         obj.update(['a', 'b', 'c'])
>         result = obj.union({'d', 'e'})
>         case.assertSetEqual(result, {'a', 'b', 'c', 'd', 'e'})
>         case.assertTrue(obj.isdisjoint({'d', 'e'}))
>         case.assertFalse(obj.isdisjoint({'a', 'd'}))
> 
>         case.assertTrue(obj.issubset({'a', 'b', 'c', 'd'}))
>         case.assertFalse(obj.issubset({'a', 'b'}))
>         case.assertLess(obj, {'a', 'b', 'c', 'd'})
>         case.assertLessEqual(obj, {'a', 'b', 'c'})
> 
>         case.assertTrue(obj.issuperset({'a', 'b'}))
>         case.assertFalse(obj.issuperset({'a', 'b', 'd'}))
>         case.assertGreater(obj, {'a'})
>         case.assertGreaterEqual(obj, {'a', 'b'})
> 
>     def test_set(self):
>         o = self.manager.set()
>         self.run_worker(self._test_set_operator_symbols, o)
>         o = self.manager.set()
>         self.run_worker(self._test_set_operator_methods, o)
>         o = self.manager.set()
>         self.run_worker(self._test_set_comparisons, o)
> 
>     def test_set_init(self):
>         o = self.manager.set({'a', 'b', 'c'})
>         self.assertSetEqual(o, {'a', 'b', 'c'})
>         o = self.manager.set(["a", "b", "c"])
>         self.assertSetEqual(o, {"a", "b", "c"})
>         o = self.manager.set({"a": 1, "b": 2, "c": 3})
>         self.assertSetEqual(o, {"a", "b", "c"})
>         self.assertRaises(RemoteError, self.manager.set, 1234)
> 
>     def test_set_contain_all_method(self):
>         o = self.manager.set()
>         set_methods = {
>             '__and__', '__class_getitem__', '__contains__', '__iand__', '__ior__',
>             '__isub__', '__iter__', '__ixor__', '__len__', '__or__', '__rand__',
>             '__ror__', '__rsub__', '__rxor__', '__sub__', '__xor__',
>             '__ge__', '__gt__', '__le__', '__lt__',
>             'add', 'clear', 'copy', 'difference', 'difference_update', 'discard',
>             'intersection', 'intersection_update', 'isdisjoint', 'issubset',
>             'issuperset', 'pop', 'remove', 'symmetric_difference',
>             'symmetric_difference_update', 'union', 'update',
>         }
>         self.assertLessEqual(set_methods, set(dir(o)))
> 
# ----------------------------------------------------------------------
diff Python-3.14.0a6/Modules/_multiprocessing/semaphore.c Python-3.14.0a7/Modules/_multiprocessing/semaphore.c
68c68
< _GetSemaphoreValue(HANDLE handle, long *value)
---
> _GetSemaphoreValue(HANDLE handle, int *value)
diff Python-3.14.0a6/Modules/_multiprocessing/clinic/semaphore.c.h Python-3.14.0a7/Modules/_multiprocessing/clinic/semaphore.c.h
36a37
>         Py_hash_t ob_hash;
39a41
>         .ob_hash = -1,
142a145
>         Py_hash_t ob_hash;
145a149
>         .ob_hash = -1,
239a244
>         Py_hash_t ob_hash;
242a248
>         .ob_hash = -1,
579c585
< /*[clinic end generated code: output=dddd8e989525f565 input=a9049054013a1b77]*/
---
> /*[clinic end generated code: output=d1e349d4ee3d4bbf input=a9049054013a1b77]*/
diff Python-3.14.0a6/Lib/multiprocessing/connection.py Python-3.14.0a7/Lib/multiprocessing/connection.py
324a325,327
> 
>                     sentinel = object()
>                     return_value = sentinel
326,329c329,344
<                         if err == _winapi.ERROR_IO_PENDING:
<                             waitres = _winapi.WaitForMultipleObjects(
<                                 [ov.event], False, INFINITE)
<                             assert waitres == WAIT_OBJECT_0
---
>                         try:
>                             if err == _winapi.ERROR_IO_PENDING:
>                                 waitres = _winapi.WaitForMultipleObjects(
>                                     [ov.event], False, INFINITE)
>                                 assert waitres == WAIT_OBJECT_0
>                         except:
>                             ov.cancel()
>                             raise
>                         finally:
>                             nread, err = ov.GetOverlappedResult(True)
>                             if err == 0:
>                                 f = io.BytesIO()
>                                 f.write(ov.getbuffer())
>                                 return_value = f
>                             elif err == _winapi.ERROR_MORE_DATA:
>                                 return_value = self._get_more_data(ov, maxsize)
331,340c346,350
<                         ov.cancel()
<                         raise
<                     finally:
<                         nread, err = ov.GetOverlappedResult(True)
<                         if err == 0:
<                             f = io.BytesIO()
<                             f.write(ov.getbuffer())
<                             return f
<                         elif err == _winapi.ERROR_MORE_DATA:
<                             return self._get_more_data(ov, maxsize)
---
>                         if return_value is sentinel:
>                             raise
> 
>                     if return_value is not sentinel:
>                         return return_value
diff Python-3.14.0a6/Lib/multiprocessing/managers.py Python-3.14.0a7/Lib/multiprocessing/managers.py
1062c1062
<     _exposed_ = ('acquire', 'release')
---
>     _exposed_ = ('acquire', 'release', 'locked')
1067a1068,1069
>     def locked(self):
>         return self._callmethod('locked')
1075c1077
<     _exposed_ = ('acquire', 'release', 'wait', 'notify', 'notify_all')
---
>     _exposed_ = ('acquire', 'release', 'locked', 'wait', 'notify', 'notify_all')
diff Python-3.14.0a6/Lib/multiprocessing/resource_tracker.py Python-3.14.0a7/Lib/multiprocessing/resource_tracker.py
78,86c78,94
<     def _stop(self):
<         with self._lock:
<             # This should not happen (_stop() isn't called by a finalizer)
<             # but we check for it anyway.
<             if self._lock._recursion_count() > 1:
<                 return self._reentrant_call_error()
<             if self._fd is None:
<                 # not running
<                 return
---
>     def __del__(self):
>         # making sure child processess are cleaned before ResourceTracker
>         # gets destructed.
>         # see https://github.com/python/cpython/issues/88887
>         self._stop(use_blocking_lock=False)
> 
>     def _stop(self, use_blocking_lock=True):
>         if use_blocking_lock:
>             with self._lock:
>                 self._stop_locked()
>         else:
>             acquired = self._lock.acquire(blocking=False)
>             try:
>                 self._stop_locked()
>             finally:
>                 if acquired:
>                     self._lock.release()
88,90c96,114
<             # closing the "alive" file descriptor stops main()
<             os.close(self._fd)
<             self._fd = None
---
>     def _stop_locked(
>         self,
>         close=os.close,
>         waitpid=os.waitpid,
>         waitstatus_to_exitcode=os.waitstatus_to_exitcode,
>     ):
>         # This shouldn't happen (it might when called by a finalizer)
>         # so we check for it anyway.
>         if self._lock._recursion_count() > 1:
>             return self._reentrant_call_error()
>         if self._fd is None:
>             # not running
>             return
>         if self._pid is None:
>             return
> 
>         # closing the "alive" file descriptor stops main()
>         close(self._fd)
>         self._fd = None
92c116
<             _, status = os.waitpid(self._pid, 0)
---
>         _, status = waitpid(self._pid, 0)
94c118
<             self._pid = None
---
>         self._pid = None
96,100c120,124
<             try:
<                 self._exitcode = os.waitstatus_to_exitcode(status)
<             except ValueError:
<                 # os.waitstatus_to_exitcode may raise an exception for invalid values
<                 self._exitcode = None
---
>         try:
>             self._exitcode = waitstatus_to_exitcode(status)
>         except ValueError:
>             # os.waitstatus_to_exitcode may raise an exception for invalid values
>             self._exitcode = None
diff Python-3.14.0a6/Lib/multiprocessing/synchronize.py Python-3.14.0a7/Lib/multiprocessing/synchronize.py
92a93,95
>     def locked(self):
>         return self._semlock._count() != 0
> 
diff Python-3.14.0a6/Lib/test/_test_multiprocessing.py Python-3.14.0a7/Lib/test/_test_multiprocessing.py 
1488a1489
>         self.assertTrue(lock.locked())
1490a1492
>         self.assertFalse(lock.locked())
1551a1554
>         self.assertTrue(lock.locked())
1554a1558
>         self.assertTrue(lock.locked())
1556a1561
>         self.assertFalse(lock.locked())
1560,1561c1565,1570
<         with self.Lock():
<             pass
---
>         with self.Lock() as locked:
>             self.assertTrue(locked)
> 
>     def test_rlock_context(self):
>         with self.RLock() as locked:
>             self.assertTrue(locked)
6256a6266
>         obj.locked()
6267a6278
>         obj.locked()
6269c6280
<     def test_rlock(self, lname="Lock"):
---
>     def test_rlock(self, lname="RLock"):
# ----------------------------------------------------------------------
diff Python-3.14.0a7/Lib/multiprocessing/popen_fork.py Python-3.14.0b1/Lib/multiprocessing/popen_fork.py
56a57,59
>     def interrupt(self):
>         self._send_signal(signal.SIGINT)
> 
diff Python-3.14.0a7/Lib/multiprocessing/process.py Python-3.14.0b1/Lib/multiprocessing/process.py
127a128,134
>     def interrupt(self):
>         '''
>         Terminate process; sends SIGINT signal
>         '''
>         self._check_closed()
>         self._popen.interrupt()
> 
diff Python-3.14.0a7/Lib/multiprocessing/synchronize.py Python-3.14.0b1/Lib/multiprocessing/synchronize.py
94c94
<         return self._semlock._count() != 0
---
>         return self._semlock._is_zero()
diff Python-3.14.0a7/Lib/test/_test_multiprocessing.py Python-3.14.0b1/Lib/test/_test_multiprocessing.py 
515a516,520
>     def _sleep_no_int_handler(cls):
>         signal.signal(signal.SIGINT, signal.SIG_DFL)
>         cls._sleep_some()
> 
>     @classmethod
519c524
<     def _kill_process(self, meth):
---
>     def _kill_process(self, meth, target=None):
523c528
<         p = self.Process(target=self._sleep_some)
---
>         p = self.Process(target=target or self._sleep_some)
569a575,587
>     @unittest.skipIf(os.name == 'nt', "POSIX only")
>     def test_interrupt(self):
>         exitcode = self._kill_process(multiprocessing.Process.interrupt)
>         self.assertEqual(exitcode, 1)
>         # exit code 1 is hard-coded for uncaught exceptions
>         # (KeyboardInterrupt in this case)
>         # in multiprocessing.BaseProcess._bootstrap
> 
>     @unittest.skipIf(os.name == 'nt', "POSIX only")
>     def test_interrupt_no_handler(self):
>         exitcode = self._kill_process(multiprocessing.Process.interrupt, target=self._sleep_no_int_handler)
>         self.assertEqual(exitcode, -signal.SIGINT)
> 
1494a1513,1534
>     @classmethod
>     def _test_lock_locked_2processes(cls, lock, event, res):
>         lock.acquire()
>         res.value = lock.locked()
>         event.set()
> 
>     @unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes')
>     def test_lock_locked_2processes(self):
>         if self.TYPE != 'processes':
>             self.skipTest('test not appropriate for {}'.format(self.TYPE))
> 
>         lock = self.Lock()
>         event = self.Event()
>         res = self.Value('b', 0)
>         p = self.Process(target=self._test_lock_locked_2processes,
>                          args=(lock, event, res))
>         p.start()
>         event.wait()
>         self.assertTrue(lock.locked())
>         self.assertTrue(res.value)
>         p.join()
> 
1563a1604,1620
>     @unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes')
>     def test_rlock_locked_2processes(self):
>         if self.TYPE != 'processes':
>             self.skipTest('test not appropriate for {}'.format(self.TYPE))
> 
>         rlock = self.RLock()
>         event = self.Event()
>         res = self.Value('b', 0)
>         # target is the same as for the test_lock_locked_2processes test.
>         p = self.Process(target=self._test_lock_locked_2processes,
>                          args=(rlock, event, res))
>         p.start()
>         event.wait()
>         self.assertTrue(rlock.locked())
>         self.assertTrue(res.value)
>         p.join()
> 
# ----------------------------------------------------------------------
diff Python-3.14.0b1/Lib/multiprocessing/connection.py Python-3.14.0b2/Lib/multiprocessing/connection.py
79c79
<         return tempfile.mktemp(prefix='listener-', dir=util.get_temp_dir())
---
>         return tempfile.mktemp(prefix='sock-', dir=util.get_temp_dir())
diff Python-3.14.0b1/Lib/multiprocessing/context.py Python-3.14.0b2/Lib/multiprocessing/context.py
148c148
<         if sys.platform == 'win32' and getattr(sys, 'frozen', False):
---
>         if self.get_start_method() == 'spawn' and getattr(sys, 'frozen', False):
diff Python-3.14.0b1/Lib/multiprocessing/util.py Python-3.14.0b2/Lib/multiprocessing/util.py
22c22
<     'sub_debug', 'debug', 'info', 'sub_warning', 'get_logger',
---
>     'sub_debug', 'debug', 'info', 'sub_warning', 'warn', 'get_logger',
36a37
> WARNING = 30
55a57,60
> def warn(msg, *args):
>     if _logger:
>         _logger.log(WARNING, msg, *args, stacklevel=2)
> 
123a129,143
> # Maximum length of a socket file path is usually between 92 and 108 [1],
> # but Linux is known to use a size of 108 [2]. BSD-based systems usually
> # use a size of 104 or 108 and Windows does not create AF_UNIX sockets.
> #
> # [1]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_un.h.html
> # [2]: https://man7.org/linux/man-pages/man7/unix.7.html.
> 
> if sys.platform == 'linux':
>     _SUN_PATH_MAX = 108
> elif sys.platform.startswith(('openbsd', 'freebsd')):
>     _SUN_PATH_MAX = 104
> else:
>     # On Windows platforms, we do not create AF_UNIX sockets.
>     _SUN_PATH_MAX = None if os.name == 'nt' else 92
> 
132a153,206
> def _get_base_temp_dir(tempfile):
>     """Get a temporary directory where socket files will be created.
> 
>     To prevent additional imports, pass a pre-imported 'tempfile' module.
>     """
>     if os.name == 'nt':
>         return None
>     # Most of the time, the default temporary directory is /tmp. Thus,
>     # listener sockets files "$TMPDIR/pymp-XXXXXXXX/sock-XXXXXXXX" do
>     # not have a path length exceeding SUN_PATH_MAX.
>     #
>     # If users specify their own temporary directory, we may be unable
>     # to create those files. Therefore, we fall back to the system-wide
>     # temporary directory /tmp, assumed to exist on POSIX systems.
>     #
>     # See https://github.com/python/cpython/issues/132124.
>     base_tempdir = tempfile.gettempdir()
>     # Files created in a temporary directory are suffixed by a string
>     # generated by tempfile._RandomNameSequence, which, by design,
>     # is 8 characters long.
>     #
>     # Thus, the length of socket filename will be:
>     #
>     #   len(base_tempdir + '/pymp-XXXXXXXX' + '/sock-XXXXXXXX')
>     sun_path_len = len(base_tempdir) + 14 + 14
>     if sun_path_len <= _SUN_PATH_MAX:
>         return base_tempdir
>     # Fallback to the default system-wide temporary directory.
>     # This ignores user-defined environment variables.
>     #
>     # On POSIX systems, /tmp MUST be writable by any application [1].
>     # We however emit a warning if this is not the case to prevent
>     # obscure errors later in the execution.
>     #
>     # On some legacy systems, /var/tmp and /usr/tmp can be present
>     # and will be used instead.
>     #
>     # [1]: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s18.html
>     dirlist = ['/tmp', '/var/tmp', '/usr/tmp']
>     try:
>         base_system_tempdir = tempfile._get_default_tempdir(dirlist)
>     except FileNotFoundError:
>         warn("Process-wide temporary directory %s will not be usable for "
>              "creating socket files and no usable system-wide temporary "
>              "directory was found in %s", base_tempdir, dirlist)
>         # At this point, the system-wide temporary directory is not usable
>         # but we may assume that the user-defined one is, even if we will
>         # not be able to write socket files out there.
>         return base_tempdir
>     warn("Ignoring user-defined temporary directory: %s", base_tempdir)
>     # at most max(map(len, dirlist)) + 14 + 14 = 36 characters
>     assert len(base_system_tempdir) + 14 + 14 <= _SUN_PATH_MAX
>     return base_system_tempdir
> 
138c212,213
<         tempdir = tempfile.mkdtemp(prefix='pymp-')
---
>         base_tempdir = _get_base_temp_dir(tempfile)
>         tempdir = tempfile.mkdtemp(prefix='pymp-', dir=base_tempdir)
diff Python-3.14.0b1/Lib/test/_test_multiprocessing.py Python-3.14.0b2/Lib/test/_test_multiprocessing.py 
516c516,521
<     def _sleep_no_int_handler(cls):
---
>     def _sleep_some_event(cls, event):
>         event.set()
>         time.sleep(100)
> 
>     @classmethod
>     def _sleep_no_int_handler(cls, event):
518c523
<         cls._sleep_some()
---
>         cls._sleep_some_event(event)
528c533,536
<         p = self.Process(target=target or self._sleep_some)
---
>         event = self.Event()
>         if not target:
>             target = self._sleep_some_event
>         p = self.Process(target=target, args=(event,))
546,547c554,558
<         # XXX maybe terminating too soon causes the problems on Gentoo...
<         time.sleep(1)
---
>         timeout = support.SHORT_TIMEOUT
>         if not event.wait(timeout):
>             p.terminate()
>             p.join()
>             self.fail(f"event not signaled in {timeout} seconds")
6823a6835,6856
>     def test_forked_thread_not_started(self):
>         # gh-134381: Ensure that a thread that has not been started yet in
>         # the parent process can be started within a forked child process.
> 
>         if multiprocessing.get_start_method() != "fork":
>             self.skipTest("fork specific test")
> 
>         q = multiprocessing.Queue()
>         t = threading.Thread(target=lambda: q.put("done"), daemon=True)
> 
>         def child():
>             t.start()
>             t.join()
> 
>         p = multiprocessing.Process(target=child)
>         p.start()
>         p.join(support.SHORT_TIMEOUT)
> 
>         self.assertEqual(p.exitcode, 0)
>         self.assertEqual(q.get_nowait(), "done")
>         close_queue(q)
> 
# ----------------------------------------------------------------------
diff Python-3.14.0b2/Lib/multiprocessing/forkserver.py Python-3.14.0b4/Lib/multiprocessing/forkserver.py
224a225,228
>         # gh-135335: flush stdout/stderr in case any of the preloaded modules
>         # wrote to them, otherwise children might inherit buffered data
>         util._flush_std_streams()
> 
diff Python-3.14.0b2/Lib/test/_test_multiprocessing.py Python-3.14.0b4/Lib/test/_test_multiprocessing.py
6791a6792,6820
>     def test_std_streams_flushed_after_preload(self):
>         # gh-135335: Check fork server flushes standard streams after
>         # preloading modules
>         if multiprocessing.get_start_method() != "forkserver":
>             self.skipTest("forkserver specific test")
> 
>         # Create a test module in the temporary directory on the child's path
>         # TODO: This can all be simplified once gh-126631 is fixed and we can
>         #       use __main__ instead of a module.
>         dirname = os.path.join(self._temp_dir, 'preloaded_module')
>         init_name = os.path.join(dirname, '__init__.py')
>         os.mkdir(dirname)
>         with open(init_name, "w") as f:
>             cmd = '''if 1:
>                 import sys
>                 print('stderr', end='', file=sys.stderr)
>                 print('stdout', end='', file=sys.stdout)
>             '''
>             f.write(cmd)
> 
>         name = os.path.join(os.path.dirname(__file__), 'mp_preload_flush.py')
>         env = {'PYTHONPATH': self._temp_dir}
>         _, out, err = test.support.script_helper.assert_python_ok(name, **env)
> 
>         # Check stderr first, as it is more likely to be useful to see in the
>         # event of a failure.
>         self.assertEqual(err.decode().rstrip(), 'stderr')
>         self.assertEqual(out.decode().rstrip(), 'stdout')
> 
# ----------------------------------------------------------------------
diff Python-3.14.0b4/Lib/multiprocessing/resource_tracker.py Python-3.14.0rc1/Lib/multiprocessing/resource_tracker.py
50,55c50,51
<         _CLEANUP_FUNCS.update({
<             'semaphore': _multiprocessing.sem_unlink,
<         })
<     _CLEANUP_FUNCS.update({
<         'shared_memory': _posixshmem.shm_unlink,
<     })
---
>         _CLEANUP_FUNCS['semaphore'] = _multiprocessing.sem_unlink
>     _CLEANUP_FUNCS['shared_memory'] = _posixshmem.shm_unlink
# ----------------------------------------------------------------------
diff Python-3.14.0rc1/Lib/multiprocessing/forkserver.py Python-3.14.1/Lib/multiprocessing/forkserver.py
147a148
>             main_kws = {}
149d149
<                 desired_keys = {'main_path', 'sys_path'}
151,153c151,154
<                 main_kws = {x: y for x, y in data.items() if x in desired_keys}
<             else:
<                 main_kws = {}
---
>                 if 'sys_path' in data:
>                     main_kws['sys_path'] = data['sys_path']
>                 if 'init_main_from_path' in data:
>                     main_kws['main_path'] = data['init_main_from_path']
diff Python-3.14.0rc1/Lib/multiprocessing/popen_spawn_posix.py Python-3.14.1/Lib/multiprocessing/popen_spawn_posix.py
59a60,63
>             os.close(child_r)
>             child_r = None
>             os.close(child_w)
>             child_w = None
diff Python-3.14.0rc1/Lib/multiprocessing/process.py Python-3.14.1/Lib/multiprocessing/process.py
80c80
<     def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
---
>     def __init__(self, group=None, target=None, name=None, args=(), kwargs=None,
92c92
<         self._kwargs = dict(kwargs)
---
>         self._kwargs = dict(kwargs) if kwargs else {}
diff Python-3.14.0rc1/Lib/multiprocessing/queues.py Python-3.14.1/Lib/multiprocessing/queues.py
124c124
<         return self._maxsize - self._sem._semlock._get_value()
---
>         return self._maxsize - self._sem.get_value()
diff Python-3.14.0rc1/Lib/multiprocessing/resource_tracker.py Python-3.14.1/Lib/multiprocessing/resource_tracker.py
17a18
> import base64
22a24,26
> from collections import deque
> 
> import json
64a69
>         self._reentrant_messages = deque()
101c106
<             return self._reentrant_call_error()
---
>             raise self._reentrant_call_error()
112c117,122
<         _, status = waitpid(self._pid, 0)
---
>         try:
>             _, status = waitpid(self._pid, 0)
>         except ChildProcessError:
>             self._pid = None
>             self._exitcode = None
>             return
130a141,213
>         return self._ensure_running_and_write()
> 
>     def _teardown_dead_process(self):
>         os.close(self._fd)
> 
>         # Clean-up to avoid dangling processes.
>         try:
>             # _pid can be None if this process is a child from another
>             # python process, which has started the resource_tracker.
>             if self._pid is not None:
>                 os.waitpid(self._pid, 0)
>         except ChildProcessError:
>             # The resource_tracker has already been terminated.
>             pass
>         self._fd = None
>         self._pid = None
>         self._exitcode = None
> 
>         warnings.warn('resource_tracker: process died unexpectedly, '
>                       'relaunching.  Some resources might leak.')
> 
>     def _launch(self):
>         fds_to_pass = []
>         try:
>             fds_to_pass.append(sys.stderr.fileno())
>         except Exception:
>             pass
>         r, w = os.pipe()
>         try:
>             fds_to_pass.append(r)
>             # process will out live us, so no need to wait on pid
>             exe = spawn.get_executable()
>             args = [
>                 exe,
>                 *util._args_from_interpreter_flags(),
>                 '-c',
>                 f'from multiprocessing.resource_tracker import main;main({r})',
>             ]
>             # bpo-33613: Register a signal mask that will block the signals.
>             # This signal mask will be inherited by the child that is going
>             # to be spawned and will protect the child from a race condition
>             # that can make the child die before it registers signal handlers
>             # for SIGINT and SIGTERM. The mask is unregistered after spawning
>             # the child.
>             prev_sigmask = None
>             try:
>                 if _HAVE_SIGMASK:
>                     prev_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS)
>                 pid = util.spawnv_passfds(exe, args, fds_to_pass)
>             finally:
>                 if prev_sigmask is not None:
>                     signal.pthread_sigmask(signal.SIG_SETMASK, prev_sigmask)
>         except:
>             os.close(w)
>             raise
>         else:
>             self._fd = w
>             self._pid = pid
>         finally:
>             os.close(r)
> 
>     def _make_probe_message(self):
>         """Return a JSON-encoded probe message."""
>         return (
>             json.dumps(
>                 {"cmd": "PROBE", "rtype": "noop"},
>                 ensure_ascii=True,
>                 separators=(",", ":"),
>             )
>             + "\n"
>         ).encode("ascii")
> 
>     def _ensure_running_and_write(self, msg=None):
134c217,220
<                 return self._reentrant_call_error()
---
>                 if msg is None:
>                     raise self._reentrant_call_error()
>                 return self._reentrant_messages.append(msg)
> 
137,143c223,226
<                 if self._check_alive():
<                     # => still alive
<                     return
<                 # => dead, launch it again
<                 os.close(self._fd)
< 
<                 # Clean-up to avoid dangling processes.
---
>                 if msg is None:
>                     to_send = self._make_probe_message()
>                 else:
>                     to_send = msg
145,154c228,231
<                     # _pid can be None if this process is a child from another
<                     # python process, which has started the resource_tracker.
<                     if self._pid is not None:
<                         os.waitpid(self._pid, 0)
<                 except ChildProcessError:
<                     # The resource_tracker has already been terminated.
<                     pass
<                 self._fd = None
<                 self._pid = None
<                 self._exitcode = None
---
>                     self._write(to_send)
>                 except OSError:
>                     self._teardown_dead_process()
>                     self._launch()
156,157c233,235
<                 warnings.warn('resource_tracker: process died unexpectedly, '
<                               'relaunching.  Some resources might leak.')
---
>                 msg = None  # message was sent in probe
>             else:
>                 self._launch()
159c237
<             fds_to_pass = []
---
>         while True:
161,193c239,244
<                 fds_to_pass.append(sys.stderr.fileno())
<             except Exception:
<                 pass
<             cmd = 'from multiprocessing.resource_tracker import main;main(%d)'
<             r, w = os.pipe()
<             try:
<                 fds_to_pass.append(r)
<                 # process will out live us, so no need to wait on pid
<                 exe = spawn.get_executable()
<                 args = [exe] + util._args_from_interpreter_flags()
<                 args += ['-c', cmd % r]
<                 # bpo-33613: Register a signal mask that will block the signals.
<                 # This signal mask will be inherited by the child that is going
<                 # to be spawned and will protect the child from a race condition
<                 # that can make the child die before it registers signal handlers
<                 # for SIGINT and SIGTERM. The mask is unregistered after spawning
<                 # the child.
<                 prev_sigmask = None
<                 try:
<                     if _HAVE_SIGMASK:
<                         prev_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS)
<                     pid = util.spawnv_passfds(exe, args, fds_to_pass)
<                 finally:
<                     if prev_sigmask is not None:
<                         signal.pthread_sigmask(signal.SIG_SETMASK, prev_sigmask)
<             except:
<                 os.close(w)
<                 raise
<             else:
<                 self._fd = w
<                 self._pid = pid
<             finally:
<                 os.close(r)
---
>                 reentrant_msg = self._reentrant_messages.popleft()
>             except IndexError:
>                 break
>             self._write(reentrant_msg)
>         if msg is not None:
>             self._write(msg)
200c251
<             os.write(self._fd, b'PROBE:0:noop\n')
---
>             os.write(self._fd, self._make_probe_message())
214,230c265
<     def _send(self, cmd, name, rtype):
<         try:
<             self.ensure_running()
<         except ReentrantCallError:
<             # The code below might or might not work, depending on whether
<             # the resource tracker was already running and still alive.
<             # Better warn the user.
<             # (XXX is warnings.warn itself reentrant-safe? :-)
<             warnings.warn(
<                 f"ResourceTracker called reentrantly for resource cleanup, "
<                 f"which is unsupported. "
<                 f"The {rtype} object {name!r} might leak.")
<         msg = '{0}:{1}:{2}\n'.format(cmd, name, rtype).encode('ascii')
<         if len(msg) > 512:
<             # posix guarantees that writes to a pipe of less than PIPE_BUF
<             # bytes are atomic, and that PIPE_BUF >= 512
<             raise ValueError('msg too long')
---
>     def _write(self, msg):
232,233c267
<         assert nbytes == len(msg), "nbytes {0:n} but len(msg) {1:n}".format(
<             nbytes, len(msg))
---
>         assert nbytes == len(msg), f"{nbytes=} != {len(msg)=}"
234a269,290
>     def _send(self, cmd, name, rtype):
>         # POSIX guarantees that writes to a pipe of less than PIPE_BUF (512 on Linux)
>         # bytes are atomic. Therefore, we want the message to be shorter than 512 bytes.
>         # POSIX shm_open() and sem_open() require the name, including its leading slash,
>         # to be at most NAME_MAX bytes (255 on Linux)
>         # With json.dump(..., ensure_ascii=True) every non-ASCII byte becomes a 6-char
>         # escape like \uDC80.
>         # As we want the overall message to be kept atomic and therefore smaller than 512,
>         # we encode encode the raw name bytes with URL-safe Base64 - so a 255 long name
>         # will not exceed 340 bytes.
>         b = name.encode('utf-8', 'surrogateescape')
>         if len(b) > 255:
>             raise ValueError('shared memory name too long (max 255 bytes)')
>         b64 = base64.urlsafe_b64encode(b).decode('ascii')
> 
>         payload = {"cmd": cmd, "rtype": rtype, "base64_name": b64}
>         msg = (json.dumps(payload, ensure_ascii=True, separators=(",", ":")) + "\n").encode("ascii")
> 
>         # The entire JSON message is guaranteed < PIPE_BUF (512 bytes) by construction.
>         assert len(msg) <= 512, f"internal error: message too long ({len(msg)} bytes)"
> 
>         self._ensure_running_and_write(msg)
265c321,337
<                     cmd, name, rtype = line.strip().decode('ascii').split(':')
---
>                     try:
>                         obj = json.loads(line.decode('ascii'))
>                     except Exception as e:
>                         raise ValueError("malformed resource_tracker message: %r" % (line,)) from e
> 
>                     cmd = obj["cmd"]
>                     rtype = obj["rtype"]
>                     b64  = obj.get("base64_name", "")
> 
>                     if not isinstance(cmd, str) or not isinstance(rtype, str) or not isinstance(b64, str):
>                         raise ValueError("malformed resource_tracker fields: %r" % (obj,))
> 
>                     try:
>                         name = base64.urlsafe_b64decode(b64).decode('utf-8', 'surrogateescape')
>                     except ValueError as e:
>                         raise ValueError("malformed resource_tracker base64_name: %r" % (b64,)) from e
> 
diff Python-3.14.0rc1/Lib/multiprocessing/synchronize.py Python-3.14.1/Lib/multiprocessing/synchronize.py
137a138,142
>         '''Returns current value of Semaphore.
> 
>         Raises NotImplementedError on Mac OSX
>         because of broken sem_getvalue().
>         '''
142c147
<             value = self._semlock._get_value()
---
>             value = self.get_value()
158c163
<             value = self._semlock._get_value()
---
>             value = self.get_value()
250,251c255,256
<             num_waiters = (self._sleeping_count._semlock._get_value() -
<                            self._woken_count._semlock._get_value())
---
>             num_waiters = (self._sleeping_count.get_value() -
>                            self._woken_count.get_value())
diff Python-3.14.0rc1/Lib/multiprocessing/util.py Python-3.14.1/Lib/multiprocessing/util.py
129,134c129,136
< # Maximum length of a socket file path is usually between 92 and 108 [1],
< # but Linux is known to use a size of 108 [2]. BSD-based systems usually
< # use a size of 104 or 108 and Windows does not create AF_UNIX sockets.
< #
< # [1]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_un.h.html
< # [2]: https://man7.org/linux/man-pages/man7/unix.7.html.
---
> # Maximum length of a NULL-terminated [1] socket file path is usually
> # between 92 and 108 [2], but Linux is known to use a size of 108 [3].
> # BSD-based systems usually use a size of 104 or 108 and Windows does
> # not create AF_UNIX sockets.
> #
> # [1]: https://github.com/python/cpython/issues/140734
> # [2]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_un.h.html
> # [3]: https://man7.org/linux/man-pages/man7/unix.7.html
174c176
<     # Thus, the length of socket filename will be:
---
>     # Thus, the socket file path length (without NULL terminator) will be:
178c180,182
<     if sun_path_len <= _SUN_PATH_MAX:
---
>     # Strict inequality to account for the NULL terminator.
>     # See https://github.com/python/cpython/issues/140734.
>     if sun_path_len < _SUN_PATH_MAX:
204c208
<     assert len(base_system_tempdir) + 14 + 14 <= _SUN_PATH_MAX
---
>     assert len(base_system_tempdir) + 14 + 14 < _SUN_PATH_MAX
diff Python-3.14.0rc1/Lib/multiprocessing/dummy/__init__.py Python-3.14.1/Lib/multiprocessing/dummy/__init__.py
36c36
<     def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
---
>     def __init__(self, group=None, target=None, name=None, args=(), kwargs=None):
diff Python-3.14.0rc1/Lib/test/_test_multiprocessing.py Python-3.14.1/Lib/test/_test_multiprocessing.py 
1183c1183
<         #queue.put(1)
---
>         queue.put(1)
1207c1207,1209
<         time.sleep(DELTA)
---
>         for _ in support.sleeping_retry(support.SHORT_TIMEOUT):
>             if not queue_empty(queue):
>                 break
1210,1211c1212
<         # Hangs unexpectedly, remove for now
<         #self.assertEqual(queue.get(), 1)
---
>         self.assertEqual(queue.get_nowait(), 1)
1215c1216
<         self.assertEqual(queue.get_nowait(), 5)
---
>         self.assertEqual(queue.get(), 5)
5175a5177,5193
> #
> # Regression tests for BaseProcess kwargs handling
> #
> 
> class TestBaseProcessKwargs(unittest.TestCase):
>     def test_default_kwargs_not_shared_between_instances(self):
>         # Creating multiple Process instances without passing kwargs
>         # must create independent empty dicts (no shared state).
>         p1 = multiprocessing.Process(target=lambda: None)
>         p2 = multiprocessing.Process(target=lambda: None)
>         self.assertIsInstance(p1._kwargs, dict)
>         self.assertIsInstance(p2._kwargs, dict)
>         self.assertIsNot(p1._kwargs, p2._kwargs)
>         # Mutating one should not affect the other
>         p1._kwargs['x'] = 1
>         self.assertNotIn('x', p2._kwargs)
> 
6798,6811d6815
<         # Create a test module in the temporary directory on the child's path
<         # TODO: This can all be simplified once gh-126631 is fixed and we can
<         #       use __main__ instead of a module.
<         dirname = os.path.join(self._temp_dir, 'preloaded_module')
<         init_name = os.path.join(dirname, '__init__.py')
<         os.mkdir(dirname)
<         with open(init_name, "w") as f:
<             cmd = '''if 1:
<                 import sys
<                 print('stderr', end='', file=sys.stderr)
<                 print('stdout', end='', file=sys.stdout)
<             '''
<             f.write(cmd)
< 
6813,6814c6817
<         env = {'PYTHONPATH': self._temp_dir}
<         _, out, err = test.support.script_helper.assert_python_ok(name, **env)
---
>         _, out, err = test.support.script_helper.assert_python_ok(name)
6818,6819c6821,6822
<         self.assertEqual(err.decode().rstrip(), 'stderr')
<         self.assertEqual(out.decode().rstrip(), 'stdout')
---
>         self.assertEqual(err.decode().rstrip(), '__main____mp_main__')
>         self.assertEqual(out.decode().rstrip(), '__main____mp_main__')
6885a6889,6900
>     def test_preload_main(self):
>         # gh-126631: Check that __main__ can be pre-loaded
>         if multiprocessing.get_start_method() != "forkserver":
>             self.skipTest("forkserver specific test")
> 
>         name = os.path.join(os.path.dirname(__file__), 'mp_preload_main.py')
>         _, out, err = test.support.script_helper.assert_python_ok(name)
>         self.assertEqual(err, b'')
> 
>         # The trailing empty string comes from split() on output ending with \n
>         out = out.decode().split("\n")
>         self.assertEqual(out, ['__main__', '__mp_main__', 'f', 'f', ''])
7132a7148,7191
> 
> 
> @unittest.skipUnless(HAS_SHMEM, "requires multiprocessing.shared_memory")
> class TestSharedMemoryNames(unittest.TestCase):
>     def test_that_shared_memory_name_with_colons_has_no_resource_tracker_errors(self):
>         # Test script that creates and cleans up shared memory with colon in name
>         test_script = textwrap.dedent("""
>             import sys
>             from multiprocessing import shared_memory
>             import time
> 
>             # Test various patterns of colons in names
>             test_names = [
>                 "a:b",
>                 "a:b:c",
>                 "test:name:with:many:colons",
>                 ":starts:with:colon",
>                 "ends:with:colon:",
>                 "::double::colons::",
>                 "name\\nwithnewline",
>                 "name-with-trailing-newline\\n",
>                 "\\nname-starts-with-newline",
>                 "colons:and\\nnewlines:mix",
>                 "multi\\nline\\nname",
>             ]
> 
>             for name in test_names:
>                 try:
>                     shm = shared_memory.SharedMemory(create=True, size=100, name=name)
>                     shm.buf[:5] = b'hello'  # Write something to the shared memory
>                     shm.close()
>                     shm.unlink()
> 
>                 except Exception as e:
>                     print(f"Error with name '{name}': {e}", file=sys.stderr)
>                     sys.exit(1)
> 
>             print("SUCCESS")
>         """)
> 
>         rc, out, err = script_helper.assert_python_ok("-c", test_script)
>         self.assertIn(b"SUCCESS", out)
>         self.assertNotIn(b"traceback", err.lower(), err)
>         self.assertNotIn(b"resource_tracker.py", err, err)
diff Python-3.14.0rc1/Lib/test/mp_preload_flush.py Python-3.14.1/Lib/test/mp_preload_flush.py 
4c4,5
< modname = 'preloaded_module'
---
> print(__name__, end='', file=sys.stderr)
> print(__name__, end='', file=sys.stdout)
6,7d6
<     if modname in sys.modules:
<         raise AssertionError(f'{modname!r} is not in sys.modules')
9d7
<     multiprocessing.set_forkserver_preload([modname])
14,15d11
< elif modname not in sys.modules:
<     raise AssertionError(f'{modname!r} is not in sys.modules')
# ----------------------------------------------------------------------
diff Python-3.14.1/Lib/multiprocessing/resource_tracker.py Python-3.14.2/Lib/multiprocessing/resource_tracker.py
70a71,77
>         # True to use colon-separated lines, rather than JSON lines,
>         # for internal communication. (Mainly for testing).
>         # Filenames not supported by the simple format will always be sent
>         # using JSON.
>         # The reader should understand all formats.
>         self._use_simple_format = True
> 
203c210,212
<         """Return a JSON-encoded probe message."""
---
>         """Return a probe message."""
>         if self._use_simple_format:
>             return b'PROBE:0:noop\n'
269a279,287
>         if self._use_simple_format and '\n' not in name:
>             msg = f"{cmd}:{name}:{rtype}\n".encode("ascii")
>             if len(msg) > 512:
>                 # posix guarantees that writes to a pipe of less than PIPE_BUF
>                 # bytes are atomic, and that PIPE_BUF >= 512
>                 raise ValueError('msg too long')
>             self._ensure_running_and_write(msg)
>             return
> 
288a307
>         assert msg.startswith(b'{')
298a318,341
> def _decode_message(line):
>     if line.startswith(b'{'):
>         try:
>             obj = json.loads(line.decode('ascii'))
>         except Exception as e:
>             raise ValueError("malformed resource_tracker message: %r" % (line,)) from e
> 
>         cmd = obj["cmd"]
>         rtype = obj["rtype"]
>         b64  = obj.get("base64_name", "")
> 
>         if not isinstance(cmd, str) or not isinstance(rtype, str) or not isinstance(b64, str):
>             raise ValueError("malformed resource_tracker fields: %r" % (obj,))
> 
>         try:
>             name = base64.urlsafe_b64decode(b64).decode('utf-8', 'surrogateescape')
>         except ValueError as e:
>             raise ValueError("malformed resource_tracker base64_name: %r" % (b64,)) from e
>     else:
>         cmd, rest = line.strip().decode('ascii').split(':', maxsplit=1)
>         name, rtype = rest.rsplit(':', maxsplit=1)
>     return cmd, rtype, name
> 
> 
321,337c364
<                     try:
<                         obj = json.loads(line.decode('ascii'))
<                     except Exception as e:
<                         raise ValueError("malformed resource_tracker message: %r" % (line,)) from e
< 
<                     cmd = obj["cmd"]
<                     rtype = obj["rtype"]
<                     b64  = obj.get("base64_name", "")
< 
<                     if not isinstance(cmd, str) or not isinstance(rtype, str) or not isinstance(b64, str):
<                         raise ValueError("malformed resource_tracker fields: %r" % (obj,))
< 
<                     try:
<                         name = base64.urlsafe_b64decode(b64).decode('utf-8', 'surrogateescape')
<                     except ValueError as e:
<                         raise ValueError("malformed resource_tracker base64_name: %r" % (b64,)) from e
< 
---
>                     cmd, rtype, name = _decode_message(line)
diff Python-3.14.1/Lib/test/_test_multiprocessing.py Python-3.14.2/Lib/test/_test_multiprocessing.py 
42c42
< 
---
> from test.support import subTests
4286a4287,4299
> def resource_tracker_format_subtests(func):
>     """Run given test using both resource tracker communication formats"""
>     def _inner(self, *args, **kwargs):
>         tracker = resource_tracker._resource_tracker
>         for use_simple_format in False, True:
>             with (
>                 self.subTest(use_simple_format=use_simple_format),
>                 unittest.mock.patch.object(
>                     tracker, '_use_simple_format', use_simple_format)
>             ):
>                 func(self, *args, **kwargs)
>     return _inner
> 
4563a4577
>     @resource_tracker_format_subtests
4813a4828
>     @resource_tracker_format_subtests
4840a4856
>     @resource_tracker_format_subtests
7152c7168,7170
<     def test_that_shared_memory_name_with_colons_has_no_resource_tracker_errors(self):
---
>     @subTests('use_simple_format', (True, False))
>     def test_that_shared_memory_name_with_colons_has_no_resource_tracker_errors(
>             self, use_simple_format):
7156a7175
>             from multiprocessing import resource_tracker
7158a7178,7179
>             resource_tracker._resource_tracker._use_simple_format = %s
> 
7186c7207
<         """)
---
>         """ % use_simple_format)
