blob: 4b475c6bf72787456ffee87efe9a40099e05af8b [file] [log] [blame]
Benjamin Peterson190d56e2008-06-11 02:40:25 +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) {
50 PyErr_Format(PyExc_IOError, "invalid handle %"
51 PY_FORMAT_SIZE_T "d", (Py_ssize_t)handle);
52 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{
99 char *buffer;
100 Py_ssize_t length, offset=0, size=PY_SSIZE_T_MIN;
101 int res;
102
103 if (!PyArg_ParseTuple(args, F_RBUFFER "#|" F_PY_SSIZE_T F_PY_SSIZE_T,
104 &buffer, &length, &offset, &size))
105 return NULL;
106
107 CHECK_WRITABLE(self);
108
109 if (offset < 0) {
110 PyErr_SetString(PyExc_ValueError, "offset is negative");
111 return NULL;
112 }
113 if (length < offset) {
114 PyErr_SetString(PyExc_ValueError, "buffer length < offset");
115 return NULL;
116 }
117
118 if (size == PY_SSIZE_T_MIN) {
119 size = length - offset;
120 } else {
121 if (size < 0) {
122 PyErr_SetString(PyExc_ValueError, "size is negative");
123 return NULL;
124 }
125 if (offset + size > length) {
126 PyErr_SetString(PyExc_ValueError,
127 "buffer length < offset + size");
128 return NULL;
129 }
130 }
131
Benjamin Peterson190d56e2008-06-11 02:40:25 +0000132 res = conn_send_string(self, buffer + offset, size);
Benjamin Peterson190d56e2008-06-11 02:40:25 +0000133
134 if (res < 0)
135 return mp_SetError(PyExc_IOError, res);
136
137 Py_RETURN_NONE;
138}
139
140static PyObject *
141connection_recvbytes(ConnectionObject *self, PyObject *args)
142{
143 char *freeme = NULL;
144 Py_ssize_t res, maxlength = PY_SSIZE_T_MAX;
145 PyObject *result = NULL;
146
147 if (!PyArg_ParseTuple(args, "|" F_PY_SSIZE_T, &maxlength))
148 return NULL;
149
150 CHECK_READABLE(self);
151
152 if (maxlength < 0) {
153 PyErr_SetString(PyExc_ValueError, "maxlength < 0");
154 return NULL;
155 }
156
Benjamin Peterson190d56e2008-06-11 02:40:25 +0000157 res = conn_recv_string(self, self->buffer, CONNECTION_BUFFER_SIZE,
158 &freeme, maxlength);
Benjamin Peterson190d56e2008-06-11 02:40:25 +0000159
160 if (res < 0) {
161 if (res == MP_BAD_MESSAGE_LENGTH) {
162 if ((self->flags & WRITABLE) == 0) {
163 Py_BEGIN_ALLOW_THREADS
164 CLOSE(self->handle);
165 Py_END_ALLOW_THREADS
166 self->handle = INVALID_HANDLE_VALUE;
167 } else {
168 self->flags = WRITABLE;
169 }
170 }
171 mp_SetError(PyExc_IOError, res);
172 } else {
173 if (freeme == NULL) {
174 result = PyString_FromStringAndSize(self->buffer, res);
175 } else {
176 result = PyString_FromStringAndSize(freeme, res);
177 PyMem_Free(freeme);
178 }
179 }
180
181 return result;
182}
183
184static PyObject *
185connection_recvbytes_into(ConnectionObject *self, PyObject *args)
186{
187 char *freeme = NULL, *buffer = NULL;
188 Py_ssize_t res, length, offset = 0;
189 PyObject *result = NULL;
190
191 if (!PyArg_ParseTuple(args, "w#|" F_PY_SSIZE_T,
192 &buffer, &length, &offset))
193 return NULL;
194
195 CHECK_READABLE(self);
196
197 if (offset < 0) {
198 PyErr_SetString(PyExc_ValueError, "negative offset");
199 return NULL;
200 }
201
202 if (offset > length) {
203 PyErr_SetString(PyExc_ValueError, "offset too large");
204 return NULL;
205 }
206
Benjamin Peterson190d56e2008-06-11 02:40:25 +0000207 res = conn_recv_string(self, buffer+offset, length-offset,
208 &freeme, PY_SSIZE_T_MAX);
Benjamin Peterson190d56e2008-06-11 02:40:25 +0000209
210 if (res < 0) {
211 if (res == MP_BAD_MESSAGE_LENGTH) {
212 if ((self->flags & WRITABLE) == 0) {
213 Py_BEGIN_ALLOW_THREADS
214 CLOSE(self->handle);
215 Py_END_ALLOW_THREADS
216 self->handle = INVALID_HANDLE_VALUE;
217 } else {
218 self->flags = WRITABLE;
219 }
220 }
221 mp_SetError(PyExc_IOError, res);
222 } else {
223 if (freeme == NULL) {
224 result = PyInt_FromSsize_t(res);
225 } else {
226 result = PyObject_CallFunction(BufferTooShort,
227 F_RBUFFER "#",
228 freeme, res);
229 PyMem_Free(freeme);
230 if (result) {
231 PyErr_SetObject(BufferTooShort, result);
232 Py_DECREF(result);
233 }
234 return NULL;
235 }
236 }
237
238 return result;
239}
240
241/*
242 * Functions for transferring objects
243 */
244
245static PyObject *
246connection_send_obj(ConnectionObject *self, PyObject *obj)
247{
248 char *buffer;
249 int res;
250 Py_ssize_t length;
251 PyObject *pickled_string = NULL;
252
253 CHECK_WRITABLE(self);
254
255 pickled_string = PyObject_CallFunctionObjArgs(pickle_dumps, obj,
256 pickle_protocol, NULL);
257 if (!pickled_string)
258 goto failure;
259
260 if (PyString_AsStringAndSize(pickled_string, &buffer, &length) < 0)
261 goto failure;
262
Benjamin Peterson190d56e2008-06-11 02:40:25 +0000263 res = conn_send_string(self, buffer, (int)length);
Benjamin Peterson190d56e2008-06-11 02:40:25 +0000264
265 if (res < 0) {
266 mp_SetError(PyExc_IOError, res);
267 goto failure;
268 }
269
270 Py_XDECREF(pickled_string);
271 Py_RETURN_NONE;
272
273 failure:
274 Py_XDECREF(pickled_string);
275 return NULL;
276}
277
278static PyObject *
279connection_recv_obj(ConnectionObject *self)
280{
281 char *freeme = NULL;
282 Py_ssize_t res;
283 PyObject *temp = NULL, *result = NULL;
284
285 CHECK_READABLE(self);
286
Benjamin Peterson190d56e2008-06-11 02:40:25 +0000287 res = conn_recv_string(self, self->buffer, CONNECTION_BUFFER_SIZE,
288 &freeme, PY_SSIZE_T_MAX);
Benjamin Peterson190d56e2008-06-11 02:40:25 +0000289
290 if (res < 0) {
291 if (res == MP_BAD_MESSAGE_LENGTH) {
292 if ((self->flags & WRITABLE) == 0) {
293 Py_BEGIN_ALLOW_THREADS
294 CLOSE(self->handle);
295 Py_END_ALLOW_THREADS
296 self->handle = INVALID_HANDLE_VALUE;
297 } else {
298 self->flags = WRITABLE;
299 }
300 }
301 mp_SetError(PyExc_IOError, res);
302 } else {
303 if (freeme == NULL) {
304 temp = PyString_FromStringAndSize(self->buffer, res);
305 } else {
306 temp = PyString_FromStringAndSize(freeme, res);
307 PyMem_Free(freeme);
308 }
309 }
310
311 if (temp)
312 result = PyObject_CallFunctionObjArgs(pickle_loads,
313 temp, NULL);
314 Py_XDECREF(temp);
315 return result;
316}
317
318/*
319 * Other functions
320 */
321
322static PyObject *
323connection_poll(ConnectionObject *self, PyObject *args)
324{
325 PyObject *timeout_obj = NULL;
326 double timeout = 0.0;
327 int res;
328
329 CHECK_READABLE(self);
330
331 if (!PyArg_ParseTuple(args, "|O", &timeout_obj))
332 return NULL;
333
334 if (timeout_obj == NULL) {
335 timeout = 0.0;
336 } else if (timeout_obj == Py_None) {
337 timeout = -1.0; /* block forever */
338 } else {
339 timeout = PyFloat_AsDouble(timeout_obj);
340 if (PyErr_Occurred())
341 return NULL;
342 if (timeout < 0.0)
343 timeout = 0.0;
344 }
345
346 Py_BEGIN_ALLOW_THREADS
347 res = conn_poll(self, timeout);
348 Py_END_ALLOW_THREADS
349
350 switch (res) {
351 case TRUE:
352 Py_RETURN_TRUE;
353 case FALSE:
354 Py_RETURN_FALSE;
355 default:
356 return mp_SetError(PyExc_IOError, res);
357 }
358}
359
360static PyObject *
361connection_fileno(ConnectionObject* self)
362{
363 if (self->handle == INVALID_HANDLE_VALUE) {
364 PyErr_SetString(PyExc_IOError, "handle is invalid");
365 return NULL;
366 }
367 return PyInt_FromLong((long)self->handle);
368}
369
370static PyObject *
371connection_close(ConnectionObject *self)
372{
373 if (self->handle != INVALID_HANDLE_VALUE) {
374 Py_BEGIN_ALLOW_THREADS
375 CLOSE(self->handle);
376 Py_END_ALLOW_THREADS
377 self->handle = INVALID_HANDLE_VALUE;
378 }
379
380 Py_RETURN_NONE;
381}
382
383static PyObject *
384connection_repr(ConnectionObject *self)
385{
386 static char *conn_type[] = {"read-only", "write-only", "read-write"};
387
388 assert(self->flags >= 1 && self->flags <= 3);
389 return FROM_FORMAT("<%s %s, handle %" PY_FORMAT_SIZE_T "d>",
390 conn_type[self->flags - 1],
391 CONNECTION_NAME, (Py_ssize_t)self->handle);
392}
393
394/*
395 * Getters and setters
396 */
397
398static PyObject *
399connection_closed(ConnectionObject *self, void *closure)
400{
401 return PyBool_FromLong((long)(self->handle == INVALID_HANDLE_VALUE));
402}
403
404static PyObject *
405connection_readable(ConnectionObject *self, void *closure)
406{
407 return PyBool_FromLong((long)(self->flags & READABLE));
408}
409
410static PyObject *
411connection_writable(ConnectionObject *self, void *closure)
412{
413 return PyBool_FromLong((long)(self->flags & WRITABLE));
414}
415
416/*
417 * Tables
418 */
419
420static PyMethodDef connection_methods[] = {
421 {"send_bytes", (PyCFunction)connection_sendbytes, METH_VARARGS,
422 "send the byte data from a readable buffer-like object"},
423 {"recv_bytes", (PyCFunction)connection_recvbytes, METH_VARARGS,
424 "receive byte data as a string"},
425 {"recv_bytes_into",(PyCFunction)connection_recvbytes_into,METH_VARARGS,
426 "receive byte data into a writeable buffer-like object\n"
427 "returns the number of bytes read"},
428
429 {"send", (PyCFunction)connection_send_obj, METH_O,
430 "send a (picklable) object"},
431 {"recv", (PyCFunction)connection_recv_obj, METH_NOARGS,
432 "receive a (picklable) object"},
433
434 {"poll", (PyCFunction)connection_poll, METH_VARARGS,
435 "whether there is any input available to be read"},
436 {"fileno", (PyCFunction)connection_fileno, METH_NOARGS,
437 "file descriptor or handle of the connection"},
438 {"close", (PyCFunction)connection_close, METH_NOARGS,
439 "close the connection"},
440
441 {NULL} /* Sentinel */
442};
443
444static PyGetSetDef connection_getset[] = {
445 {"closed", (getter)connection_closed, NULL,
446 "True if the connection is closed", NULL},
447 {"readable", (getter)connection_readable, NULL,
448 "True if the connection is readable", NULL},
449 {"writable", (getter)connection_writable, NULL,
450 "True if the connection is writable", NULL},
451 {NULL}
452};
453
454/*
455 * Connection type
456 */
457
458PyDoc_STRVAR(connection_doc,
459 "Connection type whose constructor signature is\n\n"
460 " Connection(handle, readable=True, writable=True).\n\n"
461 "The constructor does *not* duplicate the handle.");
462
463PyTypeObject CONNECTION_TYPE = {
464 PyVarObject_HEAD_INIT(NULL, 0)
465 /* tp_name */ "_multiprocessing." CONNECTION_NAME,
466 /* tp_basicsize */ sizeof(ConnectionObject),
467 /* tp_itemsize */ 0,
468 /* tp_dealloc */ (destructor)connection_dealloc,
469 /* tp_print */ 0,
470 /* tp_getattr */ 0,
471 /* tp_setattr */ 0,
472 /* tp_compare */ 0,
473 /* tp_repr */ (reprfunc)connection_repr,
474 /* tp_as_number */ 0,
475 /* tp_as_sequence */ 0,
476 /* tp_as_mapping */ 0,
477 /* tp_hash */ 0,
478 /* tp_call */ 0,
479 /* tp_str */ 0,
480 /* tp_getattro */ 0,
481 /* tp_setattro */ 0,
482 /* tp_as_buffer */ 0,
483 /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
484 Py_TPFLAGS_HAVE_WEAKREFS,
485 /* tp_doc */ connection_doc,
486 /* tp_traverse */ 0,
487 /* tp_clear */ 0,
488 /* tp_richcompare */ 0,
489 /* tp_weaklistoffset */ offsetof(ConnectionObject, weakreflist),
490 /* tp_iter */ 0,
491 /* tp_iternext */ 0,
492 /* tp_methods */ connection_methods,
493 /* tp_members */ 0,
494 /* tp_getset */ connection_getset,
495 /* tp_base */ 0,
496 /* tp_dict */ 0,
497 /* tp_descr_get */ 0,
498 /* tp_descr_set */ 0,
499 /* tp_dictoffset */ 0,
500 /* tp_init */ 0,
501 /* tp_alloc */ 0,
502 /* tp_new */ connection_new,
503};
504
505#endif /* CONNECTION_H */