I ran across this interesting Scala class by Vaclav Pech which makes the data concurrent rather than the code (if I understood the author correctly).  The ~ (extraction) and << (insertion) operators looked nifty.
Looking to do the same in Java, I see four key requirements:
- Insertion can happen only once.
- Extraction is idempotent (infinitely repeatable, non-modifying).
- Extraction blocks until insertion has completed.
- Insertion and extraction are both atomic.
With these in mind, I write this:
public class DataFlowReference<T> {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private volatile T item;
    private volatile boolean set = false;
    private volatile boolean called = false;
    public T get()
            throws InterruptedException {
        lock.lockInterruptibly();
        try {
            while (true) {
                if (set)
                    return item;
                if (!called) {
                    called = true;
                    onFirstGet();
                    continue;
                }
                condition.await();
            }
        } finally {
            lock.unlock();
        }
    }
    public void setOnce(final T value) {
        lock.lock();
        try {
            if (set)
                return;
            set = true;
            item = value;
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
    protected void onFirstGet() {
    }
    // Object
    @Override
    public boolean equals(final Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        final DataFlowReference that = (DataFlowReference) o;
        return set && item.equals(that.item);
    }
    @Override
    public int hashCode() {
        return set ? item.hashCode() : super.hashCode();
    }
    @Override
    public String toString() {
        return "(" + set + ':' + item + ')';
    }
} Use of DataFlowReference follows the examples from Vaclav's page: declare dataflow references, create threads whose runables use setOnce() and get(), invoke them all together.
onFirstGet() supports "just in time" supply of item and would call setOnce(T) with a fresh value.
UPDATE: The original version of this class had a horrible, obvious race condition. Caveat plicator.
 
 
1 comment:
You have basically implemented a Future with external setting semantics. For something similar, see:
http://labs.atlassian.com/source/browse/CONCURRENT/trunk/src/main/java/com/atlassian/util/concurrent/SettableFuture.java?r=2602
and a related conversation (Offering data to Futures):
http://cs.oswego.edu/pipermail/concurrency-interest/2009-May/thread.html#6161
Post a Comment