Skip to content

Commit fabbece

Browse files
committed
- Replaced implementation of the Generator class. The previous version was not yielding correctly.
git-svn-id: svn://192.168.0.80/JavaXT/javaxt-core@1077 2c7b0aa6-e0b2-3c4e-bb4a-8b65b6c465ff
1 parent 506ff60 commit fabbece

File tree

1 file changed

+114
-103
lines changed

1 file changed

+114
-103
lines changed

src/javaxt/utils/Generator.java

Lines changed: 114 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -29,113 +29,124 @@ public void run() {
2929
}
3030
};
3131
</pre>
32+
*
33+
* Clients can iterate through the generated results using standard iterators
34+
* or an enhanced for loop like this:
35+
<pre>
36+
for (String row : generator){
37+
System.out.println(row);
38+
}
39+
</pre>
3240
*
33-
* @author Adrian Kuhn &lt;akuhn(at)iam.unibe.ch&gt;
41+
* @author Michael Herrmann
42+
* https://github.com/mherrmann/java-generator-functions
3443
*
3544
******************************************************************************/
3645

3746
public abstract class Generator<T> implements Iterable<T> {
3847

39-
public abstract void run();
40-
41-
public Iterator<T> iterator() {
42-
return new Iter();
43-
}
44-
45-
private static final Object DONE = new Object();
46-
private static final Object EMPTY = new Object();
47-
private Object drop = EMPTY;
48-
private Thread th = null;
49-
50-
private synchronized Object take() {
51-
while (drop == EMPTY) {
52-
try {
53-
wait();
54-
} catch (InterruptedException ex) {
55-
Thread.currentThread().interrupt();
56-
}
57-
}
58-
Object temp = drop;
59-
if (drop != DONE)
60-
drop = EMPTY;
61-
notifyAll();
62-
return temp;
63-
}
64-
65-
private synchronized void put(Object value) {
66-
if (drop == DONE)
67-
throw new IllegalStateException();
68-
if (drop != EMPTY)
69-
throw new IllegalStateException();
70-
drop = value;
71-
notifyAll();
72-
while (drop != EMPTY) {
73-
try {
74-
wait();
75-
} catch (InterruptedException ex) {
76-
Thread.currentThread().interrupt();
77-
}
78-
}
79-
}
80-
81-
protected void yield(T value) {
82-
put(value);
83-
}
84-
85-
public synchronized void done() {
86-
if (drop == DONE)
87-
throw new IllegalStateException();
88-
if (drop != EMPTY)
89-
throw new IllegalStateException();
90-
drop = DONE;
91-
notifyAll();
92-
}
93-
94-
private class Iter implements Iterator<T>, Runnable {
95-
96-
private Object next = EMPTY;
97-
98-
public Iter() {
99-
if (th != null)
100-
throw new IllegalStateException("Can not run coroutine twice");
101-
th = new Thread(this);
102-
th.setDaemon(true);
103-
th.start();
104-
}
105-
106-
public void run() {
107-
Generator.this.run();
108-
done();
109-
}
110-
111-
public boolean hasNext() {
112-
if (next == EMPTY)
113-
next = take();
114-
return next != DONE;
115-
}
116-
117-
@SuppressWarnings("unchecked")
118-
public T next() {
119-
if (next == EMPTY)
120-
next = take();
121-
if (next == DONE)
122-
throw new NoSuchElementException();
123-
Object temp = next;
124-
next = EMPTY;
125-
return (T) temp;
126-
}
127-
128-
public void remove() {
129-
throw new UnsupportedOperationException();
130-
131-
}
132-
133-
@SuppressWarnings("deprecation")
134-
@Override
135-
protected void finalize() throws Throwable {
136-
th.stop(); // let's commit suicide
137-
}
138-
139-
}
140-
48+
private class Condition {
49+
private boolean isSet;
50+
public synchronized void set() {
51+
isSet = true;
52+
notify();
53+
}
54+
public synchronized void await() throws InterruptedException {
55+
try {
56+
if (isSet)
57+
return;
58+
wait();
59+
} finally {
60+
isSet = false;
61+
}
62+
}
63+
}
64+
65+
static ThreadGroup THREAD_GROUP;
66+
67+
Thread producer;
68+
private boolean hasFinished;
69+
private final Condition itemAvailableOrHasFinished = new Condition();
70+
private final Condition itemRequested = new Condition();
71+
private T nextItem;
72+
private boolean nextItemAvailable;
73+
private RuntimeException exceptionRaisedByProducer;
74+
75+
@Override
76+
public Iterator<T> iterator() {
77+
return new Iterator<T>() {
78+
@Override
79+
public boolean hasNext() {
80+
return waitForNext();
81+
}
82+
@Override
83+
public T next() {
84+
if (!waitForNext())
85+
throw new NoSuchElementException();
86+
nextItemAvailable = false;
87+
return nextItem;
88+
}
89+
@Override
90+
public void remove() {
91+
throw new UnsupportedOperationException();
92+
}
93+
private boolean waitForNext() {
94+
if (nextItemAvailable)
95+
return true;
96+
if (hasFinished)
97+
return false;
98+
if (producer == null)
99+
startProducer();
100+
itemRequested.set();
101+
try {
102+
itemAvailableOrHasFinished.await();
103+
} catch (InterruptedException e) {
104+
hasFinished = true;
105+
}
106+
if (exceptionRaisedByProducer != null)
107+
throw exceptionRaisedByProducer;
108+
return !hasFinished;
109+
}
110+
};
111+
}
112+
113+
protected abstract void run() throws InterruptedException;
114+
115+
protected void yield(T element) throws InterruptedException {
116+
nextItem = element;
117+
nextItemAvailable = true;
118+
itemAvailableOrHasFinished.set();
119+
itemRequested.await();
120+
}
121+
122+
private void startProducer() {
123+
assert producer == null;
124+
if (THREAD_GROUP == null)
125+
THREAD_GROUP = new ThreadGroup("generatorfunctions");
126+
producer = new Thread(THREAD_GROUP, new Runnable() {
127+
@Override
128+
public void run() {
129+
try {
130+
itemRequested.await();
131+
Generator.this.run();
132+
} catch (InterruptedException e) {
133+
// No need to do anything here; Remaining steps in run()
134+
// will cleanly shut down the thread.
135+
} catch (RuntimeException e) {
136+
exceptionRaisedByProducer = e;
137+
}
138+
hasFinished = true;
139+
itemAvailableOrHasFinished.set();
140+
}
141+
});
142+
producer.setDaemon(true);
143+
producer.start();
144+
}
145+
146+
@Override
147+
protected void finalize() throws Throwable {
148+
producer.interrupt();
149+
producer.join();
150+
super.finalize();
151+
}
141152
}

0 commit comments

Comments
 (0)