@@ -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) ;
@@ -346,8 +351,7 @@ pub unsafe extern "C" fn cass_future_ready(
346
351
return cass_false;
347
352
} ;
348
353
349
- let state_guard = future. state . lock ( ) . unwrap ( ) ;
350
- match state_guard. value {
354
+ match future. result . get ( ) {
351
355
None => cass_false,
352
356
Some ( _) => cass_true,
353
357
}
@@ -362,7 +366,7 @@ pub unsafe extern "C" fn cass_future_error_code(
362
366
return CassError :: CASS_ERROR_LIB_BAD_PARAMS ;
363
367
} ;
364
368
365
- future. with_waited_result ( |r : & mut CassFutureResult | match r {
369
+ future. with_waited_result ( |r : & CassFutureResult | match r {
366
370
Ok ( CassResultValue :: QueryError ( err) ) => err. to_cass_error ( ) ,
367
371
Err ( ( err, _) ) => * err,
368
372
_ => CassError :: CASS_OK ,
@@ -381,7 +385,7 @@ pub unsafe extern "C" fn cass_future_error_message(
381
385
} ;
382
386
383
387
future. with_waited_state ( |state : & mut CassFutureState | {
384
- let value = & state . value ;
388
+ let value = future . result . get ( ) ;
385
389
let msg = state
386
390
. err_string
387
391
. get_or_insert_with ( || match value. as_ref ( ) . unwrap ( ) {
@@ -408,7 +412,7 @@ pub unsafe extern "C" fn cass_future_get_result(
408
412
} ;
409
413
410
414
future
411
- . with_waited_result ( |r : & mut CassFutureResult | -> Option < Arc < CassResult > > {
415
+ . with_waited_result ( |r : & CassFutureResult | -> Option < Arc < CassResult > > {
412
416
match r. as_ref ( ) . ok ( ) ? {
413
417
CassResultValue :: QueryResult ( qr) => Some ( Arc :: clone ( qr) ) ,
414
418
_ => None ,
@@ -427,7 +431,7 @@ pub unsafe extern "C" fn cass_future_get_error_result(
427
431
} ;
428
432
429
433
future
430
- . with_waited_result ( |r : & mut CassFutureResult | -> Option < Arc < CassErrorResult > > {
434
+ . with_waited_result ( |r : & CassFutureResult | -> Option < Arc < CassErrorResult > > {
431
435
match r. as_ref ( ) . ok ( ) ? {
432
436
CassResultValue :: QueryError ( qr) => Some ( Arc :: clone ( qr) ) ,
433
437
_ => None ,
@@ -446,7 +450,7 @@ pub unsafe extern "C" fn cass_future_get_prepared(
446
450
} ;
447
451
448
452
future
449
- . with_waited_result ( |r : & mut CassFutureResult | -> Option < Arc < CassPrepared > > {
453
+ . with_waited_result ( |r : & CassFutureResult | -> Option < Arc < CassPrepared > > {
450
454
match r. as_ref ( ) . ok ( ) ? {
451
455
CassResultValue :: Prepared ( p) => Some ( Arc :: clone ( p) ) ,
452
456
_ => None ,
@@ -465,7 +469,7 @@ pub unsafe extern "C" fn cass_future_tracing_id(
465
469
return CassError :: CASS_ERROR_LIB_BAD_PARAMS ;
466
470
} ;
467
471
468
- future. with_waited_result ( |r : & mut CassFutureResult | match r {
472
+ future. with_waited_result ( |r : & CassFutureResult | match r {
469
473
Ok ( CassResultValue :: QueryResult ( result) ) => match result. tracing_id {
470
474
Some ( id) => {
471
475
unsafe { * tracing_id = CassUuid :: from ( id) } ;
@@ -489,21 +493,7 @@ pub unsafe extern "C" fn cass_future_coordinator(
489
493
future. with_waited_result ( |r| match r {
490
494
Ok ( CassResultValue :: QueryResult ( result) ) => {
491
495
// unwrap: Coordinator is `None` only for tests.
492
- let coordinator_ptr = result. coordinator . as_ref ( ) . unwrap ( ) as * const Coordinator ;
493
-
494
- // We need to 'extend' the lifetime of returned Coordinator so safe FFI api does not complain.
495
- // The lifetime of "result" reference provided to this closure is the lifetime of a mutex guard.
496
- // We are guaranteed, that once the future is resolved (i.e. this closure is called), the result will not
497
- // be modified in any way. Thus, we can guarantee that returned coordinator lives as long as underlying
498
- // CassResult lives (i.e. longer than the lifetime of acquired mutex guard).
499
- //
500
- // SAFETY: Coordinator's lifetime is tied to the lifetime of underlying CassResult, thus:
501
- // 1. Coordinator lives as long as the underlying CassResult lives
502
- // 2. Coordinator will not be moved as long as underlying CassResult is not freed
503
- // 3. Coordinator is immutable once future is resolved (because CassResult is set once)
504
- let coordinator_ref = unsafe { & * coordinator_ptr } ;
505
-
506
- RefFFI :: as_ptr ( coordinator_ref)
496
+ RefFFI :: as_ptr ( result. coordinator . as_ref ( ) . unwrap ( ) )
507
497
}
508
498
_ => RefFFI :: null ( ) ,
509
499
} )
0 commit comments