comparison Modules/socketmodule.c @ 64959:aed04b3f0fe3

Issue #9950: Fix socket.sendall() crash or misbehaviour when a signal is received. Now sendall() properly calls signal handlers if necessary, and retries sending if these returned successfully, including on sockets with a timeout.
author Antoine Pitrou <solipsis@pitrou.net>
date Mon, 27 Sep 2010 17:52:25 +0000
parents 7d1752d62050
children 0f8c2eb89f46
comparison
equal deleted inserted replaced
64958:ff5d3e5c4bb5 64959:aed04b3f0fe3
2566 static PyObject * 2566 static PyObject *
2567 sock_sendall(PySocketSockObject *s, PyObject *args) 2567 sock_sendall(PySocketSockObject *s, PyObject *args)
2568 { 2568 {
2569 char *buf; 2569 char *buf;
2570 Py_ssize_t len, n = -1; 2570 Py_ssize_t len, n = -1;
2571 int flags = 0, timeout; 2571 int flags = 0, timeout, saved_errno;
2572 Py_buffer pbuf; 2572 Py_buffer pbuf;
2573 2573
2574 if (!PyArg_ParseTuple(args, "y*|i:sendall", &pbuf, &flags)) 2574 if (!PyArg_ParseTuple(args, "y*|i:sendall", &pbuf, &flags))
2575 return NULL; 2575 return NULL;
2576 buf = pbuf.buf; 2576 buf = pbuf.buf;
2579 if (!IS_SELECTABLE(s)) { 2579 if (!IS_SELECTABLE(s)) {
2580 PyBuffer_Release(&pbuf); 2580 PyBuffer_Release(&pbuf);
2581 return select_error(); 2581 return select_error();
2582 } 2582 }
2583 2583
2584 Py_BEGIN_ALLOW_THREADS
2585 do { 2584 do {
2585 Py_BEGIN_ALLOW_THREADS
2586 timeout = internal_select(s, 1); 2586 timeout = internal_select(s, 1);
2587 n = -1; 2587 n = -1;
2588 if (timeout) 2588 if (!timeout) {
2589 break;
2590 #ifdef __VMS 2589 #ifdef __VMS
2591 n = sendsegmented(s->sock_fd, buf, len, flags); 2590 n = sendsegmented(s->sock_fd, buf, len, flags);
2592 #else 2591 #else
2593 n = send(s->sock_fd, buf, len, flags); 2592 n = send(s->sock_fd, buf, len, flags);
2594 #endif 2593 #endif
2594 }
2595 Py_END_ALLOW_THREADS
2596 if (timeout == 1) {
2597 PyBuffer_Release(&pbuf);
2598 PyErr_SetString(socket_timeout, "timed out");
2599 return NULL;
2600 }
2601 /* PyErr_CheckSignals() might change errno */
2602 saved_errno = errno;
2603 /* We must run our signal handlers before looping again.
2604 send() can return a successful partial write when it is
2605 interrupted, so we can't restrict ourselves to EINTR. */
2606 if (PyErr_CheckSignals()) {
2607 PyBuffer_Release(&pbuf);
2608 return NULL;
2609 }
2595 if (n < 0) { 2610 if (n < 0) {
2596 #ifdef EINTR 2611 /* If interrupted, try again */
2597 /* We must handle EINTR here as there is no way for 2612 if (saved_errno == EINTR)
2598 * the caller to know how much was sent otherwise. */
2599 if (errno == EINTR) {
2600 /* Run signal handlers. If an exception was
2601 * raised, abort and leave this socket in
2602 * an unknown state. */
2603 if (PyErr_CheckSignals())
2604 return NULL;
2605 continue; 2613 continue;
2606 } 2614 else
2607 #endif 2615 break;
2608 break;
2609 } 2616 }
2610 buf += n; 2617 buf += n;
2611 len -= n; 2618 len -= n;
2612 } while (len > 0); 2619 } while (len > 0);
2613 Py_END_ALLOW_THREADS
2614 PyBuffer_Release(&pbuf); 2620 PyBuffer_Release(&pbuf);
2615 2621
2616 if (timeout == 1) {
2617 PyErr_SetString(socket_timeout, "timed out");
2618 return NULL;
2619 }
2620 if (n < 0) 2622 if (n < 0)
2621 return s->errorhandler(); 2623 return s->errorhandler();
2622 2624
2623 Py_INCREF(Py_None); 2625 Py_INCREF(Py_None);
2624 return Py_None; 2626 return Py_None;