DictionaryPool.java revision e30c05800fa463ef622132b0df466f5455281fc1
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package com.android.inputmethod.latin.spellcheck;
18
19import android.util.Log;
20
21import java.util.Locale;
22import java.util.concurrent.LinkedBlockingQueue;
23import java.util.concurrent.TimeUnit;
24
25/**
26 * A blocking queue that creates dictionaries up to a certain limit as necessary.
27 * As a deadlock-detecting device, if waiting for more than TIMEOUT = 3 seconds, we
28 * will clear the queue and generate its contents again. This is transparent for
29 * the client code, but may help with sloppy clients.
30 */
31@SuppressWarnings("serial")
32public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
33    private final static String TAG = DictionaryPool.class.getSimpleName();
34    // How many seconds we wait for a dictionary to become available. Past this delay, we give up in
35    // fear some bug caused a deadlock, and reset the whole pool.
36    private final static int TIMEOUT = 3;
37    private final AndroidSpellCheckerService mService;
38    private final int mMaxSize;
39    private final Locale mLocale;
40    private int mSize;
41    private volatile boolean mClosed;
42
43    public DictionaryPool(final int maxSize, final AndroidSpellCheckerService service,
44            final Locale locale) {
45        super();
46        mMaxSize = maxSize;
47        mService = service;
48        mLocale = locale;
49        mSize = 0;
50        mClosed = false;
51    }
52
53    @Override
54    public DictAndProximity poll(final long timeout, final TimeUnit unit)
55            throws InterruptedException {
56        final DictAndProximity dict = poll();
57        if (null != dict) return dict;
58        synchronized(this) {
59            if (mSize >= mMaxSize) {
60                // Our pool is already full. Wait until some dictionary is ready, or TIMEOUT
61                // expires to avoid a deadlock.
62                final DictAndProximity result = super.poll(timeout, unit);
63                if (null == result) {
64                    Log.e(TAG, "Deadlock detected ! Resetting dictionary pool");
65                    clear();
66                    mSize = 1;
67                    return mService.createDictAndProximity(mLocale);
68                } else {
69                    return result;
70                }
71            } else {
72                ++mSize;
73                return mService.createDictAndProximity(mLocale);
74            }
75        }
76    }
77
78    // Convenience method
79    public DictAndProximity pollWithDefaultTimeout() {
80        try {
81            return poll(TIMEOUT, TimeUnit.SECONDS);
82        } catch (InterruptedException e) {
83            return null;
84        }
85    }
86
87    public void close() {
88        synchronized(this) {
89            mClosed = true;
90            for (DictAndProximity dict : this) {
91                dict.mDictionary.close();
92            }
93            clear();
94        }
95    }
96
97    @Override
98    public boolean offer(final DictAndProximity dict) {
99        if (mClosed) {
100            dict.mDictionary.close();
101            return false;
102        } else {
103            return super.offer(dict);
104        }
105    }
106}
107