/**
* Copyright 2013 Google Inc.
*
* 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.google.devcoin.utils;
import com.google.common.util.concurrent.Callables;
import com.google.common.util.concurrent.CycleDetectingLockFactory;
import com.google.common.util.concurrent.Futures;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
import static com.google.common.base.Preconditions.checkState;
/**
* Various threading related utilities. Provides a wrapper around explicit lock creation that lets you control whether
* bitcoinj performs cycle detection or not. Cycle detection is useful to detect bugs but comes with a small cost.
* Also provides a worker thread that is designed for event listeners to be dispatched on.
*/
public class Threading {
/**
* An executor with one thread that is intended for running event listeners on. This ensures all event listener code
* runs without any locks being held. It's intended for the API user to run things on. Callbacks registered by
* bitcoinj internally shouldn't normally run here, although currently there are a few exceptions.
*/
public static final ExecutorService USER_THREAD;
/**
* A dummy executor that just invokes the runnable immediately. Use this over
* {@link com.google.common.util.concurrent.MoreExecutors#sameThreadExecutor()} because the latter creates a new
* object each time in order to implement the more complex {@link ExecutorService} interface, which is overkill
* for our needs.
*/
public static final Executor SAME_THREAD;
// For safety reasons keep track of the thread we use to run user-provided event listeners to avoid deadlock.
private static volatile WeakReference<Thread> vUserThread;
/**
* Put a dummy task into the queue and wait for it to be run. Because it's single threaded, this means all
* tasks submitted before this point are now completed. Usually you won't want to use this method - it's a
* convenience primarily used in unit testing. If you want to wait for an event to be called the right thing
* to do is usually to create a {@link com.google.common.util.concurrent.SettableFuture} and then call set
* on it. You can then either block on that future, compose it, add listeners to it and so on.
*/
public static void waitForUserCode() {
// If this assert fires it means you have a bug in your code - you can't call this method inside your own
// event handlers because it would never return. If you aren't calling this method explicitly, then that
// means there's a bug in bitcoinj.
if (vUserThread != null) {
checkState(vUserThread.get() != null && vUserThread.get() != Thread.currentThread(),
"waitForUserCode() run on user code thread would deadlock.");
}
Futures.getUnchecked(USER_THREAD.submit(Callables.returning(null)));
}
/**
* An exception handler that will be invoked for any exceptions that occur in the user thread, and
* any unhandled exceptions that are caught whilst the framework is processing network traffic or doing other
* background tasks. The purpose of this is to allow you to report back unanticipated crashes from your users
* to a central collection center for analysis and debugging. You should configure this <b>before</b> any
* bitcoinj library code is run, setting it after you started network traffic and other forms of processing
* may result in the change not taking effect.
*/
@Nullable
public static volatile Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static {
// Default policy goes here. If you want to change this, use one of the static methods before
// instantiating any bitcoinj objects. The policy change will take effect only on new objects
// from that point onwards.
throwOnLockCycles();
USER_THREAD = Executors.newSingleThreadExecutor(new ThreadFactory() {
@Nonnull @Override public Thread newThread(@Nonnull Runnable runnable) {
Thread t = new Thread(runnable);
t.setName("bitcoinj user thread");
t.setDaemon(true);
t.setUncaughtExceptionHandler(uncaughtExceptionHandler);
vUserThread = new WeakReference<Thread>(t);
return t;
}
});
SAME_THREAD = new Executor() {
@Override
public void execute(@Nonnull Runnable runnable) {
runnable.run();
}
};
}
private static CycleDetectingLockFactory.Policy policy;
public static CycleDetectingLockFactory factory;
public static ReentrantLock lock(String name) {
return factory.newReentrantLock(name);
}
public static void warnOnLockCycles() {
setPolicy(CycleDetectingLockFactory.Policies.WARN);
}
public static void throwOnLockCycles() {
setPolicy(CycleDetectingLockFactory.Policies.THROW);
}
public static void ignoreLockCycles() {
setPolicy(CycleDetectingLockFactory.Policies.DISABLED);
}
public static void setPolicy(CycleDetectingLockFactory.Policy policy) {
Threading.policy = policy;
factory = CycleDetectingLockFactory.newInstance(policy);
}
public static CycleDetectingLockFactory.Policy getPolicy() {
return policy;
}
}