Skip to content

Commit 6bbcb2f

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 40b5386 commit 6bbcb2f

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();
@@ -149,6 +151,13 @@ fn safe<F: FnOnce() -> R + std::panic::UnwindSafe, R>(default: R, f: F) -> R {
149151
std::panic::catch_unwind(f).unwrap_or(default)
150152
}
151153

154+
fn android_runtime_worker_threads() -> usize {
155+
std::thread::available_parallelism()
156+
.map(|n| n.get())
157+
.unwrap_or(ANDROID_RUNTIME_MIN_WORKERS)
158+
.clamp(ANDROID_RUNTIME_MIN_WORKERS, ANDROID_RUNTIME_MAX_WORKERS)
159+
}
160+
152161
/// Build a throwaway tokio runtime for one-shot blocking calls from JNI.
153162
/// Small, single-worker — sufficient for probes and cert ops.
154163
fn one_shot_runtime() -> Option<Runtime> {
@@ -201,8 +210,9 @@ pub extern "system" fn Java_com_therealaleph_mhrv_Native_startProxy(
201210

202211
// Try to build the runtime first — if allocation fails we want to
203212
// know before spinning up anything stateful.
213+
let worker_threads = android_runtime_worker_threads();
204214
let rt = match tokio::runtime::Builder::new_multi_thread()
205-
.worker_threads(4)
215+
.worker_threads(worker_threads)
206216
.enable_all()
207217
.thread_name("mhrv-worker")
208218
.build()
@@ -213,6 +223,7 @@ pub extern "system" fn Java_com_therealaleph_mhrv_Native_startProxy(
213223
return 0i64;
214224
}
215225
};
226+
tracing::info!("android: tokio runtime worker_threads={}", worker_threads);
216227

217228
let base = crate::data_dir::data_dir();
218229
let mitm = match MitmCertManager::new_in(&base) {

0 commit comments

Comments
 (0)