All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aerospike.client.async.HashedWheelTimer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project 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.
 */
/*
 * Copyright 2012-2021 Aerospike, Inc.
 *
 * Portions may be licensed to Aerospike, Inc. under one or more contributor
 * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0.
 *
 * 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.aerospike.client.async;

import java.util.concurrent.TimeUnit;

import com.aerospike.client.Log;
import com.aerospike.client.util.Util;

/**
 * This HashedWheelTimer is a simplified version of netty's
 * HashedWheelTimer.
 * 

* {@link HashedWheelTimer} is based on * George Varghese and * Tony Lauck's paper, * 'Hashed * and Hierarchical Timing Wheels: data structures to efficiently implement a * timer facility'. More comprehensive slides are located * here. *

* This HashedWheelTimer runs directly in each event loop thread. All HashedWheelTimer method calls * must also occur in its defined event loop thread. */ public final class HashedWheelTimer implements Runnable { private final EventLoop eventLoop; private final HashedWheelBucket[] wheel; private final ScheduleTask schedule; private final long tickDuration; private long startTime; private long tick; private final int mask; public HashedWheelTimer(EventLoop eventLoop, long tickDuration, TimeUnit unit, int ticksPerWheel) { this.eventLoop = eventLoop; this.tickDuration = unit.toNanos(tickDuration); int normalizedTicksPerWheel = 1; while (normalizedTicksPerWheel < ticksPerWheel) { normalizedTicksPerWheel <<= 1; } wheel = new HashedWheelBucket[normalizedTicksPerWheel]; for (int i = 0; i < wheel.length; i++) { wheel[i] = new HashedWheelBucket(); } mask = wheel.length - 1; // Prevent overflow. if (this.tickDuration >= Long.MAX_VALUE / wheel.length) { throw new IllegalArgumentException(String.format( "tickDuration: %d (expected: 0 < tickDuration in nanos < %d", tickDuration, Long.MAX_VALUE / wheel.length)); } schedule = new ScheduleTask(this); } public void start() { startTime = System.nanoTime(); eventLoop.schedule(schedule, tickDuration, TimeUnit.NANOSECONDS); } public void run() { long currentTime = System.nanoTime() - startTime; long expectTime = tickDuration * (tick + 1); while (expectTime <= currentTime) { int idx = (int) (tick & mask); wheel[idx].expireTimeouts(currentTime); tick++; expectTime += tickDuration; } eventLoop.schedule(schedule, expectTime - currentTime, TimeUnit.NANOSECONDS); } public void addTimeout(HashedWheelTimeout timeout, long deadline) { timeout.deadline = deadline - startTime; timeout.next = null; timeout.prev = null; long calculated = timeout.deadline / tickDuration; timeout.remainingRounds = (calculated - tick) / wheel.length; final long ticks = Math.max(calculated, tick); int stopIndex = (int) (ticks & mask); HashedWheelBucket bucket = wheel[stopIndex]; bucket.addTimeout(timeout); } public static final class HashedWheelTimeout { private final TimerTask task; private long deadline; private long remainingRounds; private HashedWheelTimeout next; private HashedWheelTimeout prev; private HashedWheelBucket bucket; HashedWheelTimeout(TimerTask task) { this.task = task; } public boolean active() { return bucket != null; } public void cancel() { if (bucket != null) { bucket.remove(this); } } private void expire() { try { task.timeout(); } catch (Throwable t) { if (Log.warnEnabled()) { Log.warn("task.timeout() failed: " + Util.getErrorMessage(t)); } } } } private static final class HashedWheelBucket { private HashedWheelTimeout head; private HashedWheelTimeout tail; public void addTimeout(HashedWheelTimeout timeout) { timeout.bucket = this; if (head == null) { head = tail = timeout; } else { tail.next = timeout; timeout.prev = tail; tail = timeout; } } public void expireTimeouts(long deadline) { HashedWheelTimeout timeout = head; // process all timeouts while (timeout != null) { HashedWheelTimeout next = timeout.next; if (timeout.remainingRounds <= 0) { next = remove(timeout); if (timeout.deadline > deadline) { // Should not happen. Do not throw exception because that would break all // other timeouts in this iteration. if (Log.warnEnabled()) { Log.warn("timeout.deadline (" + timeout.deadline + ") > deadline (" + deadline + ")"); } } timeout.expire(); } else { timeout.remainingRounds--; } timeout = next; } } public HashedWheelTimeout remove(HashedWheelTimeout timeout) { HashedWheelTimeout next = timeout.next; // remove timeout that was either processed or cancelled by updating the linked-list if (timeout.prev != null) { timeout.prev.next = next; } if (timeout.next != null) { timeout.next.prev = timeout.prev; } if (timeout == head) { // if timeout is also the tail we need to adjust the entry too if (timeout == tail) { tail = null; head = null; } else { head = next; } } else if (timeout == tail) { // if the timeout is the tail modify the tail to be the prev node. tail = timeout.prev; } // null out prev, next and bucket to allow for GC. timeout.prev = null; timeout.next = null; timeout.bucket = null; return next; } } /* public void printRemaining() { System.out.println("Search remaining buckets"); for (int i = 0; i < wheel.length; i++) { HashedWheelBucket bucket = wheel[i]; if (bucket.head != null || bucket.tail != null) { System.out.println("Bucket " + i + " exists"); } } } */ }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy