diff options
Diffstat (limited to 'lib/qiovec.c')
-rw-r--r-- | lib/qiovec.c | 280 |
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 ; +} ; + + |