Continuations supersede SwingWorker and Foxtrot

SwingWorker is a pretty complete approach to off-event-queue execution, but clumsy syntactically:

http://download.java.net/jdk6/docs/api/javax/swing/SwingWorker.html

FoxTrot improves the API a bit for the simple cases but relies on the funky trick of pushing a new event thread:

http://foxtrot.sourceforge.net/docs/worker.php

Turns out that using continuations you can make the simple cases even simpler. Try running the following using Mustang's jrunscript -f demo.js:

function cont() {
    return new Continuation();
}

function offEQ(execsvc) {
    var c = cont();
    if (c instanceof Continuation) {
        execsvc.submit(
            new java.lang.Runnable({
                  run:
                    function() {
                        c();
                    }
                })
            );
        return true;
    } else {
        return false;
    }
}

function onEQ() {
    var c = cont();
    if (c instanceof Continuation) {
        java.awt.EventQueue.invokeLater(
            function() {
                c();
            });
        return true;
    } else {
        return false;
    }
}

function showThread() {
    print(java.lang.Thread.currentThread());
}

var swing = Packages.javax.swing;
var frame = new swing.JFrame("Demo");
frame.setLayout(java.awt.FlowLayout());
var button = new swing.JButton("Start");
var es = java.util.concurrent.Executors.newSingleThreadExecutor();
button.addActionListener(
    function(event) {
        field.setText("clicked");
        button.setEnabled(false);
        for (var i = 0; i < 10; i++) {
            field.setText("sleeping #" + i);
            showThread();
            if (offEQ(es)) return;
            showThread();
            java.lang.Thread.sleep(1000);
            if (onEQ()) return;
            showThread();
        }
        field.setText("done");
        button.setEnabled(true);
    });
frame.add(button);
var field = new swing.JTextField(20);
frame.add(field);
field.setText("idle");
field.setEditable(false);
frame.setDefaultCloseOperation(3);
frame.pack();
frame.setVisible(true);
java.lang.Thread.sleep(9999999);

Unfortunately continuations are not available in Java itself, only in the bundled Rhino JavaScript. Some day...

Comments:

Hmm, just found Javaflow... should make it possible to do continuations in Java code if you don't mind preprocessing a lot of classfiles! Probably not the most efficient way of doing things.

Posted by Jesse Glick on February 20, 2006 at 05:08 AM EST #

Another usage is easy enumerations (or iterators):
importPackage(java.util);

function enumeration(impl) {
    var next;
    var full = false;
    var end = false;
    var c1, c2;
    function peek() {
        if (end || full) {
            return;
        }
        if (c1 == null) {
            c1 = new Continuation();
            impl(function(val) {
                next = val;
                full = true;
                c2 = new Continuation();
                c1();
            });
            end = true;
        } else {
            c2();
        }
    }
    return new Enumeration({
        hasMoreElements: function() {
            peek();
            return !end;
        },
        nextElement: function() {
            peek();
            full = false;
            return next;
        }
    });
}

var e = enumeration(function(yield) {
    for (i = 0; i < 10; i++) {
        yield(i);
    }
});

println(Collections.list(e));

Posted by Jesse Glick on May 30, 2006 at 08:40 AM EDT #

Here is the above example in a hypothetical dialect of Java supporting continuations:
import java.util.\*;

interface Yielder<T> {
    void yield(T);
}
interface Generator<T> {
    void generate(Yielder<T> y);
}
static <T> Enumeration<T> generatorEnumeration(final Generator<T> g) {
    return new Enumeration<T>() {
        T next;
        boolean full, end;
        Continuation<Void> c1, c2;
        void peek() {
            if (end || full) {
                return;
            }
            if (c1 == null) {
                c1 = new return;
                g.generate(
                    new Yielder<T>() {
                        public void yield(T val) {
                            next = val;
                            full = true;
                            c2 = new return;
                            c1.return;
                        }
                    });
                end = true;
            } else {
                c2.return;
            }
        }
        public boolean hasMoreElements() {
            peek();
            return !end;
        }
        public T nextElement() {
            peek();
            if (end) {
                throw new NoSuchElementException();
            }
            assert full;
            full = false;
            return next;
        }
    };
}

Enumeration<Integer> e = generatorEnumeration(
    new Generator<Integer>() {
        public void generate(Yielder<Integer> y) {
            for (int i = 0; i < 10; i++) {
                y.yield(i);
            }
        }
    });
System.out.println(Collections.list(e));

Posted by Jesse Glick on May 30, 2006 at 08:52 AM EDT #

Even more easily:
import java.util.\*;

public abstract class GeneratorEnumeration<T> implements Enumeration<T> {
    private T next;
    private boolean full, end;
    private Continuation<Void> c1, c2;
    private void peek() {
        if (end || full) {
            return;
        }
        if (c1 == null) {
            c1 = new return;
            generate();
            end = true;
        } else {
            c2.return;
        }
    }
    public final boolean hasMoreElements() {
        peek();
        return !end;
    }
    public final T nextElement() {
        peek();
        if (end) {
            throw new NoSuchElementException();
        }
        assert full;
        full = false;
        return next;
    }
    protected final void yield(T val) {
        next = val;
        full = true;
        c2 = new return;
        c1.return;
    }
    protected abstract void generate();
}


Enumeration<Integer> e = new GeneratorEnumeration<Integer>() {
    protected void generate() {
        for (int i = 0; i < 10; i++) {
            yield(i);
        }
    }
};
System.out.println(Collections.list(e));

Posted by Jesse Glick on May 30, 2006 at 09:05 AM EDT #

Or you could actually use my implementation of continuations, which does a single processing to a class while it's loading (no pre-compiler required etc). Much simpler syntax, and you don't need to learn an additional language (Rhino in this case) to achieve your goal. Also, the entry bar is at Java 5 and not 6, which means something to some people.

Posted by Aviad Ben Dov on July 30, 2007 at 08:10 AM EDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

jglick

Search

Categories
Archives
« July 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  
       
Today