diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/InMemoryLogger.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/InMemoryLogger.kt index 25a7ebd06b..152d8355a5 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/InMemoryLogger.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/InMemoryLogger.kt @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.testing +import org.signal.core.util.ThreadUtil import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.logging.Log import java.util.concurrent.CountDownLatch @@ -13,7 +14,7 @@ typealias LogPredicate = (Entry) -> Boolean */ 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() private val logEntries = mutableListOf() diff --git a/app/src/main/java/org/signal/glide/common/executor/FrameDecoderExecutor.java b/app/src/main/java/org/signal/glide/common/executor/FrameDecoderExecutor.java index 6a7aae0892..701f39d274 100644 --- a/app/src/main/java/org/signal/glide/common/executor/FrameDecoderExecutor.java +++ b/app/src/main/java/org/signal/glide/common/executor/FrameDecoderExecutor.java @@ -8,6 +8,8 @@ package org.signal.glide.common.executor; import android.os.HandlerThread; import android.os.Looper; +import org.signal.core.util.ThreadUtil; + import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; @@ -39,7 +41,7 @@ public class FrameDecoderExecutor { public Looper getLooper(int taskId) { int idx = taskId % sPoolNumber; if (idx >= mHandlerThreadGroup.size()) { - HandlerThread handlerThread = new HandlerThread("FrameDecoderExecutor-" + idx); + HandlerThread handlerThread = new HandlerThread("FrameDecoderExecutor-" + idx, ThreadUtil.PRIORITY_BACKGROUND_THREAD); handlerThread.start(); mHandlerThreadGroup.add(handlerThread); diff --git a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioCodec.java b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioCodec.java index f220bc0c3f..5d54a8fa29 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioCodec.java +++ b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioCodec.java @@ -7,6 +7,7 @@ import android.media.MediaCodecInfo; import android.media.MediaFormat; import android.media.MediaRecorder; import android.os.ParcelFileDescriptor; +import android.os.Process; import org.signal.core.util.StreamUtil; import org.signal.core.util.logging.Log; @@ -65,6 +66,7 @@ public class AudioCodec implements Recorder { new Thread(new Runnable() { @Override public void run() { + Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO); MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); byte[] audioRecordData = new byte[bufferSize]; ByteBuffer[] codecInputBuffers = mediaCodec.getInputBuffers(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java index c4c3ca8132..568828379f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java @@ -9,6 +9,7 @@ import android.os.ParcelFileDescriptor; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.signal.core.util.ThreadUtil; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; 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 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 AudioRecordingHandler uiHandler; diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java index 9df96ec8ab..b8bf9e6333 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java @@ -9,6 +9,7 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; 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.SignalExecutors; import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations; @@ -137,7 +138,7 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr signalWebSocket, Optional.of(new SecurityEventListener(context)), 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), FeatureFlags.okHttpAutomaticRetry()); } @@ -373,7 +374,7 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr @Override public @NonNull DeadlockDetector provideDeadlockDetector() { - HandlerThread handlerThread = new HandlerThread("signal-DeadlockDetector"); + HandlerThread handlerThread = new HandlerThread("signal-DeadlockDetector", ThreadUtil.PRIORITY_BACKGROUND_THREAD); handlerThread.start(); return new DeadlockDetector(new Handler(handlerThread.getLooper()), TimeUnit.SECONDS.toMillis(5)); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/emoji/JumboEmoji.kt b/app/src/main/java/org/thoughtcrime/securesms/emoji/JumboEmoji.kt index 715bfd91a3..4b25ba8bce 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/emoji/JumboEmoji.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/emoji/JumboEmoji.kt @@ -27,7 +27,7 @@ private val TAG = Log.tag(JumboEmoji::class.java) */ 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 diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/InAppScheduler.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/InAppScheduler.java index a6bdeb2105..cb4afe5a50 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/InAppScheduler.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/InAppScheduler.java @@ -7,6 +7,7 @@ import androidx.annotation.NonNull; import com.annimon.stream.Stream; +import org.signal.core.util.ThreadUtil; import org.signal.core.util.logging.Log; import java.util.List; @@ -30,7 +31,7 @@ class InAppScheduler implements Scheduler { private final Handler handler; InAppScheduler(@NonNull JobManager jobManager) { - HandlerThread handlerThread = new HandlerThread("InAppScheduler"); + HandlerThread handlerThread = new HandlerThread("InAppScheduler", ThreadUtil.PRIORITY_BACKGROUND_THREAD); handlerThread.start(); this.jobManager = jobManager; diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DefaultExecutorFactory.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DefaultExecutorFactory.java index a9d4591009..871df359b5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DefaultExecutorFactory.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DefaultExecutorFactory.java @@ -1,7 +1,10 @@ package org.thoughtcrime.securesms.jobmanager.impl; +import android.os.Process; + import androidx.annotation.NonNull; +import org.signal.core.util.ThreadUtil; import org.thoughtcrime.securesms.jobmanager.ExecutorFactory; import java.util.concurrent.ExecutorService; @@ -10,6 +13,11 @@ import java.util.concurrent.Executors; public class DefaultExecutorFactory implements ExecutorFactory { @Override 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(); + } + }); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueStore.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueStore.java index 036c2e6256..8bc2a7f35e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueStore.java @@ -5,6 +5,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import org.signal.core.util.ThreadUtil; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler; @@ -35,7 +36,7 @@ public final class KeyValueStore implements KeyValueReader { private KeyValueDataSet dataSet; public KeyValueStore(@NonNull KeyValuePersistentStorage storage) { - this.executor = SignalExecutors.newCachedSingleThreadExecutor("signal-KeyValueStore"); + this.executor = SignalExecutors.newCachedSingleThreadExecutor("signal-KeyValueStore", ThreadUtil.PRIORITY_BACKGROUND_THREAD); this.storage = storage; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaUploadRepository.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaUploadRepository.java index 730a322d29..7bb5a47fde 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaUploadRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaUploadRepository.java @@ -8,6 +8,7 @@ import androidx.annotation.WorkerThread; import com.annimon.stream.Stream; +import org.signal.core.util.ThreadUtil; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.attachments.Attachment; @@ -56,7 +57,7 @@ public class MediaUploadRepository { public MediaUploadRepository(@NonNull Context context) { this.context = context; 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) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java index 45bca92165..f6846839ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java @@ -9,6 +9,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.signal.core.util.ExceptionUtil; +import org.signal.core.util.ThreadUtil; import org.signal.core.util.concurrent.SignalExecutors; import org.thoughtcrime.securesms.notifications.v2.DefaultMessageNotifier; import org.thoughtcrime.securesms.notifications.v2.ConversationId; @@ -28,7 +29,7 @@ public class OptimizedMessageNotifier implements MessageNotifier { @MainThread 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); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java b/app/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java index 152ad927e8..2de016453e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java @@ -36,6 +36,7 @@ import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import org.signal.core.util.StreamUtil; +import org.signal.core.util.ThreadUtil; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.BuildConfig; @@ -201,7 +202,7 @@ public final class PartProvider extends BaseContentProvider { @RequiresApi(26) private ParcelFileDescriptor getParcelStreamProxyForAttachment(AttachmentId attachmentId) throws IOException { 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()); ParcelFileDescriptor parcelFileDescriptor = storageManager.openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_ONLY, diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipientCache.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipientCache.java index 04e41455b7..b3cb736c0c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipientCache.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipientCache.java @@ -49,7 +49,7 @@ public final class LiveRecipientCache { private final AtomicBoolean warmedUp; 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 diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/TimedEventManager.java b/app/src/main/java/org/thoughtcrime/securesms/service/TimedEventManager.java index 6bc9f1011e..4a5c145288 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/TimedEventManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/TimedEventManager.java @@ -16,6 +16,7 @@ import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import org.signal.core.util.PendingIntentFlags; +import org.signal.core.util.ThreadUtil; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.util.ServiceUtil; @@ -30,7 +31,7 @@ public abstract class TimedEventManager { private final Handler handler; public TimedEventManager(@NonNull Application application, @NonNull String threadName) { - HandlerThread handlerThread = new HandlerThread(threadName); + HandlerThread handlerThread = new HandlerThread(threadName, ThreadUtil.PRIORITY_BACKGROUND_THREAD); handlerThread.start(); this.application = application; diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/LocalMetrics.kt b/app/src/main/java/org/thoughtcrime/securesms/util/LocalMetrics.kt index 2ff12ead24..37c97fd1fa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/LocalMetrics.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/LocalMetrics.kt @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.util +import org.signal.core.util.ThreadUtil import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.database.LocalMetricsDatabase @@ -29,7 +30,7 @@ object LocalMetrics { private val eventsById: MutableMap = LRUCache(200) private val lastSplitTimeById: MutableMap = 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()) } @JvmStatic diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt index d35510f0c1..0544f69327 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt @@ -8,6 +8,7 @@ import android.media.AudioManager import android.media.SoundPool import android.net.Uri import android.os.Build +import org.signal.core.util.ThreadUtil import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.logging.Log 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?) { - 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 var state: State = State.UNINITIALIZED diff --git a/core-util/src/main/java/org/signal/core/util/ThreadUtil.java b/core-util/src/main/java/org/signal/core/util/ThreadUtil.java index dbd4df62f3..439c6ba938 100644 --- a/core-util/src/main/java/org/signal/core/util/ThreadUtil.java +++ b/core-util/src/main/java/org/signal/core/util/ThreadUtil.java @@ -2,6 +2,7 @@ package org.signal.core.util; import android.os.Handler; import android.os.Looper; +import android.os.Process; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -18,6 +19,20 @@ import java.util.concurrent.ExecutorService; */ 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; @VisibleForTesting diff --git a/core-util/src/main/java/org/signal/core/util/concurrent/SignalExecutors.java b/core-util/src/main/java/org/signal/core/util/concurrent/SignalExecutors.java index 4bc6b926c6..fbed003f19 100644 --- a/core-util/src/main/java/org/signal/core/util/concurrent/SignalExecutors.java +++ b/core-util/src/main/java/org/signal/core/util/concurrent/SignalExecutors.java @@ -1,6 +1,7 @@ package org.signal.core.util.concurrent; import android.os.HandlerThread; +import android.os.Process; import androidx.annotation.NonNull; @@ -17,15 +18,20 @@ import java.util.concurrent.atomic.AtomicInteger; public final class SignalExecutors { - public static final ExecutorService UNBOUNDED = ThreadUtil.trace(Executors.newCachedThreadPool(new NumberedThreadFactory("signal-unbounded"))); - public static final ExecutorService BOUNDED = ThreadUtil.trace(Executors.newFixedThreadPool(4, new NumberedThreadFactory("signal-bounded"))); - public static final ExecutorService SERIAL = ThreadUtil.trace(Executors.newSingleThreadExecutor(new NumberedThreadFactory("signal-serial"))); - public static final ExecutorService BOUNDED_IO = ThreadUtil.trace(newCachedBoundedExecutor("signal-io-bounded", 1, 32, 30)); + 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", ThreadUtil.PRIORITY_BACKGROUND_THREAD))); + 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", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD, 1, 32, 30)); private SignalExecutors() {} - public static ExecutorService newCachedSingleThreadExecutor(final String name) { - ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 15, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), r -> new Thread(r, 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) { + @Override public void run() { + Process.setThreadPriority(priority); + super.run(); + } + }); executor.allowCoreThreadTimeOut(true); 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 * 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, maxThreads, timeoutSeconds, @@ -55,7 +61,7 @@ public final class SignalExecutors { return false; } } - }, new NumberedThreadFactory(name)); + }, new NumberedThreadFactory(name, priority)); threadPool.setRejectedExecutionHandler((runnable, executor) -> { try { @@ -73,28 +79,36 @@ public final class SignalExecutors { * which processor work in FIFO order. */ 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) { - HandlerThread handlerThread = new HandlerThread(name); + public static HandlerThread getAndStartHandlerThread(@NonNull String name, int priority) { + HandlerThread handlerThread = new HandlerThread(name, priority); handlerThread.start(); return handlerThread; } private static class NumberedThreadFactory implements ThreadFactory { + private final int priority; private final String baseName; private final AtomicInteger counter; - NumberedThreadFactory(@NonNull String baseName) { + NumberedThreadFactory(@NonNull String baseName, int priority) { + this.priority = priority; this.baseName = baseName; this.counter = new AtomicInteger(); } @Override 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(); + } + }; } } } diff --git a/device-transfer/lib/src/main/java/org/signal/devicetransfer/DeviceTransferClient.java b/device-transfer/lib/src/main/java/org/signal/devicetransfer/DeviceTransferClient.java index 5d9079a638..80fd4fda9f 100644 --- a/device-transfer/lib/src/main/java/org/signal/devicetransfer/DeviceTransferClient.java +++ b/device-transfer/lib/src/main/java/org/signal/devicetransfer/DeviceTransferClient.java @@ -14,6 +14,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.greenrobot.eventbus.EventBus; +import org.signal.core.util.ThreadUtil; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; @@ -80,7 +81,7 @@ final class DeviceTransferClient implements Handler.Callback { this.context = context; this.clientTask = clientTask; 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.autoRestart = () -> { Log.i(TAG, "Restarting WiFi Direct since we haven't found anything yet and it could be us."); diff --git a/device-transfer/lib/src/main/java/org/signal/devicetransfer/DeviceTransferServer.java b/device-transfer/lib/src/main/java/org/signal/devicetransfer/DeviceTransferServer.java index 82d561e6bd..933e6dc872 100644 --- a/device-transfer/lib/src/main/java/org/signal/devicetransfer/DeviceTransferServer.java +++ b/device-transfer/lib/src/main/java/org/signal/devicetransfer/DeviceTransferServer.java @@ -14,6 +14,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.greenrobot.eventbus.EventBus; +import org.signal.core.util.ThreadUtil; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; import org.signal.devicetransfer.SelfSignedIdentity.SelfSignedKeys; @@ -70,7 +71,7 @@ final class DeviceTransferServer implements Handler.Callback { this.context = context; this.serverTask = serverTask; 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); } diff --git a/device-transfer/lib/src/main/java/org/signal/devicetransfer/WifiDirect.java b/device-transfer/lib/src/main/java/org/signal/devicetransfer/WifiDirect.java index a5c2acfd41..9226c63e6d 100644 --- a/device-transfer/lib/src/main/java/org/signal/devicetransfer/WifiDirect.java +++ b/device-transfer/lib/src/main/java/org/signal/devicetransfer/WifiDirect.java @@ -92,7 +92,7 @@ public final class WifiDirect { WifiDirect(@NonNull Context context) { this.context = context.getApplicationContext(); - this.wifiDirectCallbacksHandler = SignalExecutors.getAndStartHandlerThread("wifi-direct-cb"); + this.wifiDirectCallbacksHandler = SignalExecutors.getAndStartHandlerThread("wifi-direct-cb", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD); } /** diff --git a/paging/lib/src/main/java/org/signal/paging/FixedSizePagingController.java b/paging/lib/src/main/java/org/signal/paging/FixedSizePagingController.java index 8bbe842f8a..9c4ac60c94 100644 --- a/paging/lib/src/main/java/org/signal/paging/FixedSizePagingController.java +++ b/paging/lib/src/main/java/org/signal/paging/FixedSizePagingController.java @@ -2,6 +2,7 @@ package org.signal.paging; import androidx.annotation.NonNull; +import org.signal.core.util.ThreadUtil; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; @@ -23,7 +24,7 @@ class FixedSizePagingController implements PagingController { 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 final PagedDataSource dataSource;