@@ -10,7 +10,7 @@ use futures_util::FutureExt;
1010use jni:: EnvUnowned ;
1111use jni:: errors:: ThrowRuntimeExAndDefault ;
1212use jni:: objects:: { JByteArray , JClass , JObject } ;
13- use jni:: sys:: { jbyteArray, jint } ;
13+ use jni:: sys:: jbyteArray;
1414
1515use crate :: daemon_env:: with_cached_daemon_env;
1616use crate :: streaming_closures:: {
@@ -24,6 +24,13 @@ use crate::streaming_closures::{
2424mod streaming_buffer;
2525use streaming_buffer:: { PullPushBuffers , mark_streaming_buffer_reusable} ;
2626
27+ // Runtime / streaming configuration JNI hooks (seeded from
28+ // `VesperaBridge.init()` before the first dispatch) live in a sidecar
29+ // module so this file stays focused on the per-request dispatch symbols.
30+ #[ path = "jni_impl_config.rs" ]
31+ mod config;
32+ pub use config:: { runtime_worker_threads, streaming_chunk_size} ;
33+
2734/// Multi-threaded Tokio runtime shared across all JNI calls.
2835///
2936/// Worker thread count defaults to Tokio's heuristic (number of
@@ -41,11 +48,6 @@ pub static RUNTIME: LazyLock<tokio::runtime::Runtime> = LazyLock::new(|| {
4148 . expect ( "failed to create Tokio runtime" )
4249} ) ;
4350
44- const MIN_RUNTIME_WORKERS : usize = 1 ;
45- const MAX_RUNTIME_WORKERS : usize = 1024 ;
46-
47- static RUNTIME_WORKER_THREADS : std:: sync:: OnceLock < Option < usize > > = std:: sync:: OnceLock :: new ( ) ;
48-
4951/// Cap on each per-thread sync runtime's blocking pool.
5052///
5153/// [`block_on_sync_runtime`] builds ONE current-thread runtime per calling
@@ -200,106 +202,6 @@ use support::{
200202 setup_stream_with_header, throw_streaming_abort,
201203} ;
202204
203- /// Worker thread count for the shared [`RUNTIME`], resolved once
204- /// (first hit wins, then fixed for the process lifetime):
205- ///
206- /// 1. [`set_runtime_worker_threads`] called before the runtime is
207- /// first used (the `configureRuntime0` JNI hook from
208- /// `VesperaBridge.init()` lands here)
209- /// 2. `VESPERA_RUNTIME_WORKERS` environment variable
210- /// 3. `None` — Tokio's default (number of logical CPUs)
211- ///
212- /// Values are clamped to `[1, 1024]`.
213- #[ must_use]
214- pub fn runtime_worker_threads ( ) -> Option < usize > {
215- * RUNTIME_WORKER_THREADS . get_or_init ( || {
216- std:: env:: var ( "VESPERA_RUNTIME_WORKERS" )
217- . ok ( )
218- . and_then ( |raw| raw. trim ( ) . parse :: < usize > ( ) . ok ( ) )
219- . map ( |v| v. clamp ( MIN_RUNTIME_WORKERS , MAX_RUNTIME_WORKERS ) )
220- } )
221- }
222-
223- /// Override the shared runtime's worker thread count **before the
224- /// first dispatch**. Returns `false` when the value was already
225- /// fixed. Clamped to `[1, 1024]`.
226- pub fn set_runtime_worker_threads ( workers : usize ) -> bool {
227- RUNTIME_WORKER_THREADS
228- . set ( Some (
229- workers. clamp ( MIN_RUNTIME_WORKERS , MAX_RUNTIME_WORKERS ) ,
230- ) )
231- . is_ok ( )
232- }
233-
234- /// `com.devfive.vespera.bridge.VesperaBridge.configureRuntime0(int) -> void`
235- ///
236- /// Seeds the shared Tokio runtime's worker thread count **before
237- /// the first dispatch**. Values `<= 0` leave the setting
238- /// untouched (env var / Tokio default applies). Calls after the
239- /// configuration is fixed are silently ignored.
240- #[ unsafe( no_mangle) ]
241- pub extern "system" fn Java_com_devfive_vespera_bridge_VesperaBridge_configureRuntime0 < ' local > (
242- _unowned_env : EnvUnowned < ' local > ,
243- _class : JClass < ' local > ,
244- worker_threads : jint ,
245- ) {
246- // Defensive `catch_unwind`: this body cannot panic today, but it is
247- // an `extern "system"` JNI symbol, so guard it for consistency with
248- // the dispatch symbols — an unwind must never cross the FFI boundary.
249- let _ = std:: panic:: catch_unwind ( || {
250- if let Ok ( workers) = usize:: try_from ( worker_threads)
251- && workers > 0
252- {
253- let _ = set_runtime_worker_threads ( workers) ;
254- }
255- } ) ;
256- }
257-
258- /// Per-chunk buffer size for streaming dispatches.
259- ///
260- /// Resolved once per process by
261- /// [`vespera_inprocess::streaming_chunk_bytes`] (default 256 KiB;
262- /// override via the `VESPERA_STREAMING_CHUNK_BYTES` env var or the
263- /// `configureStreaming0` JNI setter called from
264- /// `VesperaBridge.init()`). Large enough to amortise JNI call
265- /// overhead, small enough to keep memory bounded for multi-GB
266- /// streams. Subsequent calls are a single atomic load.
267- pub fn streaming_chunk_size ( ) -> usize {
268- vespera_inprocess:: streaming_chunk_bytes ( )
269- }
270-
271- /// `com.devfive.vespera.bridge.VesperaBridge.configureStreaming0(int, int) -> void`
272- ///
273- /// Seeds the process-wide streaming configuration **before the
274- /// first dispatch**. Values `<= 0` leave the corresponding
275- /// setting untouched (env var / default applies). Calls after
276- /// the configuration is fixed (first dispatch already ran, or a
277- /// previous call set it) are silently ignored — the JNI side has
278- /// no use for the failure signal beyond logging, which Java owns.
279- #[ unsafe( no_mangle) ]
280- pub extern "system" fn Java_com_devfive_vespera_bridge_VesperaBridge_configureStreaming0 < ' local > (
281- _unowned_env : EnvUnowned < ' local > ,
282- _class : JClass < ' local > ,
283- chunk_bytes : jint ,
284- channel_capacity : jint ,
285- ) {
286- // Defensive `catch_unwind` — see `configureRuntime0`: keep every JNI
287- // `extern "system"` symbol panic-safe even though this body cannot
288- // panic with the current setters.
289- let _ = std:: panic:: catch_unwind ( || {
290- if let Ok ( bytes) = usize:: try_from ( chunk_bytes)
291- && bytes > 0
292- {
293- let _ = vespera_inprocess:: set_streaming_chunk_bytes ( bytes) ;
294- }
295- if let Ok ( slots) = usize:: try_from ( channel_capacity)
296- && slots > 0
297- {
298- let _ = vespera_inprocess:: set_streaming_channel_capacity ( slots) ;
299- }
300- } ) ;
301- }
302-
303205/// `com.devfive.vespera.bridge.VesperaBridge.dispatchBytes(byte[]) -> byte[]`
304206///
305207/// **Synchronous** binary wire-format JNI entry point. Blocks the
0 commit comments