Fix potential invalid select timeouts.
[jagi.git] / src / jagi / event / Driver.java
index c9ca638..59f54a3 100644 (file)
@@ -26,7 +26,18 @@ public class Driver {
            current.set(this);
            w.handle(evs);
        } catch(Throwable t) {
-           error(w, t);
+           error(w, t, "handling event");
+       } finally {
+           current.remove();
+       }
+    }
+
+    protected void close(Watcher w) {
+       try {
+           current.set(this);
+           w.close();
+       } catch(Throwable t) {
+           error(w, t, "closing");
        } finally {
            current.remove();
        }
@@ -36,8 +47,8 @@ public class Driver {
        worker.submit(task);
     }
 
-    protected void error(Watcher w, Throwable t) {
-       hlog.log(Level.WARNING, w + ": uncaught error when handling event", t);
+    protected void error(Watcher w, Throwable t, String thing) {
+       hlog.log(Level.WARNING, w + ": uncaught error when " + thing, t);
        remove(w);
     }
 
@@ -59,6 +70,8 @@ public class Driver {
        }
 
        void handle(Watcher w, int evs) {
+           if(!watching.containsKey(w))
+               return;
            try {
                pause(w);
                submit(() -> {
@@ -88,8 +101,8 @@ public class Driver {
            boolean quit = false;
            Throwable error = null;
            try {
+               double now = time();
                while(true) {
-                   double now = time();
                    long timeout = 0;
                    synchronized(selectors) {
                        Double first = timeheap.keypeek();
@@ -99,7 +112,7 @@ public class Driver {
                            return;
                        }
                        if(first != null)
-                           timeout = (long)Math.ceil((first - now) * 1000);
+                           timeout = Math.max((long)Math.ceil((first - now) * 1000), 0);
                    }
                    poll.selectedKeys().clear();
                    try {
@@ -109,6 +122,13 @@ public class Driver {
                    }
                    for(SelectionKey key : poll.selectedKeys())
                        handle((Watcher)key.attachment(), key.readyOps());
+                   now = time();
+                   while(true) {
+                       Double first = timeheap.keypeek();
+                       if((first == null) || (first > now))
+                           break;
+                       handle(timeheap.remove(), 0);
+                   }
                }
            } catch(Throwable t) {
                error = t;
@@ -138,7 +158,7 @@ public class Driver {
            int evs = w.events();
            double timeout = w.timeout();
            boolean hastime = timeout < Double.POSITIVE_INFINITY;
-           if((evs == 0) && !hastime) {
+           if(evs < 0) {
                remove(w);
                return;
            }
@@ -154,10 +174,11 @@ public class Driver {
            int evs = w.events();
            double timeout = w.timeout();
            boolean hastime = timeout < Double.POSITIVE_INFINITY;
-           if((evs == 0) && !hastime) {
-               w.close();
+           if(evs < 0) {
+               submit(() -> close(w));
                return;
            }
+           w.added(Driver.this);
            try {
                watching.put(w, ch.register(poll, evs, w));
            } catch(ClosedChannelException e) {
@@ -178,7 +199,7 @@ public class Driver {
                throw(new RuntimeException(w + ": inconsistent internal state"));
            if(wc == null)
                throw(new IllegalStateException(w + ": not registered"));
-           w.close();
+           submit(() -> close(w));
            poll.wakeup();
        }
 
@@ -189,7 +210,7 @@ public class Driver {
            int evs = w.events();
            double timeout = w.timeout();
            boolean hastime = timeout < Double.POSITIVE_INFINITY;
-           if((evs == 0) && !hastime) {
+           if(evs < 0) {
                remove(w);
                return;
            }