@@ -9,11 +9,10 @@ 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
@@ -52,14 +51,14 @@ impl BoundCallback {
52
51
53
52
#[ derive( Default ) ]
54
53
struct CassFutureState {
55
- value : Option < CassFutureResult > ,
56
54
err_string : Option < String > ,
57
55
callback : Option < BoundCallback > ,
58
56
join_handle : Option < JoinHandle < ( ) > > ,
59
57
}
60
58
61
59
pub struct CassFuture {
62
60
state : Mutex < CassFutureState > ,
61
+ result : OnceLock < CassFutureResult > ,
63
62
wait_for_value : Condvar ,
64
63
}
65
64
@@ -89,14 +88,18 @@ impl CassFuture {
89
88
) -> Arc < CassFuture > {
90
89
let cass_fut = Arc :: new ( CassFuture {
91
90
state : Mutex :: new ( Default :: default ( ) ) ,
91
+ result : OnceLock :: new ( ) ,
92
92
wait_for_value : Condvar :: new ( ) ,
93
93
} ) ;
94
94
let cass_fut_clone = Arc :: clone ( & cass_fut) ;
95
95
let join_handle = RUNTIME . spawn ( async move {
96
96
let r = fut. await ;
97
97
let maybe_cb = {
98
98
let mut guard = cass_fut_clone. state . lock ( ) . unwrap ( ) ;
99
- guard. value = Some ( r) ;
99
+ cass_fut_clone
100
+ . result
101
+ . set ( r)
102
+ . expect ( "Tried to resolve future result twice!" ) ;
100
103
// Take the callback and call it after releasing the lock
101
104
guard. callback . take ( )
102
105
} ;
@@ -117,16 +120,17 @@ impl CassFuture {
117
120
118
121
pub fn new_ready ( r : CassFutureResult ) -> Arc < Self > {
119
122
Arc :: new ( CassFuture {
120
- state : Mutex :: new ( CassFutureState {
121
- value : Some ( r) ,
122
- ..Default :: default ( )
123
- } ) ,
123
+ state : Mutex :: new ( CassFutureState :: default ( ) ) ,
124
+ result : OnceLock :: from ( r) ,
124
125
wait_for_value : Condvar :: new ( ) ,
125
126
} )
126
127
}
127
128
128
- pub fn with_waited_result < T > ( & self , f : impl FnOnce ( & mut CassFutureResult ) -> T ) -> T {
129
- 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 ( ) ) )
130
134
}
131
135
132
136
/// Awaits the future until completion.
@@ -155,7 +159,7 @@ impl CassFuture {
155
159
guard = self
156
160
. wait_for_value
157
161
. wait_while ( guard, |state| {
158
- state . value . is_none ( ) && state. join_handle . is_none ( )
162
+ self . result . get ( ) . is_none ( ) && state. join_handle . is_none ( )
159
163
} )
160
164
// unwrap: Error appears only when mutex is poisoned.
161
165
. unwrap ( ) ;
@@ -173,10 +177,10 @@ impl CassFuture {
173
177
174
178
fn with_waited_result_timed < T > (
175
179
& self ,
176
- f : impl FnOnce ( & mut CassFutureResult ) -> T ,
180
+ f : impl FnOnce ( & CassFutureResult ) -> T ,
177
181
timeout_duration : Duration ,
178
182
) -> Result < T , FutureError > {
179
- 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)
180
184
}
181
185
182
186
/// Tries to await the future with a given timeout.
@@ -244,7 +248,7 @@ impl CassFuture {
244
248
let ( guard_result, timeout_result) = self
245
249
. wait_for_value
246
250
. wait_timeout_while ( guard, remaining_timeout, |state| {
247
- state . value . is_none ( ) && state. join_handle . is_none ( )
251
+ self . result . get ( ) . is_none ( ) && state. join_handle . is_none ( )
248
252
} )
249
253
// unwrap: Error appears only when mutex is poisoned.
250
254
. unwrap ( ) ;
@@ -277,7 +281,7 @@ impl CassFuture {
277
281
return CassError :: CASS_ERROR_LIB_CALLBACK_ALREADY_SET ;
278
282
}
279
283
let bound_cb = BoundCallback { cb, data } ;
280
- if lock . value . is_some ( ) {
284
+ if self . result . get ( ) . is_some ( ) {
281
285
// The value is already available, we need to call the callback ourselves
282
286
mem:: drop ( lock) ;
283
287
bound_cb. invoke ( self_ptr) ;
@@ -347,8 +351,7 @@ pub unsafe extern "C" fn cass_future_ready(
347
351
return cass_false;
348
352
} ;
349
353
350
- let state_guard = future. state . lock ( ) . unwrap ( ) ;
351
- match state_guard. value {
354
+ match future. result . get ( ) {
352
355
None => cass_false,
353
356
Some ( _) => cass_true,
354
357
}
@@ -363,7 +366,7 @@ pub unsafe extern "C" fn cass_future_error_code(
363
366
return CassError :: CASS_ERROR_LIB_BAD_PARAMS ;
364
367
} ;
365
368
366
- future. with_waited_result ( |r : & mut CassFutureResult | match r {
369
+ future. with_waited_result ( |r : & CassFutureResult | match r {
367
370
Ok ( CassResultValue :: QueryError ( err) ) => err. to_cass_error ( ) ,
368
371
Err ( ( err, _) ) => * err,
369
372
_ => CassError :: CASS_OK ,
@@ -382,7 +385,7 @@ pub unsafe extern "C" fn cass_future_error_message(
382
385
} ;
383
386
384
387
future. with_waited_state ( |state : & mut CassFutureState | {
385
- let value = & state . value ;
388
+ let value = future . result . get ( ) ;
386
389
let msg = state
387
390
. err_string
388
391
. get_or_insert_with ( || match value. as_ref ( ) . unwrap ( ) {
@@ -409,7 +412,7 @@ pub unsafe extern "C" fn cass_future_get_result(
409
412
} ;
410
413
411
414
future
412
- . with_waited_result ( |r : & mut CassFutureResult | -> Option < Arc < CassResult > > {
415
+ . with_waited_result ( |r : & CassFutureResult | -> Option < Arc < CassResult > > {
413
416
match r. as_ref ( ) . ok ( ) ? {
414
417
CassResultValue :: QueryResult ( qr) => Some ( Arc :: clone ( qr) ) ,
415
418
_ => None ,
@@ -428,7 +431,7 @@ pub unsafe extern "C" fn cass_future_get_error_result(
428
431
} ;
429
432
430
433
future
431
- . with_waited_result ( |r : & mut CassFutureResult | -> Option < Arc < CassErrorResult > > {
434
+ . with_waited_result ( |r : & CassFutureResult | -> Option < Arc < CassErrorResult > > {
432
435
match r. as_ref ( ) . ok ( ) ? {
433
436
CassResultValue :: QueryError ( qr) => Some ( Arc :: clone ( qr) ) ,
434
437
_ => None ,
@@ -447,7 +450,7 @@ pub unsafe extern "C" fn cass_future_get_prepared(
447
450
} ;
448
451
449
452
future
450
- . with_waited_result ( |r : & mut CassFutureResult | -> Option < Arc < CassPrepared > > {
453
+ . with_waited_result ( |r : & CassFutureResult | -> Option < Arc < CassPrepared > > {
451
454
match r. as_ref ( ) . ok ( ) ? {
452
455
CassResultValue :: Prepared ( p) => Some ( Arc :: clone ( p) ) ,
453
456
_ => None ,
@@ -466,7 +469,7 @@ pub unsafe extern "C" fn cass_future_tracing_id(
466
469
return CassError :: CASS_ERROR_LIB_BAD_PARAMS ;
467
470
} ;
468
471
469
- future. with_waited_result ( |r : & mut CassFutureResult | match r {
472
+ future. with_waited_result ( |r : & CassFutureResult | match r {
470
473
Ok ( CassResultValue :: QueryResult ( result) ) => match result. tracing_id {
471
474
Some ( id) => {
472
475
unsafe { * tracing_id = CassUuid :: from ( id) } ;
@@ -490,21 +493,7 @@ pub unsafe extern "C" fn cass_future_coordinator(
490
493
future. with_waited_result ( |r| match r {
491
494
Ok ( CassResultValue :: QueryResult ( result) ) => {
492
495
// unwrap: Coordinator is `None` only for tests.
493
- let coordinator_ptr = result. coordinator . as_ref ( ) . unwrap ( ) as * const Coordinator ;
494
-
495
- // We need to 'extend' the lifetime of returned Coordinator so safe FFI api does not complain.
496
- // The lifetime of "result" reference provided to this closure is the lifetime of a mutex guard.
497
- // We are guaranteed, that once the future is resolved (i.e. this closure is called), the result will not
498
- // be modified in any way. Thus, we can guarantee that returned coordinator lives as long as underlying
499
- // CassResult lives (i.e. longer than the lifetime of acquired mutex guard).
500
- //
501
- // SAFETY: Coordinator's lifetime is tied to the lifetime of underlying CassResult, thus:
502
- // 1. Coordinator lives as long as the underlying CassResult lives
503
- // 2. Coordinator will not be moved as long as underlying CassResult is not freed
504
- // 3. Coordinator is immutable once future is resolved (because CassResult is set once)
505
- let coordinator_ref = unsafe { & * coordinator_ptr } ;
506
-
507
- RefFFI :: as_ptr ( coordinator_ref)
496
+ RefFFI :: as_ptr ( result. coordinator . as_ref ( ) . unwrap ( ) )
508
497
}
509
498
_ => RefFFI :: null ( ) ,
510
499
} )
0 commit comments