Пример #1
0
 private void doUsed(final Node node) {
   Node moreUsed = node.mMoreUsed;
   if (moreUsed != null) {
     Node lessUsed = node.mLessUsed;
     moreUsed.mLessUsed = lessUsed;
     if (lessUsed == null) {
       mLeastRecentlyUsed = moreUsed;
     } else {
       lessUsed.mMoreUsed = moreUsed;
     }
     node.mMoreUsed = null;
     (node.mLessUsed = mMostRecentlyUsed).mMoreUsed = node;
     mMostRecentlyUsed = node;
   }
   releaseExclusive();
 }
Пример #2
0
 /**
  * Caller must hold latch, have checked that this list isn't closed, and have checked that
  * node.mLessUsed is null.
  */
 private void doMakeEvictableNow(final Node node) {
   Node least = mLeastRecentlyUsed;
   if (node != least) {
     node.mMoreUsed = least;
     if (least == null) {
       mMostRecentlyUsed = node;
     } else {
       least.mLessUsed = node;
     }
     mLeastRecentlyUsed = node;
   }
 }
Пример #3
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();
    }
  }
Пример #4
0
  /** Caller must hold latch. */
  private void doMakeUnevictable(final Node node) {
    final Node lessUsed = node.mLessUsed;
    final Node moreUsed = node.mMoreUsed;

    if (lessUsed != null) {
      node.mLessUsed = null;
      if (moreUsed != null) {
        node.mMoreUsed = null;
        lessUsed.mMoreUsed = moreUsed;
        moreUsed.mLessUsed = lessUsed;
      } else if (node == mMostRecentlyUsed) {
        mMostRecentlyUsed = lessUsed;
        lessUsed.mMoreUsed = null;
      }
    } else if (node == mLeastRecentlyUsed) {
      mLeastRecentlyUsed = moreUsed;
      if (moreUsed != null) {
        node.mMoreUsed = null;
        moreUsed.mLessUsed = null;
      } else {
        mMostRecentlyUsed = null;
      }
    }
  }
Пример #5
0
 /**
  * Allow a Node which was allocated as unevictable to be evictable, starting off as the most
  * recently used.
  */
 void makeEvictable(final Node node) {
   acquireExclusive();
   try {
     // Only insert if not closed and if not already in the list. The node latch doesn't
     // need to be held, and so a concurrent call to the unused method might insert the
     // node sooner.
     if (mMaxSize != 0 && node.mMoreUsed == null) {
       Node most = mMostRecentlyUsed;
       if (node != most) {
         node.mLessUsed = most;
         if (most == null) {
           mLeastRecentlyUsed = node;
         } else {
           most.mMoreUsed = node;
         }
         mMostRecentlyUsed = node;
       }
     }
   } finally {
     releaseExclusive();
   }
 }
Пример #6
0
  /** Must be called when object is no longer referenced. */
  void delete() {
    acquireExclusive();
    try {
      // Prevent new allocations.
      mMaxSize = 0;

      Node node = mLeastRecentlyUsed;
      mLeastRecentlyUsed = null;
      mMostRecentlyUsed = null;

      while (node != null) {
        Node next = node.mMoreUsed;
        node.mLessUsed = null;
        node.mMoreUsed = null;

        // Free memory and make node appear to be evicted.
        node.delete(mDatabase);

        node = next;
      }
    } finally {
      releaseExclusive();
    }
  }
Пример #7
0
  /**
   * Caller must acquire latch, which is released by this method.
   *
   * @param arena optional
   * @param mode MODE_UNEVICTABLE
   */
  private Node doAllocLatchedNode(Object arena, int mode) throws DatabaseException {
    try {
      mDatabase.checkClosed();

      /*P*/ byte[] page;
      /*P*/
      // [
      page = p_calloc(arena, mPageSize);
      /*P*/
      // |
      /*P*/
      // page = mDatabase.mFullyMapped ? p_nonTreePage() : p_calloc(arena, mPageSize);
      /*P*/
      // ]

      Node node = new Node(this, page);
      node.acquireExclusive();
      mSize++;

      if ((mode & MODE_UNEVICTABLE) == 0) {
        Node most = mMostRecentlyUsed;
        node.mLessUsed = most;
        if (most == null) {
          mLeastRecentlyUsed = node;
        } else {
          most.mMoreUsed = node;
        }
        mMostRecentlyUsed = node;
      }

      // Return with node latch still held.
      return node;
    } finally {
      releaseExclusive();
    }
  }
Пример #8
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;
  }