From c138fc279178a6224a48c35dc7cb9b5c3e6ee879 Mon Sep 17 00:00:00 2001 From: Michael Graff Date: Fri, 3 Sep 1999 16:38:08 +0000 Subject: [PATCH] multi-buffer isc_socket_recvv() -- needs testing --- lib/isc/include/isc/socket.h | 58 +++++++++++++- lib/isc/unix/socket.c | 146 ++++++++++++++++++++++++++++++++++- 2 files changed, 202 insertions(+), 2 deletions(-) diff --git a/lib/isc/include/isc/socket.h b/lib/isc/include/isc/socket.h index c1f05f7c51..b18dadef69 100644 --- a/lib/isc/include/isc/socket.h +++ b/lib/isc/include/isc/socket.h @@ -439,7 +439,7 @@ isc_socket_recv(isc_socket_t *sock, isc_region_t *region, * the completion event will be posted to the task 'task.' If minimum * is zero, the exact number of bytes requested in the region must * be read for an event to be posted. This only makes sense for TCP - * connections, and is always set to the full buffer for UDP. + * connections, and is always set to 1 byte for UDP. * * The read will complete when the desired number of bytes have been * read, if end-of-input occurs, or if an error occurs. A read done @@ -455,6 +455,62 @@ isc_socket_recv(isc_socket_t *sock, isc_region_t *region, * * 'region' is a valid region * + * 'minimum' <= region.length + * + * 'task' is a valid task + * + * action != NULL and is a valid action + * + * Returns: + * + * ISC_R_SUCCESS + * ISC_R_NOMEMORY + * ISC_R_UNEXPECTED + * + * Event results: + * + * ISC_R_SUCCESS + * ISC_R_UNEXPECTED + * XXX needs other net-type errors + */ + +isc_result_t +isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist, + unsigned int minimum, + isc_task_t *task, isc_taskaction_t action, void *arg); +/* + * Receive from 'socket', storing the results in region. + * + * Notes: + * + * Let 'length' refer to the sum of all available regions in the + * list of buffers 'buflist'. + * + * If 'minimum' is non-zero and at least that many bytes are read, + * the completion event will be posted to the task 'task.' If minimum + * is zero, the exact number of bytes requested in the buflist must + * be read for an event to be posted. This only makes sense for TCP + * connections, and is always set to 1 byte for UDP. + * + * The read will complete when the desired number of bytes have been + * read, if end-of-input occurs, or if an error occurs. A read done + * event with the given 'action' and 'arg' will be posted to the + * event queue of 'task'. + * + * The caller may neither read from nor write to any buffer in the + * buffer list, nor may it link or unlink these buffers from any list. + * On successful completion, '*buflist' will be empty, and the list of + * all buffers will be returned in the done event's 'bufferlist' + * member. On error return, '*buflist' will be unchanged. + * + * Requires: + * + * 'socket' is a valid socket + * + * 'buflist' is non-NULL, and '*buflist' contain at least one buffer. + * + * 'minimum' is <= sum of all available regions. + * * 'task' is a valid task * * action != NULL and is a valid action diff --git a/lib/isc/unix/socket.c b/lib/isc/unix/socket.c index ba2b770301..51b20ef9a9 100644 --- a/lib/isc/unix/socket.c +++ b/lib/isc/unix/socket.c @@ -536,7 +536,10 @@ doio_recv(isc_socket_t *sock, isc_socketevent_t *dev) int cc; struct iovec iov[ISC_SOCKET_MAXSCATTERGATHER]; size_t read_count; + size_t actual_count; struct msghdr msghdr; + isc_buffer_t *buffer; + isc_region_t available; build_msghdr_recv(sock, dev, &msghdr, iov, ISC_SOCKET_MAXSCATTERGATHER, &read_count); @@ -603,11 +606,32 @@ doio_recv(isc_socket_t *sock, isc_socketevent_t *dev) */ process_cmsg(sock, &msghdr, dev); + /* + * update the buffers (if any) and the i/o count + */ + dev->n += cc; + actual_count = cc; + buffer = ISC_LIST_HEAD(dev->bufferlist); + while (buffer != NULL && actual_count > 0) { + isc_buffer_available(buffer, &available); + if (available.length <= actual_count) { + actual_count -= available.length; + isc_buffer_add(buffer, available.length); + } else { + isc_buffer_add(buffer, actual_count); + actual_count = 0; + break; + } + buffer = ISC_LIST_NEXT(buffer, link); + if (buffer == NULL) { + INSIST(actual_count == 0); + } + } + /* * If we read less than we expected, update counters, * and let the upper layer poke the descriptor. */ - dev->n += cc; if (((size_t)cc != read_count) && (dev->n < dev->minimum)) return (DOIO_SOFT); @@ -1850,6 +1874,126 @@ isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist, unsigned int minimum, isc_task_t *task, isc_taskaction_t action, void *arg) { + isc_socketevent_t *dev; + isc_socketmgr_t *manager; + isc_task_t *ntask = NULL; + isc_boolean_t was_empty; + unsigned int iocount; + isc_region_t available; + isc_buffer_t *buffer; + + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(buflist != NULL); + REQUIRE(!ISC_LIST_EMPTY(*buflist)); + REQUIRE(task != NULL); + REQUIRE(action != NULL); + + manager = sock->manager; + REQUIRE(VALID_MANAGER(manager)); + + iocount = 0; + buffer = ISC_LIST_HEAD(*buflist); + while (buffer != NULL) { + isc_buffer_available(buffer, &available); + iocount += available.length; + } + REQUIRE(iocount > 0); + + LOCK(&sock->lock); + + dev = allocate_socketevent(sock, ISC_SOCKEVENT_RECVDONE, action, arg); + if (dev == NULL) { + UNLOCK(&sock->lock); + return (ISC_R_NOMEMORY); + } + + /*** + *** From here down, only ISC_R_SUCCESS can be returned. Any further + *** error information will result in the done event being posted + *** to the task rather than this function failing. + ***/ + + /* + * UDP sockets are always partial read + */ + if (sock->type == isc_sockettype_udp) + dev->minimum = 1; + else { + if (minimum == 0) + dev->minimum = iocount; + else + dev->minimum = minimum; + } + + dev->result = ISC_R_SUCCESS; + dev->n = 0; + dev->sender = task; + + /* + * Move each buffer from the passed in list to our internal one. + */ + buffer = ISC_LIST_HEAD(*buflist); + while (buffer != NULL) { + ISC_LIST_DEQUEUE(*buflist, buffer, link); + ISC_LIST_ENQUEUE(dev->bufferlist, buffer, link); + } + + was_empty = ISC_LIST_EMPTY(sock->recv_list); + + /* + * If the read queue is empty, try to do the I/O right now. + */ + if (!was_empty) + goto queue; + + if (sock->recv_result != ISC_R_SUCCESS) { + send_recvdone_event(sock, &dev, sock->recv_result); + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); + } + + switch (doio_recv(sock, dev)) { + case DOIO_SOFT: + goto queue; + break; + + case DOIO_EOF: + send_recvdone_event(sock, &dev, ISC_R_EOF); + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); + break; + + case DOIO_HARD: + case DOIO_UNEXPECTED: + case DOIO_SUCCESS: + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); + break; + } + + queue: + /* + * We couldn't read all or part of the request right now, so queue + * it. + * + * Attach to socket and to task + */ + isc_task_attach(task, &ntask); + dev->attributes |= ISC_SOCKEVENTATTR_ATTACHED; + + /* + * Enqueue the request. If the socket was previously not being + * watched, poke the watcher to start paying attention to it. + */ + ISC_LIST_ENQUEUE(sock->recv_list, dev, link); + if (was_empty) + select_poke(sock->manager, sock->fd); + + XTRACE(TRACE_RECV, + ("isc_socket_recvv: queued event %p, task %p\n", dev, ntask)); + + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); } isc_result_t