@@ -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 <akuhn(at)iam.unibe.ch>
41+ * @author Michael Herrmann
42+ * https://github.com/mherrmann/java-generator-functions
3443 *
3544 ******************************************************************************/
3645
3746public 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