Skip to content

Commit a3f3495

Browse files
committed
fix(android): size proxy runtime workers by device capacity
Android startup built the long-lived proxy Tokio runtime with a fixed four-worker pool on every device. That is unnecessarily large on low-core phones, tablets in power-saving mode, and embedded Android targets, while still being capped enough that larger devices do not need more than a small bounded pool for this local proxy workload. Add a dedicated worker-count helper for the Android proxy runtime. It reads the platform's available parallelism when the runtime starts, falls back to the minimum proxy-safe worker count when the value is unavailable, and clamps the result between two and four workers. The lower bound keeps accept, tunnel, stats, and shutdown work from competing on a single worker; the upper bound preserves the previous maximum and avoids creating a wider scheduler on constrained mobile CPUs. Use the computed value when constructing the runtime and log the selected worker count once during startup. One-shot JNI runtimes used for probes and certificate operations remain current-thread runtimes, and no Kotlin method signatures, config fields, proxy routing behavior, or Android lifecycle contracts change.
1 parent 923e940 commit a3f3495

1 file changed

Lines changed: 12 additions & 1 deletion

File tree

src/android_jni.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ extern "C" {
7070

7171
const ANDROID_LOG_INFO: i32 = 4;
7272
const LOG_RING_CAP: usize = 500;
73+
const ANDROID_RUNTIME_MIN_WORKERS: usize = 2;
74+
const ANDROID_RUNTIME_MAX_WORKERS: usize = 4;
7375

7476
fn log_ring() -> &'static Mutex<VecDeque<String>> {
7577
static RING: OnceLock<Mutex<VecDeque<String>>> = OnceLock::new();
@@ -158,6 +160,13 @@ fn safe<F: FnOnce() -> R + std::panic::UnwindSafe, R>(default: R, f: F) -> R {
158160
std::panic::catch_unwind(f).unwrap_or(default)
159161
}
160162

163+
fn android_runtime_worker_threads() -> usize {
164+
std::thread::available_parallelism()
165+
.map(|n| n.get())
166+
.unwrap_or(ANDROID_RUNTIME_MIN_WORKERS)
167+
.clamp(ANDROID_RUNTIME_MIN_WORKERS, ANDROID_RUNTIME_MAX_WORKERS)
168+
}
169+
161170
/// Build a throwaway tokio runtime for one-shot blocking calls from JNI.
162171
/// Small, single-worker — sufficient for probes and cert ops.
163172
fn one_shot_runtime() -> Option<Runtime> {
@@ -210,8 +219,9 @@ pub extern "system" fn Java_com_therealaleph_mhrv_Native_startProxy(
210219

211220
// Try to build the runtime first — if allocation fails we want to
212221
// know before spinning up anything stateful.
222+
let worker_threads = android_runtime_worker_threads();
213223
let rt = match tokio::runtime::Builder::new_multi_thread()
214-
.worker_threads(4)
224+
.worker_threads(worker_threads)
215225
.enable_all()
216226
.thread_name("mhrv-worker")
217227
.build()
@@ -222,6 +232,7 @@ pub extern "system" fn Java_com_therealaleph_mhrv_Native_startProxy(
222232
return 0i64;
223233
}
224234
};
235+
tracing::info!("android: tokio runtime worker_threads={}", worker_threads);
225236

226237
let base = crate::data_dir::data_dir();
227238
let mitm = match MitmCertManager::new_in(&base) {

0 commit comments

Comments
 (0)