Skip to content

Commit 25cb370

Browse files
committed
future: implement cass_future_coordinator
There is one tricky part that we need to handle: the coordinator is held by CassResult, which is stored under Mutex in CassFuture. In result, the reference we obtain in `CassFuture::with_waited_result` closure has a lifetime of the mutex guard (temporary lifetime during the function call). We need to extend the lifetime of returned coordinator - further reasoning is explained in the comment in code.
1 parent 490bfcd commit 25cb370

File tree

2 files changed

+41
-1
lines changed

2 files changed

+41
-1
lines changed

scylla-rust-wrapper/src/future.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ use crate::cass_error::CassErrorMessage;
55
use crate::cass_error::ToCassError;
66
use crate::execution_error::CassErrorResult;
77
use crate::prepared::CassPrepared;
8-
use crate::query_result::CassResult;
8+
use crate::query_result::{CassNode, CassResult};
99
use crate::types::*;
1010
use crate::uuid::CassUuid;
1111
use futures::future;
12+
use scylla::response::Coordinator;
1213
use std::future::Future;
1314
use std::mem;
1415
use std::os::raw::c_void;
@@ -476,6 +477,38 @@ pub unsafe extern "C" fn cass_future_tracing_id(
476477
})
477478
}
478479

480+
#[unsafe(no_mangle)]
481+
pub unsafe extern "C" fn cass_future_coordinator(
482+
future_raw: CassBorrowedSharedPtr<CassFuture, CMut>,
483+
) -> CassBorrowedSharedPtr<CassNode, CConst> {
484+
let Some(future) = ArcFFI::as_ref(future_raw) else {
485+
tracing::error!("Provided null future to cass_future_coordinator!");
486+
return RefFFI::null();
487+
};
488+
489+
future.with_waited_result(|r| match r {
490+
Ok(CassResultValue::QueryResult(result)) => {
491+
// 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)
507+
}
508+
_ => RefFFI::null(),
509+
})
510+
}
511+
479512
#[cfg(test)]
480513
mod tests {
481514
use crate::testing::{assert_cass_error_eq, assert_cass_future_error_message_eq};

scylla-rust-wrapper/src/query_result.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ pub(crate) struct CassRowsResultSharedData {
4646
pub(crate) metadata: Arc<CassResultMetadata>,
4747
}
4848

49+
pub type CassNode = Coordinator;
50+
51+
// Borrowed from CassResult in cass_future_coordinator.
52+
impl FFI for CassNode {
53+
type Origin = FromRef;
54+
}
55+
4956
pub struct CassResult {
5057
pub tracing_id: Option<Uuid>,
5158
pub paging_state_response: PagingStateResponse,

0 commit comments

Comments
 (0)