summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/qpselect.c632
-rw-r--r--lib/qpselect.h17
2 files changed, 449 insertions, 200 deletions
diff --git a/lib/qpselect.c b/lib/qpselect.c
index e792bbaa..9ab25018 100644
--- a/lib/qpselect.c
+++ b/lib/qpselect.c
@@ -87,11 +87,6 @@
*
* During an action function modes may be enabled/disabled, actions changed,
* the file removed from the selection... there are no restrictions.
- *
- *
- * TODO: worry about closing down a selection.
- *
- * TODO: worry about closing down files
*/
static int qps_super_set_map_made = 0 ;
@@ -105,9 +100,9 @@ static void qps_make_super_set_map(void) ;
/* Forward references */
static qps_file qps_file_lookup_fd(qps_selection qps, int fd, qps_file insert) ;
static void qps_file_remove(qps_selection qps, qps_file qf) ;
-static int qps_file_check(qps_selection qps, qps_file qf) ;
static void qps_super_set_zero(fd_super_set* p_set, int n) ;
static int qps_next_fd_pending(fd_super_set* pending, int fd, int fd_last) ;
+static void qps_selection_validate(qps_selection qps) ;
/* See qps_make_super_set_map() and qps_pselect() below. */
static short fd_byte_count[FD_SETSIZE] ; /* number of bytes for fds 0..fd */
@@ -149,22 +144,34 @@ qps_selection_init_new(qps_selection qps)
* signum -- no signal to be enabled
* sigmask -- unset
*
- * So nothing else to do.
+ * So nothing else to do -- see also qps_selection_re_init(), below.
*/
return qps ;
} ;
+/* Re-initialise a selection.
+ */
+static void
+qps_selection_re_init(qps_selection qps)
+{
+ memset(qps, 0, sizeof(struct qps_selection)) ;
+} ;
+
/* Add given file to the selection, setting its fd and pointer to further
* file information. All modes are disabled.
*
* This initialises most of the qps_file structure, but not the actions.
*
- * Adding a file using the same fd as an existing file is a fatal error.
+ * Adding a file using the same fd as an existing file is a FATAL error.
+ *
+ * Adding a file which is already a member a selection is a FATAL error.
*/
void
qps_add_file(qps_selection qps, qps_file qf, int fd, void* file_info)
{
+ passert(qf->selection == NULL) ;
+
qf->selection = qps ;
qf->file_info = file_info ;
@@ -175,24 +182,60 @@ qps_add_file(qps_selection qps, qps_file qf, int fd, void* file_info)
qps_file_lookup_fd(qps, fd, qf) ; /* Add. */
} ;
-/* Remove given file from the selection.
+/* Remove given file from its selection, if any.
*
* It is the callers responsibility to ensure that the file is in a suitable
* state to be removed from the selection.
*
- * The file is disabled in all modes.
- *
- * NB: it is a FATAL error to remove a file that is not a member of its
- * selection.
+ * When the file is removed it is disabled in all modes.
*/
void
qps_remove_file(qps_file qf)
{
- passert(qf->selection != NULL) ;
+ if (qf->selection != NULL)
+ qps_file_remove(qf->selection, qf) ;
+} ;
+
+/* Ream (another) file out of the selection.
+ *
+ * If selection is empty, release the qps_selection structure, if required.
+ *
+ * See: #define qps_selection_ream_free(qps)
+ * #define qps_selection_ream_keep(qps)
+ *
+ * Useful for emptying out and discarding a selection:
+ *
+ * while ((qf = qps_selection_ream_free(qps)))
+ * ... do what's required to release the qps_file
+ *
+ * The file is removed from the selection before being returned.
+ *
+ * Returns NULL when selection is empty (and has been released, if required).
+ *
+ * If the selection is not released, it may be reused without reinitialisation.
+ *
+ * NB: once reaming has started, the selection MUST NOT be used for anything,
+ * and the process MUST be run to completion.
+ */
+qps_file
+qps_selection_ream(qps_selection qps, int free_structure)
+{
+ qps_file qf ;
+
+ qf = vector_ream_keep(&qps->files) ;
+ if (qf != NULL)
+ qps_file_remove(qps, qf) ;
+ else
+ {
+ passert(qps->fd_count == 0) ;
- qps_file_remove(qf->selection, qf) ;
+ if (free_structure)
+ XFREE(MTYPE_QPS_SELECTION, qps) ;
+ else
+ qps_selection_re_init(qps) ;
+ } ;
- qf->selection = NULL ; /* no longer a selection member */
+ return qf ;
} ;
/* Set the signal mask for the selection.
@@ -248,10 +291,17 @@ qps_pselect(qps_selection qps, qtime_t timeout)
fd_set* p_fds[qps_mnum_count] ;
int n ;
- /* Convert timeout time to interval for pselect() */
- timeout -= qt_get_monotonic() ;
- if (timeout < 0)
- timeout = 0 ;
+ /* TODO: put this under a debug skip */
+ qps_selection_validate(qps) ;
+
+ /* If there is stuff still pending, tidy up by zeroising the result */
+ /* vectors. This is to make sure that when bits are copied from */
+ /* the enabled vectors, there are none from a previous run of pselect */
+ /* left hanging about. (pselect SHOULD ignore everything above the */
+ /* given count of fds -- but it does no harm to be tidy, and should */
+ /* not have to do this often.) */
+ if (qps->pend_count != 0)
+ qps_super_set_zero(qps->enabled, qps_mnum_count) ;
/* Prepare the argument/result bitmaps */
/* Capture pend_mnum and tried_count[] */
@@ -274,6 +324,11 @@ qps_pselect(qps_selection qps, qtime_t timeout)
qps->tried_fd_last = qps->fd_last ;
qps->pend_fd = 0 ;
+ /* Convert timeout time to interval for pselect() */
+ timeout -= qt_get_monotonic() ;
+ if (timeout < 0)
+ timeout = 0 ;
+
/* Finally ready for the main event */
n = pselect(qps->fd_last + 1, p_fds[qps_read_mnum],
p_fds[qps_write_mnum],
@@ -314,12 +369,19 @@ qps_dispatch_next(qps_selection qps)
qps_file qf ;
qps_mnum_t mnum ;
+ /* TODO: put this under a debug skip */
+ qps_selection_validate(qps) ;
+
if (qps->pend_count == 0)
return 0 ; /* quit immediately of nothing to do. */
fd = qps->pend_fd ;
mnum = qps->pend_mnum ;
+ dassert( (mnum >= 0) && (mnum < qps_mnum_count)
+ && (qps->tried_count[mnum] != 0)
+ && (qps->pend_count > 0) ) ;
+
while (1)
{
fd = qps_next_fd_pending(&(qps->results[mnum]), fd, qps->tried_fd_last) ;
@@ -328,6 +390,7 @@ qps_dispatch_next(qps_selection qps)
do /* step to next mode that was not empty */
{
+ qps->tried_count[mnum] = 0 ; /* tidy up as we go */
++mnum ;
if (mnum >= qps_mnum_count)
zabort("Unexpectedly ran out of pending stuff") ;
@@ -338,7 +401,15 @@ qps_dispatch_next(qps_selection qps)
} ;
qps->pend_count -= 1 ; /* one less pending */
- qps->pend_fd = fd ; /* update scan */
+
+ if (qps->pend_count > 0)
+ qps->pend_fd = fd ; /* update scan */
+ else
+ {
+ qps->pend_mnum = 0 ; /* tidy up as we complete */
+ qps->pend_fd = 0 ;
+ qps->tried_fd_last = 0 ;
+ } ;
qf = qps_file_lookup_fd(qps, fd, NULL) ;
@@ -417,8 +488,8 @@ qps_enable_mode(qps_file qf, qps_mnum_t mnum, qps_action* action)
qps_mbit_t mbit = qps_mbit(mnum) ;
qps_selection qps = qf->selection ;
- dassert((qps != NULL) && (qps_file_check(qps, qf))) ;
- dassert(mnum <= qps_mnum_count) ;
+ dassert(qps != NULL) ;
+ dassert((mnum >= 0) && (mnum <= qps_mnum_count)) ;
if (action != NULL)
qf->actions[mnum] = action ;
@@ -447,10 +518,7 @@ qps_enable_mode(qps_file qf, qps_mnum_t mnum, qps_action* action)
void
qps_set_action(qps_file qf, qps_mnum_t mnum, qps_action* action)
{
- qps_selection qps = qf->selection ;
-
- dassert((qps != NULL) && (qps_file_check(qps, qf))) ;
- dassert(mnum < qps_mnum_count) ;
+ dassert((mnum >= 0) && (mnum <= qps_mnum_count)) ;
if (action == NULL)
passert((qf->enabled_bits & qps_mbit(mnum)) == 0) ;
@@ -464,6 +532,10 @@ qps_set_action(qps_file qf, qps_mnum_t mnum, qps_action* action)
*
* Note that this is modestly "optimised" to deal with disabling a single mode.
* (Much of the time only the write mode will be being disabled !)
+ *
+ * NB: it is safe to disable modes which are not enabled -- even if the file
+ * is not currently a member of a selection. (If it is not a member of a
+ * collection no modes should be enabled !)
*/
static qps_mnum_t qps_first_mnum[qps_mbit(qps_mnum_count)] =
@@ -478,18 +550,6 @@ static qps_mnum_t qps_first_mnum[qps_mbit(qps_mnum_count)] =
2 /* 7 -> 2 -- B2 is first bit */
} ;
-static qps_mbit_t qps_first_mbit[qps_mbit(qps_mnum_count)] =
- {
- 0, /* 0 -> 0 -- no bit set */
- 1, /* 1 -> 1 -- B0 is first bit */
- 2, /* 2 -> 2 -- B1 is first bit */
- 2, /* 3 -> 2 -- B1 is first bit */
- 4, /* 4 -> 4 -- B2 is first bit */
- 4, /* 5 -> 4 -- B2 is first bit */
- 4, /* 6 -> 4 -- B2 is first bit */
- 4 /* 7 -> 4 -- B2 is first bit */
- } ;
-
CONFIRM(qps_mbit(qps_mnum_count) == 8) ;
void
@@ -499,8 +559,7 @@ qps_disable_modes(qps_file qf, qps_mbit_t mbits)
qps_selection qps = qf->selection ;
- dassert((qps != NULL) && (qps_file_check(qps, qf))) ;
- dassert(mbits <= qps_all_mbits) ;
+ dassert((mbits >= 0) && (mbits <= qps_all_mbits)) ;
mbits &= qf->enabled_bits ; /* don't bother with any not enabled */
qf->enabled_bits ^= mbits ; /* unset what we're about to disable */
@@ -509,21 +568,29 @@ qps_disable_modes(qps_file qf, qps_mbit_t mbits)
{
mnum = qps_first_mnum[mbits] ;
- if (FD_ISSET(qf->fd, &(qps->enabled[mnum].fdset)))
- {
- FD_CLR(qf->fd, &(qps->enabled[mnum].fdset)) ;
- dassert(qps->enabled_count[mnum] > 0) ;
- --qps->enabled_count[mnum] ;
- } ;
+ dassert(qps->enabled_count[mnum] > 0) ;
+ dassert(FD_ISSET(qf->fd, &(qps->enabled[mnum].fdset))) ;
+
+ FD_CLR(qf->fd, &(qps->enabled[mnum].fdset)) ;
+ --qps->enabled_count[mnum] ;
+
if ((qps->pend_count != 0) && (qps->tried_count[mnum] != 0)
&& (FD_ISSET(qf->fd, &(qps->results[mnum].fdset))))
{
FD_CLR(qf->fd, &(qps->results[mnum].fdset)) ;
- dassert(qps->pend_count > 0) ;
--qps->pend_count ;
+ if (qps->pend_count == 0)
+ {
+ qps_mnum_t m ;
+ qps->tried_fd_last = 0 ;
+ for (m = 0 ; m < qps_mnum_count ; ++m)
+ qps->tried_count[mnum] = 0 ;
+ qps->pend_mnum = 0 ;
+ qps->pend_fd = 0 ;
+ } ;
} ;
- mbits ^= qps_first_mbit[mnum] ;
+ mbits ^= qps_mbit(mnum) ;
} ;
} ;
@@ -561,59 +628,62 @@ static qps_file
qps_file_lookup_fd(qps_selection qps, int fd, qps_file insert)
{
qps_file qf ;
+ vector_index i ;
+ int ret ;
- dassert((fd >= 0) && (fd < FD_SETSIZE)) ;
-
- /* If we are expecting to insert, then now may be the time to change */
- /* up to a directly address files vector. */
- if ((insert != NULL) && !qps->fd_direct && (qps->fd_count > 9))
- {
- vector_index i ;
- vector tmp ;
-
- tmp = vector_move_here(NULL, &qps->files) ;
-
- for (VECTOR_ITEMS(tmp, qf, i))
- vector_set_item(&qps->files, qf->fd, qf) ;
-
- vector_free(tmp) ;
+ dassert((fd >= 0) && (fd <= qps->fd_last)) ;
- qps->fd_direct = 1 ;
- } ;
-
- /* Look-up and perhaps insert. */
+ /* Look-up */
+ /* */
+ /* Set i = index for entry in files vector */
+ /* Set ret = 0 <=> i is exact index. */
+ /* < 0 <=> i is just after where entry may be inserted */
+ /* > 0 <=> i is just before where entry may be inserted */
if (qps->fd_direct)
{
- qf = vector_get_item(&qps->files, fd) ; /* NULL if not there */
- if (insert != NULL)
- {
- if (qf != NULL)
- zabort("File with given fd already exists in qps_selection") ;
- qf = insert ;
- vector_set_item(&qps->files, fd, insert) ;
- } ;
+ i = fd ; /* index of entry */
+ ret = 0 ; /* how to insert, if do */
}
else
+ i = vector_bsearch(&qps->files, (vector_bsearch_cmp*)qps_fd_cmp,
+ &fd, &ret) ;
+ if (ret == 0)
+ qf = vector_get_item(&qps->files, i) ; /* NULL if not there */
+ else
+ qf = NULL ; /* not there */
+
+ /* Insert now, if required and can: keep fd_count and fd_last up to date. */
+ if (insert != NULL)
{
- int ret ;
- vector_index i = vector_bsearch(&qps->files,
- (vector_bsearch_cmp*)qps_fd_cmp,
- &fd, &ret) ;
- if (insert != NULL)
+ if (qf != NULL)
+ zabort("File with given fd already exists in qps_selection") ;
+
+ /* If required, change up to a directly addressed files vector. */
+ if (!qps->fd_direct && (qps->fd_count > 9))
{
- if (ret == 0)
- zabort("File with given fd already exists in qps_selection") ;
- qf = insert ;
- vector_insert_item_here(&qps->files, i, ret, insert) ;
+ vector tmp ;
+
+ tmp = vector_move_here(NULL, &qps->files) ;
+
+ while ((qf = vector_pop_item(tmp)) != NULL)
+ vector_set_item(&qps->files, qf->fd, qf) ;
+
+ vector_free(tmp) ;
+
+ qps->fd_direct = 1 ;
+
+ i = fd ; /* index is now the fd */
+ ret = 0 ; /* and insert there */
} ;
- } ;
- /* If we inserted, keep fd_count and fd_last up to date. */
- if (insert != NULL)
- {
+ /* Now can insert accordint to i & ret */
+ vector_insert_item_here(&qps->files, i, ret, insert) ;
+
++qps->fd_count ;
if (fd > qps->fd_last)
qps->fd_last = fd ;
+
+ qf = insert ; /* will return what we just inserted. */
} ;
/* Sanity checking. */
@@ -623,122 +693,60 @@ qps_file_lookup_fd(qps_selection qps, int fd, qps_file insert)
return qf ;
} ;
-/* Check file in selection.
- *
- * This looks up the file in the selection and performs a number of sanity
- * checks.
- *
- * NB: it is a FATAL error if the file has an invalid fd, or the selection does
- * not natch the file's selection, or the files selection is NULL.
- *
- * NB: it is a FATAL error for the file not to be in the selection when looked
- * up by its fd.
- *
- * When (or if) returns, returns 1.
- */
-static int
-qps_file_check(qps_selection qps, qps_file qf)
-{
- qps_file qff ;
- int fd = qf->fd ;
-
- passert((fd >= 0) && (fd < FD_SETSIZE)
- && (qf->selection != NULL) && (qps == qf->selection)) ;
-
- if (qps->fd_direct)
- qff = vector_get_item(&qps->files, fd) ; /* NULL if not there */
- else
- {
- int ret ;
- vector_index i = vector_bsearch(&qps->files,
- (vector_bsearch_cmp*)qps_fd_cmp,
- &fd, &ret) ;
- if (ret == 0)
- qff = vector_get_item(&qps->files, i) ;
- else
- qff = NULL ;
- } ;
-
- passert(qff == qf) ;
-
- return 1 ;
-} ;
-
-/* Remove file from selection by file-descriptor.
- *
- * Returns the file we found and have removed (if any).
+/* Remove file from selection.
*
- * TODO: should deleting a non existent file be fatal ?
+ * NB: FATAL error if file is not in the selection, or the file-descriptor
+ * is invalid (or refers to some other file !).
*/
-static qps_file
-qps_file_remove_fd(qps_selection qps, int fd)
+static void
+qps_file_remove(qps_selection qps, qps_file qf)
{
- qps_file qf ;
- int fd_last ;
+ qps_file qfd ;
+ int fd_last ;
- dassert((fd >= 0) && (fd < FD_SETSIZE)) ;
+ passert((qf->fd >= 0) && (qf->fd <= qps->fd_last) && (qps == qf->selection)) ;
/* Look-up and remove. */
if (qps->fd_direct)
{
- qf = vector_unset_item(&qps->files, fd) ; /* NULL if not there */
+ qfd = vector_unset_item(&qps->files, qf->fd) ; /* NULL if not there */
fd_last = vector_end(&qps->files) - 1 ;
}
else
{
+ qps_file qf_last ;
int ret ;
vector_index i = vector_bsearch(&qps->files,
(vector_bsearch_cmp*)qps_fd_cmp,
- &fd, &ret) ;
+ &qf->fd, &ret) ;
if (ret == 0)
- {
- qps_file qf_last ;
- qf = vector_delete_item(&qps->files, i) ;
+ qfd = vector_delete_item(&qps->files, i) ;
+ else
+ qfd = NULL ;
- qf_last = vector_get_last_item(&qps->files) ;
- if (qf_last == NULL)
- fd_last = -1 ;
- else
- fd_last = qf_last->fd ;
- }
+ qf_last = vector_get_last_item(&qps->files) ;
+ if (qf_last != NULL)
+ fd_last = qf_last->fd ;
else
- qf = NULL ;
+ fd_last = -1 ;
} ;
- /* If we removed, keep fd_count and fd_last up to date. */
- /* Also, remove the fd from all vectors. */
- if (qf != NULL)
- {
- dassert(qps->fd_count != 0) ;
- ++qps->fd_count ;
+ passert(qfd == qf) ; /* must have been there and be the expected file */
- dassert( ((qps->fd_count != 0) && (fd_last >= 0)) ||
- ((qps->fd_count == 0) && (fd_last < 0)) ) ;
+ /* Keep fd_count and fd_last up to date. */
+ dassert(qps->fd_count > 0) ;
+ --qps->fd_count ;
- qps->fd_last = (fd_last >= 0) ? fd_last : 0 ;
+ dassert( ((qps->fd_count != 0) && (fd_last >= 0)) ||
+ ((qps->fd_count == 0) && (fd_last < 0)) ) ;
- qps_disable_modes(qf, qps_all_mbits) ;
- } ;
+ qps->fd_last = (fd_last >= 0) ? fd_last : 0 ;
- /* Return the file we found and have removed. */
- return qf ;
-} ;
+ /* Also, remove the from all vectors. */
+ qps_disable_modes(qf, qps_all_mbits) ;
-/* Remove file from selection.
- *
- * NB: FATAL error if file is not in the selection, or the file-descriptor
- * is invalid (or refers to some other file !).
- */
-static void
-qps_file_remove(qps_selection qps, qps_file qf)
-{
- qps_file qfd ;
-
- passert((qf->fd >= 0) && (qf->fd < FD_SETSIZE) && (qps == qf->selection)) ;
-
- qfd = qps_file_remove_fd(qps, qf->fd) ;
-
- passert(qfd == qf) ;
+ /* Is no longer in the selection. */
+ qf->selection = NULL ;
} ;
/*==============================================================================
@@ -790,20 +798,15 @@ qps_next_fd_pending(fd_super_set* pending, int fd, int fd_last)
while (pending->words[fd_word_map[fd]] == 0) /* step past zero words */
{
- fd = (fd & ~ 0x001F) + FD_WORD_BITS ;
+ fd = (fd & ~ (FD_WORD_BITS - 1)) + FD_WORD_BITS ;
/* step to start of next word */
if (fd > fd_last)
return -1 ; /* quit if past last */
} ;
fd &= ~0x0007 ; /* step back to first in byte */
- while (1)
+ while ((b = pending->bytes[fd_byte_map[fd]]) == 0)
{
- b = pending->bytes[fd_byte_map[fd]] ;
- /* byte associated with fd */
- if (b != 0)
- break ; /* stop looking */
-
fd += 8 ;
if (fd > fd_last)
return -1 ;
@@ -933,26 +936,32 @@ qps_make_super_set_map(void)
for (i = 0 ; i < 256 ; ++i)
fd_first_map[i] = -1 ;
- for (i = 0 ; i < 8 ; ++i)
+ for (fd = 0 ; fd < 8 ; ++fd)
{
- uint8_t fdb = fd_bit_map[i] ;
- uint8_t b ;
- for (b = 0 ; b < 256 ; ++b)
- if ((fd_first_map[b] == -1) && ((b & fdb) != 0))
- fd_first_map[b] = i ;
+ uint8_t fdb = fd_bit_map[fd] ;
+ for (i = 0 ; i < 256 ; ++i)
+ if ((fd_first_map[i] == -1) && ((i & fdb) != 0))
+ fd_first_map[i] = fd ;
} ;
+ for (i = 0 ; i < 256 ; ++i)
+ if (fd_first_map[i] == -1)
+ zabort("Broken fd_first_map -- missing bits") ;
+
/* (7) construct fd_byte_count[] -- number of bytes required to */
/* include fds 0..fd. */
i = 0 ;
for (fd = 0 ; fd < FD_SETSIZE ; ++fd)
{
- fd_byte_count[fd] = fd_byte_map[fd] + 1 ;
- if (fd_byte_count[fd] < i)
- fd_byte_count[fd] = i ;
+ int c = fd_byte_map[fd] + 1 ;
+
+ if (c < i)
+ c = i ; /* use largest so far. => big-endian */
else
- i = fd_byte_count[fd] ;
+ i = c ; /* keep largest so far up to date */
+
+ fd_byte_count[fd] = c ;
} ;
/* Phew -- we're all set now */
@@ -971,3 +980,232 @@ qps_super_set_zero(fd_super_set* p_set, int n)
{
memset(p_set, 0, SIZE(fd_super_set, n)) ;
} ;
+
+#if 0 /* Mask unused function */
+/* Copy 'n' contiguous fd_super_sets
+ */
+static void
+qps_super_set_copy(fd_super_set* p_dst, fd_super_set* p_src, int n)
+{
+ memcpy(p_dst, p_src, SIZE(fd_super_set, n)) ;
+} ;
+#endif
+
+/* Compare 'n' contiguous fd_super_sets
+ */
+static int
+qps_super_set_cmp(fd_super_set* p_a, fd_super_set* p_b, int n)
+{
+ return memcmp(p_a, p_b, SIZE(fd_super_set, n)) ;
+} ;
+
+/* Count the number of bits set in 'n' contiguous fd_super_sets.
+ */
+static int
+qps_super_set_count(fd_super_set* p_set, int n)
+{
+ fd_word_t* p ;
+ int count = 0 ;
+
+ n *= FD_SUPER_SET_WORD_SIZE ;
+ confirm(sizeof(fd_super_set) == (FD_SUPER_SET_WORD_SIZE * FD_WORD_BYTES)) ;
+
+ p = (fd_word_t*)p_set ;
+ while (n--)
+ {
+ fd_word_t w = *p++ ;
+ while (w != 0)
+ {
+ ++count ;
+ w &= (w - 1) ;
+ } ;
+ } ;
+
+ return count ;
+} ;
+
+/*==============================================================================
+ * Selection state check -- for debug purposes.
+ *
+ * Runs a check across a given selection and verifies that:
+ *
+ * 1) for !fd_direct that the files are in fd order in the vector
+ * and are unique, and there are no NULL entries.
+ * 2) for fd_direct that the file fd and the index match
+ * and the last entry is not NULL
+ * 3) that all files point at the selection
+ * 4) that the enabled modes in each file are valid
+ * 5) the number of files in the selection matches fd_count.
+ * 6) the highest numbered fd matches fd_last
+ * 7) that the enabled counts in the selection are correct
+ * 8) that the enabled modes in each file match the enabled modes in the
+ * selection
+ * 9) that no extraneous fds are set in the enabled vectors
+ *
+ * If there are no pending fds:
+ *
+ * 10) if there are no pending fds, that the results vectors are empty
+ * that tried_fd_last, tried_count[], pend_mnum and pend_fd are all zero.
+ *
+ * If there are pending fds:
+ *
+ * 11) that pend_mnum is valid and pend_fd <= tried_fd_last.
+ *
+ * 12) that the tried_count for modes 0..pend_mnum-1 is zero,
+ * and the tried_count for pend_mnum is not.
+ *
+ * 13) that the result vectors for modes where tried count == 0 are empty.
+ *
+ * 14) that the remaining result bits are a subset of the enabled bits.
+ *
+ * 15) that no bits beyond tried_fd_last are set in the result vectors.
+ *
+ * 16) that no bits before pend_fd are set in the pemd_mnum result vector.
+ *
+ * 17) that the number of bits remaining matches pend_count.
+ */
+static void
+qps_selection_validate(qps_selection qps)
+{
+ int fd_last ;
+ int enabled_count[qps_mnum_count] ;
+ fd_full_set enabled ;
+
+ qps_file qf ;
+ int fd, n, mnum, p_mnum ;
+ vector_index i ;
+
+ /* 1..4) Run down the selection vector and check. */
+ /* Collect new enabled_count and enabled bit vectors. */
+
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ enabled_count[mnum] = 0 ;
+ qps_super_set_zero(enabled, qps_mnum_count) ;
+
+ n = 0 ;
+ fd_last = -1 ;
+ for (VECTOR_ITEMS(&qps->files, qf, i))
+ {
+ if (qf != NULL)
+ {
+ ++n ; /* Number of files */
+
+ if (qps->fd_direct)
+ {
+ if (qf->fd != (int)i) /* index and fd must match */
+ zabort("File vector index and fd mismatch") ;
+ }
+ else
+ {
+ if (qf->fd <= fd_last) /* must be unique and in order */
+ zabort("File vector not in order") ;
+ } ;
+
+ fd_last = qf->fd ; /* keep track of last fd */
+
+ if (qf->selection != qps) /* file must refer to selection */
+ zabort("File does not refer to its selection") ;
+
+ if ((qf->enabled_bits < 0) || (qf->enabled_bits > qps_all_mbits))
+ zabort("File enabled bits are invalid") ;
+
+ /* Capture enabled state of all files. */
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if (qf->enabled_bits & qps_mbit(mnum))
+ {
+ ++enabled_count[mnum] ;
+ FD_SET(qf->fd, &enabled[mnum].fdset) ;
+ } ;
+ }
+ else
+ if (!qps->fd_direct)
+ zabort("Found NULL entry in !fd_direct files vector") ;
+ } ;
+
+ if ((n != 0) && (vector_get_last_item(&qps->files) == NULL))
+ zabort("Last entry in file vector is NULL") ;
+
+ /* 5) check that the number of files tallies. */
+ if (n != qps->fd_count)
+ zabort("Number of files in the selection does not tally") ;
+
+ /* 6) check the last fd */
+ if ( ((n == 0) && (qps->fd_last !=0)) || (fd_last != qps->fd_last) )
+ zabort("The last fd does not tally") ;
+
+ /* 7) check that the enabled counts tally. */
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if (enabled_count[mnum] != qps->enabled_count[mnum])
+ zabort("Enabled counts do not tally") ;
+
+ /* 8..9) Check that the enabled vectors are the same as the ones just */
+ /* created by scanning the files. */
+ if (qps_super_set_cmp(enabled, qps->enabled, qps_mnum_count) != 0)
+ zabort("Enabled bit vectors do not tally") ;
+
+ /* 10) if there are no pending fds, check that everything is zero. */
+ if (qps->pend_count == 0)
+ {
+ n = (qps_super_set_count(qps->results, qps_mnum_count) != 0) ;
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ n |= (qps->tried_count[mnum] != 0) ;
+ n |= (qps->tried_fd_last != 0) || (qps->pend_mnum != 0)
+ || (qps->pend_fd != 0) ;
+ if (n)
+ zabort("Nothing pending, but pending state not zero") ;
+
+ return ;
+ } ;
+
+ /* This is to stop gcc whining about signed/unsigned comparisons. */
+ p_mnum = qps->pend_mnum ;
+
+ /* 11) that pend_mnum is valid and pend_fd <= tried_fd_last. */
+ if ( (p_mnum < 0) || (p_mnum > qps_mnum_count)
+ || (qps->pend_fd < 0)
+ || (qps->pend_fd > qps->tried_fd_last) )
+ zabort("Invalid pend_mnum or pend_fd") ;
+
+ /* 12) check tried_count[] */
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ {
+ if ((mnum < p_mnum) && (qps->tried_count[mnum] != 0))
+ zabort("Non-zero tried_count for mode < pend_mnum") ;
+ if ((mnum == p_mnum) && (qps->tried_count[qps->pend_mnum] <= 0))
+ zabort("Zero tried_count for pend_mnum") ;
+ if ((mnum > p_mnum) && (qps->tried_count[mnum] < 0))
+ zabort("Invalid tried_count for mode > pend_mnum") ;
+ } ;
+
+ /* 13) check result vectors for modes where tried count == 0 */
+ n = (qps_super_set_count(qps->results, qps_mnum_count) != 0) ;
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if ((qps->tried_count[mnum] == 0)
+ && (qps_super_set_count(&qps->results[mnum], 1) != 0))
+ zabort("Non-empty bit vector where tried count == 0") ;
+
+ /* 14) check remaining results are a subset of the enableds. */
+ /* 15) check no bit beyond tried_fd_last is set in the results. */
+ /* 16) check no bit before pend_fd is set in the pemd_mnum results. */
+ /* 17) check the number of bits remaining matches pend_count. */
+
+ n = 0 ;
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if (qps->tried_count[mnum] != 0)
+ {
+ for (fd = 0 ; fd < FD_SETSIZE ; ++fd)
+ if (FD_ISSET(fd, &qps->results[mnum].fdset))
+ {
+ ++n ;
+ if (fd > qps->tried_fd_last)
+ zabort("Found pending fd beyond tried_fd_last") ;
+ if ( ! FD_ISSET(fd, &qps->results[mnum].fdset))
+ zabort("Found pending fd which is not enabled") ;
+ if ((mnum == p_mnum) && (fd < qps->pend_fd))
+ zabort("Found pending fd < current next pending") ;
+ } ;
+ } ;
+
+ if (n != qps->pend_count)
+ zabort("Non-empty bit vector where tried count == 0") ;
+} ;
diff --git a/lib/qpselect.h b/lib/qpselect.h
index 03180f23..6aeca52c 100644
--- a/lib/qpselect.h
+++ b/lib/qpselect.h
@@ -63,7 +63,7 @@ enum qps_mbits /* "mode" bits: error/read/write */
qps_write_mbit = qps_mbit(qps_write_mnum),
qps_all_mbits = qps_mbit(qps_mnum_count) - 1
-};
+} ;
typedef enum qps_mbits qps_mbit_t ;
@@ -89,7 +89,7 @@ typedef uint32_t fd_word_t ;
CONFIRM(FD_WORD_BITS == (FD_WORD_BYTES * 8)) ; /* for completeness */
#define FD_SUPER_SET_WORD_SIZE ((FD_SETSIZE + FD_WORD_BITS - 1) / FD_WORD_BITS)
-#define FD_SUPER_SET_BYTE_SIZE ((FD_SETSIZE + FD_WORD_BITS - 1) / 8)
+#define FD_SUPER_SET_BYTE_SIZE (FD_SUPER_SET_WORD_SIZE * FD_WORD_BYTES)
/* Make sure that the overlay is at least as big as the fd_set ! */
CONFIRM(FD_SUPER_SET_BYTE_SIZE >= sizeof(fd_set)) ;
@@ -101,6 +101,9 @@ typedef union /* see qps_make_super_set_map() */
fd_set fdset ;
} fd_super_set ;
+/* Make sure that the fd_super_set is an exact number of fd_word_t words */
+CONFIRM(sizeof(fd_super_set) == (FD_SUPER_SET_WORD_SIZE * FD_WORD_BYTES)) ;
+
/*==============================================================================
* Action function.
*
@@ -127,7 +130,7 @@ struct qps_selection
int fd_last ; /* highest numbered fd we are looking after */
int enabled_count[qps_mnum_count] ; /* no. enabled fds in each mode */
- fd_full_set enabled ; /* bit vectors for select enabled stuff */
+ fd_full_set enabled ; /* bit vectors for pselect enabled stuff */
int tried_fd_last ; /* highest numbered fd on last pselect */
int tried_count[qps_mnum_count] ; /* enabled_count on last pselect */
@@ -166,6 +169,14 @@ qps_add_file(qps_selection qps, qps_file qf, int fd, void* file_info) ;
void
qps_remove_file(qps_file qf) ;
+qps_file
+qps_selection_ream(qps_selection qps, int free_structure) ;
+
+/* Ream out selection and free the selection structure. */
+#define qps_selection_ream_free(qps) qps_selection_ream(qps, 1)
+/* Ream out selection but keep the selection structure. */
+#define qps_selection_ream_keep(qps) qps_selection_ream(qps, 0)
+
void
qps_set_signal(qps_selection qps, int signum, sigset_t sigmask) ;