Stop reading dictionary while regenerating. (DO NOT MERGE)

Cherrypick of Iead7268a9371b48d729a5f65074ccbc05f3185db

Bug: 10831272
Change-Id: Ib6f314ac68696616532ff9c05c7f35813137bf9f
main
Keisuke Kuroyanagi 2013-09-27 21:44:26 +09:00 committed by Ken Wakasa
parent 2aea83a4f9
commit 10fa6a2e51
1 changed files with 88 additions and 62 deletions

View File

@ -39,6 +39,7 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
/** /**
@ -72,14 +73,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
new FormatSpec.FormatOptions(3 /* version */, true /* supportsDynamicUpdate */); 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 * A static map of update controllers, each of which records the time of accesses to a single
* dictionary file. The key for this map is the filename and the value is the shared dictionary * binary dictionary file and tracks whether the file is regenerating. The key for this map is
* time recorder associated with that filename. * the filename and the value is the shared dictionary time recorder associated with that
* filename.
*/ */
private static volatile ConcurrentHashMap<String, DictionaryTimeRecorder> private static final ConcurrentHashMap<String, DictionaryUpdateController>
sFilenameDictionaryTimeRecorderMap = CollectionUtils.newConcurrentHashMap(); sFilenameDictionaryUpdateControllerMap = CollectionUtils.newConcurrentHashMap();
private static volatile ConcurrentHashMap<String, PrioritizedSerialExecutor> private static final ConcurrentHashMap<String, PrioritizedSerialExecutor>
sFilenameExecutorMap = CollectionUtils.newConcurrentHashMap(); sFilenameExecutorMap = CollectionUtils.newConcurrentHashMap();
/** The application context. */ /** The application context. */
@ -106,13 +108,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
private final boolean mIsUpdatable; private final boolean mIsUpdatable;
// TODO: remove, once dynamic operations is serialized // TODO: remove, once dynamic operations is serialized
/** Records access to the shared binary dictionary file across multiple instances. */ /** Controls updating the shared binary dictionary file across multiple instances. */
private final DictionaryTimeRecorder mFilenameDictionaryTimeRecorder; private final DictionaryUpdateController mFilenameDictionaryUpdateController;
// TODO: remove, once dynamic operations is serialized // TODO: remove, once dynamic operations is serialized
/** Records access to the local binary dictionary for this instance. */ /** Controls updating the local binary dictionary for this instance. */
private final DictionaryTimeRecorder mPerInstanceDictionaryTimeRecorder = private final DictionaryUpdateController mPerInstanceDictionaryUpdateController =
new DictionaryTimeRecorder(); new DictionaryUpdateController();
/* A extension for a binary dictionary file. */ /* A extension for a binary dictionary file. */
public static final String DICT_FILE_EXTENSION = ".dict"; public static final String DICT_FILE_EXTENSION = ".dict";
@ -134,15 +136,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected abstract boolean hasContentChanged(); 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) { String filename) {
DictionaryTimeRecorder recorder = sFilenameDictionaryTimeRecorderMap.get(filename); DictionaryUpdateController recorder = sFilenameDictionaryUpdateControllerMap.get(filename);
if (recorder == null) { if (recorder == null) {
synchronized(sFilenameDictionaryTimeRecorderMap) { synchronized(sFilenameDictionaryUpdateControllerMap) {
recorder = new DictionaryTimeRecorder(); recorder = new DictionaryUpdateController();
sFilenameDictionaryTimeRecorderMap.put(filename, recorder); sFilenameDictionaryUpdateControllerMap.put(filename, recorder);
} }
} }
return recorder; return recorder;
@ -192,7 +194,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
mContext = context; mContext = context;
mIsUpdatable = isUpdatable; mIsUpdatable = isUpdatable;
mBinaryDictionary = null; mBinaryDictionary = null;
mFilenameDictionaryTimeRecorder = getDictionaryTimeRecorder(filename); mFilenameDictionaryUpdateController = getDictionaryUpdateController(filename);
// Currently, only dynamic personalization dictionary is updatable. // Currently, only dynamic personalization dictionary is updatable.
mDictionaryWriter = getDictionaryWriter(context, dictType, isUpdatable); mDictionaryWriter = getDictionaryWriter(context, dictType, isUpdatable);
} }
@ -352,6 +354,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
final int sessionId) { final int sessionId) {
reloadDictionaryIfRequired(); reloadDictionaryIfRequired();
if (isRegenerating()) {
return null;
}
final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder = final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder =
new AsyncResultHolder<ArrayList<SuggestedWordInfo>>(); new AsyncResultHolder<ArrayList<SuggestedWordInfo>>();
@ -412,6 +417,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
} }
protected boolean isValidWordInner(final String word) { protected boolean isValidWordInner(final String word) {
if (isRegenerating()) {
return false;
}
final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>(); final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
getExecutor(mFilename).executePrioritized(new Runnable() { getExecutor(mFilename).executePrioritized(new Runnable() {
@Override @Override
@ -437,7 +445,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* dictionary exists, this method will generate one. * dictionary exists, this method will generate one.
*/ */
protected void loadDictionary() { protected void loadDictionary() {
mPerInstanceDictionaryTimeRecorder.mLastUpdateRequestTime = SystemClock.uptimeMillis(); mPerInstanceDictionaryUpdateController.mLastUpdateRequestTime = SystemClock.uptimeMillis();
reloadDictionaryIfRequired(); reloadDictionaryIfRequired();
} }
@ -448,8 +456,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
private void loadBinaryDictionary() { private void loadBinaryDictionary() {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "Loading binary dictionary: " + mFilename + " request=" Log.d(TAG, "Loading binary dictionary: " + mFilename + " request="
+ mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime + " update=" + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update="
+ mFilenameDictionaryTimeRecorder.mLastUpdateTime); + mFilenameDictionaryUpdateController.mLastUpdateTime);
} }
final File file = new File(mContext.getFilesDir(), mFilename); final File file = new File(mContext.getFilesDir(), mFilename);
@ -487,8 +495,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
private void writeBinaryDictionary() { private void writeBinaryDictionary() {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "Generating binary dictionary: " + mFilename + " request=" Log.d(TAG, "Generating binary dictionary: " + mFilename + " request="
+ mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime + " update=" + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update="
+ mFilenameDictionaryTimeRecorder.mLastUpdateTime); + mFilenameDictionaryUpdateController.mLastUpdateTime);
} }
if (needsToReloadBeforeWriting()) { if (needsToReloadBeforeWriting()) {
mDictionaryWriter.clear(); mDictionaryWriter.clear();
@ -531,11 +539,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/ */
protected void setRequiresReload(final boolean requiresRebuild) { protected void setRequiresReload(final boolean requiresRebuild) {
final long time = SystemClock.uptimeMillis(); final long time = SystemClock.uptimeMillis();
mPerInstanceDictionaryTimeRecorder.mLastUpdateRequestTime = time; mPerInstanceDictionaryUpdateController.mLastUpdateRequestTime = time;
mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime = time; mFilenameDictionaryUpdateController.mLastUpdateRequestTime = time;
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "Reload request: " + mFilename + ": request=" + time + " update=" 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() { public final void reloadDictionaryIfRequired() {
if (!isReloadRequired()) return; if (!isReloadRequired()) return;
reloadDictionary(); if (setIsRegeneratingIfNotRegenerating()) {
reloadDictionary();
}
} }
/** /**
* Returns whether a dictionary reload is required. * Returns whether a dictionary reload is required.
*/ */
private boolean isReloadRequired() { 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() { getExecutor(mFilename).execute(new Runnable() {
@Override @Override
public void run() { public void run() {
final long time = SystemClock.uptimeMillis(); try {
final boolean dictionaryFileExists = dictionaryFileExists(); final long time = SystemClock.uptimeMillis();
if (mFilenameDictionaryTimeRecorder.isOutOfDate() || !dictionaryFileExists) { final boolean dictionaryFileExists = dictionaryFileExists();
// If the shared dictionary file does not exist or is out of date, the first if (mFilenameDictionaryUpdateController.isOutOfDate()
// instance that acquires the lock will generate a new one. || !dictionaryFileExists) {
if (hasContentChanged() || !dictionaryFileExists) { // If the shared dictionary file does not exist or is out of date, the
// If the source content has changed or the dictionary does not exist, // first instance that acquires the lock will generate a new one.
// rebuild the binary dictionary. Empty dictionaries are supported (in the if (hasContentChanged() || !dictionaryFileExists) {
// case where loadDictionaryAsync() adds nothing) in order to provide a // If the source content has changed or the dictionary does not exist,
// uniform framework. // rebuild the binary dictionary. Empty dictionaries are supported (in
mFilenameDictionaryTimeRecorder.mLastUpdateTime = time; // 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.
mFilenameDictionaryUpdateController.mLastUpdateRequestTime =
mFilenameDictionaryUpdateController.mLastUpdateTime;
}
} else if (mBinaryDictionary == null ||
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.
mFilenameDictionaryUpdateController.mLastUpdateTime = time;
writeBinaryDictionary(); writeBinaryDictionary();
loadBinaryDictionary(); loadBinaryDictionary();
} else {
// If not, the reload request was unnecessary so revert
// LastUpdateRequestTime to LastUpdateTime.
mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime =
mFilenameDictionaryTimeRecorder.mLastUpdateTime;
} }
} else if (mBinaryDictionary == null || mPerInstanceDictionaryUpdateController.mLastUpdateTime = time;
mPerInstanceDictionaryTimeRecorder.mLastUpdateTime } finally {
< mFilenameDictionaryTimeRecorder.mLastUpdateTime) { mFilenameDictionaryUpdateController.mIsRegenerating.set(false);
// 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;
writeBinaryDictionary();
loadBinaryDictionary();
}
mPerInstanceDictionaryTimeRecorder.mLastUpdateTime = time;
} }
}); });
} }
@ -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. * Can be shared across multiple dictionary instances that access the same filename.
*/ */
private static class DictionaryTimeRecorder { private static class DictionaryUpdateController {
private volatile long mLastUpdateTime = 0; public volatile long mLastUpdateTime = 0;
private volatile long mLastUpdateRequestTime = 0; public volatile long mLastUpdateRequestTime = 0;
public volatile AtomicBoolean mIsRegenerating = new AtomicBoolean();
private boolean isOutOfDate() { public boolean isOutOfDate() {
return (mLastUpdateRequestTime > mLastUpdateTime); return (mLastUpdateRequestTime > mLastUpdateTime);
} }
} }