Add isc_socket_sendv(), sendtov(), and recvv() that work
This commit is contained in:
@@ -61,7 +61,7 @@
|
||||
|
||||
#include <isc/lang.h>
|
||||
#include <isc/boolean.h>
|
||||
#include <isc/buffer.h>
|
||||
#include <isc/bufferlist.h>
|
||||
#include <isc/result.h>
|
||||
#include <isc/event.h>
|
||||
#include <isc/eventclass.h>
|
||||
@@ -97,7 +97,7 @@ struct isc_socketevent {
|
||||
unsigned int n; /* bytes read or written */
|
||||
unsigned int offset; /* offset into buffer list */
|
||||
isc_region_t region; /* for single-buffer i/o */
|
||||
ISC_LIST(isc_buffer_t) bufferlist; /* list of buffers */
|
||||
isc_bufferlist_t bufferlist; /* list of buffers */
|
||||
isc_sockaddr_t address; /* source address */
|
||||
};
|
||||
|
||||
@@ -428,12 +428,17 @@ isc_result_t
|
||||
isc_socket_recv(isc_socket_t *sock, isc_region_t *region,
|
||||
unsigned int minimum,
|
||||
isc_task_t *task, isc_taskaction_t action, void *arg);
|
||||
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 length of 'region'.
|
||||
* Let 'length' refer to the length of 'region' or 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
|
||||
@@ -446,59 +451,11 @@ isc_socket_recv(isc_socket_t *sock, isc_region_t *region,
|
||||
* 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 'region' until it
|
||||
* has received the read completion event.
|
||||
* The caller may not modify 'region', the buffers which are passed
|
||||
* into this function, or any data they refer to until the completion
|
||||
* event is received.
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
* 'socket' is a valid socket
|
||||
*
|
||||
* '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.
|
||||
* For isc_socket_recvv():
|
||||
* 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.
|
||||
@@ -507,9 +464,11 @@ isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist,
|
||||
*
|
||||
* 'socket' is a valid socket
|
||||
*
|
||||
* 'buflist' is non-NULL, and '*buflist' contain at least one buffer.
|
||||
* For isc_socket_recv():
|
||||
* 'region' is a valid region
|
||||
*
|
||||
* 'minimum' is <= sum of all available regions.
|
||||
* For isc_socket_recvv():
|
||||
* 'buflist' is non-NULL, and '*buflist' contain at least one buffer.
|
||||
*
|
||||
* 'task' is a valid task
|
||||
*
|
||||
@@ -535,34 +494,49 @@ isc_result_t
|
||||
isc_socket_sendto(isc_socket_t *sock, isc_region_t *region,
|
||||
isc_task_t *task, isc_taskaction_t action, void *arg,
|
||||
isc_sockaddr_t *address);
|
||||
isc_result_t
|
||||
isc_socket_sendv(isc_socket_t *sock, isc_bufferlist_t *buflist,
|
||||
isc_task_t *task, isc_taskaction_t action, void *arg);
|
||||
isc_result_t
|
||||
isc_socket_sendtov(isc_socket_t *sock, isc_bufferlist_t *buflist,
|
||||
isc_task_t *task, isc_taskaction_t action, void *arg,
|
||||
isc_sockaddr_t *address);
|
||||
/*
|
||||
* Send the contents of 'region' to the socket's peer.
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* Shutting down the requestor's task *may* result in any
|
||||
* still pending writes being dropped.
|
||||
* still pending writes being dropped or completed, depending on the
|
||||
* underlying OS implementation.
|
||||
*
|
||||
* If 'action' is NULL, then no completion event will be posted.
|
||||
*
|
||||
* The caller may neither read from nor write to 'region' until it
|
||||
* has received the write completion event, or all references to the
|
||||
* socket have been detached.
|
||||
* The caller may not modify 'region', the buffers which are passed
|
||||
* into this function, or any data they refer to until the completion
|
||||
* event is received.
|
||||
*
|
||||
* For isc_socket_sendv() and isc_socket_sendtov():
|
||||
* 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
|
||||
*
|
||||
* For isc_socket_send():
|
||||
* 'region' is a valid region
|
||||
*
|
||||
* For isc_socket_sendv() and isc_socket_sendtov():
|
||||
* 'buflist' is non-NULL, and '*buflist' contain at least one buffer.
|
||||
*
|
||||
* 'task' is a valid task
|
||||
*
|
||||
* action == NULL or is a valid action
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* ISC_R_SUCCESS
|
||||
* ISC_R_NOMEMORY
|
||||
* ISC_R_UNEXPECTED
|
||||
|
||||
@@ -1879,7 +1879,6 @@ isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist,
|
||||
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));
|
||||
@@ -1891,12 +1890,7 @@ isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist,
|
||||
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;
|
||||
}
|
||||
iocount = isc_bufferlist_availablecount(buflist);
|
||||
REQUIRE(iocount > 0);
|
||||
|
||||
LOCK(&sock->lock);
|
||||
@@ -1925,8 +1919,6 @@ isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist,
|
||||
dev->minimum = minimum;
|
||||
}
|
||||
|
||||
dev->result = ISC_R_SUCCESS;
|
||||
dev->n = 0;
|
||||
dev->sender = task;
|
||||
|
||||
/*
|
||||
@@ -1936,13 +1928,13 @@ isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist,
|
||||
while (buffer != NULL) {
|
||||
ISC_LIST_DEQUEUE(*buflist, buffer, link);
|
||||
ISC_LIST_ENQUEUE(dev->bufferlist, buffer, link);
|
||||
buffer = ISC_LIST_HEAD(*buflist);
|
||||
}
|
||||
|
||||
was_empty = ISC_LIST_EMPTY(sock->recv_list);
|
||||
|
||||
/*
|
||||
* If the read queue is empty, try to do the I/O right now.
|
||||
*/
|
||||
was_empty = ISC_LIST_EMPTY(sock->recv_list);
|
||||
if (!was_empty)
|
||||
goto queue;
|
||||
|
||||
@@ -2127,8 +2119,6 @@ isc_socket_sendto(isc_socket_t *sock, isc_region_t *region,
|
||||
|
||||
LOCK(&sock->lock);
|
||||
|
||||
manager = sock->manager;
|
||||
|
||||
dev = allocate_socketevent(sock, ISC_SOCKEVENT_SENDDONE, action, arg);
|
||||
if (dev == NULL) {
|
||||
UNLOCK(&sock->lock);
|
||||
@@ -2189,6 +2179,114 @@ isc_socket_sendto(isc_socket_t *sock, isc_region_t *region,
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_socket_sendv(isc_socket_t *sock, isc_bufferlist_t *buflist,
|
||||
isc_task_t *task, isc_taskaction_t action, void *arg)
|
||||
{
|
||||
return (isc_socket_sendtov(sock, buflist, task, action, arg, NULL));
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_socket_sendtov(isc_socket_t *sock, isc_bufferlist_t *buflist,
|
||||
isc_task_t *task, isc_taskaction_t action, void *arg,
|
||||
isc_sockaddr_t *address)
|
||||
{
|
||||
isc_socketevent_t *dev;
|
||||
isc_socketmgr_t *manager;
|
||||
isc_task_t *ntask = NULL;
|
||||
isc_boolean_t was_empty;
|
||||
unsigned int iocount;
|
||||
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 = isc_bufferlist_usedcount(buflist);
|
||||
REQUIRE(iocount > 0);
|
||||
|
||||
LOCK(&sock->lock);
|
||||
|
||||
dev = allocate_socketevent(sock, ISC_SOCKEVENT_SENDDONE, 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.
|
||||
***/
|
||||
|
||||
dev->sender = task;
|
||||
|
||||
set_dev_address(address, sock, dev);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
buffer = ISC_LIST_HEAD(*buflist);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the read queue is empty, try to do the I/O right now.
|
||||
*/
|
||||
was_empty = ISC_LIST_EMPTY(sock->send_list);
|
||||
if (!was_empty)
|
||||
goto queue;
|
||||
|
||||
if (sock->send_result != ISC_R_SUCCESS) {
|
||||
send_senddone_event(sock, &dev, sock->send_result);
|
||||
UNLOCK(&sock->lock);
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
switch (doio_send(sock, dev)) {
|
||||
case DOIO_SOFT:
|
||||
goto queue;
|
||||
break;
|
||||
|
||||
case DOIO_HARD:
|
||||
case DOIO_UNEXPECTED:
|
||||
case DOIO_SUCCESS:
|
||||
UNLOCK(&sock->lock);
|
||||
return (ISC_R_SUCCESS);
|
||||
break;
|
||||
}
|
||||
|
||||
queue:
|
||||
/*
|
||||
* We couldn't send all or part of the request right now, so queue
|
||||
* it.
|
||||
*/
|
||||
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->send_list, dev, link);
|
||||
if (was_empty)
|
||||
select_poke(sock->manager, sock->fd);
|
||||
|
||||
XTRACE(TRACE_SEND,
|
||||
("isc_socket_send: queued event %p, task %p\n", dev, ntask));
|
||||
|
||||
UNLOCK(&sock->lock);
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_socket_bind(isc_socket_t *sock, isc_sockaddr_t *sockaddr)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user