blob: c67c5e76a67312dee593423d3b2cc2938bbef344 [file] [log] [blame]
/*
* Copyright 2016 The gRPC Authors
*
* 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 io.grpc.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import io.grpc.ConnectivityState;
import io.grpc.ManagedChannel;
import java.util.ArrayList;
import java.util.concurrent.Executor;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;
/**
* Manages connectivity states of the channel. Used for {@link ManagedChannel#getState} to read the
* current state of the channel, for {@link ManagedChannel#notifyWhenStateChanged} to add
* listeners to state change events, and for {@link io.grpc.LoadBalancer.Helper#updateBalancingState
* LoadBalancer.Helper#updateBalancingState} to update the state and run the {@link #gotoState}s.
*/
@NotThreadSafe
final class ConnectivityStateManager {
private ArrayList<Listener> listeners = new ArrayList<>();
private volatile ConnectivityState state = ConnectivityState.IDLE;
/**
* Adds a listener for state change event.
*
* <p>The {@code executor} must be one that can run RPC call listeners.
*/
void notifyWhenStateChanged(Runnable callback, Executor executor, ConnectivityState source) {
checkNotNull(callback, "callback");
checkNotNull(executor, "executor");
checkNotNull(source, "source");
Listener stateChangeListener = new Listener(callback, executor);
if (state != source) {
stateChangeListener.runInExecutor();
} else {
listeners.add(stateChangeListener);
}
}
/**
* Connectivity state is changed to the specified value. Will trigger some notifications that have
* been registered earlier by {@link ManagedChannel#notifyWhenStateChanged}.
*/
void gotoState(@Nonnull ConnectivityState newState) {
checkNotNull(newState, "newState");
if (state != newState && state != ConnectivityState.SHUTDOWN) {
state = newState;
if (listeners.isEmpty()) {
return;
}
// Swap out callback list before calling them, because a callback may register new callbacks,
// if run in direct executor, can cause ConcurrentModificationException.
ArrayList<Listener> savedListeners = listeners;
listeners = new ArrayList<>();
for (Listener listener : savedListeners) {
listener.runInExecutor();
}
}
}
/**
* Gets the current connectivity state of the channel. This method is threadsafe.
*/
ConnectivityState getState() {
ConnectivityState stateCopy = state;
if (stateCopy == null) {
throw new UnsupportedOperationException("Channel state API is not implemented");
}
return stateCopy;
}
private static final class Listener {
final Runnable callback;
final Executor executor;
Listener(Runnable callback, Executor executor) {
this.callback = callback;
this.executor = executor;
}
void runInExecutor() {
executor.execute(callback);
}
}
}