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

Commit 703eb8bf authored by satok's avatar satok Committed by The Android Automerger
Browse files

Fix UserHistoryBigram data contention in UserHistoryDictionary

Bug: 6637614
Change-Id: I34d26563e59d3b09bf35b8173dac5645ccb6a39f
parent bead66d1
Loading
Loading
Loading
Loading
+226 −189
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import com.android.inputmethod.latin.UserHistoryForgettingCurveUtils.ForgettingC
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Locally gathers stats about the words user types and various other signals like auto-correction
@@ -40,6 +41,8 @@ import java.util.concurrent.ConcurrentHashMap;
public class UserHistoryDictionary extends ExpandableDictionary {
    private static final String TAG = "UserHistoryDictionary";
    public static final boolean DBG_SAVE_RESTORE = false;
    public static final boolean DBG_STRESS_TEST = false;
    public static final boolean DBG_ALWAYS_WRITE = false;
    public static final boolean PROFILE_SAVE_RESTORE = LatinImeLogger.sDBG;

    /** Any pair being typed or picked */
@@ -82,7 +85,7 @@ public class UserHistoryDictionary extends ExpandableDictionary {

    private final UserHistoryDictionaryBigramList mBigramList =
            new UserHistoryDictionaryBigramList();
    private static volatile boolean sUpdatingDB = false;
    private final ReentrantLock mBigramListLock = new ReentrantLock();
    private final SharedPreferences mPrefs;

    private final static HashMap<String, String> sDictProjectionMap;
@@ -173,7 +176,10 @@ public class UserHistoryDictionary extends ExpandableDictionary {
     * The second word may not be null (a NullPointerException would be thrown).
     */
    public int addToUserHistory(final String word1, String word2, boolean isValid) {
        super.addWord(word2, null /* the "shortcut" parameter is null */, FREQUENCY_FOR_TYPED);
        if (mBigramListLock.tryLock()) {
            try {
                super.addWord(
                        word2, null /* the "shortcut" parameter is null */, FREQUENCY_FOR_TYPED);
                // Do not insert a word as a bigram of itself
                if (word2.equals(word1)) {
                    return 0;
@@ -182,20 +188,27 @@ public class UserHistoryDictionary extends ExpandableDictionary {
                if (null == word1) {
                    freq = FREQUENCY_FOR_TYPED;
                } else {
            freq = super.setBigramAndGetFrequency(word1, word2, new ForgettingCurveParams(isValid));
                    freq = super.setBigramAndGetFrequency(
                            word1, word2, new ForgettingCurveParams(isValid));
                }
        synchronized (mBigramList) {
                mBigramList.addBigram(word1, word2);
        }

                return freq;
            } finally {
                mBigramListLock.unlock();
            }
        }
        return -1;
    }

    public boolean cancelAddingUserHistory(String word1, String word2) {
        synchronized (mBigramList) {
        if (mBigramListLock.tryLock()) {
            try {
                if (mBigramList.removeBigram(word1, word2)) {
                    return super.removeBigram(word1, word2);
                }
            } finally {
                mBigramListLock.unlock();
            }
        }
        return false;
    }
@@ -204,30 +217,33 @@ public class UserHistoryDictionary extends ExpandableDictionary {
     * Schedules a background thread to write any pending words to the database.
     */
    private void flushPendingWrites() {
        synchronized (mBigramList) {
            // Nothing pending? Return
            if (mBigramList.isEmpty()) return;
        if (mBigramListLock.isLocked()) {
            return;
        }
        // Create a background thread to write the pending entries
        new UpdateDbTask(sOpenHelper, mBigramList, mLocale, this, mPrefs).execute();
    }
    }

    /** Used for testing purpose **/
    void waitUntilUpdateDBDone() {
        synchronized (mBigramList) {
            while (sUpdatingDB) {
    @Override
    public void loadDictionaryAsync() {
        // This must be run on non-main thread
        mBigramListLock.lock();
        try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
            loadDictionaryAsyncLocked();
        } finally {
            mBigramListLock.unlock();
        }
    }
            return;

    private void loadDictionaryAsyncLocked() {
        if (DBG_STRESS_TEST) {
            try {
                Log.w(TAG, "Start stress in loading: " + mLocale);
                Thread.sleep(15000);
                Log.w(TAG, "End stress in loading");
            } catch (InterruptedException e) {
            }
        }

    @Override
    public void loadDictionaryAsync() {
        synchronized(mBigramList) {
        final long last = SettingsValues.getLastUserHistoryWriteTime(mPrefs, mLocale);
        final boolean initializing = last == 0;
        final long now = System.currentTimeMillis();
@@ -235,6 +251,7 @@ public class UserHistoryDictionary extends ExpandableDictionary {
        final Cursor cursor = query(MAIN_COLUMN_LOCALE + "=?", new String[] { mLocale });
        if (null == cursor) return;
        try {
            // TODO: Call SQLiteDataBase.beginTransaction / SQLiteDataBase.endTransaction
            if (cursor.moveToFirst()) {
                final int word1Index = cursor.getColumnIndex(MAIN_COLUMN_WORD1);
                final int word2Index = cursor.getColumnIndex(MAIN_COLUMN_WORD2);
@@ -270,7 +287,6 @@ public class UserHistoryDictionary extends ExpandableDictionary {
            }
        }
    }
    }

    /**
     * Query the database
@@ -387,19 +403,11 @@ public class UserHistoryDictionary extends ExpandableDictionary {
            }
        }

        @Override
        protected void onPreExecute() {
            sUpdatingDB = true;
        }

        @Override
        protected Void doInBackground(Void... v) {
            synchronized(mBigramList) {
                final long now = PROFILE_SAVE_RESTORE ? System.currentTimeMillis() : 0;
                int profTotal = 0;
                int profInsert = 0;
                int profDelete = 0;
            SQLiteDatabase db = null;
            if (mUserHistoryDictionary.mBigramListLock.tryLock()) {
                try {
                    try {
                        db = mDbHelper.getWritableDatabase();
                    } catch (android.database.sqlite.SQLiteCantOpenDatabaseException e) {
@@ -408,9 +416,33 @@ public class UserHistoryDictionary extends ExpandableDictionary {
                    }
                    if (null == db) {
                        // Not much we can do. Just exit.
                    sUpdatingDB = false;
                        return null;
                    }
                    db.beginTransaction();
                    return doLoadTaskLocked(db);
                } finally {
                    if (db != null) {
                        db.endTransaction();
                    }
                    mUserHistoryDictionary.mBigramListLock.unlock();
                }
            }
            return null;
        }

        private Void doLoadTaskLocked(SQLiteDatabase db) {
            if (DBG_STRESS_TEST) {
                try {
                    Log.w(TAG, "Start stress in closing: " + mLocale);
                    Thread.sleep(15000);
                    Log.w(TAG, "End stress in closing");
                } catch (InterruptedException e) {
                }
            }
            final long now = PROFILE_SAVE_RESTORE ? System.currentTimeMillis() : 0;
            int profTotal = 0;
            int profInsert = 0;
            int profDelete = 0;
            db.execSQL("PRAGMA foreign_keys = ON;");
            final boolean addLevel0Bigram = mBigramList.size() <= sMaxHistoryBigrams;

@@ -433,8 +465,10 @@ public class UserHistoryDictionary extends ExpandableDictionary {
                                Log.d(TAG, "Skip update user history: " + word1 + "," + word2
                                        + "," + prevFc);
                            }
                            if (!DBG_ALWAYS_WRITE) {
                                continue;
                            }
                        }
                    } else { // bigram
                        final NextWord nw = mUserHistoryDictionary.getBigramWord(word1, word2);
                        if (nw != null) {
@@ -449,7 +483,11 @@ public class UserHistoryDictionary extends ExpandableDictionary {
                                    Log.d(TAG, "Skip update user history: " + word1 + ","
                                            + word2 + "," + prevFc);
                                }
                                if (!DBG_ALWAYS_WRITE) {
                                    continue;
                                } else {
                                    freq = fc;
                                }
                            } else if (UserHistoryForgettingCurveUtils.
                                    needsToSave(fc, isValid, addLevel0Bigram)) {
                                freq = fc;
@@ -519,15 +557,14 @@ public class UserHistoryDictionary extends ExpandableDictionary {
            checkPruneData(db);
            // Save the timestamp after we finish writing the SQL DB.
            SettingsValues.setLastUserHistoryWriteTime(mPrefs, mLocale);
                sUpdatingDB = false;
            if (PROFILE_SAVE_RESTORE) {
                final long diff = System.currentTimeMillis() - now;
                Log.w(TAG, "PROF: Write User HistoryDictionary: " + mLocale + ", "+ diff
                        + "ms. Total: " + profTotal + ". Insert: " + profInsert + ". Delete: "
                        + profDelete);
            }
            db.setTransactionSuccessful();
            return null;
            } // synchronized
        }

        private static ContentValues getContentValues(String word1, String word2, String locale) {