/** Shrinks queue to maximum allowed size. */
  private void shrink() {
    long maxMem = this.maxMemSize;

    if (maxMem > 0) {
      long startMemSize = memSize.longValue();

      if (startMemSize >= maxMem)
        for (long i = maxMem; i < startMemSize && memSize.longValue() > maxMem; ) {
          int size = shrink0();

          if (size == -1) break;

          i += size;
        }
    }

    int max = this.max;

    if (max > 0) {
      int startSize = queue.sizex();

      if (startSize >= max + (maxMem > 0 ? 1 : this.batchSize))
        for (int i = max; i < startSize && queue.sizex() > max; i++) if (shrink0() == -1) break;
    }
  }
  /** @param entry Entry to add. */
  void onAdded(GridDhtCacheEntry entry) {
    GridDhtPartitionState state = state();

    if (state == EVICTED)
      throw new GridDhtInvalidPartitionException(
          id, "Adding entry to invalid partition [part=" + id + ']');

    map.put(entry.key(), entry);

    if (!entry.isInternal()) mapPubSize.increment();
  }
  /** @param entry Entry to remove. */
  @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
  void onRemoved(GridDhtCacheEntry entry) {
    assert entry.obsolete();

    // Make sure to remove exactly this entry.
    synchronized (entry) {
      map.remove(entry.key(), entry);

      if (!entry.isInternal() && !entry.deleted()) mapPubSize.decrement();
    }

    // Attempt to evict.
    tryEvict(true);
  }
  /** {@inheritDoc} */
  @Override
  public void onEntryAccessed(boolean rmv, EvictableEntry<K, V> entry) {
    if (!rmv) {
      if (!entry.isCached()) return;

      if (touch(entry)) shrink();
    } else {
      Node<EvictableEntry<K, V>> node = entry.removeMeta();

      if (node != null) {
        queue.unlinkx(node);

        memSize.add(-entry.size());
      }
    }
  }
  /**
   * Tries to remove one item from queue.
   *
   * @return number of bytes that was free. {@code -1} if queue is empty.
   */
  private int shrink0() {
    EvictableEntry<K, V> entry = queue.poll();

    if (entry == null) return -1;

    int size = 0;

    Node<EvictableEntry<K, V>> meta = entry.removeMeta();

    if (meta != null) {
      size = entry.size();

      memSize.add(-size);

      if (!entry.evict()) touch(entry);
    }

    return size;
  }
  /**
   * @param entry Entry to touch.
   * @return {@code True} if new node has been added to queue by this call.
   */
  private boolean touch(EvictableEntry<K, V> entry) {
    Node<EvictableEntry<K, V>> node = entry.meta();

    // Entry has not been enqueued yet.
    if (node == null) {
      while (true) {
        node = queue.offerLastx(entry);

        if (entry.putMetaIfAbsent(node) != null) {
          // Was concurrently added, need to clear it from queue.
          queue.unlinkx(node);

          // Queue has not been changed.
          return false;
        } else if (node.item() != null) {
          if (!entry.isCached()) {
            // Was concurrently evicted, need to clear it from queue.
            queue.unlinkx(node);

            return false;
          }

          memSize.add(entry.size());

          return true;
        }
        // If node was unlinked by concurrent shrink() call, we must repeat the whole cycle.
        else if (!entry.removeMeta(node)) return false;
      }
    } else if (queue.unlinkx(node)) {
      // Move node to tail.
      Node<EvictableEntry<K, V>> newNode = queue.offerLastx(entry);

      if (!entry.replaceMeta(node, newNode))
        // Was concurrently added, need to clear it from queue.
        queue.unlinkx(newNode);
    }

    // Entry is already in queue.
    return false;
  }
 /** {@inheritDoc} */
 @Override
 public long getCurrentMemorySize() {
   return memSize.longValue();
 }
  /** Clears values for this partition. */
  private void clearAll() {
    GridCacheVersion clearVer = cctx.versions().next();

    boolean swap = cctx.isSwapOrOffheapEnabled();

    boolean rec = cctx.events().isRecordable(EVT_CACHE_REBALANCE_OBJECT_UNLOADED);

    Iterator<GridDhtCacheEntry> it = map.values().iterator();

    GridCloseableIterator<Map.Entry<byte[], GridCacheSwapEntry>> swapIt = null;

    if (swap
        && GridQueryProcessor.isEnabled(cctx.config())) { // Indexing needs to unswap cache values.
      Iterator<GridDhtCacheEntry> unswapIt = null;

      try {
        swapIt = cctx.swap().iterator(id);
        unswapIt = unswapIterator(swapIt);
      } catch (Exception e) {
        U.error(log, "Failed to clear swap for evicted partition: " + this, e);
      }

      if (unswapIt != null) it = F.concat(it, unswapIt);
    }

    try {
      while (it.hasNext()) {
        GridDhtCacheEntry cached = it.next();

        try {
          if (cached.clearInternal(clearVer, swap)) {
            map.remove(cached.key(), cached);

            if (!cached.isInternal()) {
              mapPubSize.decrement();

              if (rec)
                cctx.events()
                    .addEvent(
                        cached.partition(),
                        cached.key(),
                        cctx.localNodeId(),
                        (IgniteUuid) null,
                        null,
                        EVT_CACHE_REBALANCE_OBJECT_UNLOADED,
                        null,
                        false,
                        cached.rawGet(),
                        cached.hasValue(),
                        null,
                        null,
                        null);
            }
          }
        } catch (IgniteCheckedException e) {
          U.error(log, "Failed to clear cache entry for evicted partition: " + cached, e);
        }
      }
    } finally {
      U.close(swapIt, log);
    }
  }
 /** @return Number of public (non-internal) entries in this partition. */
 public int publicSize() {
   return mapPubSize.intValue();
 }
 /** Decrements public size of the map. */
 public void decrementPublicSize() {
   mapPubSize.decrement();
 }
 /** Increments public size of the map. */
 public void incrementPublicSize() {
   mapPubSize.increment();
 }