/*
* Copyright 2013 Ben Manes. All Rights Reserved.
*
* 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.github.benmanes.multiway;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import com.github.benmanes.multiway.ResourceKey.Status;
import com.google.common.base.Objects;
/**
* A key to the resource in the cache and the transfer stack.
*
* @author Ben Manes (ben.manes@gmail.com)
*/
@ThreadSafe
abstract class ResourceKey<K> extends AtomicReference<Status> implements Linked<ResourceKey<K>> {
private static final long serialVersionUID = 1L;
/** The status of the resource in the pool. */
enum Status {
/** The resource is available to be used. */
IDLE,
/** The resource is in use and in the cache */
IN_FLIGHT,
/** The resource is in use and evicted from the cache */
RETIRED,
/** The resource is not in use and not cached */
DEAD
}
final K key;
final EliminationStack<ResourceKey<K>> stack;
Object handle;
ResourceKey(EliminationStack<ResourceKey<K>> stack, Status status, K key) {
super(status);
this.key = key;
this.stack = stack;
}
/** Retrieves the resource category key. */
K getKey() {
return key;
}
/** The status of the entry in the pool. */
Status getStatus() {
return get();
}
/** Retrieves the transfer stack the resource is associated with. */
EliminationStack<ResourceKey<K>> getStack() {
return stack;
}
/** Removes the resource key from its transfer stack. */
void removeFromTransferStack() {
getStack().remove(this);
}
/** Retrieves the time, in nanoseconds, that the resource became idle in the pool. */
abstract long getAccessTime();
/** Sets the time, in nanoseconds, that the resource became idle in the pool. */
abstract void setAccessTime(long accessTimeNanos);
/** Adds the available resource to the policy to be evaluated for expiration. */
abstract Runnable getAddTask();
/** Removes the resource from the policy, e.g. when borrowed or discarded by the pool. */
abstract Runnable getRemovalTask();
/* ---------------- IDLE --> ? -------------- */
/** Attempts to transition the entry from idle to in-flight (when borrowing). */
boolean goFromIdleToInFlight() {
return compareAndSet(Status.IDLE, Status.IN_FLIGHT);
}
/** Attempts to transition the entry from idle to retired (when evicting from the idle cache). */
boolean goFromIdleToRetired() {
return compareAndSet(Status.IDLE, Status.RETIRED);
}
/** Attempts to transition the entry from idle to dead (when evicting from the cache). */
boolean goFromIdleToDead() {
return compareAndSet(Status.IDLE, Status.DEAD);
}
/* ---------------- IN_FLIGHT --> ? -------------- */
/** Attempts to transition the entry from in-flight to retired (when evicting from the cache). */
boolean goFromInFlightToRetired() {
return compareAndSet(Status.IN_FLIGHT, Status.RETIRED);
}
/** Attempts to transition the entry from in-flight to retired (when releasing the handle). */
boolean goFromInFlightToIdle() {
return compareAndSet(Status.IN_FLIGHT, Status.IDLE);
}
/* ---------------- RETIRED --> ? -------------- */
/** Attempts to transition the entry from retired to dead (when releasing the handle). */
boolean goFromRetiredToDead() {
return compareAndSet(Status.RETIRED, Status.DEAD);
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("id", System.identityHashCode(this))
.add("status", get())
.add("key", key)
.toString();
}
/** A resource key used when idle caching is disabled (does not support links). */
static final class UnlinkedResourceKey<K> extends ResourceKey<K> {
private static final long serialVersionUID = 1L;
UnlinkedResourceKey(EliminationStack<ResourceKey<K>> stack, Status status, K key) {
super(stack, status, key);
}
@Override
long getAccessTime() {
throw new UnsupportedOperationException();
}
@Override
void setAccessTime(long accessTimeNanos) {
throw new UnsupportedOperationException();
}
@Override
public ResourceKey<K> getPrevious() {
throw new UnsupportedOperationException();
}
@Override
public void setPrevious(ResourceKey<K> prev) {
throw new UnsupportedOperationException();
}
@Override
public ResourceKey<K> getNext() {
throw new UnsupportedOperationException();
}
@Override
public void setNext(ResourceKey<K> next) {
throw new UnsupportedOperationException();
}
@Override
Runnable getAddTask() {
throw new UnsupportedOperationException();
}
@Override
Runnable getRemovalTask() {
throw new UnsupportedOperationException();
}
}
/** A resource key used when idle caching is enabled (access order). */
static final class LinkedResourceKey<K> extends ResourceKey<K> {
private static final long serialVersionUID = 1L;
@GuardedBy("idleLock")
ResourceKey<K> prev;
@GuardedBy("idleLock")
ResourceKey<K> next;
final Runnable addTask;
final Runnable removalTask;
volatile long accessTimeNanos;
LinkedResourceKey(EliminationStack<ResourceKey<K>> stack, Status status,
K key, final LinkedDeque<ResourceKey<K>> idleQueue) {
super(stack, status, key);
this.addTask = new Runnable() {
@Override
@GuardedBy("idleLock")
public void run() {
if (getStatus() == Status.IDLE) {
setAccessTime(accessTimeNanos);
idleQueue.linkLast(LinkedResourceKey.this);
}
}
};
this.removalTask = new Runnable() {
@Override
@GuardedBy("idleLock")
public void run() {
idleQueue.remove(LinkedResourceKey.this);
}
};
}
@Override
long getAccessTime() {
return accessTimeNanos;
}
@Override
void setAccessTime(long accessTimeNanos) {
this.accessTimeNanos = accessTimeNanos;
}
@Override
@GuardedBy("idleLock")
public ResourceKey<K> getPrevious() {
return prev;
}
@Override
@GuardedBy("idleLock")
public void setPrevious(ResourceKey<K> prev) {
this.prev = prev;
}
@Override
@GuardedBy("idleLock")
public ResourceKey<K> getNext() {
return next;
}
@Override
@GuardedBy("idleLock")
public void setNext(ResourceKey<K> next) {
this.next = next;
}
@Override
Runnable getAddTask() {
return addTask;
}
@Override
Runnable getRemovalTask() {
return removalTask;
}
}
}