/*
 * Copyright 2012 Chris Vest
 * 
 * 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.bpool;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

import stormpot.PoolException;
import stormpot.Poolable;
import stormpot.Slot;
import stormpot.SlotInfo;

class BSlot<T extends Poolable> implements Slot, SlotInfo<T> {
  static final int LIVING = 1;
  static final int CLAIMED = 2;
  static final int TLR_CLAIMED = 3;
  static final int DEAD = 4;
  
  final BlockingQueue<BSlot<T>> live;
  private final AtomicInteger state;
  T obj;
  Exception poison;
  long created;
  long claims;
  Thread owner;
  
  public BSlot(BlockingQueue<BSlot<T>> live) {
    this.live = live;
    this.state = new AtomicInteger(DEAD);
  }
  
  public void release(Poolable obj) {
    int slotState = 0;
    do {
      slotState = state.get();
      Thread claimer = owner;
      Thread releaser = Thread.currentThread();
      if (claimer != releaser) {
        throw new PoolException(
            "Expected release from claimer " + claimer + " but was " + releaser);
      }
      if (slotState != TLR_CLAIMED && slotState != CLAIMED) {
        throw new PoolException("Slot release from bad state: " + slotState);
      }
    } while (!(slotState == CLAIMED? claim2live() : claimTlr2live()));
    if (slotState == CLAIMED) {
      live.offer(this);
    }
  }
  
  public boolean claim2live() {
    return cas(CLAIMED, LIVING);
  }
  
  public boolean claimTlr2live() {
    return cas(TLR_CLAIMED, LIVING);
  }
  
  public boolean live2claim() {
    boolean cas = cas(LIVING, CLAIMED);
    if (cas) owner = Thread.currentThread();
    return cas;
  }
  
  public boolean live2claimTlr() {
    boolean cas = cas(LIVING, TLR_CLAIMED);
    if (cas) owner = Thread.currentThread();
    return cas;
  }
  
  public boolean claimTlr2claim() {
    return cas(TLR_CLAIMED, CLAIMED);
  }
  
  public boolean claim2dead() {
    return cas(CLAIMED, DEAD);
  }
  
  public boolean dead2live() {
    return cas(DEAD, LIVING);
  }
  
  public boolean live2dead() {
    return cas(LIVING, DEAD);
  }

  private boolean cas(int expected, int update) {
    return state.compareAndSet(expected, update);
  }
  
  @Override
  public long getAgeMillis() {
    return System.currentTimeMillis() - created;
  }

  @Override
  public long getClaimCount() {
    return claims;
  }

  @Override
  public T getPoolable() {
    return obj;
  }

  public boolean isDead() {
    return state.get() == DEAD;
  }
  
  public int getState() {
    return state.get();
  }

  public void incrementClaims() {
    claims++;
  }
}

@@ -1627,24 +1627,31 @@
     
-if (slotState !=
+// We loop here because
  TLR
@@ -1662,136 +1662,243 @@
 IMED
- &&
  slot
-State != CLAIMED) %7B%0A        throw new PoolException(%22Slot release from bad state: %22 + slotState);%0A      %7D%0A    %7D while (!
+s can be concurrently changed%0A      // into normal CLAIMED slots.%0A    %7D while (!releaseState(slotState));%0A    if (slotState == CLAIMED) %7B%0A      live.offer(this);%0A    %7D%0A  %7D%0A  %0A  private boolean releaseState(int slotState) %7B%0A    if 
 (slo
@@ -1911,31 +1911,35 @@
  == 
+TLR_
 CLAIMED
-? claim2live() :
+) %7B%0A      return
  cla
@@ -1950,24 +1950,29 @@
 r2live()
-))
 ;%0A    
+%7D else 
 if (slot
@@ -1997,39 +1997,116 @@
 %7B%0A      
-live.offer(this);%0A    %7D
+return claim2live();%0A    %7D%0A    throw new PoolException(%22Slot release from bad state: %22 + slotState);
 %0A  %7D%0A  %0A

increase coverage slightly by reducing complexity in the BSlot.