Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c207955d authored by Keisuke Kuroyanagi's avatar Keisuke Kuroyanagi Committed by Android (Google) Code Review
Browse files

Merge "Use ReentrantReadWriteLock in ExpandableBinaryDictionary."

parents 2367f8f6 1737b7ff
Loading
Loading
Loading
Loading
+0 −3
Original line number Original line Diff line number Diff line
@@ -87,9 +87,6 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
        mLocale = locale;
        mLocale = locale;
        mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
        mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
        registerObserver(context);
        registerObserver(context);

        // Load the current binary dictionary from internal storage. If no binary dictionary exists,
        // reloadDictionaryIfRequired will start a new thread to generate one asynchronously.
        reloadDictionaryIfRequired();
        reloadDictionaryIfRequired();
    }
    }


+130 −123
Original line number Original line Diff line number Diff line
@@ -26,7 +26,6 @@ import com.android.inputmethod.latin.makedict.FormatSpec;
import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
import com.android.inputmethod.latin.makedict.WordProperty;
import com.android.inputmethod.latin.makedict.WordProperty;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.utils.AsyncResultHolder;
import com.android.inputmethod.latin.utils.CombinedFormatUtils;
import com.android.inputmethod.latin.utils.CombinedFormatUtils;
import com.android.inputmethod.latin.utils.ExecutorUtils;
import com.android.inputmethod.latin.utils.ExecutorUtils;
import com.android.inputmethod.latin.utils.FileUtils;
import com.android.inputmethod.latin.utils.FileUtils;
@@ -40,6 +39,8 @@ import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


/**
/**
 * Abstract base class for an expandable dictionary that can be created and updated dynamically
 * Abstract base class for an expandable dictionary that can be created and updated dynamically
@@ -59,7 +60,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    private static final boolean DBG_STRESS_TEST = false;
    private static final boolean DBG_STRESS_TEST = false;


    private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100;
    private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100;
    private static final int TIMEOUT_FOR_READ_OPS_FOR_TESTS_IN_MILLISECONDS = 10000;


    private static final int DEFAULT_MAX_UNIGRAM_COUNT = 10000;
    private static final int DEFAULT_MAX_UNIGRAM_COUNT = 10000;
    private static final int DEFAULT_MAX_BIGRAM_COUNT = 10000;
    private static final int DEFAULT_MAX_BIGRAM_COUNT = 10000;
@@ -93,9 +93,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    /** Dictionary file */
    /** Dictionary file */
    private final File mDictFile;
    private final File mDictFile;


    private final AtomicBoolean mProcessingLargeTask;
    /** Indicates whether a task for reloading the dictionary has been scheduled. */
    private final AtomicBoolean mIsReloading;

    /** Indicates whether the current dictionary needs to be reloaded. */
    private boolean mNeedsToReload;
    private boolean mNeedsToReload;


    private final ReentrantReadWriteLock mLock;


    /* A extension for a binary dictionary file. */
    /* A extension for a binary dictionary file. */
    protected static final String DICT_FILE_EXTENSION = ".dict";
    protected static final String DICT_FILE_EXTENSION = ".dict";
