Skip to content

Instantly share code, notes, and snippets.

@tyru
Last active August 17, 2017 15:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tyru/28a18184a89a160a12f8 to your computer and use it in GitHub Desktop.
Save tyru/28a18184a89a160a12f8 to your computer and use it in GitHub Desktop.
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);
}
}
}
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