/*
* Copyright 2013 BiasedBit
*
* 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 com.biasedbit.http.client.timeout;
import com.biasedbit.http.client.future.RequestFuture;
import com.biasedbit.http.client.future.RequestFutureListener;
import com.biasedbit.http.client.util.RequestContext;
import org.jboss.netty.util.*;
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
/**
* Implementation of timeout manager that uses an underlying {@link HashedWheelTimer} to manage timeouts.
* <p/>
* If an external {@link HashedWheelTimer} is provided, an instance of this class <strong>will not</strong> call
* {@link HashedWheelTimer#start()} nor {@link HashedWheelTimer#start()} when {@link #init()} and {@link #terminate()}
* are called.
* <h2>Precision vs resource consumption</h2>
* Since {@link HashedWheelTimer} has a periodic checking interval, this timer is not very precise. If, however, you
* configure the {@link HashedWheelTimer} with a very small interval, it will increase precision at the cost of more
* periodic checks (wasted CPU).
* <p/>
* The default tick is 500ms. This means that in the worst case scenario, a request will be cancelled 500ms over the
* timeout set. If this is acceptable, use this implementation rather than {@link BasicTimeoutController}. You can
* always configure a lower tick time although for HTTP requests even 1 second over the limit is okay most of the times.
*
* @author <a href="http://biasedbit.com/">Bruno de Carvalho</a>
*/
public class HashedWheelTimeoutController
implements TimeoutController {
// properties -----------------------------------------------------------------------------------------------------
private final HashedWheelTimer timer;
// constructors ---------------------------------------------------------------------------------------------------
public HashedWheelTimeoutController(long tickDuration, TimeUnit unit, int ticksPerWheel) {
timer = new HashedWheelTimer(tickDuration, unit, ticksPerWheel);
}
public HashedWheelTimeoutController() { this(500, TimeUnit.MILLISECONDS, 512); }
// TimeoutManager -------------------------------------------------------------------------------------------------
@Override public boolean init() {
timer.start();
return true;
}
@Override public void terminate() { timer.stop(); }
@SuppressWarnings({"unchecked"})
@Override public void controlTimeout(final RequestContext context) {
TimerTask task = new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
if (timeout.isExpired()) context.getFuture().failedWithCause(RequestFuture.TIMED_OUT);
}
};
Timeout t = timer.newTimeout(task, context.getTimeout(), TimeUnit.MILLISECONDS);
context.getFuture().addListener(new FutureTimeout(t));
}
// private classes ------------------------------------------------------------------------------------------------
private static class FutureTimeout<T>
implements RequestFutureListener<T> {
// internal vars ----------------------------------------------------------------------------------------------
private final WeakReference<Timeout> httpTimeout; // A weak reference to avoid circular references
// constructors -----------------------------------------------------------------------------------------------
public FutureTimeout(final Timeout t) { httpTimeout = new WeakReference<>(t); }
// RequestFutureListener ----------------------------------------------------------------------------------
@Override public void operationComplete(final RequestFuture<T> future)
throws Exception {
Timeout t = httpTimeout.get();
if (t != null) t.cancel();
}
}
}