Example #1
0
  /**
   * Indicate that node is least recently used, allowing it to be recycled immediately without
   * evicting another node. Node must be latched by caller, which is always released by this method.
   */
  void unused(final Node node) {
    // Node latch is held to ensure that it isn't used for new allocations too soon. In
    // particular, it might be used for an unevictable allocation. This method would end up
    // erroneously moving the node back into the usage list.

    try {
      acquireExclusive();
    } catch (Throwable e) {
      node.releaseExclusive();
      throw e;
    }

    try {
      Node lessUsed = node.mLessUsed;
      if (lessUsed != null) {
        Node moreUsed = node.mMoreUsed;
        lessUsed.mMoreUsed = moreUsed;
        if (moreUsed == null) {
          mMostRecentlyUsed = lessUsed;
        } else {
          moreUsed.mLessUsed = lessUsed;
        }
        node.mLessUsed = null;
        (node.mMoreUsed = mLeastRecentlyUsed).mLessUsed = node;
        mLeastRecentlyUsed = node;
      } else if (mMaxSize != 0) {
        doMakeEvictableNow(node);
      }
    } finally {
      // The node latch must be released before releasing the usage list latch, to
      // prevent the node from being immediately promoted to the most recently used by
      // tryAllocLatchedNode. The caller would acquire the usage list latch, fail to
      // acquire the node latch, and then the node gets falsely promoted.
      node.releaseExclusive();
      releaseExclusive();
    }
  }
Example #2
0
  /**
   * Returns a new or recycled Node instance, latched exclusively, with an undefined id and a clean
   * state.
   *
   * @param trial pass 1 for less aggressive recycle attempt
   * @param mode MODE_UNEVICTABLE | MODE_NO_EVICT
   * @return null if no nodes can be recycled or created
   */
  Node tryAllocLatchedNode(int trial, int mode) throws IOException {
    acquireExclusive();

    int limit = mSize;
    do {
      Node node = mLeastRecentlyUsed;
      Node moreUsed;
      if (node == null || (moreUsed = node.mMoreUsed) == null) {
        // Grow the cache if possible.
        if (mSize < mMaxSize) {
          return doAllocLatchedNode(null, mode);
        } else if (node == null) {
          break;
        }
      } else {
        // Move node to the most recently used position.
        moreUsed.mLessUsed = null;
        mLeastRecentlyUsed = moreUsed;
        node.mMoreUsed = null;
        (node.mLessUsed = mMostRecentlyUsed).mMoreUsed = node;
        mMostRecentlyUsed = node;
      }

      if (!node.tryAcquireExclusive()) {
        continue;
      }

      if (trial == 1) {
        if (node.mCachedState != CACHED_CLEAN) {
          if (mSize < mMaxSize) {
            // Grow the cache instead of evicting.
            node.releaseExclusive();
            return doAllocLatchedNode(null, mode);
          } else if ((mode & MODE_NO_EVICT) != 0) {
            node.releaseExclusive();
            break;
          }
        }

        // For first attempt, release the latch early to prevent blocking other
        // allocations while node is evicted. Subsequent attempts retain the latch,
        // preventing potential allocation starvation.

        releaseExclusive();

        if (node.evict(mDatabase)) {
          if ((mode & MODE_UNEVICTABLE) != 0) {
            node.mUsageList.makeUnevictable(node);
          }
          // Return with node latch still held.
          return node;
        }

        acquireExclusive();
      } else if ((mode & MODE_NO_EVICT) != 0) {
        if (node.mCachedState != CACHED_CLEAN) {
          // MODE_NO_EVICT is only used by non-durable database. It ensures that
          // all clean nodes are least recently used, so no need to keep looking.
          node.releaseExclusive();
          break;
        }
      } else {
        try {
          if (node.evict(mDatabase)) {
            if ((mode & MODE_UNEVICTABLE) != 0) {
              NodeUsageList usageList = node.mUsageList;
              if (usageList == this) {
                doMakeUnevictable(node);
              } else {
                releaseExclusive();
                usageList.makeUnevictable(node);
                // Return with node latch still held.
                return node;
              }
            }
            releaseExclusive();
            // Return with node latch still held.
            return node;
          }
        } catch (Throwable e) {
          releaseExclusive();
          throw e;
        }
      }
    } while (--limit > 0);

    releaseExclusive();

    return null;
  }