Rework pysocket async/await.

Attach the events directly to the pysocket data structure instead of
one event per pysocket_op.

Makes the code easier, gives us a good performance boost and reduces
the number of system calls required when doing an await on a socket.
This commit is contained in:
Joris Vink 2019-03-13 11:07:15 +01:00
parent 01f9b4fcde
commit 3b4574d791
3 changed files with 180 additions and 151 deletions

View File

@ -22,6 +22,7 @@ class EchoServer:
def __init__(self): def __init__(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(False) sock.setblocking(False)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("127.0.0.1", 6969)) sock.bind(("127.0.0.1", 6969))
sock.listen() sock.listen()

View File

@ -147,13 +147,28 @@ static PyTypeObject pytimer_type = {
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
}; };
/* XXX */
struct pysocket;
struct pysocket_op;
struct pysocket_event {
struct kore_event evt;
struct pysocket *s;
};
struct pysocket { struct pysocket {
PyObject_HEAD PyObject_HEAD
int fd; int fd;
int family; int family;
int protocol; int protocol;
int scheduled;
PyObject *socket; PyObject *socket;
socklen_t addr_len; socklen_t addr_len;
struct pysocket_event event;
struct pysocket_op *recvop;
struct pysocket_op *sendop;
union { union {
struct sockaddr_in ipv4; struct sockaddr_in ipv4;
struct sockaddr_un sun; struct sockaddr_un sun;
@ -198,9 +213,8 @@ static PyTypeObject pysocket_type = {
#define PYSOCKET_TYPE_RECVFROM 5 #define PYSOCKET_TYPE_RECVFROM 5
#define PYSOCKET_TYPE_SENDTO 6 #define PYSOCKET_TYPE_SENDTO 6
struct pysocket_data { struct pysocket_op {
struct kore_event evt; PyObject_HEAD
int fd;
int eof; int eof;
int type; int type;
void *self; void *self;
@ -217,11 +231,6 @@ struct pysocket_data {
} sendaddr; } sendaddr;
}; };
struct pysocket_op {
PyObject_HEAD
struct pysocket_data data;
};
static void pysocket_op_dealloc(struct pysocket_op *); static void pysocket_op_dealloc(struct pysocket_op *);
static PyObject *pysocket_op_await(PyObject *); static PyObject *pysocket_op_await(PyObject *);

View File

@ -1640,7 +1640,16 @@ pysocket_alloc(void)
sock->fd = -1; sock->fd = -1;
sock->family = -1; sock->family = -1;
sock->protocol = -1; sock->protocol = -1;
sock->scheduled = 0;
sock->socket = NULL; sock->socket = NULL;
sock->recvop = NULL;
sock->sendop = NULL;
sock->event.s = sock;
sock->event.evt.flags = 0;
sock->event.evt.type = KORE_TYPE_PYSOCKET;
sock->event.evt.handle = pysocket_evt_handle;
return (sock); return (sock);
} }
@ -1701,15 +1710,15 @@ pysocket_sendto(struct pysocket *sock, PyObject *args)
switch (sock->family) { switch (sock->family) {
case AF_INET: case AF_INET:
op->data.sendaddr.ipv4.sin_family = AF_INET; op->sendaddr.ipv4.sin_family = AF_INET;
op->data.sendaddr.ipv4.sin_port = htons(port); op->sendaddr.ipv4.sin_port = htons(port);
op->data.sendaddr.ipv4.sin_addr.s_addr = inet_addr(ip); op->sendaddr.ipv4.sin_addr.s_addr = inet_addr(ip);
break; break;
case AF_UNIX: case AF_UNIX:
op->data.sendaddr.sun.sun_family = AF_UNIX; op->sendaddr.sun.sun_family = AF_UNIX;
if (kore_strlcpy(op->data.sendaddr.sun.sun_path, sockaddr, if (kore_strlcpy(op->sendaddr.sun.sun_path, sockaddr,
sizeof(op->data.sendaddr.sun.sun_path)) >= sizeof(op->sendaddr.sun.sun_path)) >=
sizeof(op->data.sendaddr.sun.sun_path)) { sizeof(op->sendaddr.sun.sun_path)) {
Py_DECREF(ret); Py_DECREF(ret);
PyErr_SetString(PyExc_RuntimeError, PyErr_SetString(PyExc_RuntimeError,
"unix socket path too long"); "unix socket path too long");
@ -1745,7 +1754,7 @@ pysocket_recv(struct pysocket *sock, PyObject *args)
op = (struct pysocket_op *)obj; op = (struct pysocket_op *)obj;
if (timeo != -1) { if (timeo != -1) {
op->data.timer = kore_timer_add(pysocket_op_timeout, op->timer = kore_timer_add(pysocket_op_timeout,
timeo, op, KORE_TIMER_ONESHOT); timeo, op, KORE_TIMER_ONESHOT);
} }
@ -1836,38 +1845,35 @@ pysocket_close(struct pysocket *sock, PyObject *args)
static void static void
pysocket_op_dealloc(struct pysocket_op *op) pysocket_op_dealloc(struct pysocket_op *op)
{ {
#if defined(__linux__) if (op->type == PYSOCKET_TYPE_RECV ||
kore_platform_disable_read(op->data.fd); op->type == PYSOCKET_TYPE_RECVFROM ||
close(op->data.fd); op->type == PYSOCKET_TYPE_SEND)
#else kore_buf_cleanup(&op->buffer);
switch (op->data.type) {
switch (op->type) {
case PYSOCKET_TYPE_RECV: case PYSOCKET_TYPE_RECV:
case PYSOCKET_TYPE_ACCEPT: case PYSOCKET_TYPE_ACCEPT:
case PYSOCKET_TYPE_RECVFROM: case PYSOCKET_TYPE_RECVFROM:
kore_platform_disable_read(op->data.fd); if (op->socket->recvop != op)
fatal("recvop mismatch");
op->socket->recvop = NULL;
break; break;
case PYSOCKET_TYPE_SEND: case PYSOCKET_TYPE_SEND:
case PYSOCKET_TYPE_SENDTO: case PYSOCKET_TYPE_SENDTO:
case PYSOCKET_TYPE_CONNECT: case PYSOCKET_TYPE_CONNECT:
kore_platform_disable_write(op->data.fd); if (op->socket->sendop != op)
fatal("sendop mismatch");
op->socket->sendop = NULL;
break; break;
default:
fatal("unknown pysocket_op type %u", op->data.type);
}
#endif
if (op->data.type == PYSOCKET_TYPE_RECV ||
op->data.type == PYSOCKET_TYPE_RECVFROM ||
op->data.type == PYSOCKET_TYPE_SEND)
kore_buf_cleanup(&op->data.buffer);
if (op->data.timer != NULL) {
kore_timer_remove(op->data.timer);
op->data.timer = NULL;
} }
op->data.coro->sockop = NULL; if (op->timer != NULL) {
Py_DECREF(op->data.socket); kore_timer_remove(op->timer);
op->timer = NULL;
}
op->coro->sockop = NULL;
Py_DECREF(op->socket);
PyObject_Del((PyObject *)op); PyObject_Del((PyObject *)op);
} }
@ -1880,63 +1886,71 @@ pysocket_op_create(struct pysocket *sock, int type, const void *ptr, size_t len)
if (coro_running->sockop != NULL) if (coro_running->sockop != NULL)
fatal("pysocket_op_create: coro has active socketop"); fatal("pysocket_op_create: coro has active socketop");
switch (type) {
case PYSOCKET_TYPE_RECV:
case PYSOCKET_TYPE_ACCEPT:
case PYSOCKET_TYPE_RECVFROM:
if (sock->recvop != NULL) {
PyErr_SetString(PyExc_RuntimeError,
"only one recv operation can be done per socket");
return (NULL);
}
break;
case PYSOCKET_TYPE_SEND:
case PYSOCKET_TYPE_SENDTO:
case PYSOCKET_TYPE_CONNECT:
if (sock->sendop != NULL) {
PyErr_SetString(PyExc_RuntimeError,
"only one send operation can be done per socket");
return (NULL);
}
break;
default:
fatal("unknown pysocket_op type %u", type);
}
op = PyObject_New(struct pysocket_op, &pysocket_op_type); op = PyObject_New(struct pysocket_op, &pysocket_op_type);
if (op == NULL) if (op == NULL)
return (NULL); return (NULL);
#if defined(__linux__) op->eof = 0;
/* op->self = op;
* Duplicate the socket so each pysocket_op gets its own unique op->type = type;
* descriptor for epoll. This is so we can easily call EPOLL_CTL_DEL op->timer = NULL;
* on the fd when the pysocket_op is finished as our event code op->socket = sock;
* does not track queued events. op->coro = coro_running;
*/
if ((op->data.fd = dup(sock->fd)) == -1)
fatal("dup: %s", errno_s);
#else
op->data.fd = sock->fd;
#endif
op->data.eof = 0;
op->data.self = op;
op->data.type = type;
op->data.timer = NULL;
op->data.socket = sock;
op->data.evt.flags = 0;
op->data.coro = coro_running;
op->data.evt.type = KORE_TYPE_PYSOCKET;
op->data.evt.handle = pysocket_evt_handle;
coro_running->sockop = op; coro_running->sockop = op;
Py_INCREF(op->data.socket); Py_INCREF(op->socket);
switch (type) { switch (type) {
case PYSOCKET_TYPE_RECV: case PYSOCKET_TYPE_RECV:
case PYSOCKET_TYPE_RECVFROM: case PYSOCKET_TYPE_RECVFROM:
op->data.evt.flags |= KORE_EVENT_READ; sock->recvop = op;
kore_buf_init(&op->data.buffer, len); kore_buf_init(&op->buffer, len);
kore_platform_schedule_read(op->data.fd, &op->data);
break; break;
case PYSOCKET_TYPE_SEND: case PYSOCKET_TYPE_SEND:
case PYSOCKET_TYPE_SENDTO: case PYSOCKET_TYPE_SENDTO:
op->data.evt.flags |= KORE_EVENT_WRITE; sock->sendop = op;
kore_buf_init(&op->data.buffer, len); kore_buf_init(&op->buffer, len);
kore_buf_append(&op->data.buffer, ptr, len); kore_buf_append(&op->buffer, ptr, len);
kore_buf_reset(&op->data.buffer); kore_buf_reset(&op->buffer);
kore_platform_schedule_write(op->data.fd, &op->data);
break; break;
case PYSOCKET_TYPE_ACCEPT: case PYSOCKET_TYPE_ACCEPT:
op->data.evt.flags |= KORE_EVENT_READ; sock->recvop = op;
kore_platform_schedule_read(op->data.fd, &op->data);
break; break;
case PYSOCKET_TYPE_CONNECT: case PYSOCKET_TYPE_CONNECT:
op->data.evt.flags |= KORE_EVENT_WRITE; sock->sendop = op;
kore_platform_schedule_write(op->data.fd, &op->data);
break; break;
default: default:
fatal("unknown pysocket_op type %u", type); fatal("unknown pysocket_op type %u", type);
} }
if (sock->scheduled == 0) {
sock->scheduled = 1;
kore_platform_event_all(sock->fd, &sock->event);
}
return ((PyObject *)op); return ((PyObject *)op);
} }
@ -1952,25 +1966,25 @@ pysocket_op_iternext(struct pysocket_op *op)
{ {
PyObject *ret; PyObject *ret;
if (op->data.eof) { if (op->eof) {
if (op->data.coro->exception != NULL) { if (op->coro->exception != NULL) {
PyErr_SetString(op->data.coro->exception, PyErr_SetString(op->coro->exception,
op->data.coro->exception_msg); op->coro->exception_msg);
op->data.coro->exception = NULL; op->coro->exception = NULL;
return (NULL); return (NULL);
} }
if (op->data.type != PYSOCKET_TYPE_RECV) { if (op->type != PYSOCKET_TYPE_RECV) {
PyErr_SetString(PyExc_RuntimeError, "socket EOF"); PyErr_SetString(PyExc_RuntimeError, "socket EOF");
return (NULL); return (NULL);
} }
/* Drain the recv socket. */ /* Drain the recv socket. */
op->data.evt.flags |= KORE_EVENT_READ; op->socket->event.evt.flags |= KORE_EVENT_READ;
return (pysocket_async_recv(op)); return (pysocket_async_recv(op));
} }
switch (op->data.type) { switch (op->type) {
case PYSOCKET_TYPE_CONNECT: case PYSOCKET_TYPE_CONNECT:
ret = pysocket_async_connect(op); ret = pysocket_async_connect(op);
break; break;
@ -1998,23 +2012,23 @@ pysocket_op_timeout(void *arg, u_int64_t now)
{ {
struct pysocket_op *op = arg; struct pysocket_op *op = arg;
op->data.eof = 1; op->eof = 1;
op->data.timer = NULL; op->timer = NULL;
op->data.coro->exception = PyExc_TimeoutError; op->coro->exception = PyExc_TimeoutError;
op->data.coro->exception_msg = "timeout before operation completed"; op->coro->exception_msg = "timeout before operation completed";
if (op->data.coro->request != NULL) if (op->coro->request != NULL)
http_request_wakeup(op->data.coro->request); http_request_wakeup(op->coro->request);
else else
python_coro_wakeup(op->data.coro); python_coro_wakeup(op->coro);
} }
static PyObject * static PyObject *
pysocket_async_connect(struct pysocket_op *op) pysocket_async_connect(struct pysocket_op *op)
{ {
if (connect(op->data.fd, (struct sockaddr *)&op->data.socket->addr, if (connect(op->socket->fd, (struct sockaddr *)&op->socket->addr,
op->data.socket->addr_len) == -1) { op->socket->addr_len) == -1) {
if (errno != EALREADY && errno != EINPROGRESS && if (errno != EALREADY && errno != EINPROGRESS &&
errno != EISCONN && errno != EAGAIN) { errno != EISCONN && errno != EAGAIN) {
PyErr_SetString(PyExc_RuntimeError, errno_s); PyErr_SetString(PyExc_RuntimeError, errno_s);
@ -2036,15 +2050,20 @@ pysocket_async_accept(struct pysocket_op *op)
int fd; int fd;
struct pysocket *sock; struct pysocket *sock;
if ((sock = pysocket_alloc() ) == NULL) if (!(op->socket->event.evt.flags & KORE_EVENT_READ)) {
Py_RETURN_NONE;
}
if ((sock = pysocket_alloc()) == NULL)
return (NULL); return (NULL);
sock->addr_len = sizeof(sock->addr); sock->addr_len = sizeof(sock->addr);
if ((fd = accept(op->data.fd, if ((fd = accept(op->socket->fd,
(struct sockaddr *)&sock->addr, &sock->addr_len)) == -1) { (struct sockaddr *)&sock->addr, &sock->addr_len)) == -1) {
Py_DECREF((PyObject *)sock); Py_DECREF((PyObject *)sock);
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
op->socket->event.evt.flags &= ~KORE_EVENT_READ;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
PyErr_SetString(PyExc_RuntimeError, errno_s); PyErr_SetString(PyExc_RuntimeError, errno_s);
@ -2059,8 +2078,8 @@ pysocket_async_accept(struct pysocket_op *op)
sock->fd = fd; sock->fd = fd;
sock->socket = NULL; sock->socket = NULL;
sock->family = op->data.socket->family; sock->family = op->socket->family;
sock->protocol = op->data.socket->protocol; sock->protocol = op->socket->protocol;
PyErr_SetObject(PyExc_StopIteration, (PyObject *)sock); PyErr_SetObject(PyExc_StopIteration, (PyObject *)sock);
Py_DECREF((PyObject *)sock); Py_DECREF((PyObject *)sock);
@ -2078,36 +2097,36 @@ pysocket_async_recv(struct pysocket_op *op)
const char *ptr, *ip; const char *ptr, *ip;
PyObject *bytes, *result, *tuple; PyObject *bytes, *result, *tuple;
if (!(op->data.evt.flags & KORE_EVENT_READ)) { if (!(op->socket->event.evt.flags & KORE_EVENT_READ)) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
for (;;) { for (;;) {
if (op->data.type == PYSOCKET_TYPE_RECV) { if (op->type == PYSOCKET_TYPE_RECV) {
ret = read(op->data.fd, op->data.buffer.data, ret = read(op->socket->fd, op->buffer.data,
op->data.buffer.length); op->buffer.length);
} else { } else {
sendaddr = (struct sockaddr *)&op->data.sendaddr; sendaddr = (struct sockaddr *)&op->sendaddr;
switch (op->data.socket->family) { switch (op->socket->family) {
case AF_INET: case AF_INET:
socklen = sizeof(op->data.sendaddr.ipv4); socklen = sizeof(op->sendaddr.ipv4);
break; break;
case AF_UNIX: case AF_UNIX:
socklen = sizeof(op->data.sendaddr.sun); socklen = sizeof(op->sendaddr.sun);
break; break;
default: default:
fatal("non AF_INET/AF_UNIX in %s", __func__); fatal("non AF_INET/AF_UNIX in %s", __func__);
} }
ret = recvfrom(op->data.fd, op->data.buffer.data, ret = recvfrom(op->socket->fd, op->buffer.data,
op->data.buffer.length, 0, sendaddr, &socklen); op->buffer.length, 0, sendaddr, &socklen);
} }
if (ret == -1) { if (ret == -1) {
if (errno == EINTR) if (errno == EINTR)
continue; continue;
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
op->data.evt.flags &= ~KORE_EVENT_READ; op->socket->event.evt.flags &= ~KORE_EVENT_READ;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
PyErr_SetString(PyExc_RuntimeError, errno_s); PyErr_SetString(PyExc_RuntimeError, errno_s);
@ -2117,40 +2136,40 @@ pysocket_async_recv(struct pysocket_op *op)
break; break;
} }
op->data.coro->exception = NULL; op->coro->exception = NULL;
op->data.coro->exception_msg = NULL; op->coro->exception_msg = NULL;
if (op->data.timer != NULL) { if (op->timer != NULL) {
kore_timer_remove(op->data.timer); kore_timer_remove(op->timer);
op->data.timer = NULL; op->timer = NULL;
} }
if (op->data.type == PYSOCKET_TYPE_RECV && ret == 0) { if (op->type == PYSOCKET_TYPE_RECV && ret == 0) {
PyErr_SetNone(PyExc_StopIteration); PyErr_SetNone(PyExc_StopIteration);
return (NULL); return (NULL);
} }
ptr = (const char *)op->data.buffer.data; ptr = (const char *)op->buffer.data;
if ((bytes = PyBytes_FromStringAndSize(ptr, ret)) == NULL) if ((bytes = PyBytes_FromStringAndSize(ptr, ret)) == NULL)
return (NULL); return (NULL);
if (op->data.type == PYSOCKET_TYPE_RECV) { if (op->type == PYSOCKET_TYPE_RECV) {
PyErr_SetObject(PyExc_StopIteration, bytes); PyErr_SetObject(PyExc_StopIteration, bytes);
Py_DECREF(bytes); Py_DECREF(bytes);
return (NULL); return (NULL);
} }
switch(op->data.socket->family) { switch(op->socket->family) {
case AF_INET: case AF_INET:
port = ntohs(op->data.sendaddr.ipv4.sin_port); port = ntohs(op->sendaddr.ipv4.sin_port);
ip = inet_ntoa(op->data.sendaddr.ipv4.sin_addr); ip = inet_ntoa(op->sendaddr.ipv4.sin_addr);
if ((tuple = Py_BuildValue("(sHN)", ip, port, bytes)) == NULL) if ((tuple = Py_BuildValue("(sHN)", ip, port, bytes)) == NULL)
return (NULL); return (NULL);
break; break;
case AF_UNIX: case AF_UNIX:
if ((tuple = Py_BuildValue("(sN)", if ((tuple = Py_BuildValue("(sN)",
op->data.sendaddr.sun.sun_path, bytes)) == NULL) op->sendaddr.sun.sun_path, bytes)) == NULL)
return (NULL); return (NULL);
break; break;
default: default:
@ -2177,32 +2196,32 @@ pysocket_async_send(struct pysocket_op *op)
socklen_t socklen; socklen_t socklen;
const struct sockaddr *sendaddr; const struct sockaddr *sendaddr;
if (!(op->data.evt.flags & KORE_EVENT_WRITE)) { if (!(op->socket->event.evt.flags & KORE_EVENT_WRITE)) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
for (;;) { for (;;) {
if (op->data.type == PYSOCKET_TYPE_SEND) { if (op->type == PYSOCKET_TYPE_SEND) {
ret = write(op->data.fd, ret = write(op->socket->fd,
op->data.buffer.data + op->data.buffer.offset, op->buffer.data + op->buffer.offset,
op->data.buffer.length - op->data.buffer.offset); op->buffer.length - op->buffer.offset);
} else { } else {
sendaddr = (const struct sockaddr *)&op->data.sendaddr; sendaddr = (const struct sockaddr *)&op->sendaddr;
switch (op->data.socket->family) { switch (op->socket->family) {
case AF_INET: case AF_INET:
socklen = sizeof(op->data.sendaddr.ipv4); socklen = sizeof(op->sendaddr.ipv4);
break; break;
case AF_UNIX: case AF_UNIX:
socklen = sizeof(op->data.sendaddr.sun); socklen = sizeof(op->sendaddr.sun);
break; break;
default: default:
fatal("non AF_INET/AF_UNIX in %s", __func__); fatal("non AF_INET/AF_UNIX in %s", __func__);
} }
ret = sendto(op->data.fd, ret = sendto(op->socket->fd,
op->data.buffer.data + op->data.buffer.offset, op->buffer.data + op->buffer.offset,
op->data.buffer.length - op->data.buffer.offset, op->buffer.length - op->buffer.offset,
0, sendaddr, socklen); 0, sendaddr, socklen);
} }
@ -2210,7 +2229,8 @@ pysocket_async_send(struct pysocket_op *op)
if (errno == EINTR) if (errno == EINTR)
continue; continue;
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
op->data.evt.flags &= ~KORE_EVENT_WRITE; op->socket->event.evt.flags &=
~KORE_EVENT_WRITE;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
PyErr_SetString(PyExc_RuntimeError, errno_s); PyErr_SetString(PyExc_RuntimeError, errno_s);
@ -2219,9 +2239,9 @@ pysocket_async_send(struct pysocket_op *op)
break; break;
} }
op->data.buffer.offset += (size_t)ret; op->buffer.offset += (size_t)ret;
if (op->data.buffer.offset == op->data.buffer.length) { if (op->buffer.offset == op->buffer.length) {
PyErr_SetNone(PyExc_StopIteration); PyErr_SetNone(PyExc_StopIteration);
return (NULL); return (NULL);
} }
@ -2232,25 +2252,24 @@ pysocket_async_send(struct pysocket_op *op)
static void static void
pysocket_evt_handle(void *arg, int eof) pysocket_evt_handle(void *arg, int eof)
{ {
struct pysocket_data *data = arg; struct pysocket_event *event = arg;
struct python_coro *coro = data->coro; struct pysocket *socket = event->s;
if (coro->sockop == NULL) if ((event->evt.flags & KORE_EVENT_READ) && socket->recvop != NULL) {
fatal("pysocket_evt_handle: sockop == NULL"); if (socket->recvop->coro->request != NULL)
http_request_wakeup(socket->recvop->coro->request);
else
python_coro_wakeup(socket->recvop->coro);
socket->recvop->eof = eof;
}
/* if ((event->evt.flags & KORE_EVENT_WRITE) && socket->sendop != NULL) {
* If we are a coroutine tied to an HTTP request wake-up the if (socket->sendop->coro->request != NULL)
* HTTP request instead. That in turn will wakeup the coro and http_request_wakeup(socket->sendop->coro->request);
* continue it. else
* python_coro_wakeup(socket->sendop->coro);
* Otherwise just wakeup the coroutine so it will run next tick. socket->sendop->eof = eof;
*/ }
if (coro->request != NULL)
http_request_wakeup(coro->request);
else
python_coro_wakeup(coro);
coro->sockop->data.eof = eof;
} }
static void static void
@ -2549,7 +2568,7 @@ pyproc_timeout(void *arg, u_int64_t now)
proc->timer = NULL; proc->timer = NULL;
if (proc->coro->sockop != NULL) if (proc->coro->sockop != NULL)
proc->coro->sockop->data.eof = 1; proc->coro->sockop->eof = 1;
proc->coro->exception = PyExc_TimeoutError; proc->coro->exception = PyExc_TimeoutError;
proc->coro->exception_msg = "timeout before process exited"; proc->coro->exception_msg = "timeout before process exited";
@ -2658,7 +2677,7 @@ pyproc_recv(struct pyproc *proc, PyObject *args)
op = (struct pysocket_op *)obj; op = (struct pysocket_op *)obj;
if (timeo != -1) { if (timeo != -1) {
op->data.timer = kore_timer_add(pysocket_op_timeout, op->timer = kore_timer_add(pysocket_op_timeout,
timeo, op, KORE_TIMER_ONESHOT); timeo, op, KORE_TIMER_ONESHOT);
} }