Handle cancelled selection keys... "properly"?
authorFredrik Tolf <fredrik@dolda2000.com>
Wed, 16 Feb 2022 18:37:35 +0000 (19:37 +0100)
committerFredrik Tolf <fredrik@dolda2000.com>
Wed, 16 Feb 2022 18:37:35 +0000 (19:37 +0100)
This seems very needlessly complex for something which shouldn't even
have to be an issue, but I can't obviously see how else to handle it,
but also not really what the whole point of selectors "cancelled-sets"
is.

src/jagi/event/Driver.java

index 59f54a3..6ee920f 100644 (file)
@@ -58,6 +58,7 @@ public class Driver {
        final Map<Watcher, SelectionKey> watching = new IdentityHashMap<>();
        final Heap<Watcher, Double> timeheap = new Heap<>(Comparator.naturalOrder());
        final Map<Watcher, Object> paused = new IdentityHashMap<>();
+       final Collection<SelectionKey> cancelled = new HashSet<>();
 
        SelectPool(SelectorProvider provider) {
            this.provider = provider;
@@ -112,14 +113,26 @@ public class Driver {
                            return;
                        }
                        if(first != null)
-                           timeout = Math.max((long)Math.ceil((first - now) * 1000), 0);
+                           timeout = Math.max((long)Math.ceil((first - now) * 1000), 1);
                    }
+                   Collection<SelectionKey> precancelled;
+                   synchronized(cancelled) {
+                       precancelled = new ArrayList<>(cancelled);
+                   }
+                   if(!precancelled.isEmpty())
+                       timeout = 1;
                    poll.selectedKeys().clear();
                    try {
                        poll.select(timeout);
                    } catch(IOException e) {
                        throw(new RuntimeException(e));
                    }
+                   if(!precancelled.isEmpty()) {
+                       synchronized(cancelled) {
+                           cancelled.removeAll(precancelled);
+                           cancelled.notifyAll();
+                       }
+                   }
                    for(SelectionKey key : poll.selectedKeys())
                        handle((Watcher)key.attachment(), key.readyOps());
                    now = time();
@@ -193,14 +206,28 @@ public class Driver {
            SelectionKey wc = watching.remove(w);
            Object tc = timeheap.remove(w);
            Object pc = paused.remove(w);
-           if(wc != null)
-               wc.cancel();
+           if(wc != null) {
+               synchronized(cancelled) {
+                   cancelled.add(wc);
+                   wc.cancel();
+                   poll.wakeup();
+                   boolean irq = false;
+                   while(cancelled.contains(wc)) {
+                       try {
+                           cancelled.wait();
+                       } catch(InterruptedException e) {
+                           irq = true;
+                       }
+                   }
+                   if(irq)
+                       Thread.currentThread().interrupt();
+               }
+           }
            if(((wc != null) || (tc != null)) && (pc != null))
                throw(new RuntimeException(w + ": inconsistent internal state"));
            if(wc == null)
                throw(new IllegalStateException(w + ": not registered"));
            submit(() -> close(w));
-           poll.wakeup();
        }
 
        void update(Watcher w) {