cp -rf py3.11/examples .
cp -rf py3.11/doc .
cp -f py3.11/index.html .
cp -rf py3.11/_multiprocess _multiprocess
cp -rf py3.11/multiprocess multiprocess
cp -rf Python-3.11.0/Modules/_multiprocessing Modules/_multiprocess
# ----------------------------------------------------------------------
diff Python-3.12.0a1/Modules/_multiprocessing/semaphore.c Modules/_multiprocess/semaphore.c
10c10
< #include "multiprocessing.h"
---
> #include "multiprocess.h"
35,36c35,36
< module _multiprocessing
< class _multiprocessing.SemLock "SemLockObject *" "&_PyMp_SemLockType"
---
> module _multiprocess
> class _multiprocess.SemLock "SemLockObject *" "&_PyMp_SemLockType"
80c80
< _multiprocessing.SemLock.acquire
---
> _multiprocess.SemLock.acquire
89c89
< _multiprocessing_SemLock_acquire_impl(SemLockObject *self, int blocking,
---
> _multiprocess_SemLock_acquire_impl(SemLockObject *self, int blocking,
171c171
< _multiprocessing.SemLock.release
---
> _multiprocess.SemLock.release
177c177
< _multiprocessing_SemLock_release_impl(SemLockObject *self)
---
> _multiprocess_SemLock_release_impl(SemLockObject *self)
232c232
< #ifndef HAVE_SEM_TIMEDWAIT
---
> // ifndef HAVE_SEM_TIMEDWAIT
293c293
< #endif /* !HAVE_SEM_TIMEDWAIT */
---
> // #endif /* !HAVE_SEM_TIMEDWAIT */
296c296
< _multiprocessing.SemLock.acquire
---
> _multiprocess.SemLock.acquire
305c305
< _multiprocessing_SemLock_acquire_impl(SemLockObject *self, int blocking,
---
> _multiprocess_SemLock_acquire_impl(SemLockObject *self, int blocking,
381c381
< _multiprocessing.SemLock.release
---
> _multiprocess.SemLock.release
387c387
< _multiprocessing_SemLock_release_impl(SemLockObject *self)
---
> _multiprocess_SemLock_release_impl(SemLockObject *self)
471c471
< _multiprocessing.SemLock.__new__
---
> _multiprocess.SemLock.__new__
482c482
< _multiprocessing_SemLock_impl(PyTypeObject *type, int kind, int value,
---
> _multiprocess_SemLock_impl(PyTypeObject *type, int kind, int value,
530c530
< _multiprocessing.SemLock._rebuild
---
> _multiprocess.SemLock._rebuild
541c541
< _multiprocessing_SemLock__rebuild_impl(PyTypeObject *type, SEM_HANDLE handle,
---
> _multiprocess_SemLock__rebuild_impl(PyTypeObject *type, SEM_HANDLE handle,
581c581
< _multiprocessing.SemLock._count
---
> _multiprocess.SemLock._count
587c587
< _multiprocessing_SemLock__count_impl(SemLockObject *self)
---
> _multiprocess_SemLock__count_impl(SemLockObject *self)
594c594
< _multiprocessing.SemLock._is_mine
---
> _multiprocess.SemLock._is_mine
600c600
< _multiprocessing_SemLock__is_mine_impl(SemLockObject *self)
---
> _multiprocess_SemLock__is_mine_impl(SemLockObject *self)
608c608
< _multiprocessing.SemLock._get_value
---
> _multiprocess.SemLock._get_value
614c614
< _multiprocessing_SemLock__get_value_impl(SemLockObject *self)
---
> _multiprocess_SemLock__get_value_impl(SemLockObject *self)
633c633
< _multiprocessing.SemLock._is_zero
---
> _multiprocess.SemLock._is_zero
639c639
< _multiprocessing_SemLock__is_zero_impl(SemLockObject *self)
---
> _multiprocess_SemLock__is_zero_impl(SemLockObject *self)
661c661
< _multiprocessing.SemLock._after_fork
---
> _multiprocess.SemLock._after_fork
667c667
< _multiprocessing_SemLock__after_fork_impl(SemLockObject *self)
---
> _multiprocess_SemLock__after_fork_impl(SemLockObject *self)
675c675
< _multiprocessing.SemLock.__enter__
---
> _multiprocess.SemLock.__enter__
681c681
< _multiprocessing_SemLock___enter___impl(SemLockObject *self)
---
> _multiprocess_SemLock___enter___impl(SemLockObject *self)
684c684
<     return _multiprocessing_SemLock_acquire_impl(self, 1, Py_None);
---
>     return _multiprocess_SemLock_acquire_impl(self, 1, Py_None);
688c688
< _multiprocessing.SemLock.__exit__
---
> _multiprocess.SemLock.__exit__
699c699
< _multiprocessing_SemLock___exit___impl(SemLockObject *self,
---
> _multiprocess_SemLock___exit___impl(SemLockObject *self,
704c704
<     return _multiprocessing_SemLock_release_impl(self);
---
>     return _multiprocess_SemLock_release_impl(self);
759c759
<     {Py_tp_new, _multiprocessing_SemLock},
---
>     {Py_tp_new, _multiprocess_SemLock},
767c767
<     .name = "_multiprocessing.SemLock",
---
>     .name = "_multiprocess.SemLock",
diff Python-3.12.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"
29c29
< module _multiprocessing
---
> module _multiprocess
76c76
< _multiprocessing.closesocket
---
> _multiprocess.closesocket
84c84
< _multiprocessing_closesocket_impl(PyObject *module, HANDLE handle)
---
> _multiprocess_closesocket_impl(PyObject *module, HANDLE handle)
99c99
< _multiprocessing.recv
---
> _multiprocess.recv
108c108
< _multiprocessing_recv_impl(PyObject *module, HANDLE handle, int size)
---
> _multiprocess_recv_impl(PyObject *module, HANDLE handle, int size)
131c131
< _multiprocessing.send
---
> _multiprocess.send
140c140
< _multiprocessing_send_impl(PyObject *module, HANDLE handle, Py_buffer *buf)
---
> _multiprocess_send_impl(PyObject *module, HANDLE handle, Py_buffer *buf)
159c159
< _multiprocessing.sem_unlink
---
> _multiprocess.sem_unlink
167c167
< _multiprocessing_sem_unlink_impl(PyObject *module, const char *name)
---
> _multiprocess_sem_unlink_impl(PyObject *module, const char *name)
195c195
< 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},
282c282
< static struct PyModuleDef multiprocessing_module = {
---
> static struct PyModuleDef multiprocess_module = {
284c284
<     .m_name = "_multiprocessing",
---
>     .m_name = "_multiprocess",
287c287
<     .m_slots = multiprocessing_slots,
---
>     .m_slots = multiprocess_slots,
291c291
< PyInit__multiprocessing(void)
---
> PyInit__multiprocess(void)
293c293
<     return PyModuleDef_Init(&multiprocessing_module);
---
>     return PyModuleDef_Init(&multiprocess_module);
# ----------------------------------------------------------------------
diff Python-3.11.0/Lib/multiprocessing/managers.py Python-3.12.0a1/Lib/multiprocessing/managers.py
436d435
<                     obj, exposed, gettypeid = self.id_to_obj[ident]
diff Python-3.11.0/Lib/multiprocessing/resource_tracker.py Python-3.12.0a1/Lib/multiprocessing/resource_tracker.py
164c164
<         if len(name) > 512:
---
>         if len(msg) > 512:
167c167
<             raise ValueError('name too long')
---
>             raise ValueError('msg too long')
# ----------------------------------------------------------------------
diff Python-3.11.0/Lib/test/_test_multiprocessing.py Python-3.12.0a1/Lib/test/_test_multiprocessing.py 
126a127,128
> WAIT_ACTIVE_CHILDREN_TIMEOUT = 5.0
> 
4321,4325c4323,4325
<             deadline = time.monotonic() + support.LONG_TIMEOUT
<             t = 0.1
<             while time.monotonic() < deadline:
<                 time.sleep(t)
<                 t = min(t*2, 5)
---
>             err_msg = ("A SharedMemory segment was leaked after "
>                        "a process was abruptly terminated")
>             for _ in support.sleeping_retry(support.LONG_TIMEOUT, err_msg):
4330,4332d4329
<             else:
<                 raise AssertionError("A SharedMemory segment was leaked after"
<                                      " a process was abruptly terminated.")
5295c5292
<             import time, os, tempfile
---
>             import time, os
5301d5297
<             rand = tempfile._RandomNameSequence()
5342,5344c5338,5341
<                 deadline = time.monotonic() + support.LONG_TIMEOUT
<                 while time.monotonic() < deadline:
<                     time.sleep(.5)
---
>                 err_msg = (f"A {rtype} resource was leaked after a process was "
>                            f"abruptly terminated")
>                 for _ in support.sleeping_retry(support.SHORT_TIMEOUT,
>                                                   err_msg):
5352,5355c5349
<                 else:
<                     raise AssertionError(
<                         f"A {rtype} resource was leaked after a process was "
<                         f"abruptly terminated.")
---
> 
5440a5435,5442
>     def test_too_long_name_resource(self):
>         # gh-96819: Resource names that will make the length of a write to a pipe
>         # greater than PIPE_BUF are not allowed
>         rtype = "shared_memory"
>         too_long_name_resource = "a" * (512 - len(rtype))
>         with self.assertRaises(ValueError):
>             resource_tracker.register(too_long_name_resource, rtype)
> 
5582a5585,5586
> 
>         timeout = WAIT_ACTIVE_CHILDREN_TIMEOUT
5584,5593c5588,5589
<         t = 0.01
<         while len(multiprocessing.active_children()) > 1:
<             time.sleep(t)
<             t *= 2
<             dt = time.monotonic() - start_time
<             if dt >= 5.0:
<                 test.support.environment_altered = True
<                 support.print_warning(f"multiprocessing.Manager still has "
<                                       f"{multiprocessing.active_children()} "
<                                       f"active children after {dt} seconds")
---
>         for _ in support.sleeping_retry(timeout, error=False):
>             if len(multiprocessing.active_children()) <= 1:
5594a5591,5596
>         else:
>             dt = time.monotonic() - start_time
>             support.environment_altered = True
>             support.print_warning(f"multiprocessing.Manager still has "
>                                   f"{multiprocessing.active_children()} "
>                                   f"active children after {dt:.1f} seconds")
5699,5701c5701,5704
<         assert obj[0] == 5
<         assert obj.count(5) == 1
<         assert obj.index(5) == 0
---
>         case = unittest.TestCase()
>         case.assertEqual(obj[0], 5)
>         case.assertEqual(obj.count(5), 1)
>         case.assertEqual(obj.index(5), 0)
5706,5707c5709,5710
<         assert len(obj) == 1
<         assert obj.pop(0) == 5
---
>         case.assertEqual(len(obj), 1)
>         case.assertEqual(obj.pop(0), 5)
5713c5716
<         assert not o
---
>         self.assertIsNotNone(o)
5718,5725c5721,5729
<         assert len(obj) == 1
<         assert obj['foo'] == 5
<         assert obj.get('foo') == 5
<         assert list(obj.items()) == [('foo', 5)]
<         assert list(obj.keys()) == ['foo']
<         assert list(obj.values()) == [5]
<         assert obj.copy() == {'foo': 5}
<         assert obj.popitem() == ('foo', 5)
---
>         case = unittest.TestCase()
>         case.assertEqual(len(obj), 1)
>         case.assertEqual(obj['foo'], 5)
>         case.assertEqual(obj.get('foo'), 5)
>         case.assertListEqual(list(obj.items()), [('foo', 5)])
>         case.assertListEqual(list(obj.keys()), ['foo'])
>         case.assertListEqual(list(obj.values()), [5])
>         case.assertDictEqual(obj.copy(), {'foo': 5})
>         case.assertTupleEqual(obj.popitem(), ('foo', 5))
5731c5735
<         assert not o
---
>         self.assertIsNotNone(o)
5736,5737c5740,5742
<         assert obj.value == 1
<         assert obj.get() == 1
---
>         case = unittest.TestCase()
>         case.assertEqual(obj.value, 1)
>         case.assertEqual(obj.get(), 1)
5748,5751c5753,5757
<         assert obj[0] == 0
<         assert obj[1] == 1
<         assert len(obj) == 2
<         assert list(obj) == [0, 1]
---
>         case = unittest.TestCase()
>         case.assertEqual(obj[0], 0)
>         case.assertEqual(obj[1], 1)
>         case.assertEqual(len(obj), 2)
>         case.assertListEqual(list(obj), [0, 1])
5759,5760c5765,5767
<         assert obj.x == 0
<         assert obj.y == 1
---
>         case = unittest.TestCase()
>         case.assertEqual(obj.x, 0)
>         case.assertEqual(obj.y, 1)
5890a5898
>         timeout = WAIT_ACTIVE_CHILDREN_TIMEOUT
5892,5901c5900,5901
<         t = 0.01
<         while len(multiprocessing.active_children()) > 1:
<             time.sleep(t)
<             t *= 2
<             dt = time.monotonic() - start_time
<             if dt >= 5.0:
<                 test.support.environment_altered = True
<                 support.print_warning(f"multiprocessing.Manager still has "
<                                       f"{multiprocessing.active_children()} "
<                                       f"active children after {dt} seconds")
---
>         for _ in support.sleeping_retry(timeout, error=False):
>             if len(multiprocessing.active_children()) <= 1:
5902a5903,5908
>         else:
>             dt = time.monotonic() - start_time
>             support.environment_altered = True
>             support.print_warning(f"multiprocessing.Manager still has "
>                                   f"{multiprocessing.active_children()} "
>                                   f"active children after {dt:.1f} seconds")
# ----------------------------------------------------------------------
diff Python-3.12.0a2/Modules/_multiprocessing/semaphore.c Python-3.12.0a3/Modules/_multiprocessing/semaphore.c
82c82
<     block as blocking: bool(accept={int}) = True
---
>     block as blocking: bool = True
91c91
< /*[clinic end generated code: output=f9998f0b6b0b0872 input=86f05662cf753eb4]*/
---
> /*[clinic end generated code: output=f9998f0b6b0b0872 input=e5b45f5cbb775166]*/
298c298
<     block as blocking: bool(accept={int}) = True
---
>     block as blocking: bool = True
307c307
< /*[clinic end generated code: output=f9998f0b6b0b0872 input=86f05662cf753eb4]*/
---
> /*[clinic end generated code: output=f9998f0b6b0b0872 input=e5b45f5cbb775166]*/
477c477
<     unlink: bool(accept={int})
---
>     unlink: bool
484c484
< /*[clinic end generated code: output=30727e38f5f7577a input=b378c3ee27d3a0fa]*/
---
> /*[clinic end generated code: output=30727e38f5f7577a input=fdaeb69814471c5b]*/
diff Python-3.12.0a2/Modules/_multiprocessing/clinic/semaphore.c.h Python-3.12.0a3/Modules/_multiprocessing/clinic/semaphore.c.h
68,69c68,69
<         blocking = _PyLong_AsInt(args[0]);
<         if (blocking == -1 && PyErr_Occurred()) {
---
>         blocking = PyObject_IsTrue(args[0]);
>         if (blocking < 0) {
165,166c165,166
<         blocking = _PyLong_AsInt(args[0]);
<         if (blocking == -1 && PyErr_Occurred()) {
---
>         blocking = PyObject_IsTrue(args[0]);
>         if (blocking < 0) {
278,279c278,279
<     unlink = _PyLong_AsInt(fastargs[4]);
<     if (unlink == -1 && PyErr_Occurred()) {
---
>     unlink = PyObject_IsTrue(fastargs[4]);
>     if (unlink < 0) {
545c545
< /*[clinic end generated code: output=720d7d0066dc0954 input=a9049054013a1b77]*/
---
> /*[clinic end generated code: output=dae57a702cc01512 input=a9049054013a1b77]*/
diff Python-3.12.0a2/Lib/multiprocessing/connection.py Python-3.12.0a3/Lib/multiprocessing/connection.py
730a731,798
> # multiprocessing.connection Authentication Handshake Protocol Description
> # (as documented for reference after reading the existing code)
> # =============================================================================
> #
> # On Windows: native pipes with "overlapped IO" are used to send the bytes,
> # instead of the length prefix SIZE scheme described below. (ie: the OS deals
> # with message sizes for us)
> #
> # Protocol error behaviors:
> #
> # On POSIX, any failure to receive the length prefix into SIZE, for SIZE greater
> # than the requested maxsize to receive, or receiving fewer than SIZE bytes
> # results in the connection being closed and auth to fail.
> #
> # On Windows, receiving too few bytes is never a low level _recv_bytes read
> # error, receiving too many will trigger an error only if receive maxsize
> # value was larger than 128 OR the if the data arrived in smaller pieces.
> #
> #      Serving side                           Client side
> #     ------------------------------  ---------------------------------------
> # 0.                                  Open a connection on the pipe.
> # 1.  Accept connection.
> # 2.  New random 20 bytes -> MESSAGE
> # 3.  send 4 byte length (net order)
> #     prefix followed by:
> #       b'#CHALLENGE#' + MESSAGE
> # 4.                                  Receive 4 bytes, parse as network byte
> #                                     order integer. If it is -1, receive an
> #                                     additional 8 bytes, parse that as network
> #                                     byte order. The result is the length of
> #                                     the data that follows -> SIZE.
> # 5.                                  Receive min(SIZE, 256) bytes -> M1
> # 6.                                  Assert that M1 starts with:
> #                                       b'#CHALLENGE#'
> # 7.                                  Strip that prefix from M1 into -> M2
> # 8.                                  Compute HMAC-MD5 of AUTHKEY, M2 -> C_DIGEST
> # 9.                                  Send 4 byte length prefix (net order)
> #                                     followed by C_DIGEST bytes.
> # 10. Compute HMAC-MD5 of AUTHKEY,
> #     MESSAGE into -> M_DIGEST.
> # 11. Receive 4 or 4+8 byte length
> #     prefix (#4 dance) -> SIZE.
> # 12. Receive min(SIZE, 256) -> C_D.
> # 13. Compare M_DIGEST == C_D:
> # 14a: Match? Send length prefix &
> #       b'#WELCOME#'
> #    <- RETURN
> # 14b: Mismatch? Send len prefix &
> #       b'#FAILURE#'
> #    <- CLOSE & AuthenticationError
> # 15.                                 Receive 4 or 4+8 byte length prefix (net
> #                                     order) again as in #4 into -> SIZE.
> # 16.                                 Receive min(SIZE, 256) bytes -> M3.
> # 17.                                 Compare M3 == b'#WELCOME#':
> # 17a.                                Match? <- RETURN
> # 17b.                                Mismatch? <- CLOSE & AuthenticationError
> #
> # If this RETURNed, the connection remains open: it has been authenticated.
> #
> # Length prefixes are used consistently even though every step so far has
> # always been a singular specific fixed length.  This may help us evolve
> # the protocol in the future without breaking backwards compatibility.
> #
> # Similarly the initial challenge message from the serving side has always
> # been 20 bytes, but clients can accept a 100+ so using the length of the
> # opening challenge message as an indicator of protocol version may work.
> 
> 
diff Python-3.12.0a2/Lib/multiprocessing/pool.py Python-3.12.0a3/Lib/multiprocessing/pool.py
699c699
<                 "Cannot have cache with result_hander not alive")
---
>                 "Cannot have cache with result_handler not alive")
diff Python-3.12.0a2/Lib/multiprocessing/shared_memory.py Python-3.12.0a3/Lib/multiprocessing/shared_memory.py
176c176,179
<                 size = _winapi.VirtualQuerySize(p_buf)
---
>                 try:
>                     size = _winapi.VirtualQuerySize(p_buf)
>                 finally:
>                     _winapi.UnmapViewOfFile(p_buf)
diff Python-3.12.0a2/Lib/test/_test_multiprocessing.py Python-3.12.0a3/Lib/test/_test_multiprocessing.py 
6045c6045
<         s = SemLock(1, 0, 10, name, 0)
---
>         s = SemLock(1, 0, 10, name, False)
# ----------------------------------------------------------------------
diff Python-3.12.0a3/Lib/multiprocessing/queues.py Python-3.12.0a4/Lib/multiprocessing/queues.py
282a283,284
>     __class_getitem__ = classmethod(types.GenericAlias)
> 
# ----------------------------------------------------------------------
diff Python-3.12.0a4/Lib/multiprocessing/context.py Python-3.12.0a5/Lib/multiprocessing/context.py
260a261
>         """Returns a list of the supported start methods, default first."""
# ----------------------------------------------------------------------
diff Python-3.12.0a4/Lib/test/_test_multiprocessing.py Python-3.12.0a5/Lib/test/_test_multiprocessing.py 
4970c4970
<     def run_in_child(cls):
---
>     def run_in_child(cls, start_method):
4972,4974c4972,4976
<         r, w = multiprocessing.Pipe(duplex=False)
<         p = multiprocessing.Process(target=cls.run_in_grandchild, args=(w,))
<         p.start()
---
>         mp = multiprocessing.get_context(start_method)
>         r, w = mp.Pipe(duplex=False)
>         p = mp.Process(target=cls.run_in_grandchild, args=(w,))
>         with warnings.catch_warnings(category=DeprecationWarning):
>             p.start()
4985,4986c4987,4990
<         prog = ('from test._test_multiprocessing import TestFlags; ' +
<                 'TestFlags.run_in_child()')
---
>         prog = (
>             'from test._test_multiprocessing import TestFlags; '
>             f'TestFlags.run_in_child({multiprocessing.get_start_method()!r})'
>         )
# ----------------------------------------------------------------------
diff Python-3.12.0a5/Modules/_multiprocessing/multiprocessing.h Python-3.12.0a7/Modules/_multiprocessing/multiprocessing.h
15c15,17
< #  define WIN32_LEAN_AND_MEAN
---
> #  ifndef WIN32_LEAN_AND_MEAN
> #    define WIN32_LEAN_AND_MEAN
> #  endif
# ----------------------------------------------------------------------
diff Python-3.12.0a7/Modules/_multiprocessing/multiprocessing.c Python-3.12.0b1/Modules/_multiprocessing/multiprocessing.c
278a279
>     {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
diff Python-3.12.0a7/Modules/_multiprocessing/posixshmem.c Python-3.12.0b1/Modules/_multiprocessing/posixshmem.c
112a113,118
> static PyModuleDef_Slot module_slots[] = {
>     {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
>     {0, NULL}
> };
> 
> 
118a125
>     .m_slots = module_slots,
diff Python-3.12.0a7/Lib/multiprocessing/connection.py Python-3.12.0b1/Lib/multiprocessing/connection.py
725c725
< MESSAGE_LENGTH = 20
---
> MESSAGE_LENGTH = 40  # MUST be > 20
727,729c727,729
< CHALLENGE = b'#CHALLENGE#'
< WELCOME = b'#WELCOME#'
< FAILURE = b'#FAILURE#'
---
> _CHALLENGE = b'#CHALLENGE#'
> _WELCOME = b'#WELCOME#'
> _FAILURE = b'#FAILURE#'
753c753,758
< # 2.  New random 20 bytes -> MESSAGE
---
> # 2.  Random 20+ bytes -> MESSAGE
> #     Modern servers always send
> #     more than 20 bytes and include
> #     a {digest} prefix on it with
> #     their preferred HMAC digest.
> #     Legacy ones send ==20 bytes.
766c771,778
< # 8.                                  Compute HMAC-MD5 of AUTHKEY, M2 -> C_DIGEST
---
> # 7.1.                                Parse M2: if it is exactly 20 bytes in
> #                                     length this indicates a legacy server
> #                                     supporting only HMAC-MD5. Otherwise the
> # 7.2.                                preferred digest is looked up from an
> #                                     expected "{digest}" prefix on M2. No prefix
> #                                     or unsupported digest? <- AuthenticationError
> # 7.3.                                Put divined algorithm name in -> D_NAME
> # 8.                                  Compute HMAC-D_NAME of AUTHKEY, M2 -> C_DIGEST
769,771c781
< # 10. Compute HMAC-MD5 of AUTHKEY,
< #     MESSAGE into -> M_DIGEST.
< # 11. Receive 4 or 4+8 byte length
---
> # 10. Receive 4 or 4+8 byte length
773c783,796
< # 12. Receive min(SIZE, 256) -> C_D.
---
> # 11. Receive min(SIZE, 256) -> C_D.
> # 11.1. Parse C_D: legacy servers
> #     accept it as is, "md5" -> D_NAME
> # 11.2. modern servers check the length
> #     of C_D, IF it is 16 bytes?
> # 11.2.1. "md5" -> D_NAME
> #         and skip to step 12.
> # 11.3. longer? expect and parse a "{digest}"
> #     prefix into -> D_NAME.
> #     Strip the prefix and store remaining
> #     bytes in -> C_D.
> # 11.4. Don't like D_NAME? <- AuthenticationError
> # 12. Compute HMAC-D_NAME of AUTHKEY,
> #     MESSAGE into -> M_DIGEST.
790,796c813,863
< # Length prefixes are used consistently even though every step so far has
< # always been a singular specific fixed length.  This may help us evolve
< # the protocol in the future without breaking backwards compatibility.
< #
< # Similarly the initial challenge message from the serving side has always
< # been 20 bytes, but clients can accept a 100+ so using the length of the
< # opening challenge message as an indicator of protocol version may work.
---
> # Length prefixes are used consistently. Even on the legacy protocol, this
> # was good fortune and allowed us to evolve the protocol by using the length
> # of the opening challenge or length of the returned digest as a signal as
> # to which protocol the other end supports.
> 
> _ALLOWED_DIGESTS = frozenset(
>         {b'md5', b'sha256', b'sha384', b'sha3_256', b'sha3_384'})
> _MAX_DIGEST_LEN = max(len(_) for _ in _ALLOWED_DIGESTS)
> 
> # Old hmac-md5 only server versions from Python <=3.11 sent a message of this
> # length. It happens to not match the length of any supported digest so we can
> # use a message of this length to indicate that we should work in backwards
> # compatible md5-only mode without a {digest_name} prefix on our response.
> _MD5ONLY_MESSAGE_LENGTH = 20
> _MD5_DIGEST_LEN = 16
> _LEGACY_LENGTHS = (_MD5ONLY_MESSAGE_LENGTH, _MD5_DIGEST_LEN)
> 
> 
> def _get_digest_name_and_payload(message: bytes) -> (str, bytes):
>     """Returns a digest name and the payload for a response hash.
> 
>     If a legacy protocol is detected based on the message length
>     or contents the digest name returned will be empty to indicate
>     legacy mode where MD5 and no digest prefix should be sent.
>     """
>     # modern message format: b"{digest}payload" longer than 20 bytes
>     # legacy message format: 16 or 20 byte b"payload"
>     if len(message) in _LEGACY_LENGTHS:
>         # Either this was a legacy server challenge, or we're processing
>         # a reply from a legacy client that sent an unprefixed 16-byte
>         # HMAC-MD5 response. All messages using the modern protocol will
>         # be longer than either of these lengths.
>         return '', message
>     if (message.startswith(b'{') and
>         (curly := message.find(b'}', 1, _MAX_DIGEST_LEN+2)) > 0):
>         digest = message[1:curly]
>         if digest in _ALLOWED_DIGESTS:
>             payload = message[curly+1:]
>             return digest.decode('ascii'), payload
>     raise AuthenticationError(
>             'unsupported message length, missing digest prefix, '
>             f'or unsupported digest: {message=}')
> 
> 
> def _create_response(authkey, message):
>     """Create a MAC based on authkey and message
> 
>     The MAC algorithm defaults to HMAC-MD5, unless MD5 is not available or
>     the message has a '{digest_name}' prefix. For legacy HMAC-MD5, the response
>     is the raw MAC, otherwise the response is prefixed with '{digest_name}',
>     e.g. b'{sha256}abcdefg...'
798,799c865,894
< 
< def deliver_challenge(connection, authkey):
---
>     Note: The MAC protects the entire message including the digest_name prefix.
>     """
>     import hmac
>     digest_name = _get_digest_name_and_payload(message)[0]
>     # The MAC protects the entire message: digest header and payload.
>     if not digest_name:
>         # Legacy server without a {digest} prefix on message.
>         # Generate a legacy non-prefixed HMAC-MD5 reply.
>         try:
>             return hmac.new(authkey, message, 'md5').digest()
>         except ValueError:
>             # HMAC-MD5 is not available (FIPS mode?), fall back to
>             # HMAC-SHA2-256 modern protocol. The legacy server probably
>             # doesn't support it and will reject us anyways. :shrug:
>             digest_name = 'sha256'
>     # Modern protocol, indicate the digest used in the reply.
>     response = hmac.new(authkey, message, digest_name).digest()
>     return b'{%s}%s' % (digest_name.encode('ascii'), response)
> 
> 
> def _verify_challenge(authkey, message, response):
>     """Verify MAC challenge
> 
>     If our message did not include a digest_name prefix, the client is allowed
>     to select a stronger digest_name from _ALLOWED_DIGESTS.
> 
>     In case our message is prefixed, a client cannot downgrade to a weaker
>     algorithm, because the MAC is calculated over the entire message
>     including the '{digest_name}' prefix.
>     """
800a896,910
>     response_digest, response_mac = _get_digest_name_and_payload(response)
>     response_digest = response_digest or 'md5'
>     try:
>         expected = hmac.new(authkey, message, response_digest).digest()
>     except ValueError:
>         raise AuthenticationError(f'{response_digest=} unsupported')
>     if len(expected) != len(response_mac):
>         raise AuthenticationError(
>                 f'expected {response_digest!r} of length {len(expected)} '
>                 f'got {len(response_mac)}')
>     if not hmac.compare_digest(expected, response_mac):
>         raise AuthenticationError('digest received was wrong')
> 
> 
> def deliver_challenge(connection, authkey: bytes, digest_name='sha256'):
803a914
>     assert MESSAGE_LENGTH > _MD5ONLY_MESSAGE_LENGTH, "protocol constraint"
805,806c916,920
<     connection.send_bytes(CHALLENGE + message)
<     digest = hmac.new(authkey, message, 'md5').digest()
---
>     message = b'{%s}%s' % (digest_name.encode('ascii'), message)
>     # Even when sending a challenge to a legacy client that does not support
>     # digest prefixes, they'll take the entire thing as a challenge and
>     # respond to it with a raw HMAC-MD5.
>     connection.send_bytes(_CHALLENGE + message)
808,809c922,926
<     if response == digest:
<         connection.send_bytes(WELCOME)
---
>     try:
>         _verify_challenge(authkey, message, response)
>     except AuthenticationError:
>         connection.send_bytes(_FAILURE)
>         raise
811,812c928
<         connection.send_bytes(FAILURE)
<         raise AuthenticationError('digest received was wrong')
---
>         connection.send_bytes(_WELCOME)
814,815c930,931
< def answer_challenge(connection, authkey):
<     import hmac
---
> 
> def answer_challenge(connection, authkey: bytes):
820,822c936,942
<     assert message[:len(CHALLENGE)] == CHALLENGE, 'message = %r' % message
<     message = message[len(CHALLENGE):]
<     digest = hmac.new(authkey, message, 'md5').digest()
---
>     if not message.startswith(_CHALLENGE):
>         raise AuthenticationError(
>                 f'Protocol error, expected challenge: {message=}')
>     message = message[len(_CHALLENGE):]
>     if len(message) < _MD5ONLY_MESSAGE_LENGTH:
>         raise AuthenticationError('challenge too short: {len(message)} bytes')
>     digest = _create_response(authkey, message)
825c945
<     if response != WELCOME:
---
>     if response != _WELCOME:
diff Python-3.12.0a7/Lib/multiprocessing/process.py Python-3.12.0b1/Lib/multiprocessing/process.py
64c64
<         if p._popen.poll() is not None:
---
>         if (child_popen := p._popen) and child_popen.poll() is not None:
diff Python-3.12.0a7/Lib/test/_test_multiprocessing.py Python-3.12.0b1/Lib/test/_test_multiprocessing.py 
50a51
> from multiprocessing.connection import wait, AuthenticationError
134,135d134
< from multiprocessing.connection import wait
< 
3045c3044
< @hashlib_helper.requires_hashdigest('md5')
---
> @hashlib_helper.requires_hashdigest('sha256')
3534c3533
< @hashlib_helper.requires_hashdigest('md5')
---
> @hashlib_helper.requires_hashdigest('sha256')
3837c3836
< @hashlib_helper.requires_hashdigest('md5')
---
> @hashlib_helper.requires_hashdigest('sha256')
4639c4638
< @hashlib_helper.requires_hashdigest('md5')
---
> @hashlib_helper.requires_hashdigest('sha256')
4659c4658
<                     return multiprocessing.connection.CHALLENGE
---
>                     return multiprocessing.connection._CHALLENGE
4668a4668,4705
> 
> @hashlib_helper.requires_hashdigest('md5')
> @hashlib_helper.requires_hashdigest('sha256')
> class ChallengeResponseTest(unittest.TestCase):
>     authkey = b'supadupasecretkey'
> 
>     def create_response(self, message):
>         return multiprocessing.connection._create_response(
>             self.authkey, message
>         )
> 
>     def verify_challenge(self, message, response):
>         return multiprocessing.connection._verify_challenge(
>             self.authkey, message, response
>         )
> 
>     def test_challengeresponse(self):
>         for algo in [None, "md5", "sha256"]:
>             with self.subTest(f"{algo=}"):
>                 msg = b'is-twenty-bytes-long'  # The length of a legacy message.
>                 if algo:
>                     prefix = b'{%s}' % algo.encode("ascii")
>                 else:
>                     prefix = b''
>                 msg = prefix + msg
>                 response = self.create_response(msg)
>                 if not response.startswith(prefix):
>                     self.fail(response)
>                 self.verify_challenge(msg, response)
> 
>     # TODO(gpshead): We need integration tests for handshakes between modern
>     # deliver_challenge() and verify_response() code and connections running a
>     # test-local copy of the legacy Python <=3.11 implementations.
> 
>     # TODO(gpshead): properly annotate tests for requires_hashdigest rather than
>     # only running these on a platform supporting everything.  otherwise logic
>     # issues preventing it from working on FIPS mode setups will be hidden.
> 
4676c4713
< @hashlib_helper.requires_hashdigest('md5')
---
> @hashlib_helper.requires_hashdigest('sha256')
5540c5577
< @hashlib_helper.requires_hashdigest('md5')
---
> @hashlib_helper.requires_hashdigest('sha256')
5972c6009
<                     Temp = hashlib_helper.requires_hashdigest('md5')(Temp)
---
>                     Temp = hashlib_helper.requires_hashdigest('sha256')(Temp)
# ----------------------------------------------------------------------
diff Python-3.12.0b1/Lib/multiprocessing/spawn.py Python-3.12.0b4/Lib/multiprocessing/spawn.py
34c34
<     WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
---
>     WINSERVICE = sys.executable and sys.executable.lower().endswith("pythonservice.exe")
38c38,40
<     if sys.platform == 'win32':
---
>     if exe is None:
>         _python_exe = exe
>     elif sys.platform == 'win32':
diff Python-3.12.0b1/Lib/test/_test_multiprocessing.py Python-3.12.0b4/Lib/test/_test_multiprocessing.py 
15a16
> import functools
33a35
> from test.support import script_helper
173a176,228
> def only_run_in_spawn_testsuite(reason):
>     """Returns a decorator: raises SkipTest when SM != spawn at test time.
> 
>     This can be useful to save overall Python test suite execution time.
>     "spawn" is the universal mode available on all platforms so this limits the
>     decorated test to only execute within test_multiprocessing_spawn.
> 
>     This would not be necessary if we refactored our test suite to split things
>     into other test files when they are not start method specific to be rerun
>     under all start methods.
>     """
> 
>     def decorator(test_item):
> 
>         @functools.wraps(test_item)
>         def spawn_check_wrapper(*args, **kwargs):
>             if (start_method := multiprocessing.get_start_method()) != "spawn":
>                 raise unittest.SkipTest(f"{start_method=}, not 'spawn'; {reason}")
>             return test_item(*args, **kwargs)
> 
>         return spawn_check_wrapper
> 
>     return decorator
> 
> 
> class TestInternalDecorators(unittest.TestCase):
>     """Logic within a test suite that could errantly skip tests? Test it!"""
> 
>     @unittest.skipIf(sys.platform == "win32", "test requires that fork exists.")
>     def test_only_run_in_spawn_testsuite(self):
>         if multiprocessing.get_start_method() != "spawn":
>             raise unittest.SkipTest("only run in test_multiprocessing_spawn.")
> 
>         try:
>             @only_run_in_spawn_testsuite("testing this decorator")
>             def return_four_if_spawn():
>                 return 4
>         except Exception as err:
>             self.fail(f"expected decorated `def` not to raise; caught {err}")
> 
>         orig_start_method = multiprocessing.get_start_method(allow_none=True)
>         try:
>             multiprocessing.set_start_method("spawn", force=True)
>             self.assertEqual(return_four_if_spawn(), 4)
>             multiprocessing.set_start_method("fork", force=True)
>             with self.assertRaises(unittest.SkipTest) as ctx:
>                 return_four_if_spawn()
>             self.assertIn("testing this decorator", str(ctx.exception))
>             self.assertIn("start_method=", str(ctx.exception))
>         finally:
>             multiprocessing.set_start_method(orig_start_method, force=True)
> 
> 
5817a5873
>     @only_run_in_spawn_testsuite("spawn specific test.")
5828d5883
< 
5830d5884
< 
5832d5885
< 
5834d5886
< 
5840c5892
<         rc, out, err = test.support.script_helper.assert_python_ok(testfn)
---
>         rc, out, err = script_helper.assert_python_ok(testfn)
5843c5895
<         self.assertEqual(err, b'')
---
>         self.assertFalse(err, msg=err.decode('utf-8'))
5851a5904,5921
>     @only_run_in_spawn_testsuite("avoids redundant testing.")
>     def test_spawn_sys_executable_none_allows_import(self):
>         # Regression test for a bug introduced in
>         # https://github.com/python/cpython/issues/90876 that caused an
>         # ImportError in multiprocessing when sys.executable was None.
>         # This can be true in embedded environments.
>         rc, out, err = script_helper.assert_python_ok(
>             "-c",
>             """if 1:
>             import sys
>             sys.executable = None
>             assert "multiprocessing" not in sys.modules, "already imported!"
>             import multiprocessing
>             import multiprocessing.spawn  # This should not fail\n""",
>         )
>         self.assertEqual(rc, 0)
>         self.assertFalse(err, msg=err.decode('utf-8'))
> 
# ----------------------------------------------------------------------
diff Python-3.12.0b4/Modules/_multiprocessing/semaphore.c Python-3.12.0rc2/Modules/_multiprocessing/semaphore.c
519,521d518
<     if (handle != SEM_FAILED)
<         SEM_CLOSE(handle);
<     PyMem_Free(name_copy);
524a522,524
>     if (handle != SEM_FAILED)
>         SEM_CLOSE(handle);
>     PyMem_Free(name_copy);
558a559
>             PyErr_SetFromErrno(PyExc_OSError);
560c561
<             return PyErr_SetFromErrno(PyExc_OSError);
---
>             return NULL;
diff Python-3.12.0b4/Lib/multiprocessing/forkserver.py Python-3.12.0rc2/Lib/multiprocessing/forkserver.py
64c64
<         if not all(type(mod) is str for mod in self._preload_modules):
---
>         if not all(type(mod) is str for mod in modules_names):
diff Python-3.12.0b4/Lib/multiprocessing/spawn.py Python-3.12.0rc2/Lib/multiprocessing/spawn.py
153c153,157
<         is not going to be frozen to produce an executable.''')
---
>         is not going to be frozen to produce an executable.
> 
>         To fix this issue, refer to the "Safe importing of main module"
>         section in https://docs.python.org/3/library/multiprocessing.html
>         ''')
diff Python-3.12.0b4/Lib/multiprocessing/synchronize.py Python-3.12.0rc2/Lib/multiprocessing/synchronize.py
53,54c53,54
<         name = ctx.get_start_method()
<         unlink_now = sys.platform == 'win32' or name == 'fork'
---
>         self._is_fork_ctx = ctx.get_start_method() == 'fork'
>         unlink_now = sys.platform == 'win32' or self._is_fork_ctx
105a106,110
>             if self._is_fork_ctx:
>                 raise RuntimeError('A SemLock created in a fork context is being '
>                                    'shared with a process in a spawn context. This is '
>                                    'not supported. Please use the same context to create '
>                                    'multiprocessing objects and Process.')
112a118,119
>         # Ensure that deserialized SemLock can be serialized again (gh-108520).
>         self._is_fork_ctx = False
diff Python-3.12.0b4/Lib/test/_test_multiprocessing.py Python-3.12.0rc2/Lib/test/_test_multiprocessing.py 
331a332
>     @support.requires_resource('cpu')
4468a4470
>     @support.requires_resource('cpu')
5333a5336,5343
>     def test_context_check_module_types(self):
>         try:
>             ctx = multiprocessing.get_context('forkserver')
>         except ValueError:
>             raise unittest.SkipTest('forkserver should be available')
>         with self.assertRaisesRegex(TypeError, 'module_names must be a list of strings'):
>             ctx.set_forkserver_preload([1, 2, 3])
> 
5377a5388,5435
>     @unittest.skipIf(sys.platform == "win32",
>                      "Only Spawn on windows so no risk of mixing")
>     @only_run_in_spawn_testsuite("avoids redundant testing.")
>     def test_mixed_startmethod(self):
>         # Fork-based locks cannot be used with spawned process
>         for process_method in ["spawn", "forkserver"]:
>             queue = multiprocessing.get_context("fork").Queue()
>             process_ctx = multiprocessing.get_context(process_method)
>             p = process_ctx.Process(target=close_queue, args=(queue,))
>             err_msg = "A SemLock created in a fork"
>             with self.assertRaisesRegex(RuntimeError, err_msg):
>                 p.start()
> 
>         # non-fork-based locks can be used with all other start methods
>         for queue_method in ["spawn", "forkserver"]:
>             for process_method in multiprocessing.get_all_start_methods():
>                 queue = multiprocessing.get_context(queue_method).Queue()
>                 process_ctx = multiprocessing.get_context(process_method)
>                 p = process_ctx.Process(target=close_queue, args=(queue,))
>                 p.start()
>                 p.join()
> 
>     @classmethod
>     def _put_one_in_queue(cls, queue):
>         queue.put(1)
> 
>     @classmethod
>     def _put_two_and_nest_once(cls, queue):
>         queue.put(2)
>         process = multiprocessing.Process(target=cls._put_one_in_queue, args=(queue,))
>         process.start()
>         process.join()
> 
>     def test_nested_startmethod(self):
>         # gh-108520: Regression test to ensure that child process can send its
>         # arguments to another process
>         queue = multiprocessing.Queue()
> 
>         process = multiprocessing.Process(target=self._put_two_and_nest_once, args=(queue,))
>         process.start()
>         process.join()
> 
>         results = []
>         while not queue.empty():
>             results.append(queue.get())
> 
>         self.assertEqual(results, [2, 1])
> 
6061c6119,6120
< def install_tests_in_module_dict(remote_globs, start_method):
---
> def install_tests_in_module_dict(remote_globs, start_method,
>                                  only_type=None, exclude_types=False):
6073a6133,6136
>                 if only_type and type_ != only_type:
>                     continue
>                 if exclude_types:
>                     continue
6083a6147,6149
>             if only_type:
>                 continue
> 
# ----------------------------------------------------------------------
diff Python-3.12.0rc2/Lib/test/_test_multiprocessing.py  Python-3.12.0rc3/Lib/test/_test_multiprocessing.py 
677a678
>     @support.requires_resource('walltime')
4955a4957
>     @support.requires_resource('walltime')
4983a4986
>     @support.requires_resource('walltime')
# ----------------------------------------------------------------------
diff Python-3.12.0rc3/Lib/multiprocessing/connection.py Python-3.12.1/Lib/multiprocessing/connection.py
11a12
> import errno
273a275
>         _send_ov = None
275a278,281
>             ov = self._send_ov
>             if ov is not None:
>                 # Interrupt WaitForMultipleObjects() in _send_bytes()
>                 ov.cancel()
278a285,288
>             if self._send_ov is not None:
>                 # A connection should only be used by a single thread
>                 raise ValueError("concurrent send_bytes() calls "
>                                  "are not supported")
279a290
>             self._send_ov = ov
288a300
>                 self._send_ov = None
289a302,306
>             if err == _winapi.ERROR_OPERATION_ABORTED:
>                 # close() was called by another thread while
>                 # WaitForMultipleObjects() was waiting for the overlapped
>                 # operation.
>                 raise OSError(errno.EPIPE, "handle is closed")
diff Python-3.12.0rc3/Lib/multiprocessing/popen_spawn_win32.py Python-3.12.1/Lib/multiprocessing/popen_spawn_win32.py
16a17
> # Exit code used by Popen.terminate()
125,126c126,130
<             except OSError:
<                 if self.wait(timeout=1.0) is None:
---
>             except PermissionError:
>                 # ERROR_ACCESS_DENIED (winerror 5) is received when the
>                 # process already died.
>                 code = _winapi.GetExitCodeProcess(int(self._handle))
>                 if code == _winapi.STILL_ACTIVE:
127a132,134
>                 self.returncode = code
>             else:
>                 self.returncode = -signal.SIGTERM
diff Python-3.12.0rc3/Lib/multiprocessing/queues.py Python-3.12.1/Lib/multiprocessing/queues.py
160a161,169
>     def _terminate_broken(self):
>         # Close a Queue on error.
> 
>         # gh-94777: Prevent queue writing to a pipe which is no longer read.
>         self._reader.close()
> 
>         self.close()
>         self.join_thread()
> 
172c181,182
<             name='QueueFeederThread'
---
>             name='QueueFeederThread',
>             daemon=True,
174d183
<         self._thread.daemon = True
176,178c185,193
<         debug('doing self._thread.start()')
<         self._thread.start()
<         debug('... done self._thread.start()')
---
>         try:
>             debug('doing self._thread.start()')
>             self._thread.start()
>             debug('... done self._thread.start()')
>         except:
>             # gh-109047: During Python finalization, creating a thread
>             # can fail with RuntimeError.
>             self._thread = None
>             raise
diff Python-3.12.0rc3/Lib/multiprocessing/resource_tracker.py Python-3.12.1/Lib/multiprocessing/resource_tracker.py
53a54,57
> class ReentrantCallError(RuntimeError):
>     pass
> 
> 
57c61
<         self._lock = threading.Lock()
---
>         self._lock = threading.RLock()
60a65,72
>     def _reentrant_call_error(self):
>         # gh-109629: this happens if an explicit call to the ResourceTracker
>         # gets interrupted by a garbage collection, invoking a finalizer (*)
>         # that itself calls back into ResourceTracker.
>         #   (*) for example the SemLock finalizer
>         raise ReentrantCallError(
>             "Reentrant call into the multiprocessing resource tracker")
> 
62a75,78
>             # 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()
83a100,102
>             if self._lock._recursion_count() > 1:
>                 # The code below is certainly not reentrant-safe, so bail out
>                 return self._reentrant_call_error()
162c181,191
<         self.ensure_running()
---
>         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.")
178a208
> 
diff Python-3.12.0rc3/Lib/test/_test_multiprocessing.py Python-3.12.1/Lib/test/_test_multiprocessing.py 
81,82c81,82
< if support.check_sanitizer(address=True):
<     # bpo-45200: Skip multiprocessing tests if Python is built with ASAN to
---
> if support.HAVE_ASAN_FORK_BUG:
>     # gh-89363: Skip multiprocessing tests if Python is built with ASAN to
84c84,89
<     raise unittest.SkipTest("libasan has a pthread_create() dead lock")
---
>     raise unittest.SkipTest("libasan has a pthread_create() dead lock related to thread+fork")
> 
> 
> # gh-110666: Tolerate a difference of 100 ms when comparing timings
> # (clock resolution)
> CLOCK_RES = 0.100
560,561c565
<         if os.name != 'nt':
<             self.assertEqual(exitcode, -signal.SIGTERM)
---
>         self.assertEqual(exitcode, -signal.SIGTERM)
566a571,572
>         else:
>             self.assertEqual(exitcode, -signal.SIGTERM)
1653c1659
<             expected = 0.1
---
>             expected = 0.100
1657,1658c1663
<             # borrow logic in assertTimeout() from test/lock_tests.py
<             if not result and expected * 0.6 < dt < expected * 10.0:
---
>             if not result and (expected - CLOCK_RES) <= dt:
1677c1682
<             time.sleep(0.01)
---
>             time.sleep(0.010)
2436,2437c2441,2445
< def sqr(x, wait=0.0):
<     time.sleep(wait)
---
> def sqr(x, wait=0.0, event=None):
>     if event is None:
>         time.sleep(wait)
>     else:
>         event.wait(wait)
2576,2579c2584,2595
<         res = self.pool.apply_async(sqr, (6, TIMEOUT2 + 1.0))
<         get = TimingWrapper(res.get)
<         self.assertRaises(multiprocessing.TimeoutError, get, timeout=TIMEOUT2)
<         self.assertTimingAlmostEqual(get.elapsed, TIMEOUT2)
---
>         p = self.Pool(3)
>         try:
>             event = threading.Event() if self.TYPE == 'threads' else None
>             res = p.apply_async(sqr, (6, TIMEOUT2 + support.SHORT_TIMEOUT, event))
>             get = TimingWrapper(res.get)
>             self.assertRaises(multiprocessing.TimeoutError, get, timeout=TIMEOUT2)
>             self.assertTimingAlmostEqual(get.elapsed, TIMEOUT2)
>         finally:
>             if event is not None:
>                 event.set()
>             p.terminate()
>             p.join()
2680,2687c2696,2701
<         result = self.pool.map_async(
<             time.sleep, [0.1 for i in range(10000)], chunksize=1
<             )
<         self.pool.terminate()
<         join = TimingWrapper(self.pool.join)
<         join()
<         # Sanity check the pool didn't wait for all tasks to finish
<         self.assertLess(join.elapsed, 2.0)
---
>         # Simulate slow tasks which take "forever" to complete
>         p = self.Pool(3)
>         args = [support.LONG_TIMEOUT for i in range(10_000)]
>         result = p.map_async(time.sleep, args, chunksize=1)
>         p.terminate()
>         p.join()
4872c4886
<                 time.sleep(random.random()*0.1)
---
>                 time.sleep(random.random() * 0.100)
4912c4926
<                 time.sleep(random.random()*0.1)
---
>                 time.sleep(random.random() * 0.100)
4961c4975
<         expected = 5
---
>         timeout = 5.0  # seconds
4965c4979
<         res = wait([a, b], expected)
---
>         res = wait([a, b], timeout)
4969,4970c4983
<         self.assertLess(delta, expected * 2)
<         self.assertGreater(delta, expected * 0.5)
---
>         self.assertGreater(delta, timeout - CLOCK_RES)
4973,4974d4985
< 
<         start = time.monotonic()
4976,4977d4986
<         delta = time.monotonic() - start
< 
4979d4987
<         self.assertLess(delta, 0.4)
5437c5445,5447
<         self.assertEqual(results, [2, 1])
---
>         # gh-109706: queue.put(1) can write into the queue before queue.put(2),
>         # there is no synchronization in the test.
>         self.assertSetEqual(set(results), set([2, 1]))
# ----------------------------------------------------------------------
diff Python-3.12.1/Lib/multiprocessing/managers.py Python-3.12.2/Lib/multiprocessing/managers.py
156c156
<         self.listener = Listener(address=address, backlog=16)
---
>         self.listener = Listener(address=address, backlog=128)
diff Python-3.12.1/Lib/multiprocessing/popen_spawn_win32.py Python-3.12.2/Lib/multiprocessing/popen_spawn_win32.py
104,115c104,117
<         if self.returncode is None:
<             if timeout is None:
<                 msecs = _winapi.INFINITE
<             else:
<                 msecs = max(0, int(timeout * 1000 + 0.5))
< 
<             res = _winapi.WaitForSingleObject(int(self._handle), msecs)
<             if res == _winapi.WAIT_OBJECT_0:
<                 code = _winapi.GetExitCodeProcess(self._handle)
<                 if code == TERMINATE:
<                     code = -signal.SIGTERM
<                 self.returncode = code
---
>         if self.returncode is not None:
>             return self.returncode
> 
>         if timeout is None:
>             msecs = _winapi.INFINITE
>         else:
>             msecs = max(0, int(timeout * 1000 + 0.5))
> 
>         res = _winapi.WaitForSingleObject(int(self._handle), msecs)
>         if res == _winapi.WAIT_OBJECT_0:
>             code = _winapi.GetExitCodeProcess(self._handle)
>             if code == TERMINATE:
>                 code = -signal.SIGTERM
>             self.returncode = code
123,134c125,140
<         if self.returncode is None:
<             try:
<                 _winapi.TerminateProcess(int(self._handle), TERMINATE)
<             except PermissionError:
<                 # ERROR_ACCESS_DENIED (winerror 5) is received when the
<                 # process already died.
<                 code = _winapi.GetExitCodeProcess(int(self._handle))
<                 if code == _winapi.STILL_ACTIVE:
<                     raise
<                 self.returncode = code
<             else:
<                 self.returncode = -signal.SIGTERM
---
>         if self.returncode is not None:
>             return
> 
>         try:
>             _winapi.TerminateProcess(int(self._handle), TERMINATE)
>         except PermissionError:
>             # ERROR_ACCESS_DENIED (winerror 5) is received when the
>             # process already died.
>             code = _winapi.GetExitCodeProcess(int(self._handle))
>             if code == _winapi.STILL_ACTIVE:
>                 raise
> 
>         # gh-113009: Don't set self.returncode. Even if GetExitCodeProcess()
>         # returns an exit code different than STILL_ACTIVE, the process can
>         # still be running. Only set self.returncode once WaitForSingleObject()
>         # returns WAIT_OBJECT_0 in wait().
diff Python-3.12.1/Lib/multiprocessing/queues.py Python-3.12.2/Lib/multiprocessing/queues.py
166a167,171
>         # gh-107219: Close the connection writer which can unblock
>         # Queue._feed() if it was stuck in send_bytes().
>         if sys.platform == 'win32':
>             self._writer.close()
> 
diff Python-3.12.1/Lib/multiprocessing/resource_sharer.py Python-3.12.2/Lib/multiprocessing/resource_sharer.py
126c126
<         self._listener = Listener(authkey=process.current_process().authkey)
---
>         self._listener = Listener(authkey=process.current_process().authkey, backlog=128)
diff Python-3.12.1/Lib/multiprocessing/util.py Python-3.12.2/Lib/multiprocessing/util.py
46c46
<         _logger.log(SUBDEBUG, msg, *args)
---
>         _logger.log(SUBDEBUG, msg, *args, stacklevel=2)
50c50
<         _logger.log(DEBUG, msg, *args)
---
>         _logger.log(DEBUG, msg, *args, stacklevel=2)
54c54
<         _logger.log(INFO, msg, *args)
---
>         _logger.log(INFO, msg, *args, stacklevel=2)
58c58
<         _logger.log(SUBWARNING, msg, *args)
---
>         _logger.log(SUBWARNING, msg, *args, stacklevel=2)
133c133,136
<     rmtree(tempdir)
---
>     def onerror(func, path, err_info):
>         if not issubclass(err_info[0], FileNotFoundError):
>             raise
>     rmtree(tempdir, onerror=onerror)
diff Python-3.12.1/Lib/test/_test_multiprocessing.py Python-3.12.2/Lib/test/_test_multiprocessing.py 
2696a2697,2704
>         sleep_time = support.LONG_TIMEOUT
> 
>         if self.TYPE == 'threads':
>             # Thread pool workers can't be forced to quit, so if the first
>             # task starts early enough, we will end up waiting for it.
>             # Sleep for a shorter time, so the test doesn't block.
>             sleep_time = 1
> 
2698c2706
<         args = [support.LONG_TIMEOUT for i in range(10_000)]
---
>         args = [sleep_time for i in range(10_000)]
2699a2708
>         time.sleep(0.2)  # give some tasks a chance to start
4632a4642,4664
>     def test_filename(self):
>         logger = multiprocessing.get_logger()
>         original_level = logger.level
>         try:
>             logger.setLevel(util.DEBUG)
>             stream = io.StringIO()
>             handler = logging.StreamHandler(stream)
>             logging_format = '[%(levelname)s] [%(filename)s] %(message)s'
>             handler.setFormatter(logging.Formatter(logging_format))
>             logger.addHandler(handler)
>             logger.info('1')
>             util.info('2')
>             logger.debug('3')
>             filename = os.path.basename(__file__)
>             log_record = stream.getvalue()
>             self.assertIn(f'[INFO] [{filename}] 1', log_record)
>             self.assertIn(f'[INFO] [{filename}] 2', log_record)
>             self.assertIn(f'[DEBUG] [{filename}] 3', log_record)
>         finally:
>             logger.setLevel(original_level)
>             logger.removeHandler(handler)
>             handler.close()
> 
# ----------------------------------------------------------------------
diff Python-3.12.2/Modules/_multiprocessing/posixshmem.c Python-3.12.3/Modules/_multiprocessing/posixshmem.c
45c45,46
<     const char *name = PyUnicode_AsUTF8(path);
---
>     Py_ssize_t name_size;
>     const char *name = PyUnicode_AsUTF8AndSize(path, &name_size);
48a50,53
>     if (strlen(name) != (size_t)name_size) {
>         PyErr_SetString(PyExc_ValueError, "embedded null character");
>         return -1;
>     }
84c89,90
<     const char *name = PyUnicode_AsUTF8(path);
---
>     Py_ssize_t name_size;
>     const char *name = PyUnicode_AsUTF8AndSize(path, &name_size);
87a94,97
>     if (strlen(name) != (size_t)name_size) {
>         PyErr_SetString(PyExc_ValueError, "embedded null character");
>         return NULL;
>     }
diff Python-3.12.2/Lib/multiprocessing/connection.py Python-3.12.3/Lib/multiprocessing/connection.py
478a479
> 
480c481
<         if self._authkey:
---
>         if self._authkey is not None:
diff Python-3.12.2/Lib/test/_test_multiprocessing.py Python-3.12.3/Lib/test/_test_multiprocessing.py 
3468a3469,3492
>     def test_empty_authkey(self):
>         # bpo-43952: allow empty bytes as authkey
>         def handler(*args):
>             raise RuntimeError('Connection took too long...')
> 
>         def run(addr, authkey):
>             client = self.connection.Client(addr, authkey=authkey)
>             client.send(1729)
> 
>         key = b''
> 
>         with self.connection.Listener(authkey=key) as listener:
>             thread = threading.Thread(target=run, args=(listener.address, key))
>             thread.start()
>             try:
>                 with listener.accept() as d:
>                     self.assertEqual(d.recv(), 1729)
>             finally:
>                 thread.join()
> 
>         if self.TYPE == 'processes':
>             with self.assertRaises(OSError):
>                 listener.accept()
> 
3935a3960,3974
>     def test_shared_memory_name_with_embedded_null(self):
>         name_tsmb = self._new_shm_name('test01_null')
>         sms = shared_memory.SharedMemory(name_tsmb, create=True, size=512)
>         self.addCleanup(sms.unlink)
>         with self.assertRaises(ValueError):
>             shared_memory.SharedMemory(name_tsmb + '\0a', create=False, size=512)
>         if shared_memory._USE_POSIX:
>             orig_name = sms._name
>             try:
>                 sms._name = orig_name + '\0a'
>                 with self.assertRaises(ValueError):
>                     sms.unlink()
>             finally:
>                 sms._name = orig_name
> 
4070c4109
<     def test_invalid_shared_memory_cration(self):
---
>     def test_invalid_shared_memory_creation(self):
# ----------------------------------------------------------------------
diff Python-3.12.3/Lib/test/_test_multiprocessing.py Python-3.12.5/Lib/test/_test_multiprocessing.py 
25d24
< import pathlib
327,328c326,328
<             sys.executable.encode(),      # bytes
<             pathlib.Path(sys.executable)  # os.PathLike
---
>             os.fsencode(sys.executable),  # bytes
>             os_helper.FakePath(sys.executable),  # os.PathLike
>             os_helper.FakePath(os.fsencode(sys.executable)),  # os.PathLike bytes
1334a1335,1351
>     def test_closed_queue_empty_exceptions(self):
>         # Assert that checking the emptiness of an unused closed queue
>         # does not raise an OSError. The rationale is that q.close() is
>         # a no-op upon construction and becomes effective once the queue
>         # has been used (e.g., by calling q.put()).
>         for q in multiprocessing.Queue(), multiprocessing.JoinableQueue():
>             q.close()  # this is a no-op since the feeder thread is None
>             q.join_thread()  # this is also a no-op
>             self.assertTrue(q.empty())
> 
>         for q in multiprocessing.Queue(), multiprocessing.JoinableQueue():
>             q.put('foo')  # make sure that the queue is 'used'
>             q.close()  # close the feeder thread
>             q.join_thread()  # make sure to join the feeder thread
>             with self.assertRaisesRegex(OSError, 'is closed'):
>                 q.empty()
> 
5693a5711,5719
>     def test_empty_exceptions(self):
>         # Assert that checking emptiness of a closed queue raises
>         # an OSError, independently of whether the queue was used
>         # or not. This differs from Queue and JoinableQueue.
>         q = multiprocessing.SimpleQueue()
>         q.close()  # close the pipe
>         with self.assertRaisesRegex(OSError, 'is closed'):
>             q.empty()
> 
