Skip to content

Commit dc863dd

Browse files
author
Charles-François Natali
committed
Issue #12981: rewrite multiprocessing_{sendfd,recvfd} in Python.
1 parent 57e683e commit dc863dd

File tree

3 files changed

+19
-140
lines changed

3 files changed

+19
-140
lines changed

Lib/multiprocessing/reduction.py

+18-3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import sys
4040
import socket
4141
import threading
42+
import struct
4243

4344
import _multiprocessing
4445
from multiprocessing import current_process
@@ -51,7 +52,8 @@
5152
#
5253
#
5354

54-
if not(sys.platform == 'win32' or hasattr(_multiprocessing, 'recvfd')):
55+
if not(sys.platform == 'win32' or (hasattr(socket, 'CMSG_LEN') and
56+
hasattr(socket, 'SCM_RIGHTS'))):
5557
raise ImportError('pickling of connections not supported')
5658

5759
#
@@ -77,10 +79,23 @@ def recv_handle(conn):
7779

7880
else:
7981
def send_handle(conn, handle, destination_pid):
80-
_multiprocessing.sendfd(conn.fileno(), handle)
82+
with socket.fromfd(conn.fileno(), socket.AF_UNIX, socket.SOCK_STREAM) as s:
83+
s.sendmsg([b'x'], [(socket.SOL_SOCKET, socket.SCM_RIGHTS,
84+
struct.pack("@i", handle))])
8185

8286
def recv_handle(conn):
83-
return _multiprocessing.recvfd(conn.fileno())
87+
size = struct.calcsize("@i")
88+
with socket.fromfd(conn.fileno(), socket.AF_UNIX, socket.SOCK_STREAM) as s:
89+
msg, ancdata, flags, addr = s.recvmsg(1, socket.CMSG_LEN(size))
90+
try:
91+
cmsg_level, cmsg_type, cmsg_data = ancdata[0]
92+
if (cmsg_level == socket.SOL_SOCKET and
93+
cmsg_type == socket.SCM_RIGHTS):
94+
return struct.unpack("@i", cmsg_data[:size])[0]
95+
except (ValueError, IndexError, struct.error):
96+
pass
97+
raise RuntimeError('Invalid data received')
98+
8499

85100
#
86101
# Support for a per-process server thread which caches pickled handles

Modules/_multiprocessing/multiprocessing.c

+1-127
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@
88

99
#include "multiprocessing.h"
1010

11-
#ifdef SCM_RIGHTS
12-
#define HAVE_FD_TRANSFER 1
13-
#else
14-
#define HAVE_FD_TRANSFER 0
15-
#endif
1611

1712
PyObject *create_win32_namespace(void);
1813

@@ -75,115 +70,7 @@ ProcessingCtrlHandler(DWORD dwCtrlType)
7570
return FALSE;
7671
}
7772

