From 3e6275634b2368bd5b891fabe750d01f71983f62 Mon Sep 17 00:00:00 2001 From: Keisuke Kuroyanagi Date: Mon, 30 Sep 2013 18:16:29 +0900 Subject: [PATCH] Use reentrant lock for main dictionaries. Bug: 10964805 Change-Id: Iaa9b79fc770d8ae2ec9d7c362c90c28bc9f65ea8 --- .../inputmethod/latin/DictionaryFactory.java | 31 +++-- .../latin/ReadOnlyBinaryDictionary.java | 120 ++++++++++++++++++ 2 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index 3721132c5..828e54f14 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -51,7 +51,7 @@ public final class DictionaryFactory { if (null == locale) { Log.e(TAG, "No locale defined for dictionary"); return new DictionaryCollection(Dictionary.TYPE_MAIN, - createBinaryDictionary(context, locale)); + createReadOnlyBinaryDictionary(context, locale)); } final LinkedList dictList = CollectionUtils.newLinkedList(); @@ -59,11 +59,11 @@ public final class DictionaryFactory { BinaryDictionaryGetter.getDictionaryFiles(locale, context); if (null != assetFileList) { for (final AssetFileAddress f : assetFileList) { - final BinaryDictionary binaryDictionary = new BinaryDictionary(f.mFilename, - f.mOffset, f.mLength, useFullEditDistance, locale, Dictionary.TYPE_MAIN, - false /* isUpdatable */); - if (binaryDictionary.isValidDictionary()) { - dictList.add(binaryDictionary); + final ReadOnlyBinaryDictionary readOnlyBinaryDictionary = + new ReadOnlyBinaryDictionary(f.mFilename, f.mOffset, f.mLength, + useFullEditDistance, locale, Dictionary.TYPE_MAIN); + if (readOnlyBinaryDictionary.isValidDictionary()) { + dictList.add(readOnlyBinaryDictionary); } } } @@ -89,12 +89,12 @@ public final class DictionaryFactory { } /** - * Initializes a dictionary from a raw resource file + * Initializes a read-only binary dictionary from a raw resource file * @param context application context for reading resources * @param locale the locale to use for the resource - * @return an initialized instance of BinaryDictionary + * @return an initialized instance of ReadOnlyBinaryDictionary */ - protected static BinaryDictionary createBinaryDictionary(final Context context, + protected static ReadOnlyBinaryDictionary createReadOnlyBinaryDictionary(final Context context, final Locale locale) { AssetFileDescriptor afd = null; try { @@ -113,9 +113,8 @@ public final class DictionaryFactory { Log.e(TAG, "sourceDir is not a file: " + sourceDir); return null; } - return new BinaryDictionary(sourceDir, afd.getStartOffset(), afd.getLength(), - false /* useFullEditDistance */, locale, Dictionary.TYPE_MAIN, - false /* isUpdatable */); + return new ReadOnlyBinaryDictionary(sourceDir, afd.getStartOffset(), afd.getLength(), + false /* useFullEditDistance */, locale, Dictionary.TYPE_MAIN); } catch (android.content.res.Resources.NotFoundException e) { Log.e(TAG, "Could not find the resource"); return null; @@ -142,10 +141,10 @@ public final class DictionaryFactory { final DictionaryCollection dictionaryCollection = new DictionaryCollection(Dictionary.TYPE_MAIN); for (final AssetFileAddress address : dictionaryList) { - final BinaryDictionary binaryDictionary = new BinaryDictionary(address.mFilename, - address.mOffset, address.mLength, useFullEditDistance, locale, - Dictionary.TYPE_MAIN, false /* isUpdatable */); - dictionaryCollection.addDictionary(binaryDictionary); + final ReadOnlyBinaryDictionary readOnlyBinaryDictionary = new ReadOnlyBinaryDictionary( + address.mFilename, address.mOffset, address.mLength, useFullEditDistance, + locale, Dictionary.TYPE_MAIN); + dictionaryCollection.addDictionary(readOnlyBinaryDictionary); } return dictionaryCollection; } diff --git a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java new file mode 100644 index 000000000..68505ce38 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; + +import java.util.ArrayList; +import java.util.Locale; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * This class provides binary dictionary reading operations with locking. An instance of this class + * can be used by multiple threads. Note that different session IDs must be used when multiple + * threads get suggestions using this class. + */ +public final class ReadOnlyBinaryDictionary extends Dictionary { + /** + * A lock for accessing binary dictionary. Only closing binary dictionary is the operation + * that change the state of dictionary. + */ + private final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock(); + + private final BinaryDictionary mBinaryDictionary; + + public ReadOnlyBinaryDictionary(final String filename, final long offset, final long length, + final boolean useFullEditDistance, final Locale locale, final String dictType) { + super(dictType); + mBinaryDictionary = new BinaryDictionary(filename, offset, length, useFullEditDistance, + locale, dictType, false /* isUpdatable */); + } + + public boolean isValidDictionary() { + return mBinaryDictionary.isValidDictionary(); + } + + @Override + public ArrayList getSuggestions(final WordComposer composer, + final String prevWord, final ProximityInfo proximityInfo, + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { + return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords, + additionalFeaturesOptions, 0 /* sessionId */); + } + + @Override + public ArrayList getSuggestionsWithSessionId(final WordComposer composer, + final String prevWord, final ProximityInfo proximityInfo, + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, + final int sessionId) { + if (mLock.readLock().tryLock()) { + try { + return mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo, + blockOffensiveWords, additionalFeaturesOptions); + } finally { + mLock.readLock().unlock(); + } + } + return null; + } + + @Override + public boolean isValidWord(final String word) { + if (mLock.readLock().tryLock()) { + try { + return mBinaryDictionary.isValidWord(word); + } finally { + mLock.readLock().unlock(); + } + } + return false; + } + + @Override + public boolean shouldAutoCommit(final SuggestedWordInfo candidate) { + if (mLock.readLock().tryLock()) { + try { + return mBinaryDictionary.shouldAutoCommit(candidate); + } finally { + mLock.readLock().unlock(); + } + } + return false; + } + + @Override + public int getFrequency(final String word) { + if (mLock.readLock().tryLock()) { + try { + return mBinaryDictionary.getFrequency(word); + } finally { + mLock.readLock().unlock(); + } + } + return NOT_A_PROBABILITY; + } + + @Override + public void close() { + mLock.writeLock().lock(); + try { + mBinaryDictionary.close(); + } finally { + mLock.writeLock().unlock(); + } + } +}