From e30c05800fa463ef622132b0df466f5455281fc1 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Tue, 14 Aug 2012 15:11:53 +0900 Subject: [PATCH] Preemptive anti-deadlock device We don't know of any actual bug in the implementation of the pool that may result in a deadlock, but this still implements a means out of a deadlock if one ever arises anyway. It's meant to improve the resilience of the spell checker. Bug: 5441027 Change-Id: I689491090792dbb89110ee56631965c5bb2dc712 --- .../AndroidWordLevelSpellCheckerSession.java | 4 +-- .../latin/spellcheck/DictionaryPool.java | 30 +++++++++++++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index 0171dc06d..06f5db749 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -193,7 +193,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { if (shouldFilterOut(inText, mScript)) { DictAndProximity dictInfo = null; try { - dictInfo = mDictionaryPool.takeOrGetNull(); + dictInfo = mDictionaryPool.pollWithDefaultTimeout(); if (null == dictInfo) { return AndroidSpellCheckerService.getNotInDictEmptySuggestions(); } @@ -236,7 +236,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { boolean isInDict = true; DictAndProximity dictInfo = null; try { - dictInfo = mDictionaryPool.takeOrGetNull(); + dictInfo = mDictionaryPool.pollWithDefaultTimeout(); if (null == dictInfo) { return AndroidSpellCheckerService.getNotInDictEmptySuggestions(); } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java index 8fc632ee7..83f82faeb 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java @@ -16,14 +16,24 @@ package com.android.inputmethod.latin.spellcheck; +import android.util.Log; + import java.util.Locale; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; /** * A blocking queue that creates dictionaries up to a certain limit as necessary. + * As a deadlock-detecting device, if waiting for more than TIMEOUT = 3 seconds, we + * will clear the queue and generate its contents again. This is transparent for + * the client code, but may help with sloppy clients. */ @SuppressWarnings("serial") public class DictionaryPool extends LinkedBlockingQueue { + private final static String TAG = DictionaryPool.class.getSimpleName(); + // How many seconds we wait for a dictionary to become available. Past this delay, we give up in + // fear some bug caused a deadlock, and reset the whole pool. + private final static int TIMEOUT = 3; private final AndroidSpellCheckerService mService; private final int mMaxSize; private final Locale mLocale; @@ -41,13 +51,23 @@ public class DictionaryPool extends LinkedBlockingQueue { } @Override - public DictAndProximity take() throws InterruptedException { + public DictAndProximity poll(final long timeout, final TimeUnit unit) + throws InterruptedException { final DictAndProximity dict = poll(); if (null != dict) return dict; synchronized(this) { if (mSize >= mMaxSize) { - // Our pool is already full. Wait until some dictionary is ready. - return super.take(); + // Our pool is already full. Wait until some dictionary is ready, or TIMEOUT + // expires to avoid a deadlock. + final DictAndProximity result = super.poll(timeout, unit); + if (null == result) { + Log.e(TAG, "Deadlock detected ! Resetting dictionary pool"); + clear(); + mSize = 1; + return mService.createDictAndProximity(mLocale); + } else { + return result; + } } else { ++mSize; return mService.createDictAndProximity(mLocale); @@ -56,9 +76,9 @@ public class DictionaryPool extends LinkedBlockingQueue { } // Convenience method - public DictAndProximity takeOrGetNull() { + public DictAndProximity pollWithDefaultTimeout() { try { - return take(); + return poll(TIMEOUT, TimeUnit.SECONDS); } catch (InterruptedException e) { return null; }