summaryrefslogtreecommitdiffstats
path: root/lib/qiovec.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/qiovec.c')
-rw-r--r--lib/qiovec.c280
1 files changed, 221 insertions, 59 deletions
diff --git a/lib/qiovec.c b/lib/qiovec.c
index 38ea0dbb..2946efaa 100644
--- a/lib/qiovec.c
+++ b/lib/qiovec.c
@@ -22,6 +22,7 @@
#include <errno.h>
#include "misc.h"
+#include "qdebug_nb.h"
#include "memory.h"
#include "miyagi.h"
@@ -54,12 +55,12 @@
* Returns: address of qiovec
*/
extern qiovec
-qiovec_init_new(qiovec viov)
+qiovec_init_new(qiovec qiov)
{
- if (viov == NULL)
- viov = XCALLOC(MTYPE_QIOVEC, sizeof(struct qiovec)) ;
+ if (qiov == NULL)
+ qiov = XCALLOC(MTYPE_QIOVEC, sizeof(struct qiovec)) ;
else
- memset(viov, 0, sizeof(struct qiovec)) ;
+ memset(qiov, 0, sizeof(struct qiovec)) ;
/* Zeroising has set:
*
@@ -74,7 +75,7 @@ qiovec_init_new(qiovec viov)
* Nothing more is required.
*/
- return viov ;
+ return qiov ;
} ;
/*------------------------------------------------------------------------------
@@ -83,81 +84,188 @@ qiovec_init_new(qiovec viov)
* Returns: address of qiovec (if any) -- NULL if structure released
*/
extern qiovec
-qiovec_reset(qiovec viov, bool free_structure)
+qiovec_reset(qiovec qiov, bool free_structure)
{
- if (viov != NULL)
+ if (qiov != NULL)
{
- if (viov->vec != NULL)
- XFREE(MTYPE_QIOVEC_VEC, viov->vec) ;
+ if (qiov->vec != NULL)
+ XFREE(MTYPE_QIOVEC_VEC, qiov->vec) ;
if (free_structure)
- XFREE(MTYPE_QIOVEC, viov) ; /* sets viov = NULL */
+ XFREE(MTYPE_QIOVEC, qiov) ; /* sets qiov = NULL */
else
- qiovec_init_new(viov) ; /* re-initialise */
+ qiovec_init_new(qiov) ; /* re-initialise */
} ;
- return viov ;
+ return qiov ;
} ;
/*------------------------------------------------------------------------------
* Clear given qiovec.
*/
extern void
-qiovec_clear(qiovec viov)
+qiovec_clear(qiovec qiov)
{
- viov->i_get = 0 ;
- viov->i_put = 0 ;
- viov->writing = 0 ;
+ qiov->i_get = 0 ;
+ qiov->i_put = 0 ;
+ qiov->writing = 0 ;
} ;
/*------------------------------------------------------------------------------
- * Push item to given qiovec
- *
- * NB: avoids pushing zero length items.
+ * Establish total length of the qiov -- add up all the item lengths.
*/
-extern void
-qiovec_push(qiovec viov, const void* base, size_t len)
+extern size_t
+qiovec_length(qiovec qiov)
{
- struct iovec* p_iov ;
+ if (qiov->i_put > qiov->i_get)
+ {
+ size_t length ;
+ uint n ;
+ qiov_item item ;
- if (len == 0)
- return ;
+ n = qiov->i_put - qiov->i_get ;
+ item = &qiov->vec[qiov->i_get] ;
- if (viov->i_put >= viov->i_alloc)
+ length = item->len ;
+ while (--n)
+ length += (++item)->len ;
+
+ return length ;
+ }
+ else
{
- size_t size ;
- assert(viov->i_put == viov->i_alloc) ;
+ assert(qiov->i_get == qiov->i_put) ;
+ qiov->i_get = qiov->i_put = 0 ;
- assert( ((viov->i_alloc == 0) && (viov->vec == NULL))
- || ((viov->i_alloc != 0) && (viov->vec != NULL)) ) ;
+ return 0 ;
+ } ;
+} ;
- if (viov->i_get > 200) /* keep in check */
- {
- size = (viov->i_put - viov->i_get) * sizeof(struct iovec) ;
- if (size != 0)
- memmove(viov->vec, &viov->vec[viov->i_get], size) ;
- viov->i_put -= viov->i_get ;
- viov->i_get = 0 ;
- }
- else
- {
- viov->i_alloc += 100 ; /* a sizable chunk */
- size = viov->i_alloc * sizeof(struct iovec) ;
- if (viov->vec == NULL)
- viov->vec = XMALLOC(MTYPE_QIOVEC_VEC, size) ;
- else
- viov->vec = XREALLOC(MTYPE_QIOVEC_VEC, viov->vec, size) ;
- } ;
+/*==============================================================================
+ * Extending and shuffling qiovec in order to support push and unshift
+ * operations.
+ *
+ * Note that if unshift becomes common, then should adjust the following.
+ * Also address the tendency to reset i_get and i_put to zero whenever they
+ * are found to be equal.
+ */
+enum
+{
+ i_get_limit = 200, /* in qiovec_extend, shuffle vector down if
+ i_get is >= i_get_limit */
+
+ i_alloc_unit = 100, /* allocate in lots of this much */
+
+ i_get_margin = 20, /* in qiovec_shuffle, if have to reorganise,
+ allow for this many unshifts. */
+
+ i_put_margin = 40, /* in qiovec_shuffle, if have to reorganise,
+ allow for this many pushes. */
+} ;
+
+static void qiovec_move_vector(qiovec qiov, uint new_i_get) ;
+static void qiovec_new_vector(qiovec qiov, uint items) ;
+
+/*------------------------------------------------------------------------------
+ * Extend -- see qiovec_push.
+ *
+ * NB: underlying vector may be reorganised, reallocated, etc. All pointers
+ * to items are now INVALID.
+ */
+Private void
+qiovec_extend(qiovec qiov)
+{
+ assert(qiov->i_put <= qiov->i_alloc) ;
+ assert(qiov->i_get <= qiov->i_put) ;
+
+ assert( ((qiov->i_alloc == 0) && (qiov->vec == NULL))
+ || ((qiov->i_alloc != 0) && (qiov->vec != NULL)) ) ;
+
+ /* Short circuit if can make space by resetting indexes */
+ if ((qiov->i_put == qiov->i_get) && (qiov->i_alloc != 0))
+ {
+ qiov->i_put = 0 ;
+ qiov->i_get = 0 ;
+
+ return ;
} ;
- p_iov = &viov->vec[viov->i_put++] ;
+ if (qiov->i_get > i_get_limit) /* keep in check */
+ qiovec_move_vector(qiov, 0) ;
+ else
+ qiovec_new_vector(qiov, i_alloc_unit) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Shuffle to allow for qiovec_unshift.
+ *
+ * Don't expect this to happen often -- but will do it if required.
+ *
+ * On exit the i_get will be > 0.
+ *
+ * NB: underlying vector may be reorganised, reallocated, etc. All pointers
+ * to items are now INVALID.
+ */
+Private void
+qiovec_shuffle(qiovec qiov)
+{
+ assert(qiov->i_put <= qiov->i_alloc) ;
+ assert(qiov->i_get <= qiov->i_put) ;
+
+ assert( ((qiov->i_alloc == 0) && (qiov->vec == NULL))
+ || ((qiov->i_alloc != 0) && (qiov->vec != NULL)) ) ;
+
+ if (qiov->i_get > 0)
+ return ; /* shouldn't be here, really */
+
+ /* If do not have enough spare space for both margins, allocate extra to
+ * the tune of both margins -- will leave at least the margins and at
+ * most nearly twice those margins.
+ *
+ * This will allocate a vector is there is none at all.
+ */
+ if ((qiov->i_alloc - qiov->i_put) < (i_get_margin + i_put_margin))
+ qiovec_new_vector(qiov, i_get_margin + i_put_margin) ;
+
+ /* Shuffle into place and update i_get and i_put. */
+ qiovec_move_vector(qiov, i_get_margin) ;
- p_iov->iov_base = miyagi(base) ;
- p_iov->iov_len = len ;
+ confirm(i_get_margin > 0) ;
} ;
/*------------------------------------------------------------------------------
+ * Move contents of vector and set a new i_get (and i_put to suit).
+ */
+static void
+qiovec_move_vector(qiovec qiov, uint new_i_get)
+{
+ memmove(&qiov->vec[new_i_get], &qiov->vec[qiov->i_get],
+ (qiov->i_put - qiov->i_get) * sizeof(qiovec_t)) ;
+
+ qiov->i_put -= qiov->i_get - new_i_get ;
+ qiov->i_get = new_i_get ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Allocate or reallocate vector for given number of items.
+ */
+static void
+qiovec_new_vector(qiovec qiov, uint items)
+{
+ qiov->i_alloc += items ;
+
+ qiov->vec = XREALLOC(MTYPE_QIOVEC_VEC, qiov->vec,
+ qiov->i_alloc * sizeof(qiovec_t)) ;
+} ;
+
+/*==============================================================================
+ * Writing qiovec and iovec
+ */
+
+static ssize_t writev_qdebug_nb(int fd, struct iovec p_iov[], int n) ;
+
+/*------------------------------------------------------------------------------
* Write given qiovec -- assuming NON-BLOCKING.
*
* Does nothing if the qiovec is empty.
@@ -171,24 +279,24 @@ qiovec_push(qiovec viov, const void* base, size_t len)
* -1 => failed -- see errno
*/
extern int
-qiovec_write_nb(int fd, qiovec viov)
+qiovec_write_nb(int fd, qiovec qiov)
{
int n ;
int l ;
- n = viov->i_put - viov->i_get ;
+ n = qiov->i_put - qiov->i_get ;
- l = iovec_write_nb(fd, &viov->vec[viov->i_get], n) ;
+ l = iovec_write_nb(fd, (struct iovec*)(&qiov->vec[qiov->i_get]), n) ;
if (l == 0)
{
- viov->writing = 0 ;
- viov->i_get = viov->i_put = 0 ;
+ qiov->writing = false ;
+ qiov->i_get = qiov->i_put = 0 ;
}
else
{
- viov->writing = 1 ;
- viov->i_get += (n - l) ;
+ qiov->writing = true ;
+ qiov->i_get += (n - l) ;
} ;
return l ;
@@ -223,8 +331,6 @@ qiovec_write_nb(int fd, qiovec viov)
extern int
iovec_write_nb(int fd, struct iovec p_iov[], int n)
{
- ssize_t ret ;
-
assert(n >= 0) ;
/* Skip past any leading zero length entries */
@@ -236,7 +342,12 @@ iovec_write_nb(int fd, struct iovec p_iov[], int n)
while (n > 0)
{
- ret = writev(fd, p_iov, (n < IOV_MAX ? n : IOV_MAX)) ;
+ ssize_t ret ;
+
+ if (qdebug_nb)
+ ret = writev_qdebug_nb(fd, p_iov, (n < IOV_MAX ? n : IOV_MAX)) ;
+ else
+ ret = writev (fd, p_iov, (n < IOV_MAX ? n : IOV_MAX)) ;
if (ret > 0)
{
@@ -257,6 +368,7 @@ iovec_write_nb(int fd, struct iovec p_iov[], int n)
ret = 0 ;
} ;
} ;
+
}
else if (ret == 0)
break ; /* not sure can happen... but
@@ -267,9 +379,59 @@ iovec_write_nb(int fd, struct iovec p_iov[], int n)
if ((err == EAGAIN) || (err == EWOULDBLOCK))
break ;
if (err != EINTR)
+// assert(0) ; // Pro tem
return -1 ; /* failed */
} ;
} ;
return n ;
} ;
+
+/*==============================================================================
+ * Simulation of writev() for debug purposes -- generates lots of partial
+ * writes and lots of blocking
+ *
+ */
+
+static qrand_seq_t wseq = QRAND_SEQ_INIT(4001) ;
+
+static const int blocking_errs[] = { EAGAIN, EWOULDBLOCK, EINTR } ;
+
+/*------------------------------------------------------------------------------
+ * Simulate writev() with tiny output buffers and lots of blocking.
+ */
+static ssize_t
+writev_qdebug_nb(int fd, struct iovec p_iov[], int n)
+{
+ struct iovec* q_iov ;
+ size_t len ;
+ ssize_t ret ;
+
+ assert(n > 0) ;
+
+ if (qrand(wseq, 3) == 0) /* 1/3 chance of blocking */
+ {
+ errno = blocking_errs[qrand(wseq, 3)] ;
+ return -1 ;
+ } ;
+
+ /* Process 1..n or 1..3 entries */
+ n = qrand(wseq, (n < 3) ? n : 3) ;
+ q_iov = p_iov + n ;
+ ++n ;
+
+ /* If new last entry is not zero length, write only part of
+ * it.
+ */
+ len = q_iov->iov_len ;
+ if (len > 0)
+ q_iov->iov_len = qrand(wseq, (len < 200) ? len : 200) + 1 ;
+
+ ret = writev(fd, p_iov, n) ;
+
+ q_iov->iov_len = len ; /* restore true length of entry */
+
+ return ret ;
+} ;
+
+