Skip to content

Instantly share code, notes, and snippets.

@komiya-atsushi
Created March 3, 2014 14:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save komiya-atsushi/9326277 to your computer and use it in GitHub Desktop.
Save komiya-atsushi/9326277 to your computer and use it in GitHub Desktop.
Java で yield return ぽいことを実現しよう! ref: http://qiita.com/komiya-atsushi@github/items/3f1145eb1964efef2f50
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* Java で yield return っぽいことを実現する機能を提供します。
*
* 以下は使い方の一例です。
* <pre>
* class YielderDemo {
* public static void main(String[] args) {
* for (Integer integer : Yielder.newIterable(new Yielder<Integer>() {
* public void run() {
* for (int i = 0; i < 10; i++) {
* yield(i);
* }
* for (int i = 30; i < 50; i++) {
* yield(i);
* }
* }
* })) {
* System.out.println(integer);
* }
* }
* }
* </pre>
*
* @author KOMIYA Atsushi
*/
public abstract class Yielder<T> {
private static class Item<T> {
T object;
boolean ended = false;
private Item<T> set(T object) {
this.object = object;
return this;
}
private Item<T> end() {
this.ended = true;
return this;
}
}
private static class Context<T> {
private final BlockingQueue<Item<T>> queue;
private final Item<T>[] items;
private int index;
private boolean endReceived;
private boolean ended;
private Exception thrownException;
Context(int queueSize) {
queue = new ArrayBlockingQueue<>(queueSize);
items = new Item[queueSize + 2];
for (int i = 0; i < items.length; i++) {
items[i] = new Item<>();
}
}
synchronized boolean yield(T returnValue) {
if (endReceived) {
throw new IllegalStateException("#yieldEnd() has been called.");
}
try {
Item<T> item = items[index++].set(returnValue);
while (!queue.offer(item, 1000, TimeUnit.SECONDS)) {
if (ended) {
throw new IllegalStateException("iteration has been ended.");
}
}
if (index == items.length) {
index = 0;
}
return true;
} catch (Exception e) {
thrownException = e;
return false;
}
}
synchronized boolean yieldEnd() {
if (endReceived) {
throw new IllegalStateException("#yieldEnd() has been called.");
}
endReceived = true;
try {
Item<T> item = items[index++].end();
while (!queue.offer(item, 1000, TimeUnit.SECONDS)) {
if (ended) {
throw new IllegalStateException("iteration has been ended.");
}
}
if (index == items.length) {
index = 0;
}
return true;
} catch (InterruptedException e) {
thrownException = e;
return false;
}
}
synchronized Exception thrownException() {
return thrownException;
}
Item<T> pop() throws InterruptedException {
Item<T> result = queue.take();
if (result.ended) {
this.ended = true;
}
return result;
}
}
public static class YielderIterable<T> implements Iterable<T>, AutoCloseable {
private final Context<T> context;
private final Thread thread;
private boolean iteratorCalled;
private boolean closed;
private YielderIterable(int queueSize, Yielder<T> yielder) {
this.context = new Context<>(queueSize);
this.thread = new Thread() {
@Override
public void run() {
try {
yielder.context.set(YielderIterable.this.context);
yielder.run();
context.yieldEnd();
} catch (Exception e) {
e.printStackTrace();
} finally {
yielder.context.remove();
}
}
};
this.thread.start();
}
@Override
public Iterator<T> iterator() {
if (iteratorCalled) {
throw new IllegalStateException("#iterator() has been called");
}
iteratorCalled = true;
return new Iterator<T>() {
Item<T> nextItem;
@Override
public boolean hasNext() {
if (nextItem != null) {
return !nextItem.ended;
}
try {
nextItem = context.pop();
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return !nextItem.ended;
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Item<T> result = nextItem;
nextItem = null;
return result.object;
}
};
}
@Override
public void close() throws Exception {
if (closed) {
return;
}
closed = true;
context.endReceived = true;
context.ended = true;
thread.interrupt();
}
}
private static final int DEFAULT_QUEUE_SIZE = 1;
private ThreadLocal<Context<T>> context = new ThreadLocal<>();
public static <T> Iterable<T> newIterable(final Yielder<T> yielder) {
return new YielderIterable<>(DEFAULT_QUEUE_SIZE, yielder);
}
protected boolean yield(T returnValue) {
return context.get().yield(returnValue);
}
protected Exception thrownException() {
return context.get().thrownException();
}
public abstract void run();
}
class YielderDemo {
public static void main(String[] args) {
for (Integer integer : Yielder.newIterable(new Yielder<Integer>() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
yield(i);
}
for (int i = 30; i < 50; i++) {
yield(i);
}
}
})) {
System.out.println(integer);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment