blob: 95e4d4e109f27e8aa888c9a7b3d7d77a846b05f6 [file] [log] [blame]
Andrea Campanella0cc6acd2018-02-28 16:43:16 +01001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.t3.impl;
18
19import java.util.Iterator;
20import java.util.NoSuchElementException;
21
22/**
23 * Generator class that yields instances of T type objects as soon as they are ready.
24 *
25 * @param <T> type of the object.
26 */
27public abstract class Generator<T> implements Iterable<T> {
28
29 private class Condition {
30 private boolean isSet;
31
32 synchronized void set() {
33 isSet = true;
Andrea Campanellade46fbd2018-03-08 08:23:32 -080034 notifyAll();
Andrea Campanella0cc6acd2018-02-28 16:43:16 +010035 }
36
37 synchronized void await() throws InterruptedException {
38 try {
Andrea Campanellade46fbd2018-03-08 08:23:32 -080039
Andrea Campanella0cc6acd2018-02-28 16:43:16 +010040 if (isSet) {
41 return;
42 }
Andrea Campanellade46fbd2018-03-08 08:23:32 -080043
44 while (!isSet) {
45 wait();
46 }
Andrea Campanella0cc6acd2018-02-28 16:43:16 +010047 } finally {
48 isSet = false;
49 }
50 }
51 }
52
53 private static ThreadGroup threadGroup;
54
55 private Thread producer;
56 private boolean hasFinished;
57 private final Condition itemAvailableOrHasFinished = new Condition();
58 private final Condition itemRequested = new Condition();
59 private T nextItem;
60 private boolean nextItemAvailable;
61 private RuntimeException exceptionRaisedByProducer;
62
63 @Override
64 public Iterator<T> iterator() {
65 return new Iterator<T>() {
66 @Override
67 public boolean hasNext() {
68 return waitForNext();
69 }
70
71 @Override
72 public T next() {
73 if (!waitForNext()) {
74 throw new NoSuchElementException();
75 }
76 nextItemAvailable = false;
77 return nextItem;
78 }
79
80 @Override
81 public void remove() {
82 throw new UnsupportedOperationException();
83 }
84
85 private boolean waitForNext() {
86 if (nextItemAvailable) {
87 return true;
88 }
89 if (hasFinished) {
90 return false;
91 }
92 if (producer == null) {
93 startProducer();
94 }
95 itemRequested.set();
96 try {
97 itemAvailableOrHasFinished.await();
98 } catch (InterruptedException e) {
99 hasFinished = true;
100 }
101 if (exceptionRaisedByProducer != null) {
102 throw exceptionRaisedByProducer;
103 }
104 return !hasFinished;
105 }
106 };
107 }
108
109 protected abstract void run() throws InterruptedException;
110
111 void yield(T element) throws InterruptedException {
112 nextItem = element;
113 nextItemAvailable = true;
114 itemAvailableOrHasFinished.set();
115 itemRequested.await();
116 }
117
118 private void startProducer() {
119 assert producer == null;
Andrea Campanellade46fbd2018-03-08 08:23:32 -0800120 synchronized (this) {
121 if (threadGroup == null) {
122 threadGroup = new ThreadGroup("onos-t3-generator");
123 }
Andrea Campanella0cc6acd2018-02-28 16:43:16 +0100124 }
125 producer = new Thread(threadGroup, () -> {
126 try {
127 itemRequested.await();
128 Generator.this.run();
129 } catch (InterruptedException e) {
130 // Remaining steps in run() will shut down thread.
131 } catch (RuntimeException e) {
132 exceptionRaisedByProducer = e;
133 }
134 hasFinished = true;
135 itemAvailableOrHasFinished.set();
136 });
137 producer.setDaemon(true);
138 producer.start();
139 }
140
141 @Override
142 protected void finalize() throws Throwable {
143 producer.interrupt();
144 producer.join();
145 super.finalize();
146 }
147}