mirror of
https://github.com/python/cpython.git
synced 2025-08-31 14:07:50 +00:00
Issue #11743: Rewrite multiprocessing connection classes in pure Python.
This commit is contained in:
parent
df77e3d4a0
commit
87cf220972
15 changed files with 490 additions and 983 deletions
|
@ -1,527 +0,0 @@
|
|||
/*
|
||||
* Definition of a `Connection` type.
|
||||
* Used by `socket_connection.c` and `pipe_connection.c`.
|
||||
*
|
||||
* connection.h
|
||||
*
|
||||
* Copyright (c) 2006-2008, R Oudkerk --- see COPYING.txt
|
||||
*/
|
||||
|
||||
#ifndef CONNECTION_H
|
||||
#define CONNECTION_H
|
||||
|
||||
/*
|
||||
* Read/write flags
|
||||
*/
|
||||
|
||||
#define READABLE 1
|
||||
#define WRITABLE 2
|
||||
|
||||
#define CHECK_READABLE(self) \
|
||||
if (!(self->flags & READABLE)) { \
|
||||
PyErr_SetString(PyExc_IOError, "connection is write-only"); \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
#define CHECK_WRITABLE(self) \
|
||||
if (!(self->flags & WRITABLE)) { \
|
||||
PyErr_SetString(PyExc_IOError, "connection is read-only"); \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocation and deallocation
|
||||
*/
|
||||
|
||||
static PyObject *
|
||||
connection_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
ConnectionObject *self;
|
||||
HANDLE handle;
|
||||
BOOL readable = TRUE, writable = TRUE;
|
||||
|
||||
static char *kwlist[] = {"handle", "readable", "writable", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, F_HANDLE "|ii", kwlist,
|
||||
&handle, &readable, &writable))
|
||||
return NULL;
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE || (Py_ssize_t)handle < 0) {
|
||||
PyErr_Format(PyExc_IOError, "invalid handle %zd",
|
||||
(Py_ssize_t)handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!readable && !writable) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"either readable or writable must be true");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self = PyObject_New(ConnectionObject, type);
|
||||
if (self == NULL)
|
||||
return NULL;
|
||||
|
||||
self->weakreflist = NULL;
|
||||
self->handle = handle;
|
||||
self->flags = 0;
|
||||
|
||||
if (readable)
|
||||
self->flags |= READABLE;
|
||||
if (writable)
|
||||
self->flags |= WRITABLE;
|
||||
assert(self->flags >= 1 && self->flags <= 3);
|
||||
|
||||
return (PyObject*)self;
|
||||
}
|
||||
|
||||
static void
|
||||
connection_dealloc(ConnectionObject* self)
|
||||
{
|
||||
if (self->weakreflist != NULL)
|
||||
PyObject_ClearWeakRefs((PyObject*)self);
|
||||
|
||||
if (self->handle != INVALID_HANDLE_VALUE) {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
CLOSE(self->handle);
|
||||
Py_END_ALLOW_THREADS
|
||||
}
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions for transferring buffers
|
||||
*/
|
||||
|
||||
static PyObject *
|
||||
connection_sendbytes(ConnectionObject *self, PyObject *args)
|
||||
{
|
||||
Py_buffer pbuffer;
|
||||
char *buffer;
|
||||
Py_ssize_t length, offset=0, size=PY_SSIZE_T_MIN;
|
||||
int res;
|
||||
|
||||
if (!PyArg_ParseTuple(args, F_RBUFFER "*|" F_PY_SSIZE_T F_PY_SSIZE_T,
|
||||
&pbuffer, &offset, &size))
|
||||
return NULL;
|
||||
buffer = pbuffer.buf;
|
||||
length = pbuffer.len;
|
||||
|
||||
CHECK_WRITABLE(self); /* XXX release buffer in case of failure */
|
||||
|
||||
if (offset < 0) {
|
||||
PyBuffer_Release(&pbuffer);
|
||||
PyErr_SetString(PyExc_ValueError, "offset is negative");
|
||||
return NULL;
|
||||
}
|
||||
if (length < offset) {
|
||||
PyBuffer_Release(&pbuffer);
|
||||
PyErr_SetString(PyExc_ValueError, "buffer length < offset");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size == PY_SSIZE_T_MIN) {
|
||||
size = length - offset;
|
||||
} else {
|
||||
if (size < 0) {
|
||||
PyBuffer_Release(&pbuffer);
|
||||
PyErr_SetString(PyExc_ValueError, "size is negative");
|
||||
return NULL;
|
||||
}
|
||||
if (offset + size > length) {
|
||||
PyBuffer_Release(&pbuffer);
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"buffer length < offset + size");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
res = conn_send_string(self, buffer + offset, size);
|
||||
|
||||
PyBuffer_Release(&pbuffer);
|
||||
if (res < 0) {
|
||||
if (PyErr_Occurred())
|
||||
return NULL;
|
||||
else
|
||||
return mp_SetError(PyExc_IOError, res);
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
connection_recvbytes(ConnectionObject *self, PyObject *args)
|
||||
{
|
||||
char *freeme = NULL;
|
||||
Py_ssize_t res, maxlength = PY_SSIZE_T_MAX;
|
||||
PyObject *result = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|" F_PY_SSIZE_T, &maxlength))
|
||||
return NULL;
|
||||
|
||||
CHECK_READABLE(self);
|
||||
|
||||
if (maxlength < 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "maxlength < 0");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res = conn_recv_string(self, self->buffer, CONNECTION_BUFFER_SIZE,
|
||||
&freeme, maxlength);
|
||||
|
||||
if (res < 0) {
|
||||
if (res == MP_BAD_MESSAGE_LENGTH) {
|
||||
if ((self->flags & WRITABLE) == 0) {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
CLOSE(self->handle);
|
||||
Py_END_ALLOW_THREADS
|
||||
self->handle = INVALID_HANDLE_VALUE;
|
||||
} else {
|
||||
self->flags = WRITABLE;
|
||||
}
|
||||
}
|
||||
mp_SetError(PyExc_IOError, res);
|
||||
} else {
|
||||
if (freeme == NULL) {
|
||||
result = PyBytes_FromStringAndSize(self->buffer, res);
|
||||
} else {
|
||||
result = PyBytes_FromStringAndSize(freeme, res);
|
||||
PyMem_Free(freeme);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
connection_recvbytes_into(ConnectionObject *self, PyObject *args)
|
||||
{
|
||||
char *freeme = NULL, *buffer = NULL;
|
||||
Py_ssize_t res, length, offset = 0;
|
||||
PyObject *result = NULL;
|
||||
Py_buffer pbuf;
|
||||
|
||||
CHECK_READABLE(self);
|
||||
|
||||
if (!PyArg_ParseTuple(args, "w*|" F_PY_SSIZE_T,
|
||||
&pbuf, &offset))
|
||||
return NULL;
|
||||
|
||||
buffer = pbuf.buf;
|
||||
length = pbuf.len;
|
||||
|
||||
if (offset < 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "negative offset");
|
||||
goto _error;
|
||||
}
|
||||
|
||||
if (offset > length) {
|
||||
PyErr_SetString(PyExc_ValueError, "offset too large");
|
||||
goto _error;
|
||||
}
|
||||
|
||||
res = conn_recv_string(self, buffer+offset, length-offset,
|
||||
&freeme, PY_SSIZE_T_MAX);
|
||||
|
||||
if (res < 0) {
|
||||
if (res == MP_BAD_MESSAGE_LENGTH) {
|
||||
if ((self->flags & WRITABLE) == 0) {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
CLOSE(self->handle);
|
||||
Py_END_ALLOW_THREADS
|
||||
self->handle = INVALID_HANDLE_VALUE;
|
||||
} else {
|
||||
self->flags = WRITABLE;
|
||||
}
|
||||
}
|
||||
mp_SetError(PyExc_IOError, res);
|
||||
} else {
|
||||
if (freeme == NULL) {
|
||||
result = PyInt_FromSsize_t(res);
|
||||
} else {
|
||||
result = PyObject_CallFunction(BufferTooShort,
|
||||
F_RBUFFER "#",
|
||||
freeme, res);
|
||||
PyMem_Free(freeme);
|
||||
if (result) {
|
||||
PyErr_SetObject(BufferTooShort, result);
|
||||
Py_DECREF(result);
|
||||
}
|
||||
goto _error;
|
||||
}
|
||||
}
|
||||
|
||||
_cleanup:
|
||||
PyBuffer_Release(&pbuf);
|
||||
return result;
|
||||
|
||||
_error:
|
||||
result = NULL;
|
||||
goto _cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions for transferring objects
|
||||
*/
|
||||
|
||||
static PyObject *
|
||||
connection_send_obj(ConnectionObject *self, PyObject *obj)
|
||||
{
|
||||
char *buffer;
|
||||
int res;
|
||||
Py_ssize_t length;
|
||||
PyObject *pickled_string = NULL;
|
||||
|
||||
CHECK_WRITABLE(self);
|
||||
|
||||
pickled_string = PyObject_CallFunctionObjArgs(pickle_dumps, obj,
|
||||
pickle_protocol, NULL);
|
||||
if (!pickled_string)
|
||||
goto failure;
|
||||
|
||||
if (PyBytes_AsStringAndSize(pickled_string, &buffer, &length) < 0)
|
||||
goto failure;
|
||||
|
||||
res = conn_send_string(self, buffer, (int)length);
|
||||
|
||||
if (res < 0) {
|
||||
mp_SetError(PyExc_IOError, res);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
Py_XDECREF(pickled_string);
|
||||
Py_RETURN_NONE;
|
||||
|
||||
failure:
|
||||
Py_XDECREF(pickled_string);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
connection_recv_obj(ConnectionObject *self)
|
||||
{
|
||||
char *freeme = NULL;
|
||||
Py_ssize_t res;
|
||||
PyObject *temp = NULL, *result = NULL;
|
||||
|
||||
CHECK_READABLE(self);
|
||||
|
||||
res = conn_recv_string(self, self->buffer, CONNECTION_BUFFER_SIZE,
|
||||
&freeme, PY_SSIZE_T_MAX);
|
||||
|
||||
if (res < 0) {
|
||||
if (res == MP_BAD_MESSAGE_LENGTH) {
|
||||
if ((self->flags & WRITABLE) == 0) {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
CLOSE(self->handle);
|
||||
Py_END_ALLOW_THREADS
|
||||
self->handle = INVALID_HANDLE_VALUE;
|
||||
} else {
|
||||
self->flags = WRITABLE;
|
||||
}
|
||||
}
|
||||
mp_SetError(PyExc_IOError, res);
|
||||
} else {
|
||||
if (freeme == NULL) {
|
||||
temp = PyBytes_FromStringAndSize(self->buffer, res);
|
||||
} else {
|
||||
temp = PyBytes_FromStringAndSize(freeme, res);
|
||||
PyMem_Free(freeme);
|
||||
}
|
||||
}
|
||||
|
||||
if (temp)
|
||||
result = PyObject_CallFunctionObjArgs(pickle_loads,
|
||||
temp, NULL);
|
||||
Py_XDECREF(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Other functions
|
||||
*/
|
||||
|
||||
static PyObject *
|
||||
connection_poll(ConnectionObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *timeout_obj = NULL;
|
||||
double timeout = 0.0;
|
||||
int res;
|
||||
|
||||
CHECK_READABLE(self);
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|O", &timeout_obj))
|
||||
return NULL;
|
||||
|
||||
if (timeout_obj == NULL) {
|
||||
timeout = 0.0;
|
||||
} else if (timeout_obj == Py_None) {
|
||||
timeout = -1.0; /* block forever */
|
||||
} else {
|
||||
timeout = PyFloat_AsDouble(timeout_obj);
|
||||
if (PyErr_Occurred())
|
||||
return NULL;
|
||||
if (timeout < 0.0)
|
||||
timeout = 0.0;
|
||||
}
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
res = conn_poll(self, timeout, _save);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
switch (res) {
|
||||
case TRUE:
|
||||
Py_RETURN_TRUE;
|
||||
case FALSE:
|
||||
Py_RETURN_FALSE;
|
||||
default:
|
||||
return mp_SetError(PyExc_IOError, res);
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
connection_fileno(ConnectionObject* self)
|
||||
{
|
||||
if (self->handle == INVALID_HANDLE_VALUE) {
|
||||
PyErr_SetString(PyExc_IOError, "handle is invalid");
|
||||
return NULL;
|
||||
}
|
||||
return PyInt_FromLong((long)self->handle);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
connection_close(ConnectionObject *self)
|
||||
{
|
||||
if (self->handle != INVALID_HANDLE_VALUE) {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
CLOSE(self->handle);
|
||||
Py_END_ALLOW_THREADS
|
||||
self->handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
connection_repr(ConnectionObject *self)
|
||||
{
|
||||
static char *conn_type[] = {"read-only", "write-only", "read-write"};
|
||||
|
||||
assert(self->flags >= 1 && self->flags <= 3);
|
||||
return FROM_FORMAT("<%s %s, handle %zd>",
|
||||
conn_type[self->flags - 1],
|
||||
CONNECTION_NAME, (Py_ssize_t)self->handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Getters and setters
|
||||
*/
|
||||
|
||||
static PyObject *
|
||||
connection_closed(ConnectionObject *self, void *closure)
|
||||
{
|
||||
return PyBool_FromLong((long)(self->handle == INVALID_HANDLE_VALUE));
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
connection_readable(ConnectionObject *self, void *closure)
|
||||
{
|
||||
return PyBool_FromLong((long)(self->flags & READABLE));
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
connection_writable(ConnectionObject *self, void *closure)
|
||||
{
|
||||
return PyBool_FromLong((long)(self->flags & WRITABLE));
|
||||
}
|
||||
|
||||
/*
|
||||
* Tables
|
||||
*/
|
||||
|
||||
static PyMethodDef connection_methods[] = {
|
||||
{"send_bytes", (PyCFunction)connection_sendbytes, METH_VARARGS,
|
||||
"send the byte data from a readable buffer-like object"},
|
||||
{"recv_bytes", (PyCFunction)connection_recvbytes, METH_VARARGS,
|
||||
"receive byte data as a string"},
|
||||
{"recv_bytes_into",(PyCFunction)connection_recvbytes_into,METH_VARARGS,
|
||||
"receive byte data into a writeable buffer-like object\n"
|
||||
"returns the number of bytes read"},
|
||||
|
||||
{"send", (PyCFunction)connection_send_obj, METH_O,
|
||||
"send a (picklable) object"},
|
||||
{"recv", (PyCFunction)connection_recv_obj, METH_NOARGS,
|
||||
"receive a (picklable) object"},
|
||||
|
||||
{"poll", (PyCFunction)connection_poll, METH_VARARGS,
|
||||
"whether there is any input available to be read"},
|
||||
{"fileno", (PyCFunction)connection_fileno, METH_NOARGS,
|
||||
"file descriptor or handle of the connection"},
|
||||
{"close", (PyCFunction)connection_close, METH_NOARGS,
|
||||
"close the connection"},
|
||||
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyGetSetDef connection_getset[] = {
|
||||
{"closed", (getter)connection_closed, NULL,
|
||||
"True if the connection is closed", NULL},
|
||||
{"readable", (getter)connection_readable, NULL,
|
||||
"True if the connection is readable", NULL},
|
||||
{"writable", (getter)connection_writable, NULL,
|
||||
"True if the connection is writable", NULL},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
/*
|
||||
* Connection type
|
||||
*/
|
||||
|
||||
PyDoc_STRVAR(connection_doc,
|
||||
"Connection type whose constructor signature is\n\n"
|
||||
" Connection(handle, readable=True, writable=True).\n\n"
|
||||
"The constructor does *not* duplicate the handle.");
|
||||
|
||||
PyTypeObject CONNECTION_TYPE = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
/* tp_name */ "_multiprocessing." CONNECTION_NAME,
|
||||
/* tp_basicsize */ sizeof(ConnectionObject),
|
||||
/* tp_itemsize */ 0,
|
||||
/* tp_dealloc */ (destructor)connection_dealloc,
|
||||
/* tp_print */ 0,
|
||||
/* tp_getattr */ 0,
|
||||
/* tp_setattr */ 0,
|
||||
/* tp_reserved */ 0,
|
||||
/* tp_repr */ (reprfunc)connection_repr,
|
||||
/* tp_as_number */ 0,
|
||||
/* tp_as_sequence */ 0,
|
||||
/* tp_as_mapping */ 0,
|
||||
/* tp_hash */ 0,
|
||||
/* tp_call */ 0,
|
||||
/* tp_str */ 0,
|
||||
/* tp_getattro */ 0,
|
||||
/* tp_setattro */ 0,
|
||||
/* tp_as_buffer */ 0,
|
||||
/* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
|
||||
Py_TPFLAGS_HAVE_WEAKREFS,
|
||||
/* tp_doc */ connection_doc,
|
||||
/* tp_traverse */ 0,
|
||||
/* tp_clear */ 0,
|
||||
/* tp_richcompare */ 0,
|
||||
/* tp_weaklistoffset */ offsetof(ConnectionObject, weakreflist),
|
||||
/* tp_iter */ 0,
|
||||
/* tp_iternext */ 0,
|
||||
/* tp_methods */ connection_methods,
|
||||
/* tp_members */ 0,
|
||||
/* tp_getset */ connection_getset,
|
||||
/* tp_base */ 0,
|
||||
/* tp_dict */ 0,
|
||||
/* tp_descr_get */ 0,
|
||||
/* tp_descr_set */ 0,
|
||||
/* tp_dictoffset */ 0,
|
||||
/* tp_init */ 0,
|
||||
/* tp_alloc */ 0,
|
||||
/* tp_new */ connection_new,
|
||||
};
|
||||
|
||||
#endif /* CONNECTION_H */
|
|
@ -49,16 +49,6 @@ mp_SetError(PyObject *Type, int num)
|
|||
case MP_MEMORY_ERROR:
|
||||
PyErr_NoMemory();
|
||||
break;
|
||||
case MP_END_OF_FILE:
|
||||
PyErr_SetNone(PyExc_EOFError);
|
||||
break;
|
||||
case MP_EARLY_END_OF_FILE:
|
||||
PyErr_SetString(PyExc_IOError,
|
||||
"got end of file during message");
|
||||
break;
|
||||
case MP_BAD_MESSAGE_LENGTH:
|
||||
PyErr_SetString(PyExc_IOError, "bad message length");
|
||||
break;
|
||||
case MP_EXCEPTION_HAS_BEEN_SET:
|
||||
break;
|
||||
default:
|
||||
|
@ -257,12 +247,6 @@ PyInit__multiprocessing(void)
|
|||
BufferTooShort = PyObject_GetAttrString(temp, "BufferTooShort");
|
||||
Py_XDECREF(temp);
|
||||
|
||||
/* Add connection type to module */
|
||||
if (PyType_Ready(&ConnectionType) < 0)
|
||||
return NULL;
|
||||
Py_INCREF(&ConnectionType);
|
||||
PyModule_AddObject(module, "Connection", (PyObject*)&ConnectionType);
|
||||
|
||||
#if defined(MS_WINDOWS) || \
|
||||
(defined(HAVE_SEM_OPEN) && !defined(POSIX_SEMAPHORES_NOT_ENABLED))
|
||||
/* Add SemLock type to module */
|
||||
|
@ -286,13 +270,6 @@ PyInit__multiprocessing(void)
|
|||
#endif
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
/* Add PipeConnection to module */
|
||||
if (PyType_Ready(&PipeConnectionType) < 0)
|
||||
return NULL;
|
||||
Py_INCREF(&PipeConnectionType);
|
||||
PyModule_AddObject(module, "PipeConnection",
|
||||
(PyObject*)&PipeConnectionType);
|
||||
|
||||
/* Initialize win32 class and add to multiprocessing */
|
||||
temp = create_win32_namespace();
|
||||
if (!temp)
|
||||
|
|
|
@ -118,11 +118,8 @@
|
|||
#define MP_SUCCESS (0)
|
||||
#define MP_STANDARD_ERROR (-1)
|
||||
#define MP_MEMORY_ERROR (-1001)
|
||||
#define MP_END_OF_FILE (-1002)
|
||||
#define MP_EARLY_END_OF_FILE (-1003)
|
||||
#define MP_BAD_MESSAGE_LENGTH (-1004)
|
||||
#define MP_SOCKET_ERROR (-1005)
|
||||
#define MP_EXCEPTION_HAS_BEEN_SET (-1006)
|
||||
#define MP_SOCKET_ERROR (-1002)
|
||||
#define MP_EXCEPTION_HAS_BEEN_SET (-1003)
|
||||
|
||||
PyObject *mp_SetError(PyObject *Type, int num);
|
||||
|
||||
|
@ -135,7 +132,6 @@ extern PyObject *pickle_loads;
|
|||
extern PyObject *pickle_protocol;
|
||||
extern PyObject *BufferTooShort;
|
||||
extern PyTypeObject SemLockType;
|
||||
extern PyTypeObject ConnectionType;
|
||||
extern PyTypeObject PipeConnectionType;
|
||||
extern HANDLE sigint_event;
|
||||
|
||||
|
@ -161,26 +157,10 @@ extern HANDLE sigint_event;
|
|||
# define Py_TPFLAGS_HAVE_WEAKREFS 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Connection definition
|
||||
*/
|
||||
|
||||
#define CONNECTION_BUFFER_SIZE 1024
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
HANDLE handle;
|
||||
int flags;
|
||||
PyObject *weakreflist;
|
||||
char buffer[CONNECTION_BUFFER_SIZE];
|
||||
} ConnectionObject;
|
||||
|
||||
/*
|
||||
* Miscellaneous
|
||||
*/
|
||||
|
||||
#define MAX_MESSAGE_LENGTH 0x7fffffff
|
||||
|
||||
#ifndef MIN
|
||||
# define MIN(x, y) ((x) < (y) ? x : y)
|
||||
# define MAX(x, y) ((x) > (y) ? x : y)
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* A type which wraps a pipe handle in message oriented mode
|
||||
*
|
||||
* pipe_connection.c
|
||||
*
|
||||
* Copyright (c) 2006-2008, R Oudkerk --- see COPYING.txt
|
||||
*/
|
||||
|
||||
#include "multiprocessing.h"
|
||||
|
||||
#define CLOSE(h) CloseHandle(h)
|
||||
|
||||
/*
|
||||
* Send string to the pipe; assumes in message oriented mode
|
||||
*/
|
||||
|
||||
static Py_ssize_t
|
||||
conn_send_string(ConnectionObject *conn, char *string, size_t length)
|
||||
{
|
||||
DWORD amount_written;
|
||||
BOOL ret;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
ret = WriteFile(conn->handle, string, length, &amount_written, NULL);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
if (ret == 0 && GetLastError() == ERROR_NO_SYSTEM_RESOURCES) {
|
||||
PyErr_Format(PyExc_ValueError, "Cannnot send %" PY_FORMAT_SIZE_T "d bytes over connection", length);
|
||||
return MP_STANDARD_ERROR;
|
||||
}
|
||||
|
||||
return ret ? MP_SUCCESS : MP_STANDARD_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to read into buffer, or if buffer too small into *newbuffer.
|
||||
*
|
||||
* Returns number of bytes read. Assumes in message oriented mode.
|
||||
*/
|
||||
|
||||
static Py_ssize_t
|
||||
conn_recv_string(ConnectionObject *conn, char *buffer,
|
||||
size_t buflength, char **newbuffer, size_t maxlength)
|
||||
{
|
||||
DWORD left, length, full_length, err;
|
||||
BOOL ret;
|
||||
*newbuffer = NULL;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
ret = ReadFile(conn->handle, buffer, MIN(buflength, maxlength),
|
||||
&length, NULL);
|
||||
Py_END_ALLOW_THREADS
|
||||
if (ret)
|
||||
return length;
|
||||
|
||||
err = GetLastError();
|
||||
if (err != ERROR_MORE_DATA) {
|
||||
if (err == ERROR_BROKEN_PIPE)
|
||||
return MP_END_OF_FILE;
|
||||
return MP_STANDARD_ERROR;
|
||||
}
|
||||
|
||||
if (!PeekNamedPipe(conn->handle, NULL, 0, NULL, NULL, &left))
|
||||
return MP_STANDARD_ERROR;
|
||||
|
||||
full_length = length + left;
|
||||
if (full_length > maxlength)
|
||||
return MP_BAD_MESSAGE_LENGTH;
|
||||
|
||||
*newbuffer = PyMem_Malloc(full_length);
|
||||
if (*newbuffer == NULL)
|
||||
return MP_MEMORY_ERROR;
|
||||
|
||||
memcpy(*newbuffer, buffer, length);
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
ret = ReadFile(conn->handle, *newbuffer+length, left, &length, NULL);
|
||||
Py_END_ALLOW_THREADS
|
||||
if (ret) {
|
||||
assert(length == left);
|
||||
return full_length;
|
||||
} else {
|
||||
PyMem_Free(*newbuffer);
|
||||
return MP_STANDARD_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether any data is available for reading
|
||||
*/
|
||||
|
||||
static int
|
||||
conn_poll(ConnectionObject *conn, double timeout, PyThreadState *_save)
|
||||
{
|
||||
DWORD bytes, deadline, delay;
|
||||
int difference, res;
|
||||
BOOL block = FALSE;
|
||||
|
||||
if (!PeekNamedPipe(conn->handle, NULL, 0, NULL, &bytes, NULL))
|
||||
return MP_STANDARD_ERROR;
|
||||
|
||||
if (timeout == 0.0)
|
||||
return bytes > 0;
|
||||
|
||||
if (timeout < 0.0)
|
||||
block = TRUE;
|
||||
else
|
||||
/* XXX does not check for overflow */
|
||||
deadline = GetTickCount() + (DWORD)(1000 * timeout + 0.5);
|
||||
|
||||
Sleep(0);
|
||||
|
||||
for (delay = 1 ; ; delay += 1) {
|
||||
if (!PeekNamedPipe(conn->handle, NULL, 0, NULL, &bytes, NULL))
|
||||
return MP_STANDARD_ERROR;
|
||||
else if (bytes > 0)
|
||||
return TRUE;
|
||||
|
||||
if (!block) {
|
||||
difference = deadline - GetTickCount();
|
||||
if (difference < 0)
|
||||
return FALSE;
|
||||
if ((int)delay > difference)
|
||||
delay = difference;
|
||||
}
|
||||
|
||||
if (delay > 20)
|
||||
delay = 20;
|
||||
|
||||
Sleep(delay);
|
||||
|
||||
/* check for signals */
|
||||
Py_BLOCK_THREADS
|
||||
res = PyErr_CheckSignals();
|
||||
Py_UNBLOCK_THREADS
|
||||
|
||||
if (res)
|
||||
return MP_EXCEPTION_HAS_BEEN_SET;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "connection.h" defines the PipeConnection type using the definitions above
|
||||
*/
|
||||
|
||||
#define CONNECTION_NAME "PipeConnection"
|
||||
#define CONNECTION_TYPE PipeConnectionType
|
||||
|
||||
#include "connection.h"
|
|
@ -1,202 +0,0 @@
|
|||
/*
|
||||
* A type which wraps a socket
|
||||
*
|
||||
* socket_connection.c
|
||||
*
|
||||
* Copyright (c) 2006-2008, R Oudkerk --- see COPYING.txt
|
||||
*/
|
||||
|
||||
#include "multiprocessing.h"
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
# define WRITE(h, buffer, length) send((SOCKET)h, buffer, length, 0)
|
||||
# define READ(h, buffer, length) recv((SOCKET)h, buffer, length, 0)
|
||||
# define CLOSE(h) closesocket((SOCKET)h)
|
||||
#else
|
||||
# define WRITE(h, buffer, length) write(h, buffer, length)
|
||||
# define READ(h, buffer, length) read(h, buffer, length)
|
||||
# define CLOSE(h) close(h)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Send string to file descriptor
|
||||
*/
|
||||
|
||||
static Py_ssize_t
|
||||
_conn_sendall(HANDLE h, char *string, size_t length)
|
||||
{
|
||||
char *p = string;
|
||||
Py_ssize_t res;
|
||||
|
||||
while (length > 0) {
|
||||
res = WRITE(h, p, length);
|
||||
if (res < 0)
|
||||
return MP_SOCKET_ERROR;
|
||||
length -= res;
|
||||
p += res;
|
||||
}
|
||||
|
||||
return MP_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive string of exact length from file descriptor
|
||||
*/
|
||||
|
||||
static Py_ssize_t
|
||||
_conn_recvall(HANDLE h, char *buffer, size_t length)
|
||||
{
|
||||
size_t remaining = length;
|
||||
Py_ssize_t temp;
|
||||
char *p = buffer;
|
||||
|
||||
while (remaining > 0) {
|
||||
temp = READ(h, p, remaining);
|
||||
if (temp <= 0) {
|
||||
if (temp == 0)
|
||||
return remaining == length ?
|
||||
MP_END_OF_FILE : MP_EARLY_END_OF_FILE;
|
||||
else
|
||||
return temp;
|
||||
}
|
||||
remaining -= temp;
|
||||
p += temp;
|
||||
}
|
||||
|
||||
return MP_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a string prepended by the string length in network byte order
|
||||
*/
|
||||
|
||||
static Py_ssize_t
|
||||
conn_send_string(ConnectionObject *conn, char *string, size_t length)
|
||||
{
|
||||
Py_ssize_t res;
|
||||
/* The "header" of the message is a 32 bit unsigned number (in
|
||||
network order) which specifies the length of the "body". If
|
||||
the message is shorter than about 16kb then it is quicker to
|
||||
combine the "header" and the "body" of the message and send
|
||||
them at once. */
|
||||
if (length < (16*1024)) {
|
||||
char *message;
|
||||
|
||||
message = PyMem_Malloc(length+4);
|
||||
if (message == NULL)
|
||||
return MP_MEMORY_ERROR;
|
||||
|
||||
*(UINT32*)message = htonl((UINT32)length);
|
||||
memcpy(message+4, string, length);
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
res = _conn_sendall(conn->handle, message, length+4);
|
||||
Py_END_ALLOW_THREADS
|
||||
PyMem_Free(message);
|
||||
} else {
|
||||
UINT32 lenbuff;
|
||||
|
||||
if (length > MAX_MESSAGE_LENGTH)
|
||||
return MP_BAD_MESSAGE_LENGTH;
|
||||
|
||||
lenbuff = htonl((UINT32)length);
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
res = _conn_sendall(conn->handle, (char*)&lenbuff, 4) ||
|
||||
_conn_sendall(conn->handle, string, length);
|
||||
Py_END_ALLOW_THREADS
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to read into buffer, or failing that into *newbuffer
|
||||
*
|
||||
* Returns number of bytes read.
|
||||
*/
|
||||
|
||||
static Py_ssize_t
|
||||
conn_recv_string(ConnectionObject *conn, char *buffer,
|
||||
size_t buflength, char **newbuffer, size_t maxlength)
|
||||
{
|
||||
int res;
|
||||
UINT32 ulength;
|
||||
|
||||
*newbuffer = NULL;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
res = _conn_recvall(conn->handle, (char*)&ulength, 4);
|
||||
Py_END_ALLOW_THREADS
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
ulength = ntohl(ulength);
|
||||
if (ulength > maxlength)
|
||||
return MP_BAD_MESSAGE_LENGTH;
|
||||
|
||||
if (ulength <= buflength) {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
res = _conn_recvall(conn->handle, buffer, (size_t)ulength);
|
||||
Py_END_ALLOW_THREADS
|
||||
return res < 0 ? res : ulength;
|
||||
} else {
|
||||
*newbuffer = PyMem_Malloc((size_t)ulength);
|
||||
if (*newbuffer == NULL)
|
||||
return MP_MEMORY_ERROR;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
res = _conn_recvall(conn->handle, *newbuffer, (size_t)ulength);
|
||||
Py_END_ALLOW_THREADS
|
||||
return res < 0 ? (Py_ssize_t)res : (Py_ssize_t)ulength;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether any data is available for reading -- neg timeout blocks
|
||||
*/
|
||||
|
||||
static int
|
||||
conn_poll(ConnectionObject *conn, double timeout, PyThreadState *_save)
|
||||
{
|
||||
int res;
|
||||
fd_set rfds;
|
||||
|
||||
/*
|
||||
* Verify the handle, issue 3321. Not required for windows.
|
||||
*/
|
||||
#ifndef MS_WINDOWS
|
||||
if (((int)conn->handle) < 0 || ((int)conn->handle) >= FD_SETSIZE) {
|
||||
Py_BLOCK_THREADS
|
||||
PyErr_SetString(PyExc_IOError, "handle out of range in select()");
|
||||
Py_UNBLOCK_THREADS
|
||||
return MP_EXCEPTION_HAS_BEEN_SET;
|
||||
}
|
||||
#endif
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET((SOCKET)conn->handle, &rfds);
|
||||
|
||||
if (timeout < 0.0) {
|
||||
res = select((int)conn->handle+1, &rfds, NULL, NULL, NULL);
|
||||
} else {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = (long)timeout;
|
||||
tv.tv_usec = (long)((timeout - tv.tv_sec) * 1e6 + 0.5);
|
||||
res = select((int)conn->handle+1, &rfds, NULL, NULL, &tv);
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
return MP_SOCKET_ERROR;
|
||||
} else if (FD_ISSET(conn->handle, &rfds)) {
|
||||
return TRUE;
|
||||
} else {
|
||||
assert(res == 0);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "connection.h" defines the Connection type using defs above
|
||||
*/
|
||||
|
||||
#define CONNECTION_NAME "Connection"
|
||||
#define CONNECTION_TYPE ConnectionType
|
||||
|
||||
#include "connection.h"
|
|
@ -215,6 +215,164 @@ win32_WaitNamedPipe(PyObject *self, PyObject *args)
|
|||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
win32_closesocket(PyObject *self, PyObject *args)
|
||||
{
|
||||
HANDLE handle;
|
||||
int ret;
|
||||
|
||||
if (!PyArg_ParseTuple(args, F_HANDLE ":closesocket" , &handle))
|
||||
return NULL;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
ret = closesocket((SOCKET) handle);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
if (ret)
|
||||
return PyErr_SetExcFromWindowsErr(PyExc_IOError, WSAGetLastError());
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
win32_recv(PyObject *self, PyObject *args)
|
||||
{
|
||||
HANDLE handle;
|
||||
int size, nread;
|
||||
PyObject *buf;
|
||||
|
||||
if (!PyArg_ParseTuple(args, F_HANDLE "i:recv" , &handle, &size))
|
||||
return NULL;
|
||||
|
||||
buf = PyBytes_FromStringAndSize(NULL, size);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
nread = recv((SOCKET) handle, PyBytes_AS_STRING(buf), size, 0);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
if (nread < 0) {
|
||||
Py_DECREF(buf);
|
||||
return PyErr_SetExcFromWindowsErr(PyExc_IOError, WSAGetLastError());
|
||||
}
|
||||
_PyBytes_Resize(&buf, nread);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
win32_send(PyObject *self, PyObject *args)
|
||||
{
|
||||
HANDLE handle;
|
||||
Py_buffer buf;
|
||||
int ret;
|
||||
|
||||
if (!PyArg_ParseTuple(args, F_HANDLE "y*:send" , &handle, &buf))
|
||||
return NULL;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
ret = send((SOCKET) handle, buf.buf, buf.len, 0);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
PyBuffer_Release(&buf);
|
||||
if (ret < 0)
|
||||
return PyErr_SetExcFromWindowsErr(PyExc_IOError, WSAGetLastError());
|
||||
return PyLong_FromLong(ret);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
win32_WriteFile(PyObject *self, PyObject *args)
|
||||
{
|
||||
HANDLE handle;
|
||||
Py_buffer buf;
|
||||
int written;
|
||||
BOOL ret;
|
||||
|
||||
if (!PyArg_ParseTuple(args, F_HANDLE "y*:WriteFile" , &handle, &buf))
|
||||
return NULL;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
ret = WriteFile(handle, buf.buf, buf.len, &written, NULL);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
PyBuffer_Release(&buf);
|
||||
if (!ret)
|
||||
return PyErr_SetExcFromWindowsErr(PyExc_IOError, 0);
|
||||
return PyLong_FromLong(written);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
win32_ReadFile(PyObject *self, PyObject *args)
|
||||
{
|
||||
HANDLE handle;
|
||||
int size;
|
||||
DWORD nread;
|
||||
PyObject *buf;
|
||||
BOOL ret;
|
||||
|
||||
if (!PyArg_ParseTuple(args, F_HANDLE "i:ReadFile" , &handle, &size))
|
||||
return NULL;
|
||||
|
||||
buf = PyBytes_FromStringAndSize(NULL, size);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
ret = ReadFile(handle, PyBytes_AS_STRING(buf), size, &nread, NULL);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
if (!ret && GetLastError() != ERROR_MORE_DATA) {
|
||||
Py_DECREF(buf);
|
||||
return PyErr_SetExcFromWindowsErr(PyExc_IOError, 0);
|
||||
}
|
||||
if (_PyBytes_Resize(&buf, nread))
|
||||
return NULL;
|
||||
return Py_BuildValue("NN", buf, PyBool_FromLong(ret));
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
win32_PeekNamedPipe(PyObject *self, PyObject *args)
|
||||
{
|
||||
HANDLE handle;
|
||||
int size = 0;
|
||||
PyObject *buf = NULL;
|
||||
DWORD nread, navail, nleft;
|
||||
BOOL ret;
|
||||
|
||||
if (!PyArg_ParseTuple(args, F_HANDLE "|i:PeekNamedPipe" , &handle, &size))
|
||||
return NULL;
|
||||
|
||||
if (size < 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "negative size");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size) {
|
||||
buf = PyBytes_FromStringAndSize(NULL, size);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
ret = PeekNamedPipe(handle, PyBytes_AS_STRING(buf), size, &nread,
|
||||
&navail, &nleft);
|
||||
Py_END_ALLOW_THREADS
|
||||
if (!ret) {
|
||||
Py_DECREF(buf);
|
||||
return PyErr_SetExcFromWindowsErr(PyExc_IOError, 0);
|
||||
}
|
||||
if (_PyBytes_Resize(&buf, nread))
|
||||
return NULL;
|
||||
return Py_BuildValue("Nii", buf, navail, nleft);
|
||||
}
|
||||
else {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
ret = PeekNamedPipe(handle, NULL, 0, NULL, &navail, &nleft);
|
||||
Py_END_ALLOW_THREADS
|
||||
if (!ret) {
|
||||
return PyErr_SetExcFromWindowsErr(PyExc_IOError, 0);
|
||||
}
|
||||
return Py_BuildValue("ii", navail, nleft);
|
||||
}
|
||||
}
|
||||
|
||||
static PyMethodDef win32_methods[] = {
|
||||
WIN32_FUNCTION(CloseHandle),
|
||||
WIN32_FUNCTION(GetLastError),
|
||||
|
@ -223,8 +381,14 @@ static PyMethodDef win32_methods[] = {
|
|||
WIN32_FUNCTION(ConnectNamedPipe),
|
||||
WIN32_FUNCTION(CreateFile),
|
||||
WIN32_FUNCTION(CreateNamedPipe),
|
||||
WIN32_FUNCTION(ReadFile),
|
||||
WIN32_FUNCTION(PeekNamedPipe),
|
||||
WIN32_FUNCTION(SetNamedPipeHandleState),
|
||||
WIN32_FUNCTION(WaitNamedPipe),
|
||||
WIN32_FUNCTION(WriteFile),
|
||||
WIN32_FUNCTION(closesocket),
|
||||
WIN32_FUNCTION(recv),
|
||||
WIN32_FUNCTION(send),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
@ -244,6 +408,8 @@ create_win32_namespace(void)
|
|||
Py_INCREF(&Win32Type);
|
||||
|
||||
WIN32_CONSTANT(F_DWORD, ERROR_ALREADY_EXISTS);
|
||||
WIN32_CONSTANT(F_DWORD, ERROR_BROKEN_PIPE);
|
||||
WIN32_CONSTANT(F_DWORD, ERROR_NO_SYSTEM_RESOURCES);
|
||||
WIN32_CONSTANT(F_DWORD, ERROR_PIPE_BUSY);
|
||||
WIN32_CONSTANT(F_DWORD, ERROR_PIPE_CONNECTED);
|
||||
WIN32_CONSTANT(F_DWORD, ERROR_SEM_TIMEOUT);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue