Introduce thread priorities for threads and handlerthreads.

This commit is contained in:
Clark
2023-03-08 17:29:44 -05:00
committed by Greyson Parrelli
parent 2cef06cd6e
commit 79a062c838
22 changed files with 88 additions and 33 deletions

View File

@@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.testing package org.thoughtcrime.securesms.testing
import org.signal.core.util.ThreadUtil
import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.logging.Log import org.signal.core.util.logging.Log
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
@@ -13,7 +14,7 @@ typealias LogPredicate = (Entry) -> Boolean
*/ */
class InMemoryLogger : Log.Logger() { class InMemoryLogger : Log.Logger() {
private val executor = SignalExecutors.newCachedSingleThreadExecutor("inmemory-logger") private val executor = SignalExecutors.newCachedSingleThreadExecutor("inmemory-logger", ThreadUtil.PRIORITY_BACKGROUND_THREAD)
private val predicates = mutableListOf<LogPredicate>() private val predicates = mutableListOf<LogPredicate>()
private val logEntries = mutableListOf<Entry>() private val logEntries = mutableListOf<Entry>()

View File

@@ -8,6 +8,8 @@ package org.signal.glide.common.executor;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.Looper; import android.os.Looper;
import org.signal.core.util.ThreadUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@@ -39,7 +41,7 @@ public class FrameDecoderExecutor {
public Looper getLooper(int taskId) { public Looper getLooper(int taskId) {
int idx = taskId % sPoolNumber; int idx = taskId % sPoolNumber;
if (idx >= mHandlerThreadGroup.size()) { if (idx >= mHandlerThreadGroup.size()) {
HandlerThread handlerThread = new HandlerThread("FrameDecoderExecutor-" + idx); HandlerThread handlerThread = new HandlerThread("FrameDecoderExecutor-" + idx, ThreadUtil.PRIORITY_BACKGROUND_THREAD);
handlerThread.start(); handlerThread.start();
mHandlerThreadGroup.add(handlerThread); mHandlerThreadGroup.add(handlerThread);

View File

@@ -7,6 +7,7 @@ import android.media.MediaCodecInfo;
import android.media.MediaFormat; import android.media.MediaFormat;
import android.media.MediaRecorder; import android.media.MediaRecorder;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.Process;
import org.signal.core.util.StreamUtil; import org.signal.core.util.StreamUtil;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
@@ -65,6 +66,7 @@ public class AudioCodec implements Recorder {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
byte[] audioRecordData = new byte[bufferSize]; byte[] audioRecordData = new byte[bufferSize];
ByteBuffer[] codecInputBuffers = mediaCodec.getInputBuffers(); ByteBuffer[] codecInputBuffers = mediaCodec.getInputBuffers();

View File

@@ -9,6 +9,7 @@ import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.voice.VoiceNoteDraft; import org.thoughtcrime.securesms.components.voice.VoiceNoteDraft;
@@ -25,7 +26,7 @@ public class AudioRecorder {
private static final String TAG = Log.tag(AudioRecorder.class); private static final String TAG = Log.tag(AudioRecorder.class);
private static final ExecutorService executor = SignalExecutors.newCachedSingleThreadExecutor("signal-AudioRecorder"); private static final ExecutorService executor = SignalExecutors.newCachedSingleThreadExecutor("signal-AudioRecorder", ThreadUtil.PRIORITY_UI_BLOCKING_THREAD);
private final Context context; private final Context context;
private final AudioRecordingHandler uiHandler; private final AudioRecordingHandler uiHandler;

View File

@@ -9,6 +9,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import org.signal.core.util.Hex; import org.signal.core.util.Hex;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.DeadlockDetector; import org.signal.core.util.concurrent.DeadlockDetector;
import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations; import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations;
@@ -137,7 +138,7 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
signalWebSocket, signalWebSocket,
Optional.of(new SecurityEventListener(context)), Optional.of(new SecurityEventListener(context)),
provideGroupsV2Operations(signalServiceConfiguration).getProfileOperations(), provideGroupsV2Operations(signalServiceConfiguration).getProfileOperations(),
SignalExecutors.newCachedBoundedExecutor("signal-messages", 1, 16, 30), SignalExecutors.newCachedBoundedExecutor("signal-messages", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD, 1, 16, 30),
ByteUnit.KILOBYTES.toBytes(256), ByteUnit.KILOBYTES.toBytes(256),
FeatureFlags.okHttpAutomaticRetry()); FeatureFlags.okHttpAutomaticRetry());
} }
@@ -373,7 +374,7 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
@Override @Override
public @NonNull DeadlockDetector provideDeadlockDetector() { public @NonNull DeadlockDetector provideDeadlockDetector() {
HandlerThread handlerThread = new HandlerThread("signal-DeadlockDetector"); HandlerThread handlerThread = new HandlerThread("signal-DeadlockDetector", ThreadUtil.PRIORITY_BACKGROUND_THREAD);
handlerThread.start(); handlerThread.start();
return new DeadlockDetector(new Handler(handlerThread.getLooper()), TimeUnit.SECONDS.toMillis(5)); return new DeadlockDetector(new Handler(handlerThread.getLooper()), TimeUnit.SECONDS.toMillis(5));
} }

View File

@@ -27,7 +27,7 @@ private val TAG = Log.tag(JumboEmoji::class.java)
*/ */
object JumboEmoji { object JumboEmoji {
private val executor = ThreadUtil.trace(SignalExecutors.newCachedSingleThreadExecutor("jumbo-emoji")) private val executor = ThreadUtil.trace(SignalExecutors.newCachedSingleThreadExecutor("jumbo-emoji", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD))
const val MAX_JUMBOJI_COUNT = 5 const val MAX_JUMBOJI_COUNT = 5

View File

@@ -7,6 +7,7 @@ import androidx.annotation.NonNull;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import java.util.List; import java.util.List;
@@ -30,7 +31,7 @@ class InAppScheduler implements Scheduler {
private final Handler handler; private final Handler handler;
InAppScheduler(@NonNull JobManager jobManager) { InAppScheduler(@NonNull JobManager jobManager) {
HandlerThread handlerThread = new HandlerThread("InAppScheduler"); HandlerThread handlerThread = new HandlerThread("InAppScheduler", ThreadUtil.PRIORITY_BACKGROUND_THREAD);
handlerThread.start(); handlerThread.start();
this.jobManager = jobManager; this.jobManager = jobManager;

View File

@@ -1,7 +1,10 @@
package org.thoughtcrime.securesms.jobmanager.impl; package org.thoughtcrime.securesms.jobmanager.impl;
import android.os.Process;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.signal.core.util.ThreadUtil;
import org.thoughtcrime.securesms.jobmanager.ExecutorFactory; import org.thoughtcrime.securesms.jobmanager.ExecutorFactory;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@@ -10,6 +13,11 @@ import java.util.concurrent.Executors;
public class DefaultExecutorFactory implements ExecutorFactory { public class DefaultExecutorFactory implements ExecutorFactory {
@Override @Override
public @NonNull ExecutorService newSingleThreadExecutor(@NonNull String name) { public @NonNull ExecutorService newSingleThreadExecutor(@NonNull String name) {
return Executors.newSingleThreadExecutor(r -> new Thread(r, name)); return Executors.newSingleThreadExecutor(r -> new Thread(r, name) {
@Override public void run() {
Process.setThreadPriority(ThreadUtil.PRIORITY_BACKGROUND_THREAD);
super.run();
}
});
} }
} }

View File

@@ -5,6 +5,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler; import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler;
@@ -35,7 +36,7 @@ public final class KeyValueStore implements KeyValueReader {
private KeyValueDataSet dataSet; private KeyValueDataSet dataSet;
public KeyValueStore(@NonNull KeyValuePersistentStorage storage) { public KeyValueStore(@NonNull KeyValuePersistentStorage storage) {
this.executor = SignalExecutors.newCachedSingleThreadExecutor("signal-KeyValueStore"); this.executor = SignalExecutors.newCachedSingleThreadExecutor("signal-KeyValueStore", ThreadUtil.PRIORITY_BACKGROUND_THREAD);
this.storage = storage; this.storage = storage;
} }

View File

@@ -8,6 +8,7 @@ import androidx.annotation.WorkerThread;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
@@ -56,7 +57,7 @@ public class MediaUploadRepository {
public MediaUploadRepository(@NonNull Context context) { public MediaUploadRepository(@NonNull Context context) {
this.context = context; this.context = context;
this.uploadResults = new LinkedHashMap<>(); this.uploadResults = new LinkedHashMap<>();
this.executor = SignalExecutors.newCachedSingleThreadExecutor("signal-MediaUpload"); this.executor = SignalExecutors.newCachedSingleThreadExecutor("signal-MediaUpload", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD);
} }
public void startUpload(@NonNull Media media, @Nullable Recipient recipient) { public void startUpload(@NonNull Media media, @Nullable Recipient recipient) {

View File

@@ -9,6 +9,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.signal.core.util.ExceptionUtil; import org.signal.core.util.ExceptionUtil;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.notifications.v2.DefaultMessageNotifier; import org.thoughtcrime.securesms.notifications.v2.DefaultMessageNotifier;
import org.thoughtcrime.securesms.notifications.v2.ConversationId; import org.thoughtcrime.securesms.notifications.v2.ConversationId;
@@ -28,7 +29,7 @@ public class OptimizedMessageNotifier implements MessageNotifier {
@MainThread @MainThread
public OptimizedMessageNotifier(@NonNull Application context) { public OptimizedMessageNotifier(@NonNull Application context) {
this.limiter = new LeakyBucketLimiter(5, 1000, new Handler(SignalExecutors.getAndStartHandlerThread("signal-notifier").getLooper())); this.limiter = new LeakyBucketLimiter(5, 1000, new Handler(SignalExecutors.getAndStartHandlerThread("signal-notifier", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD).getLooper()));
this.defaultMessageNotifier = new DefaultMessageNotifier(context); this.defaultMessageNotifier = new DefaultMessageNotifier(context);
} }

View File

@@ -36,6 +36,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import org.signal.core.util.StreamUtil; import org.signal.core.util.StreamUtil;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.BuildConfig; import org.thoughtcrime.securesms.BuildConfig;
@@ -201,7 +202,7 @@ public final class PartProvider extends BaseContentProvider {
@RequiresApi(26) @RequiresApi(26)
private ParcelFileDescriptor getParcelStreamProxyForAttachment(AttachmentId attachmentId) throws IOException { private ParcelFileDescriptor getParcelStreamProxyForAttachment(AttachmentId attachmentId) throws IOException {
StorageManager storageManager = Objects.requireNonNull(getContext().getSystemService(StorageManager.class)); StorageManager storageManager = Objects.requireNonNull(getContext().getSystemService(StorageManager.class));
HandlerThread thread = SignalExecutors.getAndStartHandlerThread("storageservice-proxy"); HandlerThread thread = SignalExecutors.getAndStartHandlerThread("storageservice-proxy", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD);
Handler handler = new Handler(thread.getLooper()); Handler handler = new Handler(thread.getLooper());
ParcelFileDescriptor parcelFileDescriptor = storageManager.openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_ONLY, ParcelFileDescriptor parcelFileDescriptor = storageManager.openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_ONLY,

View File

@@ -49,7 +49,7 @@ public final class LiveRecipientCache {
private final AtomicBoolean warmedUp; private final AtomicBoolean warmedUp;
public LiveRecipientCache(@NonNull Context context) { public LiveRecipientCache(@NonNull Context context) {
this(context, ThreadUtil.trace(new FilteredExecutor(SignalExecutors.newCachedBoundedExecutor("signal-recipients", 1, 4, 15), () -> !SignalDatabase.inTransaction()))); this(context, ThreadUtil.trace(new FilteredExecutor(SignalExecutors.newCachedBoundedExecutor("signal-recipients", ThreadUtil.PRIORITY_UI_BLOCKING_THREAD, 1, 4, 15), () -> !SignalDatabase.inTransaction())));
} }
@VisibleForTesting @VisibleForTesting

View File

@@ -16,6 +16,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
import org.signal.core.util.PendingIntentFlags; import org.signal.core.util.PendingIntentFlags;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ServiceUtil;
@@ -30,7 +31,7 @@ public abstract class TimedEventManager<E> {
private final Handler handler; private final Handler handler;
public TimedEventManager(@NonNull Application application, @NonNull String threadName) { public TimedEventManager(@NonNull Application application, @NonNull String threadName) {
HandlerThread handlerThread = new HandlerThread(threadName); HandlerThread handlerThread = new HandlerThread(threadName, ThreadUtil.PRIORITY_BACKGROUND_THREAD);
handlerThread.start(); handlerThread.start();
this.application = application; this.application = application;

View File

@@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.util package org.thoughtcrime.securesms.util
import org.signal.core.util.ThreadUtil
import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.logging.Log import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.database.LocalMetricsDatabase import org.thoughtcrime.securesms.database.LocalMetricsDatabase
@@ -29,7 +30,7 @@ object LocalMetrics {
private val eventsById: MutableMap<String, LocalMetricsEvent> = LRUCache(200) private val eventsById: MutableMap<String, LocalMetricsEvent> = LRUCache(200)
private val lastSplitTimeById: MutableMap<String, Long> = LRUCache(200) private val lastSplitTimeById: MutableMap<String, Long> = LRUCache(200)
private val executor: Executor = SignalExecutors.newCachedSingleThreadExecutor("signal-LocalMetrics") private val executor: Executor = SignalExecutors.newCachedSingleThreadExecutor("signal-LocalMetrics", ThreadUtil.PRIORITY_BACKGROUND_THREAD)
private val db: LocalMetricsDatabase by lazy { LocalMetricsDatabase.getInstance(ApplicationDependencies.getApplication()) } private val db: LocalMetricsDatabase by lazy { LocalMetricsDatabase.getInstance(ApplicationDependencies.getApplication()) }
@JvmStatic @JvmStatic

View File

@@ -8,6 +8,7 @@ import android.media.AudioManager
import android.media.SoundPool import android.media.SoundPool
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import org.signal.core.util.ThreadUtil
import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.logging.Log import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.R
@@ -21,7 +22,7 @@ private val TAG = Log.tag(SignalAudioManager::class.java)
sealed class SignalAudioManager(protected val context: Context, protected val eventListener: EventListener?) { sealed class SignalAudioManager(protected val context: Context, protected val eventListener: EventListener?) {
private var commandAndControlThread = SignalExecutors.getAndStartHandlerThread("call-audio") private var commandAndControlThread = SignalExecutors.getAndStartHandlerThread("call-audio", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD)
protected val handler = SignalAudioHandler(commandAndControlThread.looper) protected val handler = SignalAudioHandler(commandAndControlThread.looper)
protected var state: State = State.UNINITIALIZED protected var state: State = State.UNINITIALIZED

View File

@@ -2,6 +2,7 @@ package org.signal.core.util;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Process;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
@@ -18,6 +19,20 @@ import java.util.concurrent.ExecutorService;
*/ */
public final class ThreadUtil { public final class ThreadUtil {
/**
* Default background thread priority.
*/
public static final int PRIORITY_BACKGROUND_THREAD = Process.THREAD_PRIORITY_BACKGROUND;
/**
* Important background thread priority. This is slightly lower priority than the UI thread. Use for critical work that should run as fast as
* possible, but shouldn't block the UI (e.g. message sends)
*/
public static final int PRIORITY_IMPORTANT_BACKGROUND_THREAD = Process.THREAD_PRIORITY_DEFAULT + Process.THREAD_PRIORITY_LESS_FAVORABLE;
/**
* As important as the UI thread. Use for absolutely critical UI blocking tasks/threads. For example fetching data for display in a recyclerview, or
* anything that will block UI.
*/
public static final int PRIORITY_UI_BLOCKING_THREAD = Process.THREAD_PRIORITY_DEFAULT;
private static volatile Handler handler; private static volatile Handler handler;
@VisibleForTesting @VisibleForTesting

View File

@@ -1,6 +1,7 @@
package org.signal.core.util.concurrent; package org.signal.core.util.concurrent;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.Process;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -17,15 +18,20 @@ import java.util.concurrent.atomic.AtomicInteger;
public final class SignalExecutors { public final class SignalExecutors {
public static final ExecutorService UNBOUNDED = ThreadUtil.trace(Executors.newCachedThreadPool(new NumberedThreadFactory("signal-unbounded"))); public static final ExecutorService UNBOUNDED = ThreadUtil.trace(Executors.newCachedThreadPool(new NumberedThreadFactory("signal-unbounded", ThreadUtil.PRIORITY_BACKGROUND_THREAD)));
public static final ExecutorService BOUNDED = ThreadUtil.trace(Executors.newFixedThreadPool(4, new NumberedThreadFactory("signal-bounded"))); public static final ExecutorService BOUNDED = ThreadUtil.trace(Executors.newFixedThreadPool(4, new NumberedThreadFactory("signal-bounded", ThreadUtil.PRIORITY_BACKGROUND_THREAD)));
public static final ExecutorService SERIAL = ThreadUtil.trace(Executors.newSingleThreadExecutor(new NumberedThreadFactory("signal-serial"))); public static final ExecutorService SERIAL = ThreadUtil.trace(Executors.newSingleThreadExecutor(new NumberedThreadFactory("signal-serial", ThreadUtil.PRIORITY_BACKGROUND_THREAD)));
public static final ExecutorService BOUNDED_IO = ThreadUtil.trace(newCachedBoundedExecutor("signal-io-bounded", 1, 32, 30)); public static final ExecutorService BOUNDED_IO = ThreadUtil.trace(newCachedBoundedExecutor("signal-io-bounded", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD, 1, 32, 30));
private SignalExecutors() {} private SignalExecutors() {}
public static ExecutorService newCachedSingleThreadExecutor(final String name) { public static ExecutorService newCachedSingleThreadExecutor(final String name, int priority) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 15, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), r -> new Thread(r, name)); ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 15, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), r -> new Thread(r, name) {
@Override public void run() {
Process.setThreadPriority(priority);
super.run();
}
});
executor.allowCoreThreadTimeOut(true); executor.allowCoreThreadTimeOut(true);
return executor; return executor;
} }
@@ -41,7 +47,7 @@ public final class SignalExecutors {
* So we make a queue that will always return false if it's non-empty to ensure new threads get * So we make a queue that will always return false if it's non-empty to ensure new threads get
* created. Then, if a task gets rejected, we simply add it to the queue. * created. Then, if a task gets rejected, we simply add it to the queue.
*/ */
public static ExecutorService newCachedBoundedExecutor(final String name, int minThreads, int maxThreads, int timeoutSeconds) { public static ExecutorService newCachedBoundedExecutor(final String name, int priority, int minThreads, int maxThreads, int timeoutSeconds) {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(minThreads, ThreadPoolExecutor threadPool = new ThreadPoolExecutor(minThreads,
maxThreads, maxThreads,
timeoutSeconds, timeoutSeconds,
@@ -55,7 +61,7 @@ public final class SignalExecutors {
return false; return false;
} }
} }
}, new NumberedThreadFactory(name)); }, new NumberedThreadFactory(name, priority));
threadPool.setRejectedExecutionHandler((runnable, executor) -> { threadPool.setRejectedExecutionHandler((runnable, executor) -> {
try { try {
@@ -73,28 +79,36 @@ public final class SignalExecutors {
* which processor work in FIFO order. * which processor work in FIFO order.
*/ */
public static ExecutorService newFixedLifoThreadExecutor(String name, int minThreads, int maxThreads) { public static ExecutorService newFixedLifoThreadExecutor(String name, int minThreads, int maxThreads) {
return new ThreadPoolExecutor(minThreads, maxThreads, 0, TimeUnit.MILLISECONDS, new LinkedBlockingLifoQueue<>(), new NumberedThreadFactory(name)); return new ThreadPoolExecutor(minThreads, maxThreads, 0, TimeUnit.MILLISECONDS, new LinkedBlockingLifoQueue<>(), new NumberedThreadFactory(name, ThreadUtil.PRIORITY_BACKGROUND_THREAD));
} }
public static HandlerThread getAndStartHandlerThread(@NonNull String name) { public static HandlerThread getAndStartHandlerThread(@NonNull String name, int priority) {
HandlerThread handlerThread = new HandlerThread(name); HandlerThread handlerThread = new HandlerThread(name, priority);
handlerThread.start(); handlerThread.start();
return handlerThread; return handlerThread;
} }
private static class NumberedThreadFactory implements ThreadFactory { private static class NumberedThreadFactory implements ThreadFactory {
private final int priority;
private final String baseName; private final String baseName;
private final AtomicInteger counter; private final AtomicInteger counter;
NumberedThreadFactory(@NonNull String baseName) { NumberedThreadFactory(@NonNull String baseName, int priority) {
this.priority = priority;
this.baseName = baseName; this.baseName = baseName;
this.counter = new AtomicInteger(); this.counter = new AtomicInteger();
} }
@Override @Override
public Thread newThread(@NonNull Runnable r) { public Thread newThread(@NonNull Runnable r) {
return new Thread(r, baseName + "-" + counter.getAndIncrement()); return new Thread(r, baseName + "-" + counter.getAndIncrement()) {
@Override
public void run() {
Process.setThreadPriority(priority);
super.run();
}
};
} }
} }
} }

View File

@@ -14,6 +14,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
@@ -80,7 +81,7 @@ final class DeviceTransferClient implements Handler.Callback {
this.context = context; this.context = context;
this.clientTask = clientTask; this.clientTask = clientTask;
this.shutdownCallback = shutdownCallback; this.shutdownCallback = shutdownCallback;
this.commandAndControlThread = SignalExecutors.getAndStartHandlerThread("client-cnc"); this.commandAndControlThread = SignalExecutors.getAndStartHandlerThread("client-cnc", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD);
this.handler = new Handler(commandAndControlThread.getLooper(), this); this.handler = new Handler(commandAndControlThread.getLooper(), this);
this.autoRestart = () -> { this.autoRestart = () -> {
Log.i(TAG, "Restarting WiFi Direct since we haven't found anything yet and it could be us."); Log.i(TAG, "Restarting WiFi Direct since we haven't found anything yet and it could be us.");

View File

@@ -14,6 +14,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.signal.devicetransfer.SelfSignedIdentity.SelfSignedKeys; import org.signal.devicetransfer.SelfSignedIdentity.SelfSignedKeys;
@@ -70,7 +71,7 @@ final class DeviceTransferServer implements Handler.Callback {
this.context = context; this.context = context;
this.serverTask = serverTask; this.serverTask = serverTask;
this.shutdownCallback = shutdownCallback; this.shutdownCallback = shutdownCallback;
this.commandAndControlThread = SignalExecutors.getAndStartHandlerThread("server-cnc"); this.commandAndControlThread = SignalExecutors.getAndStartHandlerThread("server-cnc", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD);
this.handler = new Handler(commandAndControlThread.getLooper(), this); this.handler = new Handler(commandAndControlThread.getLooper(), this);
} }

View File

@@ -92,7 +92,7 @@ public final class WifiDirect {
WifiDirect(@NonNull Context context) { WifiDirect(@NonNull Context context) {
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.wifiDirectCallbacksHandler = SignalExecutors.getAndStartHandlerThread("wifi-direct-cb"); this.wifiDirectCallbacksHandler = SignalExecutors.getAndStartHandlerThread("wifi-direct-cb", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD);
} }
/** /**

View File

@@ -2,6 +2,7 @@ package org.signal.paging;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
@@ -23,7 +24,7 @@ class FixedSizePagingController<Key, Data> implements PagingController<Key> {
private static final String TAG = Log.tag(FixedSizePagingController.class); private static final String TAG = Log.tag(FixedSizePagingController.class);
private static final Executor FETCH_EXECUTOR = SignalExecutors.newCachedSingleThreadExecutor("signal-FixedSizePagingController"); private static final Executor FETCH_EXECUTOR = SignalExecutors.newCachedSingleThreadExecutor("signal-FixedSizePagingController", ThreadUtil.PRIORITY_UI_BLOCKING_THREAD);
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
private final PagedDataSource<Key, Data> dataSource; private final PagedDataSource<Key, Data> dataSource;