| /* |
| * Copyright 2018-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.onosproject.t3.impl; |
| |
| import java.util.Iterator; |
| import java.util.NoSuchElementException; |
| |
| /** |
| * Generator class that yields instances of T type objects as soon as they are ready. |
| * |
| * @param <T> type of the object. |
| */ |
| public abstract class Generator<T> implements Iterable<T> { |
| |
| private class Condition { |
| private boolean isSet; |
| |
| synchronized void set() { |
| isSet = true; |
| notifyAll(); |
| } |
| |
| synchronized void await() throws InterruptedException { |
| try { |
| |
| if (isSet) { |
| return; |
| } |
| |
| while (!isSet) { |
| wait(); |
| } |
| } finally { |
| isSet = false; |
| } |
| } |
| } |
| |
| private static ThreadGroup threadGroup; |
| |
| private Thread producer; |
| private boolean hasFinished; |
| private final Condition itemAvailableOrHasFinished = new Condition(); |
| private final Condition itemRequested = new Condition(); |
| private T nextItem; |
| private boolean nextItemAvailable; |
| private RuntimeException exceptionRaisedByProducer; |
| |
| @Override |
| public Iterator<T> iterator() { |
| return new Iterator<T>() { |
| @Override |
| public boolean hasNext() { |
| return waitForNext(); |
| } |
| |
| @Override |
| public T next() { |
| if (!waitForNext()) { |
| throw new NoSuchElementException(); |
| } |
| nextItemAvailable = false; |
| return nextItem; |
| } |
| |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| private boolean waitForNext() { |
| if (nextItemAvailable) { |
| return true; |
| } |
| if (hasFinished) { |
| return false; |
| } |
| if (producer == null) { |
| startProducer(); |
| } |
| itemRequested.set(); |
| try { |
| itemAvailableOrHasFinished.await(); |
| } catch (InterruptedException e) { |
| hasFinished = true; |
| producer.interrupt(); |
| try { |
| producer.join(); |
| } catch (InterruptedException e1) { |
| // Interrupting the broken thread |
| Thread.currentThread().interrupt(); |
| throw new IllegalStateException(e1); |
| } |
| } |
| if (exceptionRaisedByProducer != null) { |
| throw exceptionRaisedByProducer; |
| } |
| return !hasFinished; |
| } |
| }; |
| } |
| |
| protected abstract void run() throws InterruptedException; |
| |
| void yield(T element) throws InterruptedException { |
| nextItem = element; |
| nextItemAvailable = true; |
| itemAvailableOrHasFinished.set(); |
| itemRequested.await(); |
| } |
| |
| private void startProducer() { |
| assert producer == null; |
| synchronized (this) { |
| if (threadGroup == null) { |
| threadGroup = new ThreadGroup("onos-t3-generator"); |
| } |
| } |
| producer = new Thread(threadGroup, () -> { |
| try { |
| itemRequested.await(); |
| Generator.this.run(); |
| } catch (InterruptedException e) { |
| // Remaining steps in run() will shut down thread. |
| } catch (RuntimeException e) { |
| exceptionRaisedByProducer = e; |
| } |
| hasFinished = true; |
| itemAvailableOrHasFinished.set(); |
| }); |
| producer.setDaemon(true); |
| producer.start(); |
| } |
| } |