@@ -144,8 +148,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
        mLocale = locale;
        mLocale = locale;
        mDictFile = getDictFile(context, dictName, dictFile);
        mDictFile = getDictFile(context, dictName, dictFile);
        mBinaryDictionary = null;
        mBinaryDictionary = null;
        mProcessingLargeTask = new AtomicBoolean();
        mIsReloading = new AtomicBoolean();
        mNeedsToReload = false;
        mNeedsToReload = false;
        mLock = new ReentrantReadWriteLock();
    }
    }


    public static File getDictFile(final Context context, final String dictName,
    public static File getDictFile(final Context context, final String dictName,
@@ -159,12 +164,30 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
        return dictFile != null ? dictFile.getName() : name + "." + locale.toString();
        return dictFile != null ? dictFile.getName() : name + "." + locale.toString();
    }
    }


    private void asyncExecuteTaskWithWriteLock(final Runnable task) {
        asyncExecuteTaskWithLock(mLock.writeLock(), task);
    }

    private void asyncExecuteTaskWithLock(final Lock lock, final Runnable task) {
        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    task.run();
                } finally {
                    lock.unlock();
                }
            }
        });
    }

    /**
    /**
     * Closes and cleans up the binary dictionary.
     * Closes and cleans up the binary dictionary.
     */
     */
    @Override
    @Override
    public void close() {
    public void close() {
        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
        asyncExecuteTaskWithWriteLock(new Runnable() {
            @Override
            @Override
            public void run() {
            public void run() {
                if (mBinaryDictionary != null) {
                if (mBinaryDictionary != null) {
@@ -188,6 +211,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
        return attributeMap;
        return attributeMap;
    }
    }


    private void removeBinaryDictionary() {
        asyncExecuteTaskWithWriteLock(new Runnable() {
            @Override
            public void run() {
                removeBinaryDictionaryLocked();
            }
        });
    }

    private void removeBinaryDictionaryLocked() {
    private void removeBinaryDictionaryLocked() {
        if (mBinaryDictionary != null) {
        if (mBinaryDictionary != null) {
            mBinaryDictionary.close();
            mBinaryDictionary.close();
@@ -211,7 +243,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    }
    }


    public void clear() {
    public void clear() {
        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
        asyncExecuteTaskWithWriteLock(new Runnable() {
            @Override
            @Override
            public void run() {
            public void run() {
                removeBinaryDictionaryLocked();
                removeBinaryDictionaryLocked();
@@ -224,13 +256,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
     * Check whether GC is needed and run GC if required.
     * Check whether GC is needed and run GC if required.
     */
     */
    protected void runGCIfRequired(final boolean mindsBlockByGC) {
    protected void runGCIfRequired(final boolean mindsBlockByGC) {
        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
        asyncExecuteTaskWithWriteLock(new Runnable() {
            @Override
            @Override
            public void run() {
            public void run() {
                if (mBinaryDictionary == null) {
                if (mBinaryDictionary == null) {
                    return;
                    return;
                }
                }
                runGCAfterAllPrioritizedTasksIfRequiredLocked(mindsBlockByGC);
                runGCIfRequiredLocked(mindsBlockByGC);
            }
            }
        });
        });
    }
    }
@@ -241,25 +273,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
        }
        }
    }
    }


    private void runGCAfterAllPrioritizedTasksIfRequiredLocked(final boolean mindsBlockByGC) {
        // needsToRunGC() have to be called with lock.
        if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) {
            if (setProcessingLargeTaskIfNot()) {
                // Run GC after currently existing time sensitive operations.
                ExecutorUtils.getExecutor(mDictName).executePrioritized(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            mBinaryDictionary.flushWithGC();
                        } finally {
                            mProcessingLargeTask.set(false);
                        }
                    }
                });
            }
        }
    }

    /**
    /**
     * Dynamically adds a word unigram to the dictionary. May overwrite an existing entry.
     * Dynamically adds a word unigram to the dictionary. May overwrite an existing entry.
     */
     */
@@ -267,13 +280,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
            final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
            final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
            final boolean isBlacklisted, final int timestamp) {
            final boolean isBlacklisted, final int timestamp) {
        reloadDictionaryIfRequired();
        reloadDictionaryIfRequired();
        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
        asyncExecuteTaskWithWriteLock(new Runnable() {
            @Override
            @Override
            public void run() {
            public void run() {
                if (mBinaryDictionary == null) {
                if (mBinaryDictionary == null) {
                    return;
                    return;
                }
                }
                runGCAfterAllPrioritizedTasksIfRequiredLocked(true /* mindsBlockByGC */);
                runGCIfRequiredLocked(true /* mindsBlockByGC */);
                addWordDynamicallyLocked(word, frequency, shortcutTarget, shortcutFreq,
                addWordDynamicallyLocked(word, frequency, shortcutTarget, shortcutFreq,
                        isNotAWord, isBlacklisted, timestamp);
                        isNotAWord, isBlacklisted, timestamp);
            }
            }
@@ -293,13 +306,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    public void addBigramDynamically(final String word0, final String word1,
    public void addBigramDynamically(final String word0, final String word1,
            final int frequency, final int timestamp) {
            final int frequency, final int timestamp) {
        reloadDictionaryIfRequired();
        reloadDictionaryIfRequired();
        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
        asyncExecuteTaskWithWriteLock(new Runnable() {
            @Override
            @Override
            public void run() {
            public void run() {
                if (mBinaryDictionary == null) {
                if (mBinaryDictionary == null) {
                    return;
                    return;
                }
                }
                runGCAfterAllPrioritizedTasksIfRequiredLocked(true /* mindsBlockByGC */);
                runGCIfRequiredLocked(true /* mindsBlockByGC */);
                addBigramDynamicallyLocked(word0, word1, frequency, timestamp);
                addBigramDynamicallyLocked(word0, word1, frequency, timestamp);
            }
            }
        });
        });
@@ -315,13 +328,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
     */
     */
    public void removeBigramDynamically(final String word0, final String word1) {
    public void removeBigramDynamically(final String word0, final String word1) {
        reloadDictionaryIfRequired();
        reloadDictionaryIfRequired();
        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
        asyncExecuteTaskWithWriteLock(new Runnable() {
            @Override
            @Override
            public void run() {
            public void run() {
                if (mBinaryDictionary == null) {
                if (mBinaryDictionary == null) {
                    return;
                    return;
                }
                }
                runGCAfterAllPrioritizedTasksIfRequiredLocked(true /* mindsBlockByGC */);
                runGCIfRequiredLocked(true /* mindsBlockByGC */);
                mBinaryDictionary.removeBigramWords(word0, word1);
                mBinaryDictionary.removeBigramWords(word0, word1);
            }
            }
        });
        });
@@ -338,14 +351,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
            final ArrayList<LanguageModelParam> languageModelParams,
            final ArrayList<LanguageModelParam> languageModelParams,
            final AddMultipleDictionaryEntriesCallback callback) {
            final AddMultipleDictionaryEntriesCallback callback) {
        reloadDictionaryIfRequired();
        reloadDictionaryIfRequired();
        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
        asyncExecuteTaskWithWriteLock(new Runnable() {
            @Override
            @Override
            public void run() {
            public void run() {
                try {
                    if (mBinaryDictionary == null) {
                    if (mBinaryDictionary == null) {
                        return;
                        return;
                    }
                    }
                final boolean locked = setProcessingLargeTaskIfNot();
                try {
                    mBinaryDictionary.addMultipleDictionaryEntries(
                    mBinaryDictionary.addMultipleDictionaryEntries(
                            languageModelParams.toArray(
                            languageModelParams.toArray(
                                    new LanguageModelParam[languageModelParams.size()]));
                                    new LanguageModelParam[languageModelParams.size()]));
@@ -353,9 +365,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
                    if (callback != null) {
                    if (callback != null) {
                        callback.onFinished();
                        callback.onFinished();
                    }
                    }
                    if (locked) {
                        mProcessingLargeTask.set(false);
                    }
                }
                }
            }
            }
        });
        });
@@ -367,29 +376,33 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
            final int sessionId, final float[] inOutLanguageWeight) {
            final int sessionId, final float[] inOutLanguageWeight) {
        reloadDictionaryIfRequired();
        reloadDictionaryIfRequired();
        if (processingLargeTask()) {
        boolean lockAcquired = false;
            return null;
        try {
        }
            lockAcquired = mLock.readLock().tryLock(
        final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder =
                    TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
                new AsyncResultHolder<ArrayList<SuggestedWordInfo>>();
            if (lockAcquired) {
        ExecutorUtils.getExecutor(mDictName).executePrioritized(new Runnable() {
            @Override
            public void run() {
                if (mBinaryDictionary == null) {
                if (mBinaryDictionary == null) {
                    holder.set(null);
                    return null;
                    return;
                }
                }
                final ArrayList<SuggestedWordInfo> binarySuggestion =
                final ArrayList<SuggestedWordInfo> suggestions =
                        mBinaryDictionary.getSuggestionsWithSessionId(composer, prevWord,
                        mBinaryDictionary.getSuggestionsWithSessionId(composer, prevWord,
                                proximityInfo, blockOffensiveWords, additionalFeaturesOptions,
                                proximityInfo, blockOffensiveWords, additionalFeaturesOptions,
                                sessionId, inOutLanguageWeight);
                                sessionId, inOutLanguageWeight);
                holder.set(binarySuggestion);
                if (mBinaryDictionary.isCorrupted()) {
                if (mBinaryDictionary.isCorrupted()) {
                    removeBinaryDictionaryLocked();
                    Log.i(TAG, "Dictionary (" + mDictName +") is corrupted. "
                            + "Remove and regenerate it.");
                    removeBinaryDictionary();
                }
                }
                return suggestions;
            }
            }
        });
        } catch (final InterruptedException e) {
        return holder.get(null, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS);
            Log.e(TAG, "Interrupted tryLock() in getSuggestionsWithSessionId().", e);
        } finally {
            if (lockAcquired) {
                mLock.readLock().unlock();
            }
        }
        return null;
    }
    }


    @Override
    @Override
@@ -404,17 +417,24 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    @Override
    @Override
    public boolean isValidWord(final String word) {
    public boolean isValidWord(final String word) {
        reloadDictionaryIfRequired();
        reloadDictionaryIfRequired();
        if (processingLargeTask()) {
        boolean lockAcquired = false;
        try {
            lockAcquired = mLock.readLock().tryLock(
                    TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
            if (lockAcquired) {
                if (mBinaryDictionary == null) {
                    return false;
                    return false;
                }
                }
        final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
                return isValidWordLocked(word);
        ExecutorUtils.getExecutor(mDictName).executePrioritized(new Runnable() {
            }
            @Override
        } catch (final InterruptedException e) {
            public void run() {
            Log.e(TAG, "Interrupted tryLock() in isValidWord().", e);
                holder.set(isValidWordLocked(word));
        } finally {
            if (lockAcquired) {
                mLock.readLock().unlock();
            }
            }
        });
        }
        return holder.get(false, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS);
        return false;
    }
    }


    protected boolean isValidWordLocked(final String word) {
    protected boolean isValidWordLocked(final String word) {
@@ -484,13 +504,14 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    }
    }


    /**
    /**
     * Reloads the dictionary if required.
     * Load the current binary dictionary from internal storage. If the dictionary file doesn't
     * exists or needs to be regenerated, the new dictionary file will be asynchronously generated.
     * However, the dictionary itself is accessible even before the new dictionary file is actually
     * generated. It may return a null result for getSuggestions() in that case by design.
     */
     */
    public final void reloadDictionaryIfRequired() {
    public final void reloadDictionaryIfRequired() {
        if (!isReloadRequired()) return;
        if (!isReloadRequired()) return;
        if (setProcessingLargeTaskIfNot()) {
        asyncReloadDictionary();
            reloadDictionary();
        }
    }
    }


    /**
    /**
@@ -500,38 +521,24 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
        return mBinaryDictionary == null || mNeedsToReload;
        return mBinaryDictionary == null || mNeedsToReload;
    }
    }


    private boolean processingLargeTask() {
        return mProcessingLargeTask.get();
    }

    // Returns whether the dictionary is being used for a large task. If true, we should not use
    // this dictionary for latency sensitive operations.
    private boolean setProcessingLargeTaskIfNot() {
        return mProcessingLargeTask.compareAndSet(false /* expect */ , true /* update */);
    }

    /**
    /**
     * Reloads the dictionary. Access is controlled on a per dictionary file basis and supports
     * Reloads the dictionary. Access is controlled on a per dictionary file basis.
     * concurrent calls from multiple instances that share the same dictionary file.
     */
     */
    private final void reloadDictionary() {
    private final void asyncReloadDictionary() {
        // Ensure that only one thread attempts to read or write to the shared binary dictionary
        if (mIsReloading.compareAndSet(false, true)) {
        // file at the same time.
            mNeedsToReload = false;
        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
            asyncExecuteTaskWithWriteLock(new Runnable() {
                @Override
                @Override
                public void run() {
                public void run() {
                    try {
                    try {
                        if (!dictionaryFileExists() || haveContentsChanged()) {
                        if (!dictionaryFileExists() || haveContentsChanged()) {
                        // If the shared dictionary file does not exist or is out of date and
                            // If the shared dictionary file does not exist or contents have been
                        // contents have been updated, the first instance that acquires the lock
                            // updated, generate a new one.
                        // will generate a new one
                            createNewDictionaryLocked();
                            createNewDictionaryLocked();
                        } else if (mBinaryDictionary == null) {
                        } else if (mBinaryDictionary == null) {
                        // Otherwise, if the local dictionary is older than the shared dictionary,
                            // Otherwise, load the existing dictionary.
                        // load the shared dictionary.
                            loadBinaryDictionaryLocked();
                            loadBinaryDictionaryLocked();
                        }
                        }
                    mNeedsToReload = false;
                        if (mBinaryDictionary != null && !(isValidDictionaryLocked()
                        if (mBinaryDictionary != null && !(isValidDictionaryLocked()
                                // TODO: remove the check below
                                // TODO: remove the check below
                                && matchesExpectedBinaryDictFormatVersionForThisType(
                                && matchesExpectedBinaryDictFormatVersionForThisType(
@@ -542,11 +549,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
                            createNewDictionaryLocked();
                            createNewDictionaryLocked();
                        }
                        }
                    } finally {
                    } finally {
                    mProcessingLargeTask.set(false);
                        mIsReloading.set(false);
                    }
                    }
                }
                }
            });
            });
        }
        }
    }


    // TODO: cache the file's existence so that we avoid doing a disk access each time.
    // TODO: cache the file's existence so that we avoid doing a disk access each time.
    private boolean dictionaryFileExists() {
    private boolean dictionaryFileExists() {
@@ -557,7 +565,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
     * Flush binary dictionary to dictionary file.
     * Flush binary dictionary to dictionary file.
     */
     */
    public void asyncFlushBinaryDictionary() {
    public void asyncFlushBinaryDictionary() {
        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
        asyncExecuteTaskWithWriteLock(new Runnable() {
            @Override
            @Override
            public void run() {
            public void run() {
                flushDictionaryLocked();
                flushDictionaryLocked();
@@ -568,16 +576,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    // TODO: Implement BinaryDictionary.isInDictionary().
    // TODO: Implement BinaryDictionary.isInDictionary().
    @UsedForTesting
    @UsedForTesting
    public boolean isInUnderlyingBinaryDictionaryForTests(final String word) {
    public boolean isInUnderlyingBinaryDictionaryForTests(final String word) {
        final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
        mLock.readLock().lock();
        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
        try {
            @Override
            if (mBinaryDictionary != null && mDictType == Dictionary.TYPE_USER_HISTORY) {
            public void run() {
                return mBinaryDictionary.isValidWord(word);
                if (mDictType == Dictionary.TYPE_USER_HISTORY) {
                    holder.set(mBinaryDictionary.isValidWord(word));
            }
            }
            return false;
        } finally {
            mLock.readLock().unlock();
        }
        }
        });
        return holder.get(false, TIMEOUT_FOR_READ_OPS_FOR_TESTS_IN_MILLISECONDS);
    }
    }


    @UsedForTesting
    @UsedForTesting
@@ -599,7 +606,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    @UsedForTesting
    @UsedForTesting
    public void dumpAllWordsForDebug() {
    public void dumpAllWordsForDebug() {
        reloadDictionaryIfRequired();
        reloadDictionaryIfRequired();
        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
        asyncExecuteTaskWithLock(mLock.readLock(), new Runnable() {
            @Override
            @Override
            public void run() {
            public void run() {
                Log.d(TAG, "Dump dictionary: " + mDictName);
                Log.d(TAG, "Dump dictionary: " + mDictName);
+1 −0
Original line number Original line Diff line number Diff line
@@ -264,6 +264,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
        for (final String word : words) {
        for (final String word : words) {
            UserHistoryDictionary.addToDictionary(dict, prevWord, word, true, mCurrentTime);
            UserHistoryDictionary.addToDictionary(dict, prevWord, word, true, mCurrentTime);
            prevWord = word;
            prevWord = word;
            dict.waitAllTasksForTests();
            assertTrue(dict.isInUnderlyingBinaryDictionaryForTests(word));
            assertTrue(dict.isInUnderlyingBinaryDictionaryForTests(word));
        }
        }
        forcePassingShortTime();
        forcePassingShortTime();