/** {@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;
  }