/**
   * Set local batch size for this sequences.
   *
   * @param size Sequence batch size. Must be more then 0.
   */
  @Override
  public void batchSize(int size) {
    A.ensure(size > 0, " Batch size can't be less then 0: " + size);

    lock.lock();

    try {
      batchSize = size;
    } finally {
      lock.unlock();
    }
  }
  /** {@inheritDoc} */
  @Override
  public long get() throws GridException {
    checkRemoved();

    lock.lock();

    try {
      return locVal;
    } finally {
      lock.unlock();
    }
  }
/**
 * Cache sequence implementation.
 *
 * @author 2012 Copyright (C) GridGain Systems
 * @version 4.0.2c.12042012
 */
public final class GridCacheAtomicSequenceImpl extends GridMetadataAwareAdapter
    implements GridCacheAtomicSequenceEx, Externalizable {
  /** Deserialization stash. */
  private static final ThreadLocal<GridTuple2<GridCacheContext, String>> stash =
      new ThreadLocal<GridTuple2<GridCacheContext, String>>() {
        @Override
        protected GridTuple2<GridCacheContext, String> initialValue() {
          return F.t2();
        }
      };

  /** Logger. */
  private GridLogger log;

  /** Sequence name. */
  private String name;

  /** Removed flag. */
  private volatile boolean rmvd;

  /** Sequence key. */
  private GridCacheInternalStorableKey key;

  /** Sequence projection. */
  private GridCacheProjection<GridCacheInternalStorableKey, GridCacheAtomicSequenceValue> seqView;

  /** Cache context. */
  private volatile GridCacheContext ctx;

  /** Local value of sequence. */
  private long locVal;

  /** Upper bound of local counter. */
  private long upBound;

  /** Sequence batch size */
  private volatile int batchSize;

  /** Synchronization lock. */
  private final Lock lock = new ReentrantLock();

  /** Await condition. */
  private Condition cond = lock.newCondition();

  /** Callable for execution {@link #incrementAndGet} operation in async and sync mode. */
  private final Callable<Long> incAndGetCall = internalUpdate(1, true);

  /** Callable for execution {@link #getAndIncrement} operation in async and sync mode. */
  private final Callable<Long> getAndIncCall = internalUpdate(1, false);

  /** Add and get cache call guard. */
  private final AtomicBoolean updateGuard = new AtomicBoolean();

  /** Empty constructor required by {@link Externalizable}. */
  public GridCacheAtomicSequenceImpl() {
    // No-op.
  }

  /**
   * Default constructor.
   *
   * @param name Sequence name.
   * @param key Sequence key.
   * @param seqView Sequence projection.
   * @param ctx CacheContext.
   * @param locVal Local counter.
   * @param upBound Upper bound.
   */
  public GridCacheAtomicSequenceImpl(
      String name,
      GridCacheInternalStorableKey key,
      GridCacheProjection<GridCacheInternalStorableKey, GridCacheAtomicSequenceValue> seqView,
      GridCacheContext ctx,
      long locVal,
      long upBound) {
    assert key != null;
    assert seqView != null;
    assert ctx != null;
    assert locVal <= upBound;

    batchSize = ctx.config().getAtomicSequenceReserveSize();
    this.ctx = ctx;
    this.key = key;
    this.seqView = seqView;
    this.upBound = upBound;
    this.locVal = locVal;
    this.name = name;

    log = ctx.gridConfig().getGridLogger().getLogger(getClass());
  }

  /** {@inheritDoc} */
  @Override
  public String name() {
    return name;
  }

  /** {@inheritDoc} */
  @Override
  public long get() throws GridException {
    checkRemoved();

    lock.lock();

    try {
      return locVal;
    } finally {
      lock.unlock();
    }
  }

  /** {@inheritDoc} */
  @Override
  public long incrementAndGet() throws GridException {
    return internalUpdate(1, incAndGetCall, true);
  }

  /** {@inheritDoc} */
  @Override
  public long getAndIncrement() throws GridException {
    return internalUpdate(1, getAndIncCall, false);
  }

  /** {@inheritDoc} */
  @Override
  public GridFuture<Long> incrementAndGetAsync() throws GridException {
    return internalUpdateAsync(1, incAndGetCall, true);
  }

  /** {@inheritDoc} */
  @Override
  public GridFuture<Long> getAndIncrementAsync() throws GridException {
    return internalUpdateAsync(1, getAndIncCall, false);
  }

  /** {@inheritDoc} */
  @Override
  public long addAndGet(long l) throws GridException {
    A.ensure(l > 0, " Parameter mustn't be less then 1: " + l);

    return internalUpdate(l, null, true);
  }

  /** {@inheritDoc} */
  @Override
  public GridFuture<Long> addAndGetAsync(long l) throws GridException {
    A.ensure(l > 0, " Parameter mustn't be less then 1: " + l);

    return internalUpdateAsync(l, null, true);
  }

  /** {@inheritDoc} */
  @Override
  public long getAndAdd(long l) throws GridException {
    A.ensure(l > 0, " Parameter mustn't be less then 1: " + l);

    return internalUpdate(l, null, false);
  }

  /** {@inheritDoc} */
  @Override
  public GridFuture<Long> getAndAddAsync(long l) throws GridException {
    A.ensure(l > 0, " Parameter mustn't be less then 1: " + l);

    return internalUpdateAsync(l, null, false);
  }

  /**
   * Synchronous sequence update operation. Will add given amount to the sequence value.
   *
   * @param l Increment amount.
   * @param updateCall Cache call that will update sequence reservation count in accordance with l.
   * @param updated If {@code true}, will return sequence value after update, otherwise will return
   *     sequence value prior to update.
   * @return Sequence value.
   * @throws GridException If update failed.
   */
  private long internalUpdate(long l, @Nullable Callable<Long> updateCall, boolean updated)
      throws GridException {
    checkRemoved();

    assert l > 0;

    lock.lock();

    try {
      // If reserved range isn't exhausted.
      if (locVal + l <= upBound) {
        long curVal = locVal;

        locVal += l;

        return updated ? locVal : curVal;
      }
    } finally {
      lock.unlock();
    }

    if (updateCall == null) updateCall = internalUpdate(l, updated);

    while (true) {
      if (updateGuard.compareAndSet(false, true)) {
        try {
          // This call must be outside lock.
          return CU.outTx(updateCall, ctx);
        } finally {
          lock.lock();

          try {
            updateGuard.set(false);

            cond.signalAll();
          } finally {
            lock.unlock();
          }
        }
      } else {
        lock.lock();

        try {
          while (locVal >= upBound && updateGuard.get()) {
            try {
              cond.await(500, MILLISECONDS);
            } catch (InterruptedException e) {
              throw new GridInterruptedException(e);
            }
          }

          checkRemoved();

          // If reserved range isn't exhausted.
          if (locVal + l <= upBound) {
            long curVal = locVal;

            locVal += l;

            return updated ? locVal : curVal;
          }
        } finally {
          lock.unlock();
        }
      }
    }
  }

  /**
   * Asynchronous sequence update operation. Will add given amount to the sequence value.
   *
   * @param l Increment amount.
   * @param updateCall Cache call that will update sequence reservation count in accordance with l.
   * @param updated If {@code true}, will return sequence value after update, otherwise will return
   *     sequence value prior to update.
   * @return Future indicating sequence value.
   * @throws GridException If update failed.
   */
  private GridFuture<Long> internalUpdateAsync(
      long l, @Nullable Callable<Long> updateCall, boolean updated) throws GridException {
    checkRemoved();

    A.ensure(l > 0, " Parameter mustn't be less then 1: " + l);

    lock.lock();

    try {
      // If reserved range isn't exhausted.
      if (locVal + l <= upBound) {
        long curVal = locVal;

        locVal += l;

        return new GridFinishedFuture<Long>(ctx.kernalContext(), updated ? locVal : curVal);
      }
    } finally {
      lock.unlock();
    }

    if (updateCall == null) updateCall = internalUpdate(l, updated);

    while (true) {
      if (updateGuard.compareAndSet(false, true)) {
        try {
          // This call must be outside lock.
          return ctx.closures().callLocalSafe(updateCall, true);
        } finally {
          lock.lock();

          try {
            updateGuard.set(false);

            cond.signalAll();
          } finally {
            lock.unlock();
          }
        }
      } else {
        lock.lock();

        try {
          while (locVal >= upBound && updateGuard.get()) {
            try {
              cond.await(500, MILLISECONDS);
            } catch (InterruptedException e) {
              throw new GridInterruptedException(e);
            }
          }

          checkRemoved();

          // If reserved range isn't exhausted.
          if (locVal + l <= upBound) {
            long curVal = locVal;

            locVal += l;

            return new GridFinishedFuture<Long>(ctx.kernalContext(), updated ? locVal : curVal);
          }
        } finally {
          lock.unlock();
        }
      }
    }
  }

  /**
   * Get local batch size for this sequences.
   *
   * @return Sequence batch size.
   */
  @Override
  public int batchSize() {
    return batchSize;
  }

  /**
   * Set local batch size for this sequences.
   *
   * @param size Sequence batch size. Must be more then 0.
   */
  @Override
  public void batchSize(int size) {
    A.ensure(size > 0, " Batch size can't be less then 0: " + size);

    lock.lock();

    try {
      batchSize = size;
    } finally {
      lock.unlock();
    }
  }

  /**
   * Check removed status.
   *
   * @throws GridException If removed.
   */
  private void checkRemoved() throws GridException {
    if (rmvd)
      throw new GridCacheDataStructureRemovedException("Sequence was removed from cache: " + name);
  }

  /** {@inheritDoc} */
  @Override
  public boolean onRemoved() {
    return rmvd = true;
  }

  /** {@inheritDoc} */
  @Override
  public void onInvalid(@Nullable Exception err) {
    // No-op.
  }

  /** {@inheritDoc} */
  @Override
  public GridCacheInternalStorableKey key() {
    return key;
  }

  /** {@inheritDoc} */
  @Override
  public boolean removed() {
    return rmvd;
  }

  /**
   * Method returns callable for execution all update operations in async and sync mode.
   *
   * @param l Value will be added to sequence.
   * @param updated If {@code true}, will return updated value, if {@code false}, will return
   *     previous value.
   * @return Callable for execution in async and sync mode.
   */
  @SuppressWarnings("TooBroadScope")
  private Callable<Long> internalUpdate(final long l, final boolean updated) {
    return new Callable<Long>() {
      @Override
      public Long call() throws Exception {
        GridCacheTx tx = CU.txStartInternal(ctx, seqView, PESSIMISTIC, REPEATABLE_READ);

        try {
          GridCacheAtomicSequenceValue seq = seqView.get(key);

          checkRemoved();

          assert seq != null;

          long curLocVal;

          long newUpBound;

          lock.lock();

          try {
            curLocVal = locVal;

            // If local range was already reserved in another thread.
            if (locVal + l <= upBound) {
              long retVal = locVal;

              locVal += l;

              return updated ? locVal : retVal;
            }

            long curGlobalVal = seq.get();

            long newLocVal;

            /* We should use offset because we already reserved left side of range.*/
            long off = batchSize > 1 ? batchSize - 1 : 1;

            // Calculate new values for local counter, global counter and upper bound.
            if (curLocVal + l >= curGlobalVal) {
              newLocVal = curLocVal + l;

              newUpBound = newLocVal + off;
            } else {
              newLocVal = curGlobalVal;

              newUpBound = newLocVal + off;
            }

            locVal = newLocVal;
            upBound = newUpBound;

            if (updated) curLocVal = newLocVal;
          } finally {
            lock.unlock();
          }

          // Global counter must be more than reserved upper bound.
          seq.set(newUpBound + 1);

          seqView.put(key, seq);

          tx.commit();

          return curLocVal;
        } catch (Error e) {
          log.error("Failed to get and add: " + this, e);

          throw e;
        } catch (Exception e) {
          log.error("Failed to get and add: " + this, e);

          throw e;
        } finally {
          tx.end();
        }
      }
    };
  }

  /** {@inheritDoc} */
  @Override
  public void writeExternal(ObjectOutput out) throws IOException {
    out.writeObject(ctx);
    out.writeUTF(name);
  }

  /** {@inheritDoc} */
  @Override
  public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    stash.get().set1((GridCacheContext) in.readObject());
    stash.get().set2(in.readUTF());
  }

  /**
   * Reconstructs object on demarshalling.
   *
   * @return Reconstructed object.
   * @throws ObjectStreamException Thrown in case of demarshalling error.
   */
  private Object readResolve() throws ObjectStreamException {
    GridTuple2<GridCacheContext, String> t = stash.get();

    try {
      return t.get1().dataStructures().sequence(t.get2(), 0L, false, false);
    } catch (GridException e) {
      throw U.withCause(new InvalidObjectException(e.getMessage()), e);
    }
  }

  /** {@inheritDoc} */
  @Override
  public String toString() {
    return S.toString(GridCacheAtomicSequenceImpl.class, this);
  }
}
  /**
   * Asynchronous sequence update operation. Will add given amount to the sequence value.
   *
   * @param l Increment amount.
   * @param updateCall Cache call that will update sequence reservation count in accordance with l.
   * @param updated If {@code true}, will return sequence value after update, otherwise will return
   *     sequence value prior to update.
   * @return Future indicating sequence value.
   * @throws GridException If update failed.
   */
  private GridFuture<Long> internalUpdateAsync(
      long l, @Nullable Callable<Long> updateCall, boolean updated) throws GridException {
    checkRemoved();

    A.ensure(l > 0, " Parameter mustn't be less then 1: " + l);

    lock.lock();

    try {
      // If reserved range isn't exhausted.
      if (locVal + l <= upBound) {
        long curVal = locVal;

        locVal += l;

        return new GridFinishedFuture<Long>(ctx.kernalContext(), updated ? locVal : curVal);
      }
    } finally {
      lock.unlock();
    }

    if (updateCall == null) updateCall = internalUpdate(l, updated);

    while (true) {
      if (updateGuard.compareAndSet(false, true)) {
        try {
          // This call must be outside lock.
          return ctx.closures().callLocalSafe(updateCall, true);
        } finally {
          lock.lock();

          try {
            updateGuard.set(false);

            cond.signalAll();
          } finally {
            lock.unlock();
          }
        }
      } else {
        lock.lock();

        try {
          while (locVal >= upBound && updateGuard.get()) {
            try {
              cond.await(500, MILLISECONDS);
            } catch (InterruptedException e) {
              throw new GridInterruptedException(e);
            }
          }

          checkRemoved();

          // If reserved range isn't exhausted.
          if (locVal + l <= upBound) {
            long curVal = locVal;

            locVal += l;

            return new GridFinishedFuture<Long>(ctx.kernalContext(), updated ? locVal : curVal);
          }
        } finally {
          lock.unlock();
        }
      }
    }
  }
  /**
   * Synchronous sequence update operation. Will add given amount to the sequence value.
   *
   * @param l Increment amount.
   * @param updateCall Cache call that will update sequence reservation count in accordance with l.
   * @param updated If {@code true}, will return sequence value after update, otherwise will return
   *     sequence value prior to update.
   * @return Sequence value.
   * @throws GridException If update failed.
   */
  private long internalUpdate(long l, @Nullable Callable<Long> updateCall, boolean updated)
      throws GridException {
    checkRemoved();

    assert l > 0;

    lock.lock();

    try {
      // If reserved range isn't exhausted.
      if (locVal + l <= upBound) {
        long curVal = locVal;

        locVal += l;

        return updated ? locVal : curVal;
      }
    } finally {
      lock.unlock();
    }

    if (updateCall == null) updateCall = internalUpdate(l, updated);

    while (true) {
      if (updateGuard.compareAndSet(false, true)) {
        try {
          // This call must be outside lock.
          return CU.outTx(updateCall, ctx);
        } finally {
          lock.lock();

          try {
            updateGuard.set(false);

            cond.signalAll();
          } finally {
            lock.unlock();
          }
        }
      } else {
        lock.lock();

        try {
          while (locVal >= upBound && updateGuard.get()) {
            try {
              cond.await(500, MILLISECONDS);
            } catch (InterruptedException e) {
              throw new GridInterruptedException(e);
            }
          }

          checkRemoved();

          // If reserved range isn't exhausted.
          if (locVal + l <= upBound) {
            long curVal = locVal;

            locVal += l;

            return updated ? locVal : curVal;
          }
        } finally {
          lock.unlock();
        }
      }
    }
  }