Last active
August 17, 2017 15:03
-
-
Save tyru/28a18184a89a160a12f8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.*; | |
import java.util.concurrent.*; | |
public class CoroutineMain { | |
public static void main(String[] args) { | |
final Coroutine bob = new Coroutine() { | |
@Override | |
public void corun() throws InterruptedException { | |
count("Bob", this); | |
} | |
}; | |
final Coroutine alice = new Coroutine() { | |
@Override | |
public void corun() throws InterruptedException { | |
count("Alice", this); | |
} | |
}; | |
final Coroutine tom = new Coroutine() { | |
@Override | |
public void corun() throws InterruptedException { | |
count("Tom", this); | |
} | |
}; | |
List<Coroutine> coroutines = Arrays.asList(new Coroutine[]{ bob, alice, tom }); | |
try { | |
// Blocks until all coroutines exit. | |
Coroutines.executeAll(coroutines); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
} | |
private static void count(String name, Coroutine co) throws InterruptedException { | |
for (int counter = 1; counter <= 100; counter++) { | |
System.out.println(name + ": " + counter); | |
if (counter % 3 == 0) { | |
// 3回数えるごとに他の人と交代する | |
co.yield(); | |
} | |
} | |
co.end(); | |
} | |
} | |
interface Coroutine { | |
void corun() throws InterruptedException; | |
default void yield() throws InterruptedException { | |
Context.getInstance(this).yield(this); | |
} | |
default void end() throws InterruptedException { | |
Context.getInstance(this).end(this); | |
} | |
} | |
class TopCoroutineImpl implements Coroutine, Runnable { | |
private final Coroutine co; | |
TopCoroutineImpl(Coroutine co) { | |
this.co = co; | |
} | |
@Override | |
public void run() { | |
try { | |
corun(); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
} | |
@Override | |
public void corun() throws InterruptedException { | |
co.corun(); | |
} | |
} | |
class CoroutineImpl implements Coroutine, Runnable { | |
private final Coroutine co; | |
CoroutineImpl(Coroutine co) { | |
this.co = co; | |
} | |
@Override | |
public void run() { | |
try { | |
Context.getInstance(this.co).await(this.co); | |
corun(); | |
} catch (InterruptedException e) { | |
// FIXME | |
e.printStackTrace(); | |
} | |
} | |
@Override | |
public void corun() throws InterruptedException { | |
co.corun(); | |
} | |
} | |
class Coroutines { | |
public static void executeAll(List<Coroutine> coroutines) throws InterruptedException { | |
ExecutorService executor = Executors.newFixedThreadPool(coroutines.size()); | |
boolean first = true; | |
Context context = new Context.ContextBuilder().coroutines(coroutines).build(); | |
for (Coroutine co : coroutines) { | |
executor.execute(first ? new TopCoroutineImpl(co) : new CoroutineImpl(co)); | |
first = false; | |
} | |
context.invokeContextThread(); | |
executor.shutdown(); | |
while (! executor.awaitTermination(99999, TimeUnit.DAYS)) { | |
} | |
} | |
} | |
class Context { | |
private int activeCoroutine = -1; | |
private final List<Integer> coroutineIdList; | |
private final Map<Integer, BlockingQueue<Boolean>> startQueue; | |
private final Map<Integer, BlockingQueue<Boolean>> endQueue; | |
private boolean invokedContextThread; | |
private static Map<Integer, Context> contextMap = new ConcurrentHashMap<>(); | |
private Context(ContextBuilder builder) { | |
this.coroutineIdList = builder.coroutineIdList; | |
this.startQueue = new HashMap<>(); | |
this.endQueue = new HashMap<>(); | |
for (int id : this.coroutineIdList) { | |
this.startQueue.put(id, new SynchronousQueue<>()); | |
this.endQueue.put(id, new SynchronousQueue<>()); | |
} | |
this.activeCoroutine = 0; | |
for (int id : builder.coroutineIdList) { | |
contextMap.put(id, this); | |
} | |
} | |
synchronized public static Context getInstance(Coroutine co) { | |
return Context.contextMap.get(co.hashCode()); | |
} | |
synchronized public void invokeContextThread() { | |
if (this.invokedContextThread) { | |
return; | |
} | |
ExecutorService executor = Executors.newSingleThreadExecutor(); | |
executor.execute(() -> { | |
DebugUtils.debug("Context thread started. (" + Thread.currentThread() + ")"); | |
try { | |
int endThreadsNum = 0; | |
while (true) { | |
// Coroutine thread -> Context thread | |
boolean done = endQueue.get(coroutineIdList.get(activeCoroutine)).take(); | |
if (done) { | |
endThreadsNum++; | |
if (endThreadsNum >= endQueue.size()) { | |
break; | |
} | |
} | |
activeCoroutine = activeCoroutine + 1 >= coroutineIdList.size() ? 0 : activeCoroutine + 1; | |
// Context thread -> Coroutine thread | |
startQueue.get(coroutineIdList.get(activeCoroutine)).put(false); | |
} | |
} catch (InterruptedException e) { | |
// FIXME | |
e.printStackTrace(); | |
} | |
}); | |
executor.shutdown(); | |
this.invokedContextThread = true; | |
} | |
static class ContextBuilder { | |
private List<Integer> coroutineIdList = new ArrayList<>(); | |
public ContextBuilder coroutine(Coroutine co) { | |
coroutineIdList.add(co.hashCode()); | |
return this; | |
} | |
public ContextBuilder coroutines(List<Coroutine> coroutines) { | |
for (Coroutine co : coroutines) { | |
coroutineIdList.add(co.hashCode()); | |
} | |
return this; | |
} | |
public Context build() { | |
return new Context(this); | |
} | |
} | |
public void yield(Coroutine co) throws InterruptedException { | |
DebugUtils.debug("yield()"); | |
// Coroutine thread -> Context thread | |
this.endQueue.get(co.hashCode()).put(false); | |
// Context thread -> Coroutine thread | |
this.startQueue.get(co.hashCode()).take(); | |
} | |
public void end(Coroutine co) throws InterruptedException { | |
DebugUtils.debug("end()"); | |
// Coroutine thread -> Context thread | |
this.endQueue.get(co.hashCode()).put(true); | |
} | |
/* package */ void await(Coroutine co) throws InterruptedException { | |
DebugUtils.debug("await()"); | |
// Context thread -> Coroutine thread | |
this.startQueue.get(co.hashCode()).take(); | |
} | |
} | |
class DebugUtils { | |
private static final boolean DEBUG = false; | |
public static void debug(String msg) { | |
if (DEBUG) { | |
System.out.println(msg); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Bob: 1 | |
Bob: 2 | |
Bob: 3 | |
Alice: 1 | |
Alice: 2 | |
Alice: 3 | |
Tom: 1 | |
Tom: 2 | |
Tom: 3 | |
Bob: 4 | |
Bob: 5 | |
Bob: 6 | |
Alice: 4 | |
Alice: 5 | |
Alice: 6 | |
Tom: 4 | |
Tom: 5 | |
Tom: 6 | |
Bob: 7 | |
Bob: 8 | |
Bob: 9 | |
Alice: 7 | |
Alice: 8 | |
Alice: 9 | |
Tom: 7 | |
Tom: 8 | |
Tom: 9 | |
Bob: 10 | |
Bob: 11 | |
Bob: 12 | |
Alice: 10 | |
Alice: 11 | |
Alice: 12 | |
Tom: 10 | |
Tom: 11 | |
Tom: 12 | |
Bob: 13 | |
Bob: 14 | |
Bob: 15 | |
Alice: 13 | |
Alice: 14 | |
Alice: 15 | |
Tom: 13 | |
Tom: 14 | |
Tom: 15 | |
Bob: 16 | |
Bob: 17 | |
Bob: 18 | |
Alice: 16 | |
Alice: 17 | |
Alice: 18 | |
Tom: 16 | |
Tom: 17 | |
Tom: 18 | |
Bob: 19 | |
Bob: 20 | |
Bob: 21 | |
Alice: 19 | |
Alice: 20 | |
Alice: 21 | |
Tom: 19 | |
Tom: 20 | |
Tom: 21 | |
Bob: 22 | |
Bob: 23 | |
Bob: 24 | |
Alice: 22 | |
Alice: 23 | |
Alice: 24 | |
Tom: 22 | |
Tom: 23 | |
Tom: 24 | |
Bob: 25 | |
Bob: 26 | |
Bob: 27 | |
Alice: 25 | |
Alice: 26 | |
Alice: 27 | |
Tom: 25 | |
Tom: 26 | |
Tom: 27 | |
Bob: 28 | |
Bob: 29 | |
Bob: 30 | |
Alice: 28 | |
Alice: 29 | |
Alice: 30 | |
Tom: 28 | |
Tom: 29 | |
Tom: 30 | |
Bob: 31 | |
Bob: 32 | |
Bob: 33 | |
Alice: 31 | |
Alice: 32 | |
Alice: 33 | |
Tom: 31 | |
Tom: 32 | |
Tom: 33 | |
Bob: 34 | |
Bob: 35 | |
Bob: 36 | |
Alice: 34 | |
Alice: 35 | |
Alice: 36 | |
Tom: 34 | |
Tom: 35 | |
Tom: 36 | |
Bob: 37 | |
Bob: 38 | |
Bob: 39 | |
Alice: 37 | |
Alice: 38 | |
Alice: 39 | |
Tom: 37 | |
Tom: 38 | |
Tom: 39 | |
Bob: 40 | |
Bob: 41 | |
Bob: 42 | |
Alice: 40 | |
Alice: 41 | |
Alice: 42 | |
Tom: 40 | |
Tom: 41 | |
Tom: 42 | |
Bob: 43 | |
Bob: 44 | |
Bob: 45 | |
Alice: 43 | |
Alice: 44 | |
Alice: 45 | |
Tom: 43 | |
Tom: 44 | |
Tom: 45 | |
Bob: 46 | |
Bob: 47 | |
Bob: 48 | |
Alice: 46 | |
Alice: 47 | |
Alice: 48 | |
Tom: 46 | |
Tom: 47 | |
Tom: 48 | |
Bob: 49 | |
Bob: 50 | |
Bob: 51 | |
Alice: 49 | |
Alice: 50 | |
Alice: 51 | |
Tom: 49 | |
Tom: 50 | |
Tom: 51 | |
Bob: 52 | |
Bob: 53 | |
Bob: 54 | |
Alice: 52 | |
Alice: 53 | |
Alice: 54 | |
Tom: 52 | |
Tom: 53 | |
Tom: 54 | |
Bob: 55 | |
Bob: 56 | |
Bob: 57 | |
Alice: 55 | |
Alice: 56 | |
Alice: 57 | |
Tom: 55 | |
Tom: 56 | |
Tom: 57 | |
Bob: 58 | |
Bob: 59 | |
Bob: 60 | |
Alice: 58 | |
Alice: 59 | |
Alice: 60 | |
Tom: 58 | |
Tom: 59 | |
Tom: 60 | |
Bob: 61 | |
Bob: 62 | |
Bob: 63 | |
Alice: 61 | |
Alice: 62 | |
Alice: 63 | |
Tom: 61 | |
Tom: 62 | |
Tom: 63 | |
Bob: 64 | |
Bob: 65 | |
Bob: 66 | |
Alice: 64 | |
Alice: 65 | |
Alice: 66 | |
Tom: 64 | |
Tom: 65 | |
Tom: 66 | |
Bob: 67 | |
Bob: 68 | |
Bob: 69 | |
Alice: 67 | |
Alice: 68 | |
Alice: 69 | |
Tom: 67 | |
Tom: 68 | |
Tom: 69 | |
Bob: 70 | |
Bob: 71 | |
Bob: 72 | |
Alice: 70 | |
Alice: 71 | |
Alice: 72 | |
Tom: 70 | |
Tom: 71 | |
Tom: 72 | |
Bob: 73 | |
Bob: 74 | |
Bob: 75 | |
Alice: 73 | |
Alice: 74 | |
Alice: 75 | |
Tom: 73 | |
Tom: 74 | |
Tom: 75 | |
Bob: 76 | |
Bob: 77 | |
Bob: 78 | |
Alice: 76 | |
Alice: 77 | |
Alice: 78 | |
Tom: 76 | |
Tom: 77 | |
Tom: 78 | |
Bob: 79 | |
Bob: 80 | |
Bob: 81 | |
Alice: 79 | |
Alice: 80 | |
Alice: 81 | |
Tom: 79 | |
Tom: 80 | |
Tom: 81 | |
Bob: 82 | |
Bob: 83 | |
Bob: 84 | |
Alice: 82 | |
Alice: 83 | |
Alice: 84 | |
Tom: 82 | |
Tom: 83 | |
Tom: 84 | |
Bob: 85 | |
Bob: 86 | |
Bob: 87 | |
Alice: 85 | |
Alice: 86 | |
Alice: 87 | |
Tom: 85 | |
Tom: 86 | |
Tom: 87 | |
Bob: 88 | |
Bob: 89 | |
Bob: 90 | |
Alice: 88 | |
Alice: 89 | |
Alice: 90 | |
Tom: 88 | |
Tom: 89 | |
Tom: 90 | |
Bob: 91 | |
Bob: 92 | |
Bob: 93 | |
Alice: 91 | |
Alice: 92 | |
Alice: 93 | |
Tom: 91 | |
Tom: 92 | |
Tom: 93 | |
Bob: 94 | |
Bob: 95 | |
Bob: 96 | |
Alice: 94 | |
Alice: 95 | |
Alice: 96 | |
Tom: 94 | |
Tom: 95 | |
Tom: 96 | |
Bob: 97 | |
Bob: 98 | |
Bob: 99 | |
Alice: 97 | |
Alice: 98 | |
Alice: 99 | |
Tom: 97 | |
Tom: 98 | |
Tom: 99 | |
Bob: 100 | |
Alice: 100 | |
Tom: 100 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment