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

Commit b729dcc8 authored by Vasu Nori's avatar Vasu Nori
Browse files

Revert "caching code retooled to reduce locking + handle SMP"

This reverts commit 992f7d52.

Change-Id: Ia5b789d1b2195d6ce43baffe24296c857f9b30f6
parent d3326231
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -65918,7 +65918,7 @@
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 synchronized="true"
 static="false"
 final="false"
 deprecated="not deprecated"
@@ -65957,7 +65957,7 @@
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 synchronized="true"
 static="false"
 final="false"
 deprecated="not deprecated"
+2 −3
Original line number Diff line number Diff line
@@ -99,7 +99,7 @@ import java.util.Random;
                        poolObj = mPool.get(0);
                    } else {
                        for (int i = 0; i < mMaxPoolSize; i++) {
                            if (mPool.get(i).mDb.mCache.isSqlInStatementCache(sql)) {
                            if (mPool.get(i).mDb.isSqlInStatementCache(sql)) {
                                poolObj = mPool.get(i);
                                break;
                            }
@@ -125,8 +125,7 @@ import java.util.Random;
                // there are free connections available. pick one
                // preferably a connection caching the pre-compiled statement of the given SQL
                for (int i = 0; i < poolSize; i++) {
                    if (mPool.get(i).isFree() &&
                            mPool.get(i).mDb.mCache.isSqlInStatementCache(sql)) {
                    if (mPool.get(i).isFree() && mPool.get(i).mDb.isSqlInStatementCache(sql)) {
                        poolObj = mPool.get(i);
                        break;
                    }
+0 −214
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.database.sqlite;

import android.util.Log;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * For each instance of {@link SQLiteDatabase}, this class maintains a LRU cache to store
 * the compiled query statement ids returned by sqlite database.
 *<p>
 *<ul>
 *     <li>key = SQL statement with "?" for bind args</li>
 *     <li>value = {@link SQLiteCompiledSql}</li>
 *</ul>
 * If an application opens the database and keeps it open during its entire life, then
 * there will not be an overhead of compilation of SQL statements by sqlite.
 *<p>
 * Why is this cache NOT static? because sqlite attaches compiled-sql statements to the
 * database connections.
 *<p>
 * This cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
 * (@link #setMaxSqlCacheSize(int)}).
 */
/* package */ class SQLiteCache {
    private static final String TAG = "SQLiteCache";

    /** The {@link SQLiteDatabase} instance this cache is attached to */
    private final SQLiteDatabase mDatabase;

    /** Default statement-cache size per database connection ( = instance of this class) */
    private int mMaxSqlCacheSize = 25;

    /** The LRU cache */
    private final Map<String, SQLiteCompiledSql> mCompiledQueries =
        new LinkedHashMap<String, SQLiteCompiledSql>(mMaxSqlCacheSize + 1, 0.75f, true) {
            @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.
                mDatabase.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;
            }
        };

    /** Maintains whether or not cacheFullWarning has been logged */
    private boolean mCacheFullWarning;

    /** The following 2 members maintain stats about cache hits and misses */
    private int mNumCacheHits;
    private int mNumCacheMisses;

    /**
     * Constructor used by {@link SQLiteDatabase}.
     * @param db
     */
    /* package */ SQLiteCache(SQLiteDatabase db) {
        mDatabase = db;
    }

    /**
     * Adds the given SQL and its compiled-statement to the cache, if the given SQL statement
     * doesn't already exist in cache.
     *
     * @return true if added to cache. false otherwise.
     */
    /* package */ synchronized boolean addToCompiledQueries(String sql,
            SQLiteCompiledSql compiledStatement) {
        if (mCompiledQueries.containsKey(sql)) {
            // already exists.
            return false;
        }

        /* add the given SQLiteCompiledSql compiledStatement to cache.
         * no need to worry about the cache size - because {@link #mCompiledQueries}
         * self-limits its size to {@link #mMaxSqlCacheSize}.
         */
        mCompiledQueries.put(sql, compiledStatement);

        // need to log a warning to say that the cache is full?
        if (!isCacheFullWarningLogged() && mCompiledQueries.size() == mMaxSqlCacheSize) {
            /*
             * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
             * log a warning.
             * chances are it is NOT using ? for bindargs - or cachesize is too small.
             */
            Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
                    mDatabase.getPath() +
                    ". Use setMaxSqlCacheSize() in SQLiteDatabase to increase cachesize. ");
            setCacheFullWarningLogged();
        }
        return true;
    }

    /**
     * Returns the compiled-statement for the given SQL statement, if the entry exists in cache
     * and is free to use. Returns null otherwise.
     * <p>
     * If a compiled-sql statement is returned for the caller, it is reserved for the caller.
     * So, don't use this method unless the caller needs to acquire the object.
     */
    /* package */ synchronized SQLiteCompiledSql getCompiledStatementForSql(String sql) {
        SQLiteCompiledSql compiledStatement = mCompiledQueries.get(sql);
        if (compiledStatement == null) {
            mNumCacheMisses++;
            return null;
        }
        mNumCacheHits++;
        // reserve it for the caller, if it is not already in use
        if (!compiledStatement.acquire()) {
            // couldn't acquire it since it is already in use. bug in app?
            if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
                Log.w(TAG, "Possible bug: Either using the same SQL in 2 threads at " +
                        " the same time, or previous instance of this SQL statement was " +
                        "never close()d. " + compiledStatement.toString());
            }
            return null;
        }
        return compiledStatement;
    }

    /**
     * If the given statement is in cache, it is released back to cache and it is made available for
     * others to use.
     * <p>
     * return true if the statement is put back in cache, false otherwise (false = the statement
     * is NOT in cache)
     */
    /* package */ synchronized boolean releaseBackToCache(SQLiteCompiledSql stmt) {
        if (!mCompiledQueries.containsValue(stmt)) {
            return false;
        }
        // it is in cache. release it from the caller, make it available for others to use
        stmt.free();
        return true;
    }

    /**
     * releases all compiled-sql statements in the cache.
     */
    /* package */ synchronized void dealloc() {
        for (SQLiteCompiledSql stmt : mCompiledQueries.values()) {
            stmt.setState(SQLiteCompiledSql.NSTATE_CACHE_DEALLOC);
            stmt.releaseFromDatabase();
        }
        mCompiledQueries.clear();
    }

    /**
     * see documentation on {@link SQLiteDatabase#setMaxSqlCacheSize(int)}.
     */
    /* package */ synchronized void setMaxSqlCacheSize(int cacheSize) {
        if (cacheSize > SQLiteDatabase.MAX_SQL_CACHE_SIZE || cacheSize < 0) {
            throw new IllegalStateException("expected value between 0 and " +
                    SQLiteDatabase.MAX_SQL_CACHE_SIZE);
        } else if (cacheSize < mMaxSqlCacheSize) {
            throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
                    "set with previous setMaxSqlCacheSize() call.");
        }
        mMaxSqlCacheSize = cacheSize;
    }

    /* package */ synchronized boolean isSqlInStatementCache(String sql) {
        return mCompiledQueries.containsKey(sql);
    }

    private synchronized boolean isCacheFullWarningLogged() {
        return mCacheFullWarning;
    }

    private synchronized void setCacheFullWarningLogged() {
        mCacheFullWarning = true;
    }
    /* package */ synchronized int getCacheHitNum() {
        return mNumCacheHits;
    }
    /* package */ synchronized int getCacheMissNum() {
        return mNumCacheMisses;
    }
    /* package */ synchronized int getCachesize() {
        return mCompiledQueries.size();
    }

    // only for testing
    /* package */ synchronized Set<String> getKeys() {
        return mCompiledQueries.keySet();
    }
}
+49 −106
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package android.database.sqlite;

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

/**
@@ -25,19 +24,19 @@ import android.util.Log;
 * and it is released in one of the 2 following ways
 * 1. when {@link SQLiteDatabase} object is closed.
 * 2. if this is not cached in {@link SQLiteDatabase}, {@link android.database.Cursor#close()}
 * releases this obj.
 * releaases this obj.
 */
/* package */ class SQLiteCompiledSql {

    private static final String TAG = "SQLiteCompiledSql";

    /** The database this program is compiled against. */
    /* package */ final SQLiteDatabase mDatabase;
    /* package */ SQLiteDatabase mDatabase;

    /**
     * Native linkage, do not modify. This comes from the database.
     */
    /* package */ final int nHandle;
    /* package */ int nHandle = 0;

    /**
     * Native linkage, do not modify. When non-0 this holds a reference to a valid
@@ -47,53 +46,52 @@ import android.util.Log;
     */
    /* package */ int nStatement = 0;

    /** the following 3 members are for debugging purposes */
    private final String mSqlStmt;
    private final Throwable mStackTrace;
    private int nState = 0;
    /** values the above member can take */
    private static final int NSTATE_CACHEABLE = 64;
    private static final int NSTATE_IN_CACHE = 32;
    private static final int NSTATE_INUSE = 16;
    private static final int NSTATE_INUSE_RESETMASK = 0x0f;
    /* package */ static final int NSTATE_CLOSE_NOOP = 1;
    private static final int NSTATE_EVICTED_FROM_CACHE = 2;
    /* package */ static final int NSTATE_CACHE_DEALLOC = 4;
    private static final int NSTATE_IN_FINALIZER_Q = 8;

    private SQLiteCompiledSql(SQLiteDatabase db, String sql) {
        db.verifyDbIsOpen();
        db.verifyLockOwner();
    /** the following are for debugging purposes */
    private String mSqlStmt = null;
    private Throwable mStackTrace = null;

    /** when in cache and is in use, this member is set */
    private boolean mInUse = false;

    /* package */ SQLiteCompiledSql(SQLiteDatabase db, String sql) {
        if (!db.isOpen()) {
            throw new IllegalStateException("database " + db.getPath() + " already closed");
        }
        mDatabase = db;
        mSqlStmt = sql;
        mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
        nHandle = db.mNativeHandle;
        native_compile(sql);
        this.nHandle = db.mNativeHandle;
        compile(sql, true);
    }

    /* package */ static SQLiteCompiledSql get(SQLiteDatabase db, String sql, int type) {
        // only CRUD statements are cached.
        if (type != DatabaseUtils.STATEMENT_SELECT && type != DatabaseUtils.STATEMENT_UPDATE) {
            return new SQLiteCompiledSql(db, sql);
        }
        // the given SQL statement is cacheable.
        SQLiteCompiledSql stmt = db.mCache.getCompiledStatementForSql(sql);
        if (stmt != null) {
            return stmt;
    /**
     * Compiles the given SQL into a SQLite byte code program using sqlite3_prepare_v2(). If
     * this method has been called previously without a call to close and forCompilation is set
     * to false the previous compilation will be used. Setting forceCompilation to true will
     * always re-compile the program and should be done if you pass differing SQL strings to this
     * method.
     *
     * <P>Note: this method acquires the database lock.</P>
     *
     * @param sql the SQL string to compile
     * @param forceCompilation forces the SQL to be recompiled in the event that there is an
     *  existing compiled SQL program already around
     */
    private void compile(String sql, boolean forceCompilation) {
        mDatabase.verifyLockOwner();
        // Only compile if we don't have a valid statement already or the caller has
        // explicitly requested a recompile.
        if (forceCompilation) {
            // Note that the native_compile() takes care of destroying any previously
            // existing programs before it compiles.
            native_compile(sql);
        }
        // either the entry doesn't exist in cache or the one in cache is currently in use.
        // try to add it to cache and let cache worry about what copy to keep
        stmt = new SQLiteCompiledSql(db, sql);
        stmt.nState |= NSTATE_CACHEABLE |
                ((db.mCache.addToCompiledQueries(sql, stmt)) ? NSTATE_IN_CACHE : 0);
        return stmt;
    }

    /* package */ synchronized void releaseFromDatabase() {
    /* package */ void releaseSqlStatement() {
        // Note that native_finalize() checks to make sure that nStatement is
        // non-null before destroying it.
        if (nStatement != 0) {
            nState |= NSTATE_IN_FINALIZER_Q;
            mDatabase.finalizeStatementLater(nStatement);
            nStatement = 0;
        }
@@ -103,47 +101,20 @@ import android.util.Log;
     * returns true if acquire() succeeds. false otherwise.
     */
    /* package */ synchronized boolean acquire() {
        if ((nState & NSTATE_INUSE) > 0 ) {
            // this object is already in use
        if (mInUse) {
            // someone already has acquired it.
            return false;
        }
        nState |= NSTATE_INUSE;
        mInUse = true;
        return true;
    }

    /* package */ synchronized void free() {
        nState &= NSTATE_INUSE_RESETMASK;
    }

    /* package */ void release(int type) {
        if (type != DatabaseUtils.STATEMENT_SELECT && type != DatabaseUtils.STATEMENT_UPDATE) {
            // it is not cached. release its memory from the database.
            releaseFromDatabase();
            return;
        }
        // if in cache, reset its in-use flag
        if (!mDatabase.mCache.releaseBackToCache(this)) {
            // not in cache. release its memory from the database.
            releaseFromDatabase();
        }
    /* package */ synchronized void release() {
        mInUse = false;
    }

    /* package */ synchronized void releaseIfNotInUse() {
        nState |= NSTATE_EVICTED_FROM_CACHE;
        // if it is not in use, release its memory from the database
        if ((nState & NSTATE_INUSE) == 0) {
            releaseFromDatabase();
        }
    }

    // only for testing purposes
    /* package */ synchronized boolean isInUse() {
        return (nState & NSTATE_INUSE) > 0;
    }

    /* package */ synchronized SQLiteCompiledSql setState(int val) {
        nState = nState & val;
        return this; // for chaining
        return mInUse;
    }

    /**
@@ -154,18 +125,11 @@ import android.util.Log;
        try {
            if (nStatement == 0) return;
            // finalizer should NEVER get called
            // but if the database itself is not closed and is GC'ed, then
            // all sub-objects attached to the database could end up getting GC'ed too.
            // in that case, don't print any warning.
            if ((nState & NSTATE_INUSE) == 0) {
                // no need to print warning
            } else {
            int len = mSqlStmt.length();
                Log.w(TAG, "Releasing SQL statement in finalizer. " +
                        "Could be due to close() not being called on the cursor or on the database. " +
                        toString(), mStackTrace);
            }
            releaseFromDatabase();
            Log.w(TAG, "Releasing statement in a finalizer. Please ensure " +
                    "that you explicitly call close() on your cursor: " +
                    mSqlStmt.substring(0, (len > 100) ? 100 : len), mStackTrace);
            releaseSqlStatement();
        } finally {
            super.finalize();
        }
@@ -176,27 +140,6 @@ import android.util.Log;
            StringBuilder buff = new StringBuilder();
            buff.append(" nStatement=");
            buff.append(nStatement);
            if ((nState & NSTATE_CACHEABLE) > 0) {
                buff.append(",cacheable");
            }
            if ((nState & NSTATE_IN_CACHE) > 0) {
                buff.append(",cached");
            }
            if ((nState & NSTATE_INUSE) > 0) {
                buff.append(",in_use");
            }
            if ((nState & NSTATE_CLOSE_NOOP) > 0) {
                buff.append(",no_op_close");
            }
            if ((nState & NSTATE_EVICTED_FROM_CACHE) > 0) {
                buff.append(",evicted_from_cache");
            }
            if ((nState & NSTATE_CACHE_DEALLOC) > 0) {
                buff.append(",dealloc_cache");
            }
            if ((nState & NSTATE_IN_FINALIZER_Q) > 0) {
                buff.append(",in dbFInalizerQ");
            }
            buff.append(", db=");
            buff.append(mDatabase.getPath());
            buff.append(", db_connectionNum=");
+163 −25

File changed.

Preview size limit exceeded, changes collapsed.

Loading