parent
b41f5c97a3
commit
fdbd8937f5
@ -0,0 +1,208 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2023 jimj316 |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
package moe.nekojimi.chords; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.LinkedList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Queue; |
||||||
|
import java.util.function.Consumer; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author jimj316 |
||||||
|
*/ |
||||||
|
public abstract class QueueThing<I, O> implements Consumer<I> |
||||||
|
{ |
||||||
|
// TODO: better name
|
||||||
|
protected final Queue<I> inputQueue; |
||||||
|
protected final List<QueueThing<?, I>> sources = new ArrayList<>(); |
||||||
|
protected final List<QueueThing<O, ?>> sinks = new ArrayList<>(); |
||||||
|
protected final Queue<Promise<I, O>> pendingPromises = new LinkedList<>(); |
||||||
|
|
||||||
|
protected int queueTargetSize = 0; |
||||||
|
|
||||||
|
protected QueueThing(Queue<I> inputQueue) |
||||||
|
{ |
||||||
|
this.inputQueue = inputQueue; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void accept(I t) |
||||||
|
{ |
||||||
|
|
||||||
|
// if we've got pending promises, fullfill them, otherwise just put it in the queue and notify the sinks
|
||||||
|
if (pendingPromises.isEmpty()) |
||||||
|
{ |
||||||
|
inputQueue.add(t); |
||||||
|
notifyNewInput(); |
||||||
|
} else |
||||||
|
{ |
||||||
|
Promise<I, O> promise = pendingPromises.poll(); |
||||||
|
promise.setInput(t); |
||||||
|
handlePromise(promise); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Called to notify downstream modules that there's input upstream - |
||||||
|
* consider requesting it to pull it through |
||||||
|
*/ |
||||||
|
protected void notifyNewInput() |
||||||
|
{ |
||||||
|
if (inputQueue.size() < queueTargetSize) |
||||||
|
demandInput(queueTargetSize - inputQueue.size()); |
||||||
|
|
||||||
|
for (QueueThing<O, ?> sink : sinks) |
||||||
|
{ |
||||||
|
sink.notifyNewInput(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public List<Promise<I, O>> request(int count, Consumer<O> destination) |
||||||
|
{ |
||||||
|
List<Promise<I, O>> ret = new ArrayList<>(); |
||||||
|
int demands = 0; |
||||||
|
for (int i = 0; i < count; i++) |
||||||
|
{ |
||||||
|
I input = null; |
||||||
|
if (!inputQueue.isEmpty()) |
||||||
|
{ |
||||||
|
input = inputQueue.poll(); |
||||||
|
} |
||||||
|
|
||||||
|
Promise<I, O> promise = new Promise<>(input, destination); |
||||||
|
|
||||||
|
boolean ok = handlePromise(promise); |
||||||
|
|
||||||
|
if (ok) |
||||||
|
{ |
||||||
|
// we got a promise of output so we can tell the client
|
||||||
|
ret.add(promise); |
||||||
|
} else |
||||||
|
{ |
||||||
|
// we need to get more input from sources
|
||||||
|
demands++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
demands += queueTargetSize - inputQueue.size(); |
||||||
|
|
||||||
|
if (demands > 0) |
||||||
|
{ |
||||||
|
// try to get more input from sources
|
||||||
|
int sourcePromises = demandInput(demands); |
||||||
|
// each promise of input we get represents a promise of output we can give
|
||||||
|
for (int i = 0; i < sourcePromises; i++) |
||||||
|
{ |
||||||
|
Promise<I, O> myPromise = new Promise<>(destination); |
||||||
|
pendingPromises.add(myPromise); |
||||||
|
ret.add(myPromise); |
||||||
|
} |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean handlePromise(Promise<I, O> promise) |
||||||
|
{ |
||||||
|
boolean ok; |
||||||
|
ok = completePromise(promise); |
||||||
|
return ok; |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract boolean completePromise(Promise<I, O> request); |
||||||
|
|
||||||
|
/** |
||||||
|
* Requests from sources a certain about of input, to be provided later. |
||||||
|
* |
||||||
|
* @param count the number of input items to request. |
||||||
|
* @return a number Promises of input items. May be more or less than count. |
||||||
|
*/ |
||||||
|
protected int demandInput(int count) |
||||||
|
{ |
||||||
|
int ret = 0; |
||||||
|
for (QueueThing<?, I> source : sources) |
||||||
|
{ |
||||||
|
List<Promise<?, I>> promises = (List<Promise<?, I>>) source.request(count - ret, this); |
||||||
|
ret += promises.size(); |
||||||
|
if (ret >= count) |
||||||
|
break; |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
public void addSource(QueueThing<?, I> source) |
||||||
|
{ |
||||||
|
sources.add(source); |
||||||
|
source.addSink(this); |
||||||
|
} |
||||||
|
|
||||||
|
public void removeSource(QueueThing<?, I> source) |
||||||
|
{ |
||||||
|
sources.remove(source); |
||||||
|
source.removeSink(this); |
||||||
|
} |
||||||
|
|
||||||
|
private void addSink(QueueThing<O, ?> sink) |
||||||
|
{ |
||||||
|
sinks.add(sink); |
||||||
|
} |
||||||
|
|
||||||
|
private void removeSink(QueueThing<O, ?> sink) |
||||||
|
{ |
||||||
|
sinks.remove(sink); |
||||||
|
} |
||||||
|
|
||||||
|
public static class Promise<I, O> |
||||||
|
{ |
||||||
|
|
||||||
|
private I input; |
||||||
|
private final Consumer<O> output; |
||||||
|
|
||||||
|
public Promise(I input, Consumer<O> output) |
||||||
|
{ |
||||||
|
this.input = input; |
||||||
|
this.output = output; |
||||||
|
} |
||||||
|
|
||||||
|
public Promise(Consumer<O> output) |
||||||
|
{ |
||||||
|
this.output = output; |
||||||
|
} |
||||||
|
|
||||||
|
public void setInput(I input) |
||||||
|
{ |
||||||
|
this.input = input; |
||||||
|
} |
||||||
|
|
||||||
|
public void complete(O out) |
||||||
|
{ |
||||||
|
output.accept(out); |
||||||
|
} |
||||||
|
|
||||||
|
public I getInput() |
||||||
|
{ |
||||||
|
return input; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
// protected void submit(O output)
|
||||||
|
// {
|
||||||
|
// if (nextStage != null)
|
||||||
|
// nextStage.accept(output);
|
||||||
|
// }
|
||||||
|
} |
Loading…
Reference in new issue