Skip to content

Commit 5e217bb

Browse files
committed
[3.8] bpo-39850: Add support for abstract sockets in multiprocessing (pythonGH-18866)
(cherry picked from commit 6012f30) Co-authored-by: Pablo Galindo <[email protected]>
1 parent 97bbdb2 commit 5e217bb

File tree

6 files changed

+50
-5
lines changed

6 files changed

+50
-5
lines changed

Lib/multiprocessing/connection.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def address_type(address):
102102
return 'AF_INET'
103103
elif type(address) is str and address.startswith('\\\\'):
104104
return 'AF_PIPE'
105-
elif type(address) is str:
105+
elif type(address) is str or util.is_abstract_socket_namespace(address):
106106
return 'AF_UNIX'
107107
else:
108108
raise ValueError('address type of %r unrecognized' % address)
@@ -597,7 +597,8 @@ def __init__(self, address, family, backlog=1):
597597
self._family = family
598598
self._last_accepted = None
599599

600-
if family == 'AF_UNIX':
600+
if family == 'AF_UNIX' and not util.is_abstract_socket_namespace(address):
601+
# Linux abstract socket namespaces do not need to be explicitly unlinked
601602
self._unlink = util.Finalize(
602603
self, os.unlink, args=(address,), exitpriority=0
603604
)

Lib/multiprocessing/forkserver.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ def _stop_unlocked(self):
5555
os.waitpid(self._forkserver_pid, 0)
5656
self._forkserver_pid = None
5757

58-
os.unlink(self._forkserver_address)
58+
if not util.is_abstract_socket_namespace(self._forkserver_address):
59+
os.unlink(self._forkserver_address)
5960
self._forkserver_address = None
6061

6162
def set_forkserver_preload(self, modules_names):
@@ -135,7 +136,8 @@ def ensure_running(self):
135136
with socket.socket(socket.AF_UNIX) as listener:
136137
address = connection.arbitrary_address('AF_UNIX')
137138
listener.bind(address)
138-
os.chmod(address, 0o600)
139+
if not util.is_abstract_socket_namespace(address):
140+
os.chmod(address, 0o600)
139141
listener.listen()
140142

141143
# all client processes own the write end of the "alive" pipe;

Lib/multiprocessing/managers.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1289,8 +1289,12 @@ class SharedMemoryServer(Server):
12891289

12901290
def __init__(self, *args, **kwargs):
12911291
Server.__init__(self, *args, **kwargs)
1292+
address = self.address
1293+
# The address of Linux abstract namespaces can be bytes
1294+
if isinstance(address, bytes):
1295+
address = os.fsdecode(address)
12921296
self.shared_memory_context = \
1293-
_SharedMemoryTracker(f"shmm_{self.address}_{getpid()}")
1297+
_SharedMemoryTracker(f"shm_{address}_{getpid()}")
12941298
util.debug(f"SharedMemoryServer started by pid {getpid()}")
12951299

12961300
def create(*args, **kwargs):

Lib/multiprocessing/util.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,29 @@ def log_to_stderr(level=None):
102102
_log_to_stderr = True
103103
return _logger
104104

105+
106+
# Abstract socket support
107+
108+
def _platform_supports_abstract_sockets():
109+
if sys.platform == "linux":
110+
return True
111+
if hasattr(sys, 'getandroidapilevel'):
112+
return True
113+
return False
114+
115+
116+
def is_abstract_socket_namespace(address):
117+
if not address:
118+
return False
119+
if isinstance(address, bytes):
120+
return address[0] == 0
121+
elif isinstance(address, str):
122+
return address[0] == "\0"
123+
raise TypeError('address type of {address!r} unrecognized')
124+
125+
126+
abstract_sockets_supported = _platform_supports_abstract_sockets()
127+
105128
#
106129
# Function returning a temp directory which will be removed on exit
107130
#

Lib/test/_test_multiprocessing.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3272,6 +3272,19 @@ def test_context(self):
32723272
if self.TYPE == 'processes':
32733273
self.assertRaises(OSError, l.accept)
32743274

3275+
@unittest.skipUnless(util.abstract_sockets_supported,
3276+
"test needs abstract socket support")
3277+
def test_abstract_socket(self):
3278+
with self.connection.Listener("\0something") as listener:
3279+
with self.connection.Client(listener.address) as client:
3280+
with listener.accept() as d:
3281+
client.send(1729)
3282+
self.assertEqual(d.recv(), 1729)
3283+
3284+
if self.TYPE == 'processes':
3285+
self.assertRaises(OSError, listener.accept)
3286+
3287+
32753288
class _TestListenerClient(BaseTestCase):
32763289

32773290
ALLOWED_TYPES = ('processes', 'threads')
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:mod:`multiprocessing` now supports abstract socket addresses (if abstract sockets
2+
are supported in the running platform). Patch by Pablo Galindo.

0 commit comments

Comments
 (0)