/*
* Copyright (C) 2011-2014 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.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* This class is very sensitive to the memory layout, so be careful to measure
* the effect of even the tiniest changes!
* False-sharing is a fickle and vengeful mistress.
*/
final class BSlot<T extends Poolable>
extends BSlotColdFields<T> {
static final int CLAIMED = 1;
static final int TLR_CLAIMED = 2;
static final int LIVING = 3;
static final int DEAD = 4;
public 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.");
}
public void claim2live() {
lazySet(LIVING);
}
public void claimTlr2live() {
lazySet(LIVING);
}
public void dead2live() {
lazySet(LIVING);
}
public void claim2dead() {
lazySet(DEAD);
}
public boolean live2claim() {
return compareAndSet(LIVING, CLAIMED);
}
public boolean live2claimTlr() {
return compareAndSet(LIVING, TLR_CLAIMED);
}
public boolean live2dead() {
return compareAndSet(LIVING, DEAD);
}
@Override
public long getAgeMillis() {
return System.currentTimeMillis() - created;
}
@Override
public long getClaimCount() {
return claims;
}
@Override
public T getPoolable() {
return obj;
}
public boolean isDead() {
return get() == DEAD;
}
public boolean isLive() {
return get() == LIVING;
}
public int getState() {
return get();
}
public void incrementClaims() {
claims++;
}
@Override
public long getStamp() {
return stamp;
}
@Override
public void setStamp(long stamp) {
this.stamp = stamp;
}
}
// stop checking line length
/*
The Java Object Layout rendition:
Running 64-bit HotSpot VM.
Using compressed references with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
stormpot.BSlot object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 21 88 61 (0000 0001 0010 0001 1000 1000 0110 0001)
4 4 (object header) 6c 00 00 00 (0110 1100 0000 0000 0000 0000 0000 0000)
8 4 (object header) 8a b6 61 df (1000 1010 1011 0110 0110 0001 1101 1111)
12 4 int Padding1.p0 0
16 8 long Padding1.p1 0
24 8 long Padding1.p2 0
32 8 long Padding1.p3 0
40 8 long Padding1.p4 0
48 8 long Padding1.p5 0
56 8 long Padding1.p6 0
64 4 int PaddedAtomicInteger.state 4
68 4 (alignment/padding gap) N/A
72 8 long Padding2.p1 0
80 8 long Padding2.p2 0
88 8 long Padding2.p3 0
96 8 long Padding2.p4 0
104 8 long Padding2.p5 0
112 8 long Padding2.p6 0
120 8 long Padding2.p7 0
128 8 long BSlotColdFields.stamp 0
136 8 long BSlotColdFields.created 0
144 8 long BSlotColdFields.claims 0
152 4 int BSlotColdFields.x 1818331169
156 4 int BSlotColdFields.y 938745813
160 4 int BSlotColdFields.z 452465366
164 4 int BSlotColdFields.w 1343246171
168 4 BlockingQueue BSlotColdFields.live null
172 4 Poolable BSlotColdFields.obj null
176 4 Exception BSlotColdFields.poison null
180 4 (loss due to the next object alignment)
Instance size: 184 bytes (estimated, add this JAR via -javaagent: to get accurate result)
Space losses: 4 bytes internal + 4 bytes external = 8 bytes total
*/
// start checking line length
abstract class Padding1 {
private int p0;
private long p1, p2, p3, p4, p5, p6;
}
abstract class PaddedAtomicInteger extends Padding1 {
private static final long stateFieldOffset;
private static final AtomicIntegerFieldUpdater<PaddedAtomicInteger> updater;
static {
long theStateFieldOffset = 0;
AtomicIntegerFieldUpdater<PaddedAtomicInteger> theStateUpdater = null;
if (UnsafeUtil.hasUnsafe()) {
theStateFieldOffset = UnsafeUtil.objectFieldOffset(PaddedAtomicInteger.class, "state");
} else {
theStateUpdater = AtomicIntegerFieldUpdater.newUpdater(PaddedAtomicInteger.class, "state");
}
stateFieldOffset = theStateFieldOffset;
updater = theStateUpdater;
}
private volatile int state;
public PaddedAtomicInteger(int state) {
this.state = state;
}
protected final boolean compareAndSet(int expected, int update) {
if (UnsafeUtil.hasUnsafe()) {
return UnsafeUtil.compareAndSwapInt(this, stateFieldOffset, expected, update);
} else {
return updater.compareAndSet(this, expected, update);
}
}
protected final void lazySet(int update) {
if (UnsafeUtil.hasUnsafe()) {
UnsafeUtil.putOrderedInt(this, stateFieldOffset, update);
} else {
updater.lazySet(this, update);
}
}
protected int get() {
return state;
}
}
abstract class Padding2 extends PaddedAtomicInteger {
private long p1, p2, p3, p4, p5, p6;
public 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 created;
T obj;
Exception poison;
long claims;
public BSlotColdFields(
int state,
BlockingQueue<BSlot<T>> live,
AtomicInteger poisonedSlots) {
super(state);
this.live = live;
this.poisonedSlots = poisonedSlots;
}
// XorShift PRNG with a 2^128-1 period.
int x = System.identityHashCode(this);
int y = -938745813;
int z = 452465366;
int w = 1343246171;
@Override
public int randomInt() {
int t = x^(x<<15);
//noinspection SuspiciousNameCombination
x = y; y = z; z = w;
return w = (w^(w>>>21))^(t^(t>>>4));
}
@Override
public void expire(Poolable obj) {
if (poison != BlazePool.EXPLICIT_EXPIRE_POISON) {
poison = BlazePool.EXPLICIT_EXPIRE_POISON;
}
}
}