@@ -59,25 +59,38 @@ struct CassFutureState {
59
59
join_handle : Option < JoinHandle < ( ) > > ,
60
60
}
61
61
62
- pub struct CassFuture {
62
+ enum FutureKind {
63
+ /// Future that must be resolved by the tokio runtime.
64
+ Resolvable ( ResolvableFuture ) ,
65
+
66
+ /// Future that is immediately ready with the result.
67
+ Immediate ( CassFutureResult ) ,
68
+ }
69
+
70
+ struct ResolvableFuture {
63
71
/// Mutable state of the future that requires synchronized exclusive access
64
72
/// in order to ensure thread safety of the future execution.
65
73
state : Mutex < CassFutureState > ,
66
74
67
75
/// Result of the future once it is resolved.
68
76
result : OnceLock < CassFutureResult > ,
69
77
70
- /// Required as a place to allocate the stringified error message.
71
- /// This is needed to support `cass_future_error_message`.
72
- err_string : OnceLock < String > ,
73
-
74
78
/// Used to notify threads waiting for the future's result.
75
79
wait_for_value : Condvar ,
76
80
77
81
#[ cfg( cpp_integration_testing) ]
78
82
recording_listener : Option < Arc < crate :: integration_testing:: RecordingHistoryListener > > ,
79
83
}
80
84
85
+ pub struct CassFuture {
86
+ /// One of the possible implementations of the future.
87
+ kind : FutureKind ,
88
+
89
+ /// Required as a place to allocate the stringified error message.
90
+ /// This is needed to support `cass_future_error_message`.
91
+ err_string : OnceLock < String > ,
92
+ }
93
+
81
94
impl FFI for CassFuture {
82
95
type Origin = FromArc ;
83
96
}
@@ -118,19 +131,26 @@ impl CassFuture {
118
131
> ,
119
132
) -> Arc < CassFuture > {
120
133
let cass_fut = Arc :: new ( CassFuture {
121
- state : Mutex :: new ( Default :: default ( ) ) ,
122
- result : OnceLock :: new ( ) ,
123
134
err_string : OnceLock :: new ( ) ,
124
- wait_for_value : Condvar :: new ( ) ,
125
- #[ cfg( cpp_integration_testing) ]
126
- recording_listener,
135
+ kind : FutureKind :: Resolvable ( ResolvableFuture {
136
+ state : Mutex :: new ( Default :: default ( ) ) ,
137
+ result : OnceLock :: new ( ) ,
138
+ wait_for_value : Condvar :: new ( ) ,
139
+ #[ cfg( cpp_integration_testing) ]
140
+ recording_listener,
141
+ } ) ,
127
142
} ) ;
128
143
let cass_fut_clone = Arc :: clone ( & cass_fut) ;
129
144
let join_handle = RUNTIME . spawn ( async move {
145
+ let resolvable_fut = match cass_fut_clone. kind {
146
+ FutureKind :: Resolvable ( ref resolvable) => resolvable,
147
+ _ => unreachable ! ( "CassFuture has been created as Resolvable" ) ,
148
+ } ;
149
+
130
150
let r = fut. await ;
131
151
let maybe_cb = {
132
- let mut guard = cass_fut_clone . state . lock ( ) . unwrap ( ) ;
133
- cass_fut_clone
152
+ let mut guard = resolvable_fut . state . lock ( ) . unwrap ( ) ;
153
+ resolvable_fut
134
154
. result
135
155
. set ( r)
136
156
. expect ( "Tried to resolve future result twice!" ) ;
@@ -143,23 +163,23 @@ impl CassFuture {
143
163
bound_cb. invoke ( fut_ptr) ;
144
164
}
145
165
146
- cass_fut_clone . wait_for_value . notify_all ( ) ;
166
+ resolvable_fut . wait_for_value . notify_all ( ) ;
147
167
} ) ;
148
168
{
149
- let mut lock = cass_fut. state . lock ( ) . unwrap ( ) ;
169
+ let resolvable_fut = match cass_fut. kind {
170
+ FutureKind :: Resolvable ( ref resolvable) => resolvable,
171
+ _ => unreachable ! ( "CassFuture has been created as Resolvable" ) ,
172
+ } ;
173
+ let mut lock = resolvable_fut. state . lock ( ) . unwrap ( ) ;
150
174
lock. join_handle = Some ( join_handle) ;
151
175
}
152
176
cass_fut
153
177
}
154
178
155
179
pub ( crate ) fn new_ready ( r : CassFutureResult ) -> Arc < Self > {
156
180
Arc :: new ( CassFuture {
157
- state : Mutex :: new ( CassFutureState :: default ( ) ) ,
158
- result : OnceLock :: from ( r) ,
181
+ kind : FutureKind :: Immediate ( r) ,
159
182
err_string : OnceLock :: new ( ) ,
160
- wait_for_value : Condvar :: new ( ) ,
161
- #[ cfg( cpp_integration_testing) ]
162
- recording_listener : None ,
163
183
} )
164
184
}
165
185
@@ -179,15 +199,21 @@ impl CassFuture {
179
199
/// timed out (see [CassFuture::waited_result_timed]). We need to
180
200
/// take the ownership of the handle, and complete the work.
181
201
pub ( crate ) fn waited_result < ' s > ( & ' s self ) -> & ' s CassFutureResult {
202
+ let resolvable_fut = match self . kind {
203
+ FutureKind :: Resolvable ( ref resolvable_fut) => resolvable_fut,
204
+ // The future is immediately ready, so we can return the result.
205
+ FutureKind :: Immediate ( ref r) => return r,
206
+ } ;
207
+
182
208
// Happy path: if the result is already available, we can return it
183
209
// without locking the Mutex.
184
- if let Some ( result) = self . result . get ( ) {
210
+ if let Some ( result) = resolvable_fut . result . get ( ) {
185
211
return result;
186
212
}
187
213
188
- let mut guard = self . state . lock ( ) . unwrap ( ) ;
214
+ let mut guard = resolvable_fut . state . lock ( ) . unwrap ( ) ;
189
215
loop {
190
- if let Some ( result) = self . result . get ( ) {
216
+ if let Some ( result) = resolvable_fut . result . get ( ) {
191
217
// The result is already available, we can return it.
192
218
return result;
193
219
}
@@ -201,11 +227,11 @@ impl CassFuture {
201
227
202
228
// Once we are here, the future is resolved.
203
229
// The result is guaranteed to be set.
204
- return self . result . get ( ) . unwrap ( ) ;
230
+ return resolvable_fut . result . get ( ) . unwrap ( ) ;
205
231
} else {
206
232
// Someone has taken the handle, so we need to wait for them to complete
207
233
// the future. Once they finish or timeout, we will be notified.
208
- guard = self
234
+ guard = resolvable_fut
209
235
. wait_for_value
210
236
. wait_while ( guard, |state| {
211
237
// There are two cases when we should wake up:
@@ -219,7 +245,7 @@ impl CassFuture {
219
245
// no one else will complete the future, so it's our responsibility.
220
246
// In the next iteration we will land in the branch with `block_on`
221
247
// and complete the future.
222
- self . result . get ( ) . is_none ( ) && state. join_handle . is_none ( )
248
+ resolvable_fut . result . get ( ) . is_none ( ) && state. join_handle . is_none ( )
223
249
} )
224
250
// unwrap: Error appears only when mutex is poisoned.
225
251
. unwrap ( ) ;
@@ -249,19 +275,25 @@ impl CassFuture {
249
275
& ' s self ,
250
276
timeout_duration : Duration ,
251
277
) -> Result < & ' s CassFutureResult , FutureError > {
278
+ let resolvable_fut = match self . kind {
279
+ FutureKind :: Resolvable ( ref resolvable_fut) => resolvable_fut,
280
+ // The future is immediately ready, so we can return the result.
281
+ FutureKind :: Immediate ( ref r) => return Ok ( r) ,
282
+ } ;
283
+
252
284
// Happy path: if the result is already available, we can return it
253
285
// without locking the Mutex.
254
- if let Some ( result) = self . result . get ( ) {
286
+ if let Some ( result) = resolvable_fut . result . get ( ) {
255
287
return Ok ( result) ;
256
288
}
257
289
258
- let mut guard = self . state . lock ( ) . unwrap ( ) ;
290
+ let mut guard = resolvable_fut . state . lock ( ) . unwrap ( ) ;
259
291
let deadline = tokio:: time:: Instant :: now ( )
260
292
. checked_add ( timeout_duration)
261
293
. ok_or ( FutureError :: InvalidDuration ) ?;
262
294
263
295
loop {
264
- if let Some ( result) = self . result . get ( ) {
296
+ if let Some ( result) = resolvable_fut . result . get ( ) {
265
297
// The result is already available, we can return it.
266
298
return Ok ( result) ;
267
299
}
@@ -292,9 +324,9 @@ impl CassFuture {
292
324
// - Signal one thread, so that if all other consumers are
293
325
// already waiting on condvar, one of them wakes up and
294
326
// picks up the work.
295
- guard = self . state . lock ( ) . unwrap ( ) ;
327
+ guard = resolvable_fut . state . lock ( ) . unwrap ( ) ;
296
328
guard. join_handle = Some ( returned_handle) ;
297
- self . wait_for_value . notify_one ( ) ;
329
+ resolvable_fut . wait_for_value . notify_one ( ) ;
298
330
return Err ( FutureError :: TimeoutError ) ;
299
331
}
300
332
// unwrap: JoinError appears only when future either panic'ed or canceled.
@@ -303,14 +335,14 @@ impl CassFuture {
303
335
304
336
// Once we are here, the future is resolved.
305
337
// The result is guaranteed to be set.
306
- return Ok ( self . result . get ( ) . unwrap ( ) ) ;
338
+ return Ok ( resolvable_fut . result . get ( ) . unwrap ( ) ) ;
307
339
}
308
340
} ;
309
341
} else {
310
342
// Someone has taken the handle, so we need to wait for them to complete
311
343
// the future. Once they finish or timeout, we will be notified.
312
344
let remaining_timeout = deadline. duration_since ( tokio:: time:: Instant :: now ( ) ) ;
313
- let ( guard_result, timeout_result) = self
345
+ let ( guard_result, timeout_result) = resolvable_fut
314
346
. wait_for_value
315
347
. wait_timeout_while ( guard, remaining_timeout, |state| {
316
348
// There are two cases when we should wake up:
@@ -324,7 +356,7 @@ impl CassFuture {
324
356
// no one else will complete the future, so it's our responsibility.
325
357
// In the next iteration we will land in the branch with `block_on`
326
358
// and attempt to complete the future.
327
- self . result . get ( ) . is_none ( ) && state. join_handle . is_none ( )
359
+ resolvable_fut . result . get ( ) . is_none ( ) && state. join_handle . is_none ( )
328
360
} )
329
361
// unwrap: Error appears only when mutex is poisoned.
330
362
. unwrap ( ) ;
@@ -337,7 +369,24 @@ impl CassFuture {
337
369
}
338
370
}
339
371
340
- pub ( crate ) unsafe fn set_callback (
372
+ pub ( crate ) fn into_raw ( self : Arc < Self > ) -> CassOwnedSharedPtr < Self , CMut > {
373
+ ArcFFI :: into_ptr ( self )
374
+ }
375
+
376
+ #[ cfg( cpp_integration_testing) ]
377
+ pub ( crate ) fn attempted_hosts ( & self ) -> Vec < std:: net:: SocketAddr > {
378
+ if let FutureKind :: Resolvable ( ref resolvable_fut) = self . kind
379
+ && let Some ( listener) = & resolvable_fut. recording_listener
380
+ {
381
+ listener. get_attempted_hosts ( )
382
+ } else {
383
+ vec ! [ ]
384
+ }
385
+ }
386
+ }
387
+
388
+ impl ResolvableFuture {
389
+ unsafe fn set_callback (
341
390
& self ,
342
391
self_ptr : CassBorrowedSharedPtr < CassFuture , CMut > ,
343
392
cb : NonNullFutureCallback ,
@@ -359,19 +408,6 @@ impl CassFuture {
359
408
lock. callback = Some ( bound_cb) ;
360
409
CassError :: CASS_OK
361
410
}
362
-
363
- pub ( crate ) fn into_raw ( self : Arc < Self > ) -> CassOwnedSharedPtr < Self , CMut > {
364
- ArcFFI :: into_ptr ( self )
365
- }
366
-
367
- #[ cfg( cpp_integration_testing) ]
368
- pub ( crate ) fn attempted_hosts ( & self ) -> Vec < std:: net:: SocketAddr > {
369
- if let Some ( listener) = & self . recording_listener {
370
- listener. get_attempted_hosts ( )
371
- } else {
372
- vec ! [ ]
373
- }
374
- }
375
411
}
376
412
377
413
// Do not remove; this asserts that `CassFuture` implements Send + Sync,
@@ -396,7 +432,17 @@ pub unsafe extern "C" fn cass_future_set_callback(
396
432
return CassError :: CASS_ERROR_LIB_BAD_PARAMS ;
397
433
} ;
398
434
399
- unsafe { future. set_callback ( future_raw. borrow ( ) , callback, data) }
435
+ match future. kind {
436
+ FutureKind :: Resolvable ( ref resolvable) => {
437
+ // Safety: `callback` is a valid pointer to a function that matches the signature.
438
+ unsafe { resolvable. set_callback ( future_raw. borrow ( ) , callback, data) }
439
+ }
440
+ FutureKind :: Immediate ( _) => {
441
+ let bound_cb = BoundCallback { cb : callback, data } ;
442
+ bound_cb. invoke ( future_raw. borrow ( ) ) ;
443
+ CassError :: CASS_OK
444
+ }
445
+ }
400
446
}
401
447
402
448
#[ unsafe( no_mangle) ]
@@ -433,7 +479,10 @@ pub unsafe extern "C" fn cass_future_ready(
433
479
return cass_false;
434
480
} ;
435
481
436
- future. result . get ( ) . is_some ( ) as cass_bool_t
482
+ ( match future. kind {
483
+ FutureKind :: Resolvable ( ref resolvable_fut) => resolvable_fut. result . get ( ) . is_some ( ) ,
484
+ FutureKind :: Immediate ( _) => true ,
485
+ } ) as cass_bool_t
437
486
}
438
487
439
488
#[ unsafe( no_mangle) ]
0 commit comments