blob: 581beacf9f6aa6bf242012e43dbb9913911f569f [file] [log] [blame]
Benjamin Petersone711caf2008-06-11 16:44:04 +00001/*
2 * Definition of a `Connection` type.
3 * Used by `socket_connection.c` and `pipe_connection.c`.
4 *
5 * connection.h
6 *
7 * Copyright (c) 2006-2008, R Oudkerk --- see COPYING.txt
8 */
9
10#ifndef CONNECTION_H
11#define CONNECTION_H
12
13/*
14 * Read/write flags
15 */
16
17#define READABLE 1
18#define WRITABLE 2
19
20#define CHECK_READABLE(self) \
21 if (!(self->flags & READABLE)) { \
22 PyErr_SetString(PyExc_IOError, "connection is write-only"); \
23 return NULL; \
24 }
25
26#define CHECK_WRITABLE(self) \
27 if (!(self->flags & WRITABLE)) { \
28 PyErr_SetString(PyExc_IOError, "connection is read-only"); \
29 return NULL; \
30 }
31
32/*
33 * Allocation and deallocation
34 */
35
36static PyObject *
37connection_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
38{
39 ConnectionObject *self;
40 HANDLE handle;
41 BOOL readable = TRUE, writable = TRUE;
42
43 static char *kwlist[] = {"handle", "readable", "writable", NULL};
44
45 if (!PyArg_ParseTupleAndKeywords(args, kwds, F_HANDLE "|ii", kwlist,
46 &handle, &readable, &writable))
47 return NULL;
48
49 if (handle == INVALID_HANDLE_VALUE || (Py_ssize_t)handle < 0) {
Amaury Forgeot d'Arc245c70b2008-09-10 22:24:24 +000050 PyErr_Format(PyExc_IOError, "invalid handle %zd",
51 (Py_ssize_t)handle);
Benjamin Petersone711caf2008-06-11 16:44:04 +000052 return NULL;
53 }
54
55 if (!readable && !writable) {
56 PyErr_SetString(PyExc_ValueError,
57 "either readable or writable must be true");
58 return NULL;
59 }
60
61 self = PyObject_New(ConnectionObject, type);
62 if (self == NULL)
63 return NULL;
64
65 self->weakreflist = NULL;
66 self->handle = handle;
67 self->flags = 0;
68
69 if (readable)
70 self->flags |= READABLE;
71 if (writable)
72 self->flags |= WRITABLE;
73 assert(self->flags >= 1 && self->flags <= 3);
74
75 return (PyObject*)self;
76}
77
78static void
79connection_dealloc(ConnectionObject* self)
80{
81 if (self->weakreflist != NULL)
82 PyObject_ClearWeakRefs((PyObject*)self);
83
84 if (self->handle != INVALID_HANDLE_VALUE) {
85 Py_BEGIN_ALLOW_THREADS
86 CLOSE(self->handle);
87 Py_END_ALLOW_THREADS
88 }
89 PyObject_Del(self);
90}
91
92/*
93 * Functions for transferring buffers
94 */
95
96static PyObject *
97connection_sendbytes(ConnectionObject *self, PyObject *args)
98{
Martin v. Löwis423be952008-08-13 15:53:07 +000099 Py_buffer pbuffer;
Benjamin Petersone711caf2008-06-11 16:44:04 +0000100 char *buffer;
101 Py_ssize_t length, offset=0, size=PY_SSIZE_T_MIN;
102 int res;
103
Martin v. Löwis423be952008-08-13 15:53:07 +0000104 if (!PyArg_ParseTuple(args, F_RBUFFER "*|" F_PY_SSIZE_T F_PY_SSIZE_T,
105 &pbuffer, &offset, &size))
Benjamin Petersone711caf2008-06-11 16:44:04 +0000106 return NULL;
Martin v. Löwis423be952008-08-13 15:53:07 +0000107 buffer = pbuffer.buf;
108 length = pbuffer.len;
Benjamin Petersone711caf2008-06-11 16:44:04 +0000109
Martin v. Löwis423be952008-08-13 15:53:07 +0000110 CHECK_WRITABLE(self); /* XXX release buffer in case of failure */
Benjamin Petersone711caf2008-06-11 16:44:04 +0000111
112 if (offset < 0) {
Martin v. Löwis423be952008-08-13 15:53:07 +0000113 PyBuffer_Release(&pbuffer);
Benjamin Petersone711caf2008-06-11 16:44:04 +0000114 PyErr_SetString(PyExc_ValueError, "offset is negative");
115 return NULL;
116 }
117 if (length < offset) {
Martin v. Löwis423be952008-08-13 15:53:07 +0000118 PyBuffer_Release(&pbuffer);
Benjamin Petersone711caf2008-06-11 16:44:04 +0000119 PyErr_SetString(PyExc_ValueError, "buffer length < offset");
120 return NULL;
121 }
122
123 if (size == PY_SSIZE_T_MIN) {
124 size = length - offset;
125 } else {
126 if (size < 0) {
Martin v. Löwis423be952008-08-13 15:53:07 +0000127 PyBuffer_Release(&pbuffer);
Benjamin Petersone711caf2008-06-11 16:44:04 +0000128 PyErr_SetString(PyExc_ValueError, "size is negative");
129 return NULL;
130 }
131 if (offset + size > length) {
Martin v. Löwis423be952008-08-13 15:53:07 +0000132 PyBuffer_Release(&pbuffer);
Benjamin Petersone711caf2008-06-11 16:44:04 +0000133 PyErr_SetString(PyExc_ValueError,
134 "buffer length < offset + size");
135 return NULL;
136 }
137 }
138
Benjamin Petersone711caf2008-06-11 16:44:04 +0000139 res = conn_send_string(self, buffer + offset, size);
Benjamin Petersone711caf2008-06-11 16:44:04 +0000140
Martin v. Löwis423be952008-08-13 15:53:07 +0000141 PyBuffer_Release(&pbuffer);
Benjamin Petersone711caf2008-06-11 16:44:04 +0000142 if (res < 0)
143 return mp_SetError(PyExc_IOError, res);
144
145 Py_RETURN_NONE;
146}
147
148static PyObject *
149connection_recvbytes(ConnectionObject *self, PyObject *args)
150{
151 char *freeme = NULL;
152 Py_ssize_t res, maxlength = PY_SSIZE_T_MAX;
153 PyObject *result = NULL;
154
155 if (!PyArg_ParseTuple(args, "|" F_PY_SSIZE_T, &maxlength))
156 return NULL;
157
158 CHECK_READABLE(self);
159
160 if (maxlength < 0) {
161 PyErr_SetString(PyExc_ValueError, "maxlength < 0");
162 return NULL;
163 }
164
Benjamin Petersone711caf2008-06-11 16:44:04 +0000165 res = conn_recv_string(self, self->buffer, CONNECTION_BUFFER_SIZE,
166 &freeme, maxlength);
Benjamin Petersone711caf2008-06-11 16:44:04 +0000167
168 if (res < 0) {
169 if (res == MP_BAD_MESSAGE_LENGTH) {
170 if ((self->flags & WRITABLE) == 0) {
171 Py_BEGIN_ALLOW_THREADS
172 CLOSE(self->handle);
173 Py_END_ALLOW_THREADS
174 self->handle = INVALID_HANDLE_VALUE;
175 } else {
176 self->flags = WRITABLE;
177 }
178 }
179 mp_SetError(PyExc_IOError, res);
180 } else {
181 if (freeme == NULL) {
182 result = PyBytes_FromStringAndSize(self->buffer, res);
183 } else {
184 result = PyBytes_FromStringAndSize(freeme, res);
185 PyMem_Free(freeme);
186 }
187 }
188
189 return result;
190}
191
192static PyObject *
193connection_recvbytes_into(ConnectionObject *self, PyObject *args)
194{
195 char *freeme = NULL, *buffer = NULL;
196 Py_ssize_t res, length, offset = 0;
197 PyObject *result = NULL;
Martin v. Löwis423be952008-08-13 15:53:07 +0000198 Py_buffer pbuf;
Benjamin Petersone711caf2008-06-11 16:44:04 +0000199
200 CHECK_READABLE(self);
Martin v. Löwis423be952008-08-13 15:53:07 +0000201
202 if (!PyArg_ParseTuple(args, "w*|" F_PY_SSIZE_T,
203 &pbuf, &offset))
204 return NULL;
205
206 buffer = pbuf.buf;
207 length = pbuf.len;
Benjamin Petersone711caf2008-06-11 16:44:04 +0000208
209 if (offset < 0) {
210 PyErr_SetString(PyExc_ValueError, "negative offset");
Martin v. Löwis423be952008-08-13 15:53:07 +0000211 goto _error;
Benjamin Petersone711caf2008-06-11 16:44:04 +0000212 }
213
214 if (offset > length) {
215 PyErr_SetString(PyExc_ValueError, "offset too large");
Martin v. Löwis423be952008-08-13 15:53:07 +0000216 goto _error;
Benjamin Petersone711caf2008-06-11 16:44:04 +0000217 }
218
Benjamin Petersone711caf2008-06-11 16:44:04 +0000219 res = conn_recv_string(self, buffer+offset, length-offset,
220 &freeme, PY_SSIZE_T_MAX);
Benjamin Petersone711caf2008-06-11 16:44:04 +0000221
222 if (res < 0) {
223 if (res == MP_BAD_MESSAGE_LENGTH) {
224 if ((self->flags & WRITABLE) == 0) {
225 Py_BEGIN_ALLOW_THREADS
226 CLOSE(self->handle);
227 Py_END_ALLOW_THREADS
228 self->handle = INVALID_HANDLE_VALUE;
229 } else {
230 self->flags = WRITABLE;
231 }
232 }
233 mp_SetError(PyExc_IOError, res);
234 } else {
235 if (freeme == NULL) {
236 result = PyInt_FromSsize_t(res);
237 } else {
238 result = PyObject_CallFunction(BufferTooShort,
239 F_RBUFFER "#",
240 freeme, res);
241 PyMem_Free(freeme);
242 if (result) {
243 PyErr_SetObject(BufferTooShort, result);
244 Py_DECREF(result);
245 }
Martin v. Löwis423be952008-08-13 15:53:07 +0000246 goto _error;
Benjamin Petersone711caf2008-06-11 16:44:04 +0000247 }
248 }
249
Martin v. Löwis423be952008-08-13 15:53:07 +0000250_cleanup:
251 PyBuffer_Release(&pbuf);
Benjamin Petersone711caf2008-06-11 16:44:04 +0000252 return result;
Martin v. Löwis423be952008-08-13 15:53:07 +0000253
254_error:
255 result = NULL;
256 goto _cleanup;
Benjamin Petersone711caf2008-06-11 16:44:04 +0000257}
258
259/*
260 * Functions for transferring objects
261 */
262
263static PyObject *
264connection_send_obj(ConnectionObject *self, PyObject *obj)
265{
266 char *buffer;
267 int res;
268 Py_ssize_t length;
269 PyObject *pickled_string = NULL;
270
271 CHECK_WRITABLE(self);
272
273 pickled_string = PyObject_CallFunctionObjArgs(pickle_dumps, obj,
274 pickle_protocol, NULL);
275 if (!pickled_string)
276 goto failure;
277
278 if (PyBytes_AsStringAndSize(pickled_string, &buffer, &length) < 0)
279 goto failure;
280
Benjamin Petersone711caf2008-06-11 16:44:04 +0000281 res = conn_send_string(self, buffer, (int)length);
Benjamin Petersone711caf2008-06-11 16:44:04 +0000282
283 if (res < 0) {
284 mp_SetError(PyExc_IOError, res);
285 goto failure;
286 }
287
288 Py_XDECREF(pickled_string);
289 Py_RETURN_NONE;
290
291 failure:
292 Py_XDECREF(pickled_string);
293 return NULL;
294}
295
296static PyObject *
297connection_recv_obj(ConnectionObject *self)
298{
299 char *freeme = NULL;
300 Py_ssize_t res;
301 PyObject *temp = NULL, *result = NULL;
302
303 CHECK_READABLE(self);
304
Benjamin Petersone711caf2008-06-11 16:44:04 +0000305 res = conn_recv_string(self, self->buffer, CONNECTION_BUFFER_SIZE,
306 &freeme, PY_SSIZE_T_MAX);
Benjamin Petersone711caf2008-06-11 16:44:04 +0000307
308 if (res < 0) {
309 if (res == MP_BAD_MESSAGE_LENGTH) {
310 if ((self->flags & WRITABLE) == 0) {
311 Py_BEGIN_ALLOW_THREADS
312 CLOSE(self->handle);
313 Py_END_ALLOW_THREADS
314 self->handle = INVALID_HANDLE_VALUE;
315 } else {
316 self->flags = WRITABLE;
317 }
318 }
319 mp_SetError(PyExc_IOError, res);
320 } else {
321 if (freeme == NULL) {
322 temp = PyBytes_FromStringAndSize(self->buffer, res);
323 } else {
324 temp = PyBytes_FromStringAndSize(freeme, res);
325 PyMem_Free(freeme);
326 }
327 }
328
329 if (temp)
330 result = PyObject_CallFunctionObjArgs(pickle_loads,
331 temp, NULL);
332 Py_XDECREF(temp);
333 return result;
334}
335
336/*
337 * Other functions
338 */
339
340static PyObject *
341connection_poll(ConnectionObject *self, PyObject *args)
342{
343 PyObject *timeout_obj = NULL;
344 double timeout = 0.0;
345 int res;
346
347 CHECK_READABLE(self);
348
349 if (!PyArg_ParseTuple(args, "|O", &timeout_obj))
350 return NULL;
351
352 if (timeout_obj == NULL) {
353 timeout = 0.0;
354 } else if (timeout_obj == Py_None) {
355 timeout = -1.0; /* block forever */
356 } else {
357 timeout = PyFloat_AsDouble(timeout_obj);
358 if (PyErr_Occurred())
359 return NULL;
360 if (timeout < 0.0)
361 timeout = 0.0;
362 }
363
364 Py_BEGIN_ALLOW_THREADS
Jesse Noller6214edd2009-01-19 16:23:53 +0000365 res = conn_poll(self, timeout, _save);
Benjamin Petersone711caf2008-06-11 16:44:04 +0000366 Py_END_ALLOW_THREADS
367
368 switch (res) {
369 case TRUE:
370 Py_RETURN_TRUE;
371 case FALSE:
372 Py_RETURN_FALSE;
373 default:
374 return mp_SetError(PyExc_IOError, res);
375 }
376}
377
378static PyObject *
379connection_fileno(ConnectionObject* self)
380{
381 if (self->handle == INVALID_HANDLE_VALUE) {
382 PyErr_SetString(PyExc_IOError, "handle is invalid");
383 return NULL;
384 }
385 return PyInt_FromLong((long)self->handle);
386}
387
388static PyObject *
389connection_close(ConnectionObject *self)
390{
391 if (self->handle != INVALID_HANDLE_VALUE) {
392 Py_BEGIN_ALLOW_THREADS
393 CLOSE(self->handle);
394 Py_END_ALLOW_THREADS
395 self->handle = INVALID_HANDLE_VALUE;
396 }
397
398 Py_RETURN_NONE;
399}
400
401static PyObject *
402connection_repr(ConnectionObject *self)
403{
404 static char *conn_type[] = {"read-only", "write-only", "read-write"};
405
406 assert(self->flags >= 1 && self->flags <= 3);
Amaury Forgeot d'Arc245c70b2008-09-10 22:24:24 +0000407 return FROM_FORMAT("<%s %s, handle %zd>",
Benjamin Petersone711caf2008-06-11 16:44:04 +0000408 conn_type[self->flags - 1],
409 CONNECTION_NAME, (Py_ssize_t)self->handle);
410}
411
412/*
413 * Getters and setters
414 */
415
416static PyObject *
417connection_closed(ConnectionObject *self, void *closure)
418{
419 return PyBool_FromLong((long)(self->handle == INVALID_HANDLE_VALUE));
420}
421
422static PyObject *
423connection_readable(ConnectionObject *self, void *closure)
424{
425 return PyBool_FromLong((long)(self->flags & READABLE));
426}
427
428static PyObject *
429connection_writable(ConnectionObject *self, void *closure)
430{
431 return PyBool_FromLong((long)(self->flags & WRITABLE));
432}
433
434/*
435 * Tables
436 */
437
438static PyMethodDef connection_methods[] = {
439 {"send_bytes", (PyCFunction)connection_sendbytes, METH_VARARGS,
440 "send the byte data from a readable buffer-like object"},
441 {"recv_bytes", (PyCFunction)connection_recvbytes, METH_VARARGS,
442 "receive byte data as a string"},
443 {"recv_bytes_into",(PyCFunction)connection_recvbytes_into,METH_VARARGS,
444 "receive byte data into a writeable buffer-like object\n"
445 "returns the number of bytes read"},
446
447 {"send", (PyCFunction)connection_send_obj, METH_O,
448 "send a (picklable) object"},
449 {"recv", (PyCFunction)connection_recv_obj, METH_NOARGS,
450 "receive a (picklable) object"},
451
452 {"poll", (PyCFunction)connection_poll, METH_VARARGS,
453 "whether there is any input available to be read"},
454 {"fileno", (PyCFunction)connection_fileno, METH_NOARGS,
455 "file descriptor or handle of the connection"},
456 {"close", (PyCFunction)connection_close, METH_NOARGS,
457 "close the connection"},
458
459 {NULL} /* Sentinel */
460};
461
462static PyGetSetDef connection_getset[] = {
463 {"closed", (getter)connection_closed, NULL,
464 "True if the connection is closed", NULL},
465 {"readable", (getter)connection_readable, NULL,
466 "True if the connection is readable", NULL},
467 {"writable", (getter)connection_writable, NULL,
468 "True if the connection is writable", NULL},
469 {NULL}
470};
471
472/*
473 * Connection type
474 */
475
476PyDoc_STRVAR(connection_doc,
477 "Connection type whose constructor signature is\n\n"
478 " Connection(handle, readable=True, writable=True).\n\n"
479 "The constructor does *not* duplicate the handle.");
480
481PyTypeObject CONNECTION_TYPE = {
482 PyVarObject_HEAD_INIT(NULL, 0)
483 /* tp_name */ "_multiprocessing." CONNECTION_NAME,
484 /* tp_basicsize */ sizeof(ConnectionObject),
485 /* tp_itemsize */ 0,
486 /* tp_dealloc */ (destructor)connection_dealloc,
487 /* tp_print */ 0,
488 /* tp_getattr */ 0,
489 /* tp_setattr */ 0,
Mark Dickinsone94c6792009-02-02 20:36:42 +0000490 /* tp_reserved */ 0,
Benjamin Petersone711caf2008-06-11 16:44:04 +0000491 /* tp_repr */ (reprfunc)connection_repr,
492 /* tp_as_number */ 0,
493 /* tp_as_sequence */ 0,
494 /* tp_as_mapping */ 0,
495 /* tp_hash */ 0,
496 /* tp_call */ 0,
497 /* tp_str */ 0,
498 /* tp_getattro */ 0,
499 /* tp_setattro */ 0,
500 /* tp_as_buffer */ 0,
501 /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
502 Py_TPFLAGS_HAVE_WEAKREFS,
503 /* tp_doc */ connection_doc,
504 /* tp_traverse */ 0,
505 /* tp_clear */ 0,
506 /* tp_richcompare */ 0,
507 /* tp_weaklistoffset */ offsetof(ConnectionObject, weakreflist),
508 /* tp_iter */ 0,
509 /* tp_iternext */ 0,
510 /* tp_methods */ connection_methods,
511 /* tp_members */ 0,
512 /* tp_getset */ connection_getset,
513 /* tp_base */ 0,
514 /* tp_dict */ 0,
515 /* tp_descr_get */ 0,
516 /* tp_descr_set */ 0,
517 /* tp_dictoffset */ 0,
518 /* tp_init */ 0,
519 /* tp_alloc */ 0,
520 /* tp_new */ connection_new,
521};
522
523#endif /* CONNECTION_H */