Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9b5a9355 authored by Jesse Wilson's avatar Jesse Wilson
Browse files

Adopt LRU cache in SQLite.

Change-Id: I6b43dd8843d41726254bea3a175fe28f5f061ed7
http://b/3184897
parent 9ebc6b34
Loading
Loading
Loading
Loading
+26 −60
Original line number Diff line number Diff line
@@ -33,17 +33,15 @@ import android.text.TextUtils;
import android.util.Config;
import android.util.EventLog;
import android.util.Log;
import android.util.LruCache;
import android.util.Pair;

import dalvik.system.BlockGuard;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
@@ -262,6 +260,9 @@ public class SQLiteDatabase extends SQLiteClosable {

    private final WeakHashMap<SQLiteClosable, Object> mPrograms;

    /** Default statement-cache size per database connection ( = instance of this class) */
    public static final int DEFAULT_SQL_CACHE_SIZE = 25;

    /**
     * for each instance of this class, a LRU cache is maintained to store
     * the compiled query statement ids returned by sqlite database.
@@ -274,32 +275,16 @@ public class SQLiteDatabase extends SQLiteClosable {
     * struct created when {@link SQLiteDatabase#openDatabase(String, CursorFactory, int)} is
     * invoked.
     *
     * this cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
     * this cache's max size is settable by calling the method
     * (@link #setMaxSqlCacheSize(int)}).
     */
    // default statement-cache size per database connection ( = instance of this class)
    private int mMaxSqlCacheSize = 25;
    // guarded by itself
    /* package */ final Map<String, SQLiteCompiledSql> mCompiledQueries =
        new LinkedHashMap<String, SQLiteCompiledSql>(mMaxSqlCacheSize + 1, 0.75f, true) {
    /* package */ final LruCache<String, SQLiteCompiledSql> mCompiledQueries =
        new LruCache<String, SQLiteCompiledSql>(DEFAULT_SQL_CACHE_SIZE) {
            @Override
            public boolean removeEldestEntry(Map.Entry<String, SQLiteCompiledSql> eldest) {
                // eldest = least-recently used entry
                // if it needs to be removed to accommodate a new entry,
                //     close {@link SQLiteCompiledSql} represented by this entry, if not in use
                //     and then let it be removed from the Map.
                // when this is called, the caller must be trying to add a just-compiled stmt
                // to cache; i.e., caller should already have acquired database lock AND
                // the lock on mCompiledQueries. do as assert of these two 2 facts.
            protected void entryEvicted(String key, SQLiteCompiledSql value) {
                verifyLockOwner();
                if (this.size() <= mMaxSqlCacheSize) {
                    // cache is not full. nothing needs to be removed
                    return false;
                }
                // cache is full. eldest will be removed.
                eldest.getValue().releaseIfNotInUse();
                // return true, so that this entry is removed automatically by the caller.
                return true;
                value.releaseIfNotInUse();
            }
        };
    /**
@@ -310,11 +295,6 @@ public class SQLiteDatabase extends SQLiteClosable {
    public static final int MAX_SQL_CACHE_SIZE = 100;
    private boolean mCacheFullWarning;

    /** Number of cache hits on this database connection. guarded by {@link #mCompiledQueries}. */
    private int mNumCacheHits;
    /** Number of cache misses on this database connection. guarded by {@link #mCompiledQueries}. */
    private int mNumCacheMisses;

    /** Used to find out where this object was created in case it never got closed. */
    private final Throwable mStackTrace;

@@ -2168,12 +2148,12 @@ public class SQLiteDatabase extends SQLiteClosable {
    /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
        synchronized(mCompiledQueries) {
            // don't insert the new mapping if a mapping already exists
            if (mCompiledQueries.containsKey(sql)) {
            if (mCompiledQueries.get(sql) != null) {
                return;
            }

            int maxCacheSz = (mConnectionNum == 0) ? mMaxSqlCacheSize :
                    mParentConnObj.mMaxSqlCacheSize;
            int maxCacheSz = (mConnectionNum == 0) ? mCompiledQueries.maxSize() :
                    mParentConnObj.mCompiledQueries.maxSize();

            if (SQLiteDebug.DEBUG_SQL_CACHE) {
                boolean printWarning = (mConnectionNum == 0)
@@ -2190,8 +2170,8 @@ public class SQLiteDatabase extends SQLiteClosable {
                            getPath() + ". Use setMaxSqlCacheSize() to increase cachesize. ");
                    mCacheFullWarning = true;
                    Log.d(TAG, "Here are the SQL statements in Cache of database: " + mPath);
                    for (String s : mCompiledQueries.keySet()) {
                        Log.d(TAG, "Sql stament in Cache: " + s);
                    for (String s : mCompiledQueries.snapshot().keySet()) {
                        Log.d(TAG, "Sql statement in Cache: " + s);
                    }
                }
            }
@@ -2206,10 +2186,10 @@ public class SQLiteDatabase extends SQLiteClosable {
    /** package-level access for testing purposes */
    /* package */ void deallocCachedSqlStatements() {
        synchronized (mCompiledQueries) {
            for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
            for (SQLiteCompiledSql compiledSql : mCompiledQueries.snapshot().values()) {
                compiledSql.releaseSqlStatement();
            }
            mCompiledQueries.clear();
            mCompiledQueries.evictAll();
        }
    }

@@ -2218,15 +2198,7 @@ public class SQLiteDatabase extends SQLiteClosable {
     * Returns null, if not found in the cache.
     */
    /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) {
        synchronized (mCompiledQueries) {
            SQLiteCompiledSql compiledStatement = mCompiledQueries.get(sql);
            if (compiledStatement == null) {
                mNumCacheMisses++;
                return null;
            }
            mNumCacheHits++;
            return compiledStatement;
        }
        return mCompiledQueries.get(sql);
    }

    /**
@@ -2247,23 +2219,23 @@ public class SQLiteDatabase extends SQLiteClosable {
        synchronized(mCompiledQueries) {
            if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
                throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
            } else if (cacheSize < mMaxSqlCacheSize) {
            } else if (cacheSize < mCompiledQueries.maxSize()) {
                throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
                        "set with previous setMaxSqlCacheSize() call.");
            }
            mMaxSqlCacheSize = cacheSize;
            mCompiledQueries.setMaxSize(cacheSize);
        }
    }

    /* package */ boolean isInStatementCache(String sql) {
        synchronized (mCompiledQueries) {
            return mCompiledQueries.containsKey(sql);
            return mCompiledQueries.get(sql) != null;
        }
    }

    /* package */ void releaseCompiledSqlObj(SQLiteCompiledSql compiledSql) {
    /* package */ void releaseCompiledSqlObj(String sql, SQLiteCompiledSql compiledSql) {
        synchronized (mCompiledQueries) {
            if (mCompiledQueries.containsValue(compiledSql)) {
            if (mCompiledQueries.get(sql) == compiledSql) {
                // it is in cache - reset its inUse flag
                compiledSql.release();
            } else {
@@ -2274,22 +2246,16 @@ public class SQLiteDatabase extends SQLiteClosable {
    }

    private int getCacheHitNum() {
        synchronized(mCompiledQueries) {
            return mNumCacheHits;
        }
        return mCompiledQueries.hitCount();
    }

    private int getCacheMissNum() {
        synchronized(mCompiledQueries) {
            return mNumCacheMisses;
        }
        return mCompiledQueries.missCount();
    }

    private int getCachesize() {
        synchronized(mCompiledQueries) {
        return mCompiledQueries.size();
    }
    }

    /* package */ void finalizeStatementLater(int id) {
        if (!isOpen()) {
+1 −2
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package android.database.sqlite;

import android.database.DatabaseUtils;
import android.database.Cursor;
import android.util.Log;

import java.util.HashMap;

@@ -184,7 +183,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
        if (mCompiledSql == null) {
            return;
        }
        mDatabase.releaseCompiledSqlObj(mCompiledSql);
        mDatabase.releaseCompiledSqlObj(mSql, mCompiledSql);
        mCompiledSql = null;
        nStatement = 0;
    }
+30 −4
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ public class LruCache<K, V> {

    /** Size of this cache in units. Not necessarily the number of elements. */
    private int size;
    private final int maxSize;
    private int maxSize;

    private int putCount;
    private int createCount;
@@ -154,6 +154,23 @@ public class LruCache<K, V> {
        }
    }

    /**
     * Sets the maximum size of this cache. Decreasing the maximum size may
     * evict entries from this cache.
     *
     * @param maxSize for caches that do not override {@link #sizeOf}, this is
     *     the maximum number of entries in the cache. For all other caches,
     *     this is the maximum sum of the sizes of the entries in this cache.
     */
    public synchronized final void setMaxSize(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }

        trimToSize(maxSize);
        this.maxSize = maxSize;
    }

    /**
     * Called for entries that have reached the tail of the least recently used
     * queue and are be removed. The default implementation does nothing.
@@ -196,14 +213,23 @@ public class LruCache<K, V> {
    }

    /**
     * For caches that do not override {@link #sizeOf}, this is the number of
     * entries in the cache. For all other caches, this is the sum of the sizes
     * of the entries in this cache.
     * For caches that do not override {@link #sizeOf}, this returns the number
     * of entries in the cache. For all other caches, this returns the sum of
     * the sizes of the entries in this cache.
     */
    public synchronized final int size() {
        return size;
    }

    /**
     * For caches that do not override {@link #sizeOf}, this returns the maximum
     * number of entries in the cache. For all other caches, this returns the
     * maximum sum of the sizes of the entries in this cache.
     */
    public synchronized final int maxSize() {
        return maxSize;
    }

    /**
     * Returns the number of times {@link #get} returned a value.
     */