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

Commit fad5d4ec authored by Keisuke Kuroyanagi's avatar Keisuke Kuroyanagi Committed by The Android Automerger
Browse files

Stop reading dictionary while regenerating. (DO NOT MERGE)

Cherrypick of Iead7268a9371b48d729a5f65074ccbc05f3185db

Bug: 10831272
Change-Id: Ib6f314ac68696616532ff9c05c7f35813137bf9f
parent fdd71349
Loading
Loading
Loading
Loading
+88 −62
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

/**
@@ -72,14 +73,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
            new FormatSpec.FormatOptions(3 /* version */, true /* supportsDynamicUpdate */);

    /**
     * A static map of time recorders, each of which records the time of accesses to a single binary
     * dictionary file. The key for this map is the filename and the value is the shared dictionary
     * time recorder associated with that filename.
     * A static map of update controllers, each of which records the time of accesses to a single
     * binary dictionary file and tracks whether the file is regenerating. The key for this map is
     * the filename and the value is the shared dictionary time recorder associated with that
     * filename.
     */
    private static volatile ConcurrentHashMap<String, DictionaryTimeRecorder>
            sFilenameDictionaryTimeRecorderMap = CollectionUtils.newConcurrentHashMap();
    private static final ConcurrentHashMap<String, DictionaryUpdateController>
            sFilenameDictionaryUpdateControllerMap = CollectionUtils.newConcurrentHashMap();

    private static volatile ConcurrentHashMap<String, PrioritizedSerialExecutor>
    private static final ConcurrentHashMap<String, PrioritizedSerialExecutor>
            sFilenameExecutorMap = CollectionUtils.newConcurrentHashMap();

    /** The application context. */
@@ -106,13 +108,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    private final boolean mIsUpdatable;

    // TODO: remove, once dynamic operations is serialized
    /** Records access to the shared binary dictionary file across multiple instances. */
    private final DictionaryTimeRecorder mFilenameDictionaryTimeRecorder;
    /** Controls updating the shared binary dictionary file across multiple instances. */
    private final DictionaryUpdateController mFilenameDictionaryUpdateController;

    // TODO: remove, once dynamic operations is serialized
    /** Records access to the local binary dictionary for this instance. */
    private final DictionaryTimeRecorder mPerInstanceDictionaryTimeRecorder =
            new DictionaryTimeRecorder();
    /** Controls updating the local binary dictionary for this instance. */
    private final DictionaryUpdateController mPerInstanceDictionaryUpdateController =
            new DictionaryUpdateController();

    /* A extension for a binary dictionary file. */
    public static final String DICT_FILE_EXTENSION = ".dict";