78-
/*
79-
* Unix only
80-
*/
81-
82-
#else /* !MS_WINDOWS */
83-
84-
#if HAVE_FD_TRANSFER
85-
86-
/* Functions for transferring file descriptors between processes.
87-
Reimplements some of the functionality of the fdcred
88-
module at http://www.mca-ltd.com/resources/fdcred_1.tgz. */
89-
/* Based in http://resin.csoft.net/cgi-bin/man.cgi?section=3&topic=CMSG_DATA */
90-
91-
static PyObject *
92-
multiprocessing_sendfd(PyObject *self, PyObject *args)
93-
{
94-
int conn, fd, res;
95-
struct iovec dummy_iov;
96-
char dummy_char;
97-
struct msghdr msg;
98-
struct cmsghdr *cmsg;
99-
union {
100-
struct cmsghdr hdr;
101-
unsigned char buf[CMSG_SPACE(sizeof(int))];
102-
} cmsgbuf;
103-
104-
if (!PyArg_ParseTuple(args, "ii", &conn, &fd))
105-
return NULL;
106-
107-
dummy_iov.iov_base = &dummy_char;
108-
dummy_iov.iov_len = 1;
109-
110-
memset(&msg, 0, sizeof(msg));
111-
msg.msg_control = &cmsgbuf.buf;
112-
msg.msg_controllen = sizeof(cmsgbuf.buf);
113-
msg.msg_iov = &dummy_iov;
114-
msg.msg_iovlen = 1;
115-
116-
cmsg = CMSG_FIRSTHDR(&msg);
117-
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
118-
cmsg->cmsg_level = SOL_SOCKET;
119-
cmsg->cmsg_type = SCM_RIGHTS;
120-
* (int *) CMSG_DATA(cmsg) = fd;
121-
122-
Py_BEGIN_ALLOW_THREADS
123-
res = sendmsg(conn, &msg, 0);
124-
Py_END_ALLOW_THREADS
125-
126-
if (res < 0)
127-
return PyErr_SetFromErrno(PyExc_OSError);
128-
Py_RETURN_NONE;
129-
}
130-
131-
static PyObject *
132-
multiprocessing_recvfd(PyObject *self, PyObject *args)
133-
{
134-
int conn, fd, res;
135-
char dummy_char;
136-
struct iovec dummy_iov;
137-
struct msghdr msg = {0};
138-
struct cmsghdr *cmsg;
139-
union {
140-
struct cmsghdr hdr;
141-
unsigned char buf[CMSG_SPACE(sizeof(int))];
142-
} cmsgbuf;
143-
144-
if (!PyArg_ParseTuple(args, "i", &conn))
145-
return NULL;
146-
147-
dummy_iov.iov_base = &dummy_char;
148-
dummy_iov.iov_len = 1;
149-
150-
memset(&msg, 0, sizeof(msg));
151-
msg.msg_control = &cmsgbuf.buf;
152-
msg.msg_controllen = sizeof(cmsgbuf.buf);
153-
msg.msg_iov = &dummy_iov;
154-
msg.msg_iovlen = 1;
155-
156-
cmsg = CMSG_FIRSTHDR(&msg);
157-
cmsg->cmsg_level = SOL_SOCKET;
158-
cmsg->cmsg_type = SCM_RIGHTS;
159-
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
160-
msg.msg_controllen = cmsg->cmsg_len;
161-
162-
Py_BEGIN_ALLOW_THREADS
163-
res = recvmsg(conn, &msg, 0);
164-
Py_END_ALLOW_THREADS
165-
166-
if (res < 0)
167-
return PyErr_SetFromErrno(PyExc_OSError);
168-
169-
if (msg.msg_controllen < CMSG_LEN(sizeof(int)) ||
170-
(cmsg = CMSG_FIRSTHDR(&msg)) == NULL ||
171-
cmsg->cmsg_level != SOL_SOCKET ||
172-
cmsg->cmsg_type != SCM_RIGHTS ||
173-
cmsg->cmsg_len < CMSG_LEN(sizeof(int))) {
174-
/* If at least one control message is present, there should be
175-
no room for any further data in the buffer. */
176-
PyErr_SetString(PyExc_RuntimeError, "No file descriptor received");
177-
return NULL;
178-
}
179-
180-
fd = * (int *) CMSG_DATA(cmsg);
181-
return Py_BuildValue("i", fd);
182-
}
183-
184-
#endif /* HAVE_FD_TRANSFER */
185-
186-
#endif /* !MS_WINDOWS */
73+
#endif /* MS_WINDOWS */
18774

18875

18976
/*
@@ -212,16 +99,6 @@ static PyMethodDef module_methods[] = {
21299
{"address_of_buffer", multiprocessing_address_of_buffer, METH_O,
213100
"address_of_buffer(obj) -> int\n"
214101
"Return address of obj assuming obj supports buffer inteface"},
215-
#if HAVE_FD_TRANSFER
216-
{"sendfd", multiprocessing_sendfd, METH_VARARGS,
217-
"sendfd(sockfd, fd) -> None\n"
218-
"Send file descriptor given by fd over the unix domain socket\n"
219-
"whose file decriptor is sockfd"},
220-
{"recvfd", multiprocessing_recvfd, METH_VARARGS,
221-
"recvfd(sockfd) -> fd\n"
222-
"Receive a file descriptor over a unix domain socket\n"
223-
"whose file decriptor is sockfd"},
224-
#endif
225102
{NULL}
226103
};
227104

@@ -319,9 +196,6 @@ PyInit__multiprocessing(void)
319196
#ifdef HAVE_SEM_TIMEDWAIT
320197
ADD_FLAG(HAVE_SEM_TIMEDWAIT);
321198
#endif
322-
#ifdef HAVE_FD_TRANSFER
323-
ADD_FLAG(HAVE_FD_TRANSFER);
324-
#endif
325199
#ifdef HAVE_BROKEN_SEM_GETVALUE
326200
ADD_FLAG(HAVE_BROKEN_SEM_GETVALUE);
327201
#endif

Modules/_multiprocessing/multiprocessing.h

-10
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@
33

44
#define PY_SSIZE_T_CLEAN
55

6-
#ifdef __sun
7-
/* The control message API is only available on Solaris
8-
if XPG 4.2 or later is requested. */
9-
#define _XOPEN_SOURCE 500
10-
#endif
11-
126
#include "Python.h"
137
#include "structmember.h"
148
#include "pythread.h"
@@ -29,10 +23,6 @@
2923
# define SEM_VALUE_MAX LONG_MAX
3024
#else
3125
# include <fcntl.h> /* O_CREAT and O_EXCL */
32-
# include <netinet/in.h>
33-
# include <sys/socket.h>
34-
# include <sys/uio.h>
35-
# include <arpa/inet.h> /* htonl() and ntohl() */
3626
# if defined(HAVE_SEM_OPEN) && !defined(POSIX_SEMAPHORES_NOT_ENABLED)
3727
# include <semaphore.h>
3828
typedef sem_t *SEM_HANDLE;

0 commit comments

Comments
 (0)