lib: Fixed blocker iteration bug in mtio-select introduced by mblock.
[ashd.git] / lib / mtio-select.c
index eb84a45..e0a4177 100644 (file)
@@ -18,6 +18,8 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
+#include <unistd.h>
 #include <errno.h>
 #include <sys/select.h>
 
 #include <mtio.h>
 
 static struct blocker *blockers;
+static int exitstatus;
 
 struct blocker {
     struct blocker *n, *p;
+    struct iterator *it;
     int fd;
-    int ev;
+    int ev, rev, id;
     time_t to;
     struct muth *th;
 };
 
-int block(int fd, int ev, time_t to)
-{
+struct iterator {
     struct blocker *bl;
-    int rv;
-    
-    omalloc(bl);
-    bl->fd = fd;
-    bl->ev = ev;
-    if(to > 0)
-       bl->to = time(NULL) + to;
-    bl->th = current;
+};
+
+static void addblock(struct blocker *bl)
+{
     bl->n = blockers;
     if(blockers)
        blockers->p = bl;
     blockers = bl;
-    rv = yield();
+}
+
+static void remblock(struct blocker *bl)
+{
     if(bl->n)
        bl->n->p = bl->p;
     if(bl->p)
        bl->p->n = bl->n;
     if(bl == blockers)
        blockers = bl->n;
-    free(bl);
+    if(bl->it) {
+       if((bl->it->bl = bl->n) != NULL)
+           bl->it->bl->it = bl->it;
+       bl->it = NULL;
+    }
+}
+
+struct selected mblock(time_t to, int n, struct selected *spec)
+{
+    int i, id;
+    struct blocker bls[n];
+    
+    to = (to > 0)?(time(NULL) + to):0;
+    for(i = 0; i < n; i++) {
+       bls[i] = (struct blocker){
+           .fd = spec[i].fd,
+           .ev = spec[i].ev,
+           .id = i,
+           .to = to,
+           .th = current,
+       };
+       addblock(&bls[i]);
+    }
+    id = yield();
+    for(i = 0; i < n; i++)
+       remblock(&bls[i]);
+    if(id < 0)
+       return((struct selected){.fd = -1, .ev = -1});
+    return((struct selected){.fd = bls[id].fd, .ev = bls[id].rev});
+}
+
+int block(int fd, int ev, time_t to)
+{
+    struct blocker bl;
+    int rv;
+    
+    if(fd >= FD_SETSIZE) {
+       flog(LOG_ERR, "tried to use more file descriptors than select() can handle: fd %i", fd);
+       errno = EMFILE;
+       return(-1);
+    }
+    bl = (struct blocker) {
+       .fd = fd,
+       .ev = ev,
+       .id = -1,
+       .to = (to > 0)?(time(NULL) + to):0,
+       .th = current,
+    };
+    addblock(&bl);
+    rv = yield();
+    remblock(&bl);
     return(rv);
 }
 
-void ioloop(void)
+int ioloop(void)
 {
     int ret;
     fd_set rfds, wfds, efds;
-    struct blocker *bl, *nbl;
+    struct blocker *bl;
+    struct iterator it;
     struct timeval toval;
     time_t now, timeout;
     int maxfd;
     int ev;
     
+    exitstatus = 0;
     while(blockers != NULL) {
        FD_ZERO(&rfds);
        FD_ZERO(&wfds);
@@ -93,6 +147,8 @@ void ioloop(void)
            if((bl->to != 0) && ((timeout == 0) || (timeout > bl->to)))
                timeout = bl->to;
        }
+       if(exitstatus)
+           return(exitstatus);
        toval.tv_sec = timeout - now;
        toval.tv_usec = 0;
        ret = select(maxfd + 1, &rfds, &wfds, &efds, timeout?(&toval):NULL);
@@ -103,21 +159,42 @@ void ioloop(void)
                 * probably is. */
                sleep(1);
            }
-       }
-       now = time(NULL);
-       for(bl = blockers; bl; bl = nbl) {
-           nbl = bl->n;
-           ev = 0;
-           if(FD_ISSET(bl->fd, &rfds))
-               ev |= EV_READ;
-           if(FD_ISSET(bl->fd, &wfds))
-               ev |= EV_WRITE;
-           if(FD_ISSET(bl->fd, &efds))
-               ev = -1;
-           if((ev < 0) || (ev & bl->ev))
-               resume(bl->th, ev);
-           else if((bl->to != 0) && (bl->to <= now))
-               resume(bl->th, 0);
+       } else {
+           now = time(NULL);
+           for(bl = it.bl = blockers; bl; bl = it.bl) {
+               if((it.bl = bl->n) != NULL)
+                   it.bl->it = &it;
+               ev = 0;
+               if(FD_ISSET(bl->fd, &rfds))
+                   ev |= EV_READ;
+               if(FD_ISSET(bl->fd, &wfds))
+                   ev |= EV_WRITE;
+               if(FD_ISSET(bl->fd, &efds))
+                   ev = -1;
+               if((ev < 0) || (ev & bl->ev)) {
+                   if(bl->id < 0) {
+                       resume(bl->th, ev);
+                   } else {
+                       bl->rev = ev;
+                       resume(bl->th, bl->id);
+                   }
+               } else if((bl->to != 0) && (bl->to <= now)) {
+                   if(bl->id < 0) {
+                       resume(bl->th, 0);
+                   } else {
+                       bl->rev = 0;
+                       resume(bl->th, bl->id);
+                   }
+               }
+               if(it.bl)
+                   it.bl->it = NULL;
+           }
        }
     }
+    return(0);
+}
+
+void exitioloop(int status)
+{
+    exitstatus = status;
 }