@@ -134,15 +136,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    protected abstract boolean hasContentChanged();

    /**
     * Gets the dictionary time recorder for the given filename.
     * Gets the dictionary update controller for the given filename.
     */
    private static DictionaryTimeRecorder getDictionaryTimeRecorder(
    private static DictionaryUpdateController getDictionaryUpdateController(
            String filename) {
        DictionaryTimeRecorder recorder = sFilenameDictionaryTimeRecorderMap.get(filename);
        DictionaryUpdateController recorder = sFilenameDictionaryUpdateControllerMap.get(filename);
        if (recorder == null) {
            synchronized(sFilenameDictionaryTimeRecorderMap) {
                recorder = new DictionaryTimeRecorder();
                sFilenameDictionaryTimeRecorderMap.put(filename, recorder);
            synchronized(sFilenameDictionaryUpdateControllerMap) {
                recorder = new DictionaryUpdateController();
                sFilenameDictionaryUpdateControllerMap.put(filename, recorder);
            }
        }
        return recorder;
@@ -192,7 +194,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
        mContext = context;
        mIsUpdatable = isUpdatable;
        mBinaryDictionary = null;
        mFilenameDictionaryTimeRecorder = getDictionaryTimeRecorder(filename);
        mFilenameDictionaryUpdateController = getDictionaryUpdateController(filename);
        // Currently, only dynamic personalization dictionary is updatable.
        mDictionaryWriter = getDictionaryWriter(context, dictType, isUpdatable);
    }
@@ -352,6 +354,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
            final int sessionId) {
        reloadDictionaryIfRequired();
        if (isRegenerating()) {
            return null;
        }
        final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
        final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder =
                new AsyncResultHolder<ArrayList<SuggestedWordInfo>>();
@@ -412,6 +417,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    }

    protected boolean isValidWordInner(final String word) {
        if (isRegenerating()) {
            return false;
        }
        final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
        getExecutor(mFilename).executePrioritized(new Runnable() {
            @Override
@@ -437,7 +445,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
     * dictionary exists, this method will generate one.
     */
    protected void loadDictionary() {
        mPerInstanceDictionaryTimeRecorder.mLastUpdateRequestTime = SystemClock.uptimeMillis();
        mPerInstanceDictionaryUpdateController.mLastUpdateRequestTime = SystemClock.uptimeMillis();
        reloadDictionaryIfRequired();
    }

@@ -448,8 +456,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    private void loadBinaryDictionary() {
        if (DEBUG) {
            Log.d(TAG, "Loading binary dictionary: " + mFilename + " request="
                    + mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime + " update="
                    + mFilenameDictionaryTimeRecorder.mLastUpdateTime);
                    + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update="
                    + mFilenameDictionaryUpdateController.mLastUpdateTime);
        }

        final File file = new File(mContext.getFilesDir(), mFilename);
@@ -487,8 +495,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    private void writeBinaryDictionary() {
        if (DEBUG) {
            Log.d(TAG, "Generating binary dictionary: " + mFilename + " request="
                    + mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime + " update="
                    + mFilenameDictionaryTimeRecorder.mLastUpdateTime);
                    + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update="
                    + mFilenameDictionaryUpdateController.mLastUpdateTime);
        }
        if (needsToReloadBeforeWriting()) {
            mDictionaryWriter.clear();
@@ -531,11 +539,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
     */
    protected void setRequiresReload(final boolean requiresRebuild) {
        final long time = SystemClock.uptimeMillis();
        mPerInstanceDictionaryTimeRecorder.mLastUpdateRequestTime = time;
        mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime = time;
        mPerInstanceDictionaryUpdateController.mLastUpdateRequestTime = time;
        mFilenameDictionaryUpdateController.mLastUpdateRequestTime = time;
        if (DEBUG) {
            Log.d(TAG, "Reload request: " + mFilename + ": request=" + time + " update="
                    + mFilenameDictionaryTimeRecorder.mLastUpdateTime);
                    + mFilenameDictionaryUpdateController.mLastUpdateTime);
        }
    }

@@ -544,14 +552,26 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
     */
    public final void reloadDictionaryIfRequired() {
        if (!isReloadRequired()) return;
        if (setIsRegeneratingIfNotRegenerating()) {
            reloadDictionary();
        }
    }

    /**
     * Returns whether a dictionary reload is required.
     */
    private boolean isReloadRequired() {
        return mBinaryDictionary == null || mPerInstanceDictionaryTimeRecorder.isOutOfDate();
        return mBinaryDictionary == null || mPerInstanceDictionaryUpdateController.isOutOfDate();
    }

    private boolean isRegenerating() {
        return mFilenameDictionaryUpdateController.mIsRegenerating.get();
    }

    // Returns whether the dictionary can be regenerated.
    private boolean setIsRegeneratingIfNotRegenerating() {
        return mFilenameDictionaryUpdateController.mIsRegenerating.compareAndSet(
                false /* expect */ , true /* update */);
    }

    /**
@@ -564,39 +584,44 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
        getExecutor(mFilename).execute(new Runnable() {
            @Override
            public void run() {
                try {
                    final long time = SystemClock.uptimeMillis();
                    final boolean dictionaryFileExists = dictionaryFileExists();
                if (mFilenameDictionaryTimeRecorder.isOutOfDate() || !dictionaryFileExists) {
                    // If the shared dictionary file does not exist or is out of date, the first
                    // instance that acquires the lock will generate a new one.
                    if (mFilenameDictionaryUpdateController.isOutOfDate()
                            || !dictionaryFileExists) {
                        // If the shared dictionary file does not exist or is out of date, the
                        // first instance that acquires the lock will generate a new one.
                        if (hasContentChanged() || !dictionaryFileExists) {
                            // If the source content has changed or the dictionary does not exist,
                        // rebuild the binary dictionary. Empty dictionaries are supported (in the
                        // case where loadDictionaryAsync() adds nothing) in order to provide a
                        // uniform framework.
                        mFilenameDictionaryTimeRecorder.mLastUpdateTime = time;
                            // rebuild the binary dictionary. Empty dictionaries are supported (in
                            // the case where loadDictionaryAsync() adds nothing) in order to
                            // provide a uniform framework.
                            mFilenameDictionaryUpdateController.mLastUpdateTime = time;
                            writeBinaryDictionary();
                            loadBinaryDictionary();
                        } else {
                            // If not, the reload request was unnecessary so revert
                            // LastUpdateRequestTime to LastUpdateTime.
                        mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime =
                                mFilenameDictionaryTimeRecorder.mLastUpdateTime;
                            mFilenameDictionaryUpdateController.mLastUpdateRequestTime =
                                    mFilenameDictionaryUpdateController.mLastUpdateTime;
                        }
                    } else if (mBinaryDictionary == null ||
                        mPerInstanceDictionaryTimeRecorder.mLastUpdateTime
                                < mFilenameDictionaryTimeRecorder.mLastUpdateTime) {
                    // Otherwise, if the local dictionary is older than the shared dictionary, load
                    // the shared dictionary.
                            mPerInstanceDictionaryUpdateController.mLastUpdateTime
                                    < mFilenameDictionaryUpdateController.mLastUpdateTime) {
                        // Otherwise, if the local dictionary is older than the shared dictionary,
                        // load the shared dictionary.
                        loadBinaryDictionary();
                    }
                    if (mBinaryDictionary != null && !mBinaryDictionary.isValidDictionary()) {
                        // Binary dictionary is not valid. Regenerate the dictionary file.
                    mFilenameDictionaryTimeRecorder.mLastUpdateTime = time;
                        mFilenameDictionaryUpdateController.mLastUpdateTime = time;
                        writeBinaryDictionary();
                        loadBinaryDictionary();
                    }
                mPerInstanceDictionaryTimeRecorder.mLastUpdateTime = time;
                    mPerInstanceDictionaryUpdateController.mLastUpdateTime = time;
                } finally {
                    mFilenameDictionaryUpdateController.mIsRegenerating.set(false);
                }
            }
        });
    }
@@ -636,14 +661,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    }

    /**
     * Time recorder for tracking whether the dictionary is out of date.
     * For tracking whether the dictionary is out of date and the dictionary is regenerating.
     * Can be shared across multiple dictionary instances that access the same filename.
     */
    private static class DictionaryTimeRecorder {
        private volatile long mLastUpdateTime = 0;
        private volatile long mLastUpdateRequestTime = 0;
    private static class DictionaryUpdateController {
        public volatile long mLastUpdateTime = 0;
        public volatile long mLastUpdateRequestTime = 0;
        public volatile AtomicBoolean mIsRegenerating = new AtomicBoolean();

        private boolean isOutOfDate() {
        public boolean isOutOfDate() {
            return (mLastUpdateRequestTime > mLastUpdateTime);
        }
    }