Commit c138fc27 authored by Michael Graff's avatar Michael Graff
Browse files

multi-buffer isc_socket_recvv() -- needs testing

parent 3e9a5c9d
......@@ -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
......
......@@ -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
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment