blob: 82ad628677b9b5b52933c4b01eb7a3807cef7d27 [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;
Andrea Campanellafe5d8df2018-03-12 11:07:35 -0700100 producer.interrupt();
101 try {
102 producer.join();
103 } catch (InterruptedException e1) {
104 // Interrupting the broken thread
105 Thread.currentThread().interrupt();
106 throw new IllegalStateException(e1);
107 }
Andrea Campanella0cc6acd2018-02-28 16:43:16 +0100108 }
109 if (exceptionRaisedByProducer != null) {
110 throw exceptionRaisedByProducer;
111 }
112 return !hasFinished;
113 }
114 };
115 }
116
117 protected abstract void run() throws InterruptedException;
118
119 void yield(T element) throws InterruptedException {
120 nextItem = element;
121 nextItemAvailable = true;
122 itemAvailableOrHasFinished.set();
123 itemRequested.await();
124 }
125
126 private void startProducer() {
127 assert producer == null;
Andrea Campanellade46fbd2018-03-08 08:23:32 -0800128 synchronized (this) {
129 if (threadGroup == null) {
130 threadGroup = new ThreadGroup("onos-t3-generator");
131 }
Andrea Campanella0cc6acd2018-02-28 16:43:16 +0100132 }
133 producer = new Thread(threadGroup, () -> {
134 try {
135 itemRequested.await();
136 Generator.this.run();
137 } catch (InterruptedException e) {
138 // Remaining steps in run() will shut down thread.
139 } catch (RuntimeException e) {
140 exceptionRaisedByProducer = e;
141 }
142 hasFinished = true;
143 itemAvailableOrHasFinished.set();
144 });
145 producer.setDaemon(true);
146 producer.start();
147 }
Andrea Campanella0cc6acd2018-02-28 16:43:16 +0100148}