/****************************************************************************************
* Copyright (c) 2013 Flavio Lerda <flerda@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify it under *
* the terms of the GNU General Public License as published by the Free Software *
* Foundation; either version 3 of the License, or (at your option) any later *
* version. *
* *
* This program is distributed in the hope that it will be useful, but WITHOUT ANY *
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A *
* PARTICULAR PURPOSE. See the GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License along with *
* this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************************/
package com.ichi2.utils;
import android.os.Looper;
/**
* Helper class for checking for programming errors while using threads.
*/
public class Threads {
private Threads() {
}
/**
* An object used to check a thread-access policy.
* <p>
* It will verify that calls to its {@link #checkThread()} method are done on the right thread.
*/
public interface ThreadChecker {
/**
* Checks that it is called from the right thread and fails otherwise.
*/
public void checkThread();
}
/**
* Creates a {@link ThreadChecker} that validates all access are done on the given thread.
*
* @param thread on which accesses should occur
*/
public static ThreadChecker newSingleThreadChecker(Thread thread) {
if (thread == null) {
throw new IllegalArgumentException("thread should not be null");
}
return new SingleThreadChecker(thread);
}
/**
* Creates a {@link ThreadChecker} that validates all access are done on the calling thread.
*/
public static ThreadChecker newCurrentThreadChecker() {
return new SingleThreadChecker(Thread.currentThread());
}
/**
* Creates a {@link ThreadChecker} that validates all access on the same thread, without specifying which thread.
* <p>
* The thread will be determined by the first call to {@link ThreadChecker#checkThread()} and enforced thereafter.
*/
public static ThreadChecker newLazySingleThreadChecker() {
return new SingleThreadChecker(null);
}
/**
* @return true if called from the application main thread
*/
public static boolean isOnMainThread() {
return Looper.getMainLooper() == Looper.myLooper();
}
/**
* Checks that it is called from the main thread and fails if it is called from another thread.
*/
public static void checkMainThread() {
if (!isOnMainThread()) {
throw new IllegalStateException("must be called on the main thread instead of " + Thread.currentThread());
}
}
/**
* Checks that it is not called from the main thread and fails if it is.
*/
public static void checkNotMainThread() {
if (isOnMainThread()) {
throw new IllegalStateException("must not be called on the main thread");
}
}
/**
* Helper class to track access from a single thread.
* <p>
* This class can be used to validate a single-threaded access policy to a class.
* <p>
* Each method that needs to be called from a single thread can simply call {@link #checkThread()} to validate the
* thread it is being called.
*/
private static class SingleThreadChecker implements ThreadChecker {
/** The thread that is allowed access. */
private Thread mThread;
/**
* Creates a checker for the given thread.
* <p>
* If passed {@code null}, it will detect the first thread that calls {@link #checkThread()} and make sure all
* future accesses are from that thread.
*
* @param thread that is allowed access
*/
private SingleThreadChecker(Thread thread) {
mThread = thread;
}
@Override
public void checkThread() {
// If this the first access and we have not specified a thread, record the current thread.
if (mThread == null) {
mThread = Thread.currentThread();
return;
}
if (mThread != Thread.currentThread()) {
throw new IllegalStateException("must be called from single thread: " + mThread + " instead of "
+ Thread.currentThread());
}
}
}
}