Loading java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +88 −62 Original line number Diff line number Diff line Loading @@ -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; /** Loading Loading @@ -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. */ Loading @@ -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"; Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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>>(); Loading Loading @@ -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 Loading @@ -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(); } Loading @@ -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); Loading Loading @@ -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(); Loading Loading @@ -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); } } Loading @@ -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 */); } /** Loading @@ -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); } } }); } Loading Loading @@ -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); } } Loading Loading
java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +88 −62 Original line number Diff line number Diff line Loading @@ -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; /** Loading Loading @@ -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. */ Loading @@ -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"; Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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>>(); Loading Loading @@ -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 Loading @@ -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(); } Loading @@ -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); Loading Loading @@ -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(); Loading Loading @@ -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); } } Loading @@ -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 */); } /** Loading @@ -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); } } }); } Loading Loading @@ -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); } } Loading