BSlot.java
/*
* Copyright © 2011-2019 Chris Vest (mr.chrisvest@gmail.com)
*
* 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 stormpot;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* This class is very sensitive to the memory layout, so be careful to measure
* the effect of even the tiniest changes!
* False-sharing can be quite sneaky.
*/
final class BSlot<T extends Poolable>
extends BSlotColdFields<T> {
private static final int CLAIMED = 1;
private static final int TLR_CLAIMED = 2;
private static final int LIVING = 3;
private static final int DEAD = 4;
BSlot(BlockingQueue<BSlot<T>> live, AtomicInteger poisonedSlots) {
// Volatile write in the constructor: This object must be safely published,
// so that we are sure that the volatile write happens-before other
// threads observe the pointer to this object.
super(DEAD, live, poisonedSlots);
}
public void release(Poolable obj) {
if (poison == BlazePool.EXPLICIT_EXPIRE_POISON) {
poisonedSlots.getAndIncrement();
}
int slotState = getClaimState();
lazySet(LIVING);
if (slotState == CLAIMED) {
live.offer(this);
}
}
private int getClaimState() {
int slotState = get();
if (slotState > TLR_CLAIMED) {
throw badStateOnTransitionToLive(slotState);
}
return slotState;
}
private PoolException badStateOnTransitionToLive(int slotState) {
String state;
switch (slotState) {
case DEAD: state = "DEAD"; break;
case LIVING: state = "LIVING"; break;
default: state = "STATE[" + slotState + "]";
}
return new PoolException("Slot release from bad state: " + state + ". " +
"You most likely called release() twice on the same object.");
}
void claim2live() {
lazySet(LIVING);
}
void claimTlr2live() {
lazySet(LIVING);
}
void dead2live() {
lazySet(LIVING);
}
void claim2dead() {
lazySet(DEAD);
}
boolean live2claim() {
return compareAndSet(LIVING, CLAIMED);
}
boolean live2claimTlr() {
return compareAndSet(LIVING, TLR_CLAIMED);
}
boolean live2dead() {
return compareAndSet(LIVING, DEAD);
}
@Override
public long getAgeMillis() {
return TimeUnit.NANOSECONDS.toMillis(NanoClock.elapsed(createdNanos));
}
@Override
public long getClaimCount() {
return claims;
}
@Override
public T getPoolable() {
return obj;
}
boolean isDead() {
return get() == DEAD;
}
boolean isLive() {
return get() == LIVING;
}
boolean isClaimed() {
return get() == CLAIMED;
}
void incrementClaims() {
claims++;
}
@Override
public long getStamp() {
return stamp;
}
@Override
public void setStamp(long stamp) {
this.stamp = stamp;
}
@Override
public String toString() {
int state = get();
String s;
if (state == CLAIMED) {
s = "CLAIMED";
} else if (state == TLR_CLAIMED) {
s = "TLR_CLAIMED";
} else if (state == LIVING) {
s = "LIVING";
} else if (state == DEAD) {
s = "DEAD";
} else {
s = "UnknownState(" + state + ")";
}
return "BSolt[" + s + ", obj = " + obj + ", poison = " + poison + "]";
}
}
@SuppressWarnings("unused")
abstract class Padding1 {
private byte p0;
private byte p1;
private byte p2;
private byte p3;
}
abstract class PaddedAtomicInteger extends Padding1 {
private static final VarHandle STATE;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
STATE = lookup.findVarHandle(PaddedAtomicInteger.class, "state", int.class);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new AssertionError("Failed to initialise the state VarHandle.", e);
}
}
@SuppressWarnings("FieldMayBeFinal")
private volatile int state;
PaddedAtomicInteger(int state) {
this.state = state;
}
@SuppressWarnings("SameParameterValue")
final boolean compareAndSet(int expected, int update) {
return STATE.compareAndSet(this, expected, update);
}
final void lazySet(int update) {
STATE.setOpaque(this, update);
}
int get() {
return state;
}
}
abstract class Padding2 extends PaddedAtomicInteger {
Padding2(int state) {
super(state);
}
}
abstract class BSlotColdFields<T extends Poolable> extends Padding2 implements Slot, SlotInfo<T> {
final BlockingQueue<BSlot<T>> live;
final AtomicInteger poisonedSlots;
long stamp;
long createdNanos;
T obj;
Exception poison;
long claims;
BSlotColdFields(
int state,
BlockingQueue<BSlot<T>> live,
AtomicInteger poisonedSlots) {
super(state);
this.live = live;
this.poisonedSlots = poisonedSlots;
}
@Override
public void expire(Poolable obj) {
if (poison != BlazePool.EXPLICIT_EXPIRE_POISON) {
poison = BlazePool.EXPLICIT_EXPIRE_POISON;
}
}
}