@@ -3,6 +3,7 @@ use std::{
33 ffi:: { c_char, c_void, CStr , CString } ,
44 fmt:: { self , Display , Formatter } ,
55 mem:: forget,
6+ panic:: { self , AssertUnwindSafe } ,
67 str:: Utf8Error ,
78 sync:: { Arc , Mutex , MutexGuard } ,
89} ;
@@ -64,6 +65,7 @@ enum FFIError {
6465 Utf8Error ,
6566 NullError ,
6667 InvalidJson ( String ) ,
68+ Panic ,
6769 PartialUpdate ( Vec < EvalWarning > ) ,
6870}
6971
@@ -78,6 +80,10 @@ impl Display for FFIError {
7880 "Engine state was updated but warnings were reported, this may result in some flags evaluating in unexpected ways, please report this: {:?}" ,
7981 messages
8082 ) ,
83+ FFIError :: Panic => write ! (
84+ f,
85+ "Engine panicked while processing the request. Please report this as a bug with the accompanying stack trace if available."
86+ ) ,
8187 }
8288 }
8389}
@@ -113,6 +119,16 @@ fn result_to_json_ptr<T: Serialize>(result: Result<Option<T>, FFIError>) -> *mut
113119 CString :: new ( json_string) . unwrap ( ) . into_raw ( )
114120}
115121
122+ fn guard_result < T , F > ( action : F ) -> Result < Option < T > , FFIError >
123+ where
124+ F : FnOnce ( ) -> Result < Option < T > , FFIError > ,
125+ {
126+ match panic:: catch_unwind ( AssertUnwindSafe ( action) ) {
127+ Ok ( result) => result,
128+ Err ( _) => Err ( FFIError :: Panic ) ,
129+ }
130+ }
131+
116132unsafe fn get_engine ( engine_ptr : * mut c_void ) -> Result < ManagedEngine , FFIError > {
117133 if engine_ptr. is_null ( ) {
118134 return Err ( FFIError :: NullError ) ;
@@ -178,7 +194,7 @@ pub unsafe extern "C" fn take_state(
178194 engine_ptr : * mut c_void ,
179195 json_ptr : * const c_char ,
180196) -> * const c_char {
181- let result: Result < Option < ( ) > , FFIError > = ( || {
197+ let result = guard_result :: < ( ) , _ > ( || {
182198 let guard = get_engine ( engine_ptr) ?;
183199 let mut engine = recover_lock ( & guard) ;
184200
@@ -189,7 +205,7 @@ pub unsafe extern "C" fn take_state(
189205 } else {
190206 Ok ( Some ( ( ) ) )
191207 }
192- } ) ( ) ;
208+ } ) ;
193209
194210 result_to_json_ptr ( result)
195211}
@@ -202,11 +218,11 @@ pub unsafe extern "C" fn take_state(
202218/// be called with a null pointer.
203219#[ no_mangle]
204220pub unsafe extern "C" fn get_state ( engine_ptr : * mut c_void ) -> * const c_char {
205- let result: Result < Option < ClientFeatures > , FFIError > = ( || {
221+ let result = guard_result :: < ClientFeatures , _ > ( || {
206222 let guard = get_engine ( engine_ptr) ?;
207223 let engine = recover_lock ( & guard) ;
208224 Ok ( Some ( engine. get_state ( ) ) )
209- } ) ( ) ;
225+ } ) ;
210226
211227 result_to_json_ptr ( result)
212228}
@@ -229,7 +245,7 @@ pub unsafe extern "C" fn check_enabled(
229245 context_ptr : * const c_char ,
230246 custom_strategy_results_ptr : * const c_char ,
231247) -> * const c_char {
232- let result: Result < Option < bool > , FFIError > = ( || {
248+ let result = guard_result :: < bool , _ > ( || {
233249 let guard = get_engine ( engine_ptr) ?;
234250 let engine = recover_lock ( & guard) ;
235251
@@ -241,7 +257,7 @@ pub unsafe extern "C" fn check_enabled(
241257 EnrichedContext :: from ( context, toggle_name. into ( ) , Some ( custom_strategy_results) ) ;
242258
243259 Ok ( engine. check_enabled ( & enriched_context) )
244- } ) ( ) ;
260+ } ) ;
245261
246262 result_to_json_ptr ( result)
247263}
@@ -264,7 +280,7 @@ pub unsafe extern "C" fn check_variant(
264280 context_ptr : * const c_char ,
265281 custom_strategy_results_ptr : * const c_char ,
266282) -> * const c_char {
267- let result: Result < Option < ExtendedVariantDef > , FFIError > = ( || {
283+ let result = guard_result :: < ExtendedVariantDef , _ > ( || {
268284 let guard = get_engine ( engine_ptr) ?;
269285 let engine = recover_lock ( & guard) ;
270286
@@ -278,7 +294,7 @@ pub unsafe extern "C" fn check_variant(
278294 let base_variant = engine. check_variant ( & enriched_context) ;
279295 let toggle_enabled = engine. check_enabled ( & enriched_context) . unwrap_or_default ( ) ;
280296 Ok ( base_variant. map ( |variant| variant. to_enriched_response ( toggle_enabled) ) )
281- } ) ( ) ;
297+ } ) ;
282298
283299 result_to_json_ptr ( result)
284300}
@@ -347,15 +363,15 @@ pub unsafe extern "C" fn count_toggle(
347363
348364 let enabled = enabled & 1 == 1 ;
349365
350- let result: Result < Option < ( ) > , FFIError > = ( || {
366+ let result = guard_result :: < ( ) , _ > ( || {
351367 let guard = get_engine ( engine_ptr) ?;
352368 let engine = recover_lock ( & guard) ;
353369
354370 let toggle_name = get_str ( toggle_name_ptr) ?;
355371
356372 engine. count_toggle ( toggle_name, enabled) ;
357373 Ok ( Some ( ( ) ) )
358- } ) ( ) ;
374+ } ) ;
359375
360376 result_to_json_ptr ( result)
361377}
@@ -378,7 +394,7 @@ pub unsafe extern "C" fn count_variant(
378394 toggle_name_ptr : * const c_char ,
379395 variant_name_ptr : * const c_char ,
380396) -> * const c_char {
381- let result: Result < Option < ( ) > , FFIError > = ( || {
397+ let result = guard_result :: < ( ) , _ > ( || {
382398 let guard = get_engine ( engine_ptr) ?;
383399 let engine = recover_lock ( & guard) ;
384400
@@ -387,7 +403,7 @@ pub unsafe extern "C" fn count_variant(
387403
388404 engine. count_variant ( toggle_name, variant_name) ;
389405 Ok ( Some ( ( ) ) )
390- } ) ( ) ;
406+ } ) ;
391407
392408 result_to_json_ptr ( result)
393409}
@@ -406,12 +422,12 @@ pub unsafe extern "C" fn count_variant(
406422/// `free_response` and passing in the pointer returned by this method. Failure to do so will result in a leak.
407423#[ no_mangle]
408424pub unsafe extern "C" fn get_metrics ( engine_ptr : * mut c_void ) -> * mut c_char {
409- let result: Result < Option < MetricBucket > , FFIError > = ( || {
425+ let result = guard_result :: < MetricBucket , _ > ( || {
410426 let guard = get_engine ( engine_ptr) ?;
411427 let mut engine = recover_lock ( & guard) ;
412428
413429 Ok ( engine. get_metrics ( Utc :: now ( ) ) )
414- } ) ( ) ;
430+ } ) ;
415431
416432 result_to_json_ptr ( result)
417433}
@@ -428,14 +444,14 @@ pub unsafe extern "C" fn should_emit_impression_event(
428444 engine_ptr : * mut c_void ,
429445 toggle_name_ptr : * const c_char ,
430446) -> * mut c_char {
431- let result: Result < Option < bool > , FFIError > = ( || {
447+ let result = guard_result :: < bool , _ > ( || {
432448 let guard = get_engine ( engine_ptr) ?;
433449 let engine = recover_lock ( & guard) ;
434450
435451 let toggle_name = get_str ( toggle_name_ptr) ?;
436452
437453 Ok ( Some ( engine. should_emit_impression_event ( toggle_name) ) )
438- } ) ( ) ;
454+ } ) ;
439455
440456 result_to_json_ptr ( result)
441457}
@@ -453,12 +469,12 @@ pub unsafe extern "C" fn should_emit_impression_event(
453469/// `free_response` and passing in the pointer returned by this method. Failure to do so will result in a leak.
454470#[ no_mangle]
455471pub unsafe extern "C" fn list_known_toggles ( engine_ptr : * mut c_void ) -> * mut c_char {
456- let result: Result < Option < Vec < ToggleDefinition > > , FFIError > = ( || {
472+ let result = guard_result :: < Vec < ToggleDefinition > , _ > ( || {
457473 let guard = get_engine ( engine_ptr) ?;
458474 let engine = recover_lock ( & guard) ;
459475
460476 Ok ( Some ( engine. list_known_toggles ( ) ) )
461- } ) ( ) ;
477+ } ) ;
462478
463479 result_to_json_ptr ( result)
464480}
0 commit comments