[MAJOR] spec I/O: fix allocations of spec entries for an FD
Under some circumstances, it was possible with speculative I/O to
reallocate multiple entries for the same FD if an fd_{set,clr,set}
or fd_{clr,set,clr} sequences were performed before a schedule.
Fix this by keeping a an allocation flag for each fd.
diff --git a/src/ev_sepoll.c b/src/ev_sepoll.c
index 852577c..c58bf53 100644
--- a/src/ev_sepoll.c
+++ b/src/ev_sepoll.c
@@ -115,7 +115,7 @@
* FIXME: should be a bit field */
struct fd_status {
unsigned int e:4; // read and write events status.
- unsigned int s:28; // Position in spec list. Should be last.
+ unsigned int s1:28; // Position in spec list+1. 0=not in list. Should be last.
};
static int nbspec = 0; // current size of the spec list
@@ -135,27 +135,37 @@
REGPRM1 static void alloc_spec_entry(const int fd)
{
- if (fd_list[fd].e & FD_EV_RW_SL)
+ if (fd_list[fd].s1)
return;
- fd_list[fd].s = nbspec;
- spec_list[nbspec++] = fd;
+ fd_list[fd].s1 = nbspec + 1;
+ spec_list[nbspec] = fd;
+ nbspec++;
}
-/* removes entry <pos> from the spec list and replaces it with the last one.
- * The fd_list is adjusted to match the back reference if needed.
+/* Removes entry used by fd <fd> from the spec list and replaces it with the
+ * last one. The fd_list is adjusted to match the back reference if needed.
+ * If the fd has no entry assigned, return immediately.
*/
-REGPRM1 static void delete_spec_entry(const int pos)
+REGPRM1 static void release_spec_entry(int fd)
{
- int fd;
+ unsigned int pos;
+
+ pos = fd_list[fd].s1;
+ if (!pos)
+ return;
+
+ fd_list[fd].s1 = 0;
+ pos--;
+ /* we have spec_list[pos]==fd */
nbspec--;
if (pos == nbspec)
return;
- /* we replace current FD by the highest one */
+ /* we replace current FD by the highest one, which may sometimes be the same */
fd = spec_list[nbspec];
+ fd_list[fd].s1 = pos + 1;
spec_list[pos] = fd;
- fd_list[fd].s = pos;
}
/*
@@ -234,7 +244,7 @@
REGPRM1 static void __fd_clo(int fd)
{
if (fd_list[fd].e & FD_EV_RW_SL)
- delete_spec_entry(fd_list[fd].s);
+ release_spec_entry(fd);
fd_list[fd].e &= ~(FD_EV_MASK);
}
@@ -346,7 +356,7 @@
/* This fd switched to combinations of either WAIT or
* IDLE. It must be removed from the spec list.
*/
- delete_spec_entry(spec_idx);
+ release_spec_entry(fd);
continue;
}
}