Thursday, December 17, 2009

Vacation: BlockingIterable

I love vacation: a chance to stretch my coding fingers free of work! Here is a code snack I thought of while daydreaming today—enjoy!

public final class BlockingIterable {
    public static <E> Until<E> over(final BlockingQueue<E> queue) {
        return new Until<E>(queue);
    }

    public static class Until<E> {
        private final BlockingQueue<E> queue;

        private Until(final BlockingQueue<E> queue) {
            this.queue = queue;
        }

        public Iterable<E> until(final E poison) {
            return new Iterable<E>() {
                @Override
                public Iterator<E> iterator() {
                    return new Iterator<E>() {
                        private E next = poison;

                        @Override
                        public boolean hasNext() {
                            try {
                                return poison != (next = queue.take());
                            } catch (final InterruptedException e) {
                                currentThread().interrupt();
                                next = poison;
                                return false;
                            }
                        }

                        @Override
                        public E next() {
                            if (poison == next)
                                throw new NoSuchElementException();
                            return next;
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException();
                        }
                    };
                }
            };
        }
    }
}

3 comments:

Unknown said...

Might as well take in a Queue rather than a BlockingQueue and allow it to apply to more cases, no?

Brian Oxley said...

@Bryan: I picked the blocking variety to avoid handling an empty but not yet poisoned queue. Hence, I can use take() and wait until an element arrives.

I am a little lost what to do with general Queue for the empty case. Iterator is not, generally speaking, a good interface for threaded use. The disconnection between hasNext() and next() requires callers to coordinate instead of the iterator to handle it internally.

That, in fact, is a blog post I'm thinking about writing up soon, an how non-local break in a closure solves it neatly. :)

Unknown said...

Ah, good points. I now understand the purpose of this code better.