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

Commit be6076b0 authored by Keisuke Kuroyanagi's avatar Keisuke Kuroyanagi Committed by Android Git Automerger
Browse files

am e74d4a18: Stop reading dictionary while regenerating.

* commit 'e74d4a18':
  Stop reading dictionary while regenerating.
parents 4ac33aca e74d4a18
Loading
Loading
Loading
Loading
+88 −62
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

/**
@@ -69,14 +70,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
            FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE;

    /**
     * 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. */
@@ -103,13 +105,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";
@@ -131,15 +133,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;
@@ -189,7 +191,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);
    }
@@ -347,6 +349,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>>();
@@ -407,6 +412,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
@@ -432,7 +440,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();
    }

@@ -443,8 +451,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);
@@ -482,8 +490,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();
@@ -517,11 +525,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);
        }
    }

@@ -530,14 +538,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 */);
    }

    /**
@@ -550,39 +570,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);
                }
            }
        });
    }
@@ -622,14 +647,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);
        }
    }