@@ -9,14 +9,14 @@ use crate::query_result::{CassNode, CassResult};
9
9
use crate :: types:: * ;
10
10
use crate :: uuid:: CassUuid ;
11
11
use futures:: future;
12
- use scylla:: response:: Coordinator ;
13
12
use std:: future:: Future ;
14
13
use std:: mem;
15
14
use std:: os:: raw:: c_void;
16
- use std:: sync:: { Arc , Condvar , Mutex } ;
15
+ use std:: sync:: { Arc , Condvar , Mutex , OnceLock } ;
17
16
use tokio:: task:: JoinHandle ;
18
17
use tokio:: time:: Duration ;
19
18
19
+ #[ derive( Debug ) ]
20
20
pub enum CassResultValue {
21
21
Empty ,
22
22
QueryResult ( Arc < CassResult > ) ,
@@ -51,14 +51,14 @@ impl BoundCallback {
51
51
52
52
#[ derive( Default ) ]
53
53
struct CassFutureState {
54
- value : Option < CassFutureResult > ,
55
54
err_string : Option < String > ,
56
55
callback : Option < BoundCallback > ,
57
56
join_handle : Option < JoinHandle < ( ) > > ,
58
57
}
59
58
60
59
pub struct CassFuture {
61
60
state : Mutex < CassFutureState > ,
61
+ result : OnceLock < CassFutureResult > ,
62
62
wait_for_value : Condvar ,
63
63
}
64
64
@@ -88,14 +88,18 @@ impl CassFuture {
88
88
) -> Arc < CassFuture > {
89
89
let cass_fut = Arc :: new ( CassFuture {
90
90
state : Mutex :: new ( Default :: default ( ) ) ,
91
+ result : OnceLock :: new ( ) ,
91
92
wait_for_value : Condvar :: new ( ) ,
92
93
} ) ;
93
94
let cass_fut_clone = Arc :: clone ( & cass_fut) ;
94
95
let join_handle = RUNTIME . spawn ( async move {
95
96
let r = fut. await ;
96
97
let maybe_cb = {
97
98
let mut guard = cass_fut_clone. state . lock ( ) . unwrap ( ) ;
98
- guard. value = Some ( r) ;
99
+ cass_fut_clone
100
+ . result
101
+ . set ( r)
102
+ . expect ( "Tried to resolve future result twice!" ) ;
99
103
// Take the callback and call it after releasing the lock
100
104
guard. callback . take ( )
101
105
} ;
@@ -116,16 +120,17 @@ impl CassFuture {
116
120
117
121
pub fn new_ready ( r : CassFutureResult ) -> Arc < Self > {
118
122
Arc :: new ( CassFuture {
119
- state : Mutex :: new ( CassFutureState {
120
- value : Some ( r) ,
121
- ..Default :: default ( )
122
- } ) ,
123
+ state : Mutex :: new ( CassFutureState :: default ( ) ) ,
124
+ result : OnceLock :: from ( r) ,
123
125
wait_for_value : Condvar :: new ( ) ,
124
126
} )
125
127
}
126
128
127
- pub fn with_waited_result < T > ( & self , f : impl FnOnce ( & mut CassFutureResult ) -> T ) -> T {
128
- self . with_waited_state ( |s| f ( s. value . as_mut ( ) . unwrap ( ) ) )
129
+ pub fn with_waited_result < ' s , T > ( & ' s self , f : impl FnOnce ( & ' s CassFutureResult ) -> T ) -> T
130
+ where
131
+ T : ' s ,
132
+ {
133
+ self . with_waited_state ( |_| f ( self . result . get ( ) . unwrap ( ) ) )
129
134
}
130
135
131
136
/// Awaits the future until completion.
@@ -154,7 +159,7 @@ impl CassFuture {
154
159
guard = self
155
160
. wait_for_value
156
161
. wait_while ( guard, |state| {
157
- state . value . is_none ( ) && state. join_handle . is_none ( )
162
+ self . result . get ( ) . is_none ( ) && state. join_handle . is_none ( )
158
163
} )
159
164
// unwrap: Error appears only when mutex is poisoned.
160
165
. unwrap ( ) ;
@@ -172,10 +177,10 @@ impl CassFuture {
172
177
173
178
fn with_waited_result_timed < T > (
174
179
& self ,
175
- f : impl FnOnce ( & mut CassFutureResult ) -> T ,
180
+ f : impl FnOnce ( & CassFutureResult ) -> T ,
176
181
timeout_duration : Duration ,
177
182
) -> Result < T , FutureError > {
178
- self . with_waited_state_timed ( |s | f ( s . value . as_mut ( ) . unwrap ( ) ) , timeout_duration)
183
+ self . with_waited_state_timed ( |_ | f ( self . result . get ( ) . unwrap ( ) ) , timeout_duration)
179
184
}
180
185
181
186
/// Tries to await the future with a given timeout.
@@ -243,7 +248,7 @@ impl CassFuture {
243
248
let ( guard_result, timeout_result) = self
244
249
. wait_for_value
245
250
. wait_timeout_while ( guard, remaining_timeout, |state| {
246
- state . value . is_none ( ) && state. join_handle . is_none ( )
251
+ self . result . get ( ) . is_none ( ) && state. join_handle . is_none ( )
247
252
} )
248
253
// unwrap: Error appears only when mutex is poisoned.
249
254
. unwrap ( ) ;
@@ -276,7 +281,7 @@ impl CassFuture {
276
281
return CassError :: CASS_ERROR_LIB_CALLBACK_ALREADY_SET ;
277
282
}
278
283
let bound_cb = BoundCallback { cb, data } ;
279
- if lock . value . is_some ( ) {
284
+ if self . result . get ( ) . is_some ( ) {
280
285
// The value is already available, we need to call the callback ourselves
281
286
mem:: drop ( lock) ;
282
287
bound_cb. invoke ( self_ptr) ;
@@ -335,8 +340,12 @@ pub unsafe extern "C" fn cass_future_wait_timed(
335
340
pub unsafe extern "C" fn cass_future_ready (
336
341
future_raw : CassBorrowedSharedPtr < CassFuture , CMut > ,
337
342
) -> cass_bool_t {
338
- let state_guard = ArcFFI :: as_ref ( future_raw) . unwrap ( ) . state . lock ( ) . unwrap ( ) ;
339
- match state_guard. value {
343
+ let Some ( future) = ArcFFI :: as_ref ( future_raw) else {
344
+ tracing:: error!( "Provided null future to cass_future_ready!" ) ;
345
+ return cass_false;
346
+ } ;
347
+
348
+ match future. result . get ( ) {
340
349
None => cass_false,
341
350
Some ( _) => cass_true,
342
351
}
@@ -348,7 +357,7 @@ pub unsafe extern "C" fn cass_future_error_code(
348
357
) -> CassError {
349
358
ArcFFI :: as_ref ( future_raw)
350
359
. unwrap ( )
351
- . with_waited_result ( |r : & mut CassFutureResult | match r {
360
+ . with_waited_result ( |r : & CassFutureResult | match r {
352
361
Ok ( CassResultValue :: QueryError ( err) ) => err. to_cass_error ( ) ,
353
362
Err ( ( err, _) ) => * err,
354
363
_ => CassError :: CASS_OK ,
@@ -361,19 +370,26 @@ pub unsafe extern "C" fn cass_future_error_message(
361
370
message : * mut * const :: std:: os:: raw:: c_char ,
362
371
message_length : * mut size_t ,
363
372
) {
364
- ArcFFI :: as_ref ( future)
365
- . unwrap ( )
366
- . with_waited_state ( |state : & mut CassFutureState | {
367
- let value = & state. value ;
368
- let msg = state
369
- . err_string
370
- . get_or_insert_with ( || match value. as_ref ( ) . unwrap ( ) {
371
- Ok ( CassResultValue :: QueryError ( err) ) => err. msg ( ) ,
372
- Err ( ( _, s) ) => s. msg ( ) ,
373
- _ => "" . to_string ( ) ,
374
- } ) ;
375
- unsafe { write_str_to_c ( msg. as_str ( ) , message, message_length) } ;
376
- } ) ;
373
+ let Some ( future) = ArcFFI :: as_ref ( future) else {
374
+ tracing:: error!( "Provided null future to cass_future_error_message!" ) ;
375
+ unsafe {
376
+ * message = std:: ptr:: null ( ) ;
377
+ * message_length = 0 ;
378
+ }
379
+ return ;
380
+ } ;
381
+
382
+ future. with_waited_state ( |state : & mut CassFutureState | {
383
+ let value = future. result . get ( ) ;
384
+ let msg = state
385
+ . err_string
386
+ . get_or_insert_with ( || match value. as_ref ( ) . unwrap ( ) {
387
+ Ok ( CassResultValue :: QueryError ( err) ) => err. msg ( ) ,
388
+ Err ( ( _, s) ) => s. msg ( ) ,
389
+ _ => "" . to_string ( ) ,
390
+ } ) ;
391
+ unsafe { write_str_to_c ( msg. as_str ( ) , message, message_length) } ;
392
+ } ) ;
377
393
}
378
394
379
395
#[ unsafe( no_mangle) ]
@@ -387,7 +403,7 @@ pub unsafe extern "C" fn cass_future_get_result(
387
403
) -> CassOwnedSharedPtr < CassResult , CConst > {
388
404
ArcFFI :: as_ref ( future_raw)
389
405
. unwrap ( )
390
- . with_waited_result ( |r : & mut CassFutureResult | -> Option < Arc < CassResult > > {
406
+ . with_waited_result ( |r : & CassFutureResult | -> Option < Arc < CassResult > > {
391
407
match r. as_ref ( ) . ok ( ) ? {
392
408
CassResultValue :: QueryResult ( qr) => Some ( Arc :: clone ( qr) ) ,
393
409
_ => None ,
@@ -402,7 +418,7 @@ pub unsafe extern "C" fn cass_future_get_error_result(
402
418
) -> CassOwnedSharedPtr < CassErrorResult , CConst > {
403
419
ArcFFI :: as_ref ( future_raw)
404
420
. unwrap ( )
405
- . with_waited_result ( |r : & mut CassFutureResult | -> Option < Arc < CassErrorResult > > {
421
+ . with_waited_result ( |r : & CassFutureResult | -> Option < Arc < CassErrorResult > > {
406
422
match r. as_ref ( ) . ok ( ) ? {
407
423
CassResultValue :: QueryError ( qr) => Some ( Arc :: clone ( qr) ) ,
408
424
_ => None ,
@@ -417,7 +433,7 @@ pub unsafe extern "C" fn cass_future_get_prepared(
417
433
) -> CassOwnedSharedPtr < CassPrepared , CConst > {
418
434
ArcFFI :: as_ref ( future_raw)
419
435
. unwrap ( )
420
- . with_waited_result ( |r : & mut CassFutureResult | -> Option < Arc < CassPrepared > > {
436
+ . with_waited_result ( |r : & CassFutureResult | -> Option < Arc < CassPrepared > > {
421
437
match r. as_ref ( ) . ok ( ) ? {
422
438
CassResultValue :: Prepared ( p) => Some ( Arc :: clone ( p) ) ,
423
439
_ => None ,
@@ -433,7 +449,7 @@ pub unsafe extern "C" fn cass_future_tracing_id(
433
449
) -> CassError {
434
450
ArcFFI :: as_ref ( future)
435
451
. unwrap ( )
436
- . with_waited_result ( |r : & mut CassFutureResult | match r {
452
+ . with_waited_result ( |r : & CassFutureResult | match r {
437
453
Ok ( CassResultValue :: QueryResult ( result) ) => match result. tracing_id {
438
454
Some ( id) => {
439
455
unsafe { * tracing_id = CassUuid :: from ( id) } ;
@@ -457,21 +473,7 @@ pub unsafe extern "C" fn cass_future_coordinator(
457
473
future. with_waited_result ( |r| match r {
458
474
Ok ( CassResultValue :: QueryResult ( result) ) => {
459
475
// unwrap: Coordinator is `None` only for tests.
460
- let coordinator_ptr = result. coordinator . as_ref ( ) . unwrap ( ) as * const Coordinator ;
461
-
462
- // We need to 'extend' the lifetime of returned Coordinator so safe FFI api does not complain.
463
- // The lifetime of "result" reference provided to this closure is the lifetime of a mutex guard.
464
- // We are guaranteed, that once the future is resolved (i.e. this closure is called), the result will not
465
- // be modified in any way. Thus, we can guarantee that returned coordinator lives as long as underlying
466
- // CassResult lives (i.e. longer than the lifetime of acquired mutex guard).
467
- //
468
- // SAFETY: Coordinator's lifetime is tied to the lifetime of underlying CassResult, thus:
469
- // 1. Coordinator lives as long as the underlying CassResult lives
470
- // 2. Coordinator will not be moved as long as underlying CassResult is not freed
471
- // 3. Coordinator is immutable once future is resolved (because CassResult is set once)
472
- let coordinator_ref = unsafe { & * coordinator_ptr } ;
473
-
474
- RefFFI :: as_ptr ( coordinator_ref)
476
+ RefFFI :: as_ptr ( result. coordinator . as_ref ( ) . unwrap ( ) )
475
477
}
476
478
_ => RefFFI :: null ( ) ,
477
479
} )
0 commit comments