blob: 59d3309f248ac70ce69446e80674511b8d87bc66 [file] [log] [blame]
/*
* 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.pulsar.client.impl;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class ClientCnxIdleState {
private final ClientCnx clientCnx;
/** Stat. **/
private volatile State state;
/** Create time. This field is only used to troubleshoot (analyze by dump file). **/
private final long createTime;
/** The time when marks the connection is idle. **/
private long idleMarkTime;
public ClientCnxIdleState(ClientCnx clientCnx){
this.clientCnx = clientCnx;
this.createTime = System.currentTimeMillis();
this.state = State.USING;
}
private static final AtomicReferenceFieldUpdater<ClientCnxIdleState, State> STATE_UPDATER =
AtomicReferenceFieldUpdater.newUpdater(ClientCnxIdleState.class, State.class, "state");
/**
* Indicates the usage status of the connection and whether it has been released.
*/
public enum State {
/** It was using at the time of last check. At current time may be idle. **/
USING,
/** The connection is in idle. **/
IDLE,
/** The connection is in idle and will be released soon. In this state, the connection can still be used. **/
RELEASING,
/** The connection has already been released. **/
RELEASED;
}
/**
* Get idle-stat.
* @return connection idle-stat
*/
public State getIdleStat() {
return STATE_UPDATER.get(this);
}
/**
* Compare and switch idle-stat.
* @return Whether the update is successful.Because there may be other threads competing, possible return false.
*/
boolean compareAndSetIdleStat(State originalStat, State newStat) {
return STATE_UPDATER.compareAndSet(this, originalStat, newStat);
}
/**
* @return Whether this connection is in use.
*/
public boolean isUsing() {
return getIdleStat() == State.USING;
}
/**
* @return Whether this connection is in idle.
*/
public boolean isIdle() {
return getIdleStat() == State.IDLE;
}
/**
* @return Whether this connection is in idle and will be released soon.
*/
public boolean isReleasing() {
return getIdleStat() == State.RELEASING;
}
/**
* @return Whether this connection has already been released.
*/
public boolean isReleased() {
return getIdleStat() == State.RELEASED;
}
/**
* Try to transform the state of the connection to #{@link State#IDLE}, state should only be
* transformed to #{@link State#IDLE} from state #{@link State#USING}. if the state
* is successfully transformed, "idleMarkTime" will be assigned to current time.
*/
public void tryMarkIdleAndInitIdleTime() {
if (compareAndSetIdleStat(State.USING, State.IDLE)) {
idleMarkTime = System.currentTimeMillis();
}
}
/**
* Changes the idle-state of the connection to #{@link State#USING} as much as possible, This method
* is used when connection borrow, and reset {@link #idleMarkTime} if change state to
* #{@link State#USING} success.
* @return Whether change idle-stat to #{@link State#USING} success. False is returned only if the
* connection has already been released.
*/
public boolean tryMarkUsingAndClearIdleTime() {
while (true) {
// Ensure not released
if (isReleased()) {
return false;
}
// Try mark release
if (compareAndSetIdleStat(State.IDLE, State.USING)) {
idleMarkTime = 0;
return true;
}
if (compareAndSetIdleStat(State.RELEASING, State.USING)) {
idleMarkTime = 0;
return true;
}
if (isUsing()){
return true;
}
}
}
/**
* Changes the idle-state of the connection to #{@link State#RELEASING}, This method only changes this
* connection from the #{@link State#IDLE} state to the #{@link State#RELEASING} state.
* @return Whether change idle-stat to #{@link State#RELEASING} success.
*/
public boolean tryMarkReleasing() {
return compareAndSetIdleStat(State.IDLE, State.RELEASING);
}
/**
* Changes the idle-state of the connection to #{@link State#RELEASED}, This method only changes this
* connection from the #{@link State#RELEASING} state to the #{@link State#RELEASED}
* state, and close {@param clientCnx} if change state to #{@link State#RELEASED} success.
* @return Whether change idle-stat to #{@link State#RELEASED} and close connection success.
*/
public boolean tryMarkReleasedAndCloseConnection() {
if (!compareAndSetIdleStat(State.RELEASING, State.RELEASED)) {
return false;
}
clientCnx.close();
return true;
}
/**
* Check whether the connection is idle, and if so, set the idle-state to #{@link State#IDLE}.
* If the state is already idle and the {@param maxIdleSeconds} is reached, set the state to
* #{@link State#RELEASING}.
*/
public void doIdleDetect(long maxIdleSeconds) {
if (isReleasing()) {
return;
}
if (isIdle()) {
if (maxIdleSeconds * 1000 + idleMarkTime < System.currentTimeMillis()) {
tryMarkReleasing();
}
return;
}
if (clientCnx.idleCheck()) {
tryMarkIdleAndInitIdleTime();
}
}
}