Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove the use of executor in ThreadUtils #1520

Merged
merged 1 commit into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion app/src/main/java/org/thoughtcrime/securesms/AppContext.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.thoughtcrime.securesms

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import nl.komponents.kovenant.Kovenant
import nl.komponents.kovenant.jvm.asDispatcher
import org.session.libsignal.utilities.Log
Expand All @@ -11,7 +13,7 @@ object AppContext {
fun configureKovenant() {
Kovenant.context {
callbackContext.dispatcher = Executors.newSingleThreadExecutor().asDispatcher()
workerContext.dispatcher = ThreadUtils.executorPool.asDispatcher()
workerContext.dispatcher = Dispatchers.IO.asExecutor().asDispatcher()
multipleCompletion = { v1, v2 ->
Log.d("Loki", "Promise resolved more than once (first with $v1, then with $v2); ignoring $v2.")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,51 +1,30 @@
package org.session.libsignal.utilities

import android.os.Process
import kotlinx.coroutines.Dispatchers
import java.util.concurrent.ExecutorService
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.SynchronousQueue
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit
import kotlin.coroutines.EmptyCoroutineContext

object ThreadUtils {

const val TAG = "ThreadUtils"

const val PRIORITY_IMPORTANT_BACKGROUND_THREAD = Process.THREAD_PRIORITY_DEFAULT + Process.THREAD_PRIORITY_LESS_FAVORABLE

// Paraphrased from: https://www.baeldung.com/kotlin/create-thread-pool
// "A cached thread pool such as one created via:
// `val executorPool: ExecutorService = Executors.newCachedThreadPool()`
// will utilize resources according to the requirements of submitted tasks. It will try to reuse
// existing threads for submitted tasks but will create as many threads as it needs if new tasks
// keep pouring in (with a memory usage of at least 1MB per created thread). These threads will
// live for up to 60 seconds of idle time before terminating by default. As such, it presents a
// very sharp tool that doesn't include any backpressure mechanism - and a sudden peak in load
// can bring the system down with an OutOfMemory error. We can achieve a similar effect but with
// better control by creating a ThreadPoolExecutor manually."

private val corePoolSize = Runtime.getRuntime().availableProcessors() // Default thread pool size is our CPU core count
private val maxPoolSize = corePoolSize * 4 // Allow a maximum pool size of up to 4 threads per core
private val keepAliveTimeSecs = 100L // How long to keep idle threads in the pool before they are terminated
private val workQueue = SynchronousQueue<Runnable>()
val executorPool: ExecutorService = ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTimeSecs, TimeUnit.SECONDS, workQueue)

// Note: To see how many threads are running in our app at any given time we can use:
// val threadCount = getAllStackTraces().size

@JvmStatic
fun queue(target: Runnable) {
executorPool.execute {
try {
target.run()
} catch (e: Exception) {
Log.e(TAG, e)
}
}
queue(target::run)
}

fun queue(target: () -> Unit) {
executorPool.execute {
Dispatchers.IO.dispatch(EmptyCoroutineContext) {
try {
target()
} catch (e: Exception) {
Expand Down