
org.apache.flink.streaming.runtime.io.MultipleInputSelectionHandler Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.flink.streaming.runtime.io;
import org.apache.flink.annotation.Internal;
import org.apache.flink.streaming.api.operators.InputSelectable;
import org.apache.flink.streaming.api.operators.InputSelection;
import javax.annotation.Nullable;
import java.io.IOException;
import static org.apache.flink.util.Preconditions.checkArgument;
import static org.apache.flink.util.Preconditions.checkState;
/**
* This handler is mainly used for selecting the next available input index in {@link
* StreamMultipleInputProcessor}.
*/
@Internal
public class MultipleInputSelectionHandler {
// if we directly use Long.SIZE, calculation of allSelectedMask will overflow
public static final int MAX_SUPPORTED_INPUT_COUNT = Long.SIZE - 1;
@Nullable private final InputSelectable inputSelectable;
private long selectedInputsMask = InputSelection.ALL.getInputMask();
private final long allSelectedMask;
private long availableInputsMask;
private long notFinishedInputsMask;
private long dataFinishedButNotPartition;
private boolean drainOnEndOfData = true;
private enum OperatingMode {
NO_INPUT_SELECTABLE,
INPUT_SELECTABLE_PRESENT_NO_DATA_INPUTS_FINISHED,
INPUT_SELECTABLE_PRESENT_SOME_DATA_INPUTS_FINISHED,
ALL_DATA_INPUTS_FINISHED;
}
private OperatingMode operatingMode;
public MultipleInputSelectionHandler(
@Nullable InputSelectable inputSelectable, int inputCount) {
checkSupportedInputCount(inputCount);
this.inputSelectable = inputSelectable;
this.allSelectedMask = (1L << inputCount) - 1;
this.availableInputsMask = allSelectedMask;
this.notFinishedInputsMask = allSelectedMask;
this.dataFinishedButNotPartition = 0;
if (inputSelectable != null) {
this.operatingMode = OperatingMode.INPUT_SELECTABLE_PRESENT_NO_DATA_INPUTS_FINISHED;
} else {
this.operatingMode = OperatingMode.NO_INPUT_SELECTABLE;
}
}
public static void checkSupportedInputCount(int inputCount) {
checkArgument(
inputCount <= MAX_SUPPORTED_INPUT_COUNT,
"Only up to %s inputs are supported at once, while encountered %s",
MAX_SUPPORTED_INPUT_COUNT,
inputCount);
}
public DataInputStatus updateStatusAndSelection(DataInputStatus inputStatus, int inputIndex)
throws IOException {
switch (inputStatus) {
case MORE_AVAILABLE:
nextSelection();
checkState(checkBitMask(availableInputsMask, inputIndex));
return DataInputStatus.MORE_AVAILABLE;
case NOTHING_AVAILABLE:
availableInputsMask = unsetBitMask(availableInputsMask, inputIndex);
break;
case STOPPED:
this.drainOnEndOfData = false;
// fall through
case END_OF_DATA:
dataFinishedButNotPartition = setBitMask(dataFinishedButNotPartition, inputIndex);
updateModeOnEndOfData();
break;
case END_OF_INPUT:
dataFinishedButNotPartition = unsetBitMask(dataFinishedButNotPartition, inputIndex);
notFinishedInputsMask = unsetBitMask(notFinishedInputsMask, inputIndex);
break;
default:
throw new UnsupportedOperationException("Unsupported inputStatus = " + inputStatus);
}
nextSelection();
return calculateOverallStatus(inputStatus);
}
private void updateModeOnEndOfData() {
boolean allDataInputsFinished =
((dataFinishedButNotPartition | ~notFinishedInputsMask) & allSelectedMask)
== allSelectedMask;
if (allDataInputsFinished) {
this.operatingMode = OperatingMode.ALL_DATA_INPUTS_FINISHED;
} else if (this.operatingMode
== OperatingMode.INPUT_SELECTABLE_PRESENT_NO_DATA_INPUTS_FINISHED) {
this.operatingMode = OperatingMode.INPUT_SELECTABLE_PRESENT_SOME_DATA_INPUTS_FINISHED;
}
}
private DataInputStatus calculateOverallStatus(DataInputStatus updatedStatus)
throws IOException {
if (areAllInputsFinished()) {
return DataInputStatus.END_OF_INPUT;
}
if (updatedStatus == DataInputStatus.END_OF_DATA
&& this.operatingMode == OperatingMode.ALL_DATA_INPUTS_FINISHED) {
return drainOnEndOfData ? DataInputStatus.END_OF_DATA : DataInputStatus.STOPPED;
}
if (isAnyInputAvailable()) {
return DataInputStatus.MORE_AVAILABLE;
} else {
long selectedNotFinishedInputMask = selectedInputsMask & notFinishedInputsMask;
if (selectedNotFinishedInputMask == 0) {
throw new IOException(
"Can not make a progress: all selected inputs are already finished");
}
return DataInputStatus.NOTHING_AVAILABLE;
}
}
void nextSelection() {
switch (operatingMode) {
case NO_INPUT_SELECTABLE:
case ALL_DATA_INPUTS_FINISHED:
selectedInputsMask = InputSelection.ALL.getInputMask();
break;
case INPUT_SELECTABLE_PRESENT_NO_DATA_INPUTS_FINISHED:
selectedInputsMask = inputSelectable.nextSelection().getInputMask();
break;
case INPUT_SELECTABLE_PRESENT_SOME_DATA_INPUTS_FINISHED:
selectedInputsMask =
(inputSelectable.nextSelection().getInputMask()
| dataFinishedButNotPartition)
& allSelectedMask;
break;
}
}
int selectNextInputIndex(int lastReadInputIndex) {
return InputSelection.fairSelectNextIndex(
selectedInputsMask,
availableInputsMask & notFinishedInputsMask,
lastReadInputIndex);
}
boolean shouldSetAvailableForAnotherInput() {
return (selectedInputsMask & allSelectedMask & ~availableInputsMask) != 0;
}
void setAvailableInput(int inputIndex) {
availableInputsMask = setBitMask(availableInputsMask, inputIndex);
}
void setUnavailableInput(int inputIndex) {
availableInputsMask = unsetBitMask(availableInputsMask, inputIndex);
}
boolean isAnyInputAvailable() {
return (selectedInputsMask & availableInputsMask & notFinishedInputsMask) != 0;
}
boolean isInputSelected(int inputIndex) {
return checkBitMask(selectedInputsMask, inputIndex);
}
public boolean isInputFinished(int inputIndex) {
return !checkBitMask(notFinishedInputsMask, inputIndex);
}
public boolean areAllInputsFinished() {
return notFinishedInputsMask == 0;
}
public boolean areAllDataInputsFinished() {
return this.operatingMode == OperatingMode.ALL_DATA_INPUTS_FINISHED;
}
long setBitMask(long mask, int inputIndex) {
return mask | 1L << inputIndex;
}
long unsetBitMask(long mask, int inputIndex) {
return mask & ~(1L << inputIndex);
}
boolean checkBitMask(long mask, int inputIndex) {
return (mask & (1L << inputIndex)) != 0;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy