Skip to content

Commit e83a953

Browse files
yjh0502bluejekyll
authored andcommitted
fix segfault on watch test, #58
1 parent 4b86ae5 commit e83a953

File tree

4 files changed

+71
-29
lines changed

4 files changed

+71
-29
lines changed

foundationdb/src/cluster.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ impl Cluster {
3434
/// TODO: implement Default for Cluster where: If cluster_file_path is NULL or an empty string, then a default cluster file will be used. see
3535
pub fn new(path: &str) -> ClusterGet {
3636
let path_str = std::ffi::CString::new(path).unwrap();
37-
let f = unsafe { fdb::fdb_create_cluster(path_str.as_ptr()) };
38-
ClusterGet {
39-
inner: FdbFuture::new(f),
40-
}
37+
let inner = unsafe {
38+
let f = fdb::fdb_create_cluster(path_str.as_ptr());
39+
FdbFuture::new(f)
40+
};
41+
ClusterGet { inner }
4142
}
4243

4344
// TODO: fdb_cluster_set_option impl

foundationdb/src/database.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ use transaction::*;
2626
/// Modifications to a database are performed via transactions.
2727
#[derive(Clone)]
2828
pub struct Database {
29-
cluster: Cluster,
29+
// Order of fields should not be changed, because Rust drops field top-to-bottom (rfc1857), and
30+
// database should be dropped before cluster.
3031
inner: Arc<DatabaseInner>,
32+
cluster: Cluster,
3133
}
3234
impl Database {
3335
pub(crate) fn new(cluster: Cluster, db: *mut fdb::FDBDatabase) -> Self {

foundationdb/src/future.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ pub(crate) struct FdbFuture {
2929
}
3030

3131
impl FdbFuture {
32-
pub(crate) fn new(f: *mut fdb::FDBFuture) -> Self {
32+
// `new` is marked as unsafe because it's lifetime is not well-defined.
33+
pub(crate) unsafe fn new(f: *mut fdb::FDBFuture) -> Self {
3334
Self {
3435
f: Some(f),
3536
task: None,

foundationdb/src/transaction.rs

+61-23
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ use tuple;
3333
/// Transactions are also causally consistent: once a transaction has been successfully committed, all subsequently created transactions will see the modifications made by it.
3434
#[derive(Clone)]
3535
pub struct Transaction {
36-
database: Database,
36+
// Order of fields should not be changed, because Rust drops field top-to-bottom, and
37+
// transaction should be dropped before cluster.
3738
inner: Arc<TransactionInner>,
39+
database: Database,
3840
}
3941

4042
/// Converts Rust `bool` into `fdb::fdb_bool_t`
@@ -173,6 +175,10 @@ impl Transaction {
173175
self.database.clone()
174176
}
175177

178+
fn into_database(self) -> Database {
179+
self.database
180+
}
181+
176182
/// Modify the database snapshot represented by transaction to change the given key to have the given value.
177183
///
178184
/// If the given key was not previously present in the database it is inserted. The modification affects the actual database only if transaction is later committed with `Transaction::commit`.
@@ -229,7 +235,7 @@ impl Transaction {
229235
)
230236
};
231237
TrxGet {
232-
inner: self.new_future(f),
238+
inner: self.new_fut_trx(f),
233239
}
234240
}
235241

@@ -288,7 +294,7 @@ impl Transaction {
288294
)
289295
};
290296
TrxGetKey {
291-
inner: self.new_future(f),
297+
inner: self.new_fut_trx(f),
292298
}
293299
}
294300

@@ -338,7 +344,7 @@ impl Transaction {
338344
};
339345

340346
TrxGetRange {
341-
inner: self.new_future(f),
347+
inner: self.new_fut_trx(f),
342348
opt: Some(opt),
343349
}
344350
}
@@ -379,7 +385,7 @@ impl Transaction {
379385
let trx = self.inner.inner;
380386

381387
let f = unsafe { fdb::fdb_transaction_commit(trx) };
382-
let f = self.new_future(f);
388+
let f = self.new_fut_trx(f);
383389
TrxCommit { inner: f }
384390
}
385391

@@ -442,7 +448,7 @@ impl Transaction {
442448
)
443449
};
444450
TrxGetAddressesForKey {
445-
inner: self.new_future(f),
451+
inner: self.new_fut_trx(f),
446452
}
447453
}
448454

@@ -478,14 +484,10 @@ impl Transaction {
478484
let f =
479485
unsafe { fdb::fdb_transaction_watch(trx, key.as_ptr() as *const _, key.len() as i32) };
480486
TrxWatch {
481-
inner: FdbFuture::new(f),
487+
inner: self.new_fut_non_trx(f),
482488
}
483489
}
484490

485-
fn new_future(&self, f: *mut fdb::FDBFuture) -> TrxFuture {
486-
TrxFuture::new(self.clone(), f)
487-
}
488-
489491
/// Returns an FDBFuture which will be set to the versionstamp which was used by any
490492
/// versionstamp operations in this transaction. You must first wait for the FDBFuture to be
491493
/// ready, check for errors, call fdb_future_get_key() to extract the key, and then destroy the
@@ -503,7 +505,7 @@ impl Transaction {
503505

504506
let f = unsafe { fdb::fdb_transaction_get_versionstamp(trx) };
505507
TrxVersionstamp {
506-
inner: FdbFuture::new(f),
508+
inner: self.new_fut_non_trx(f),
507509
}
508510
}
509511

@@ -516,7 +518,7 @@ impl Transaction {
516518

517519
let f = unsafe { fdb::fdb_transaction_get_read_version(trx) };
518520
TrxReadVersion {
519-
inner: self.new_future(f),
521+
inner: self.new_fut_trx(f),
520522
}
521523
}
522524

@@ -586,6 +588,14 @@ impl Transaction {
586588
))
587589
}
588590
}
591+
592+
fn new_fut_trx(&self, f: *mut fdb::FDBFuture) -> TrxFuture {
593+
TrxFuture::new(self.clone(), f)
594+
}
595+
596+
fn new_fut_non_trx(&self, f: *mut fdb::FDBFuture) -> NonTrxFuture {
597+
NonTrxFuture::new(self.database(), f)
598+
}
589599
}
590600

591601
struct TransactionInner {
@@ -863,7 +873,7 @@ impl Future for TrxGetAddressesForKey {
863873
pub struct TrxWatch {
864874
// `TrxWatch` can live longer then a parent transaction that registhers the watch, so it should
865875
// not maintain a reference to the transaction, which will prevent the transcation to be freed.
866-
inner: FdbFuture,
876+
inner: NonTrxFuture,
867877
}
868878
impl Future for TrxWatch {
869879
type Item = ();
@@ -894,7 +904,7 @@ impl Versionstamp {
894904
pub struct TrxVersionstamp {
895905
// `TrxVersionstamp` resolves after `Transaction::commit`, so like `TrxWatch` it does not
896906
// not maintain a reference to the transaction.
897-
inner: FdbFuture,
907+
inner: NonTrxFuture,
898908
}
899909
impl Future for TrxVersionstamp {
900910
type Item = Versionstamp;
@@ -940,26 +950,51 @@ impl Future for TrxReadVersion {
940950
}
941951
}
942952

953+
/// Futures that could be outlive transaction.
954+
struct NonTrxFuture {
955+
// Order of fields should not be changed, because Rust drops field top-to-bottom, and future
956+
// should be dropped before database.
957+
inner: FdbFuture,
958+
// We should maintain refcount for database, to make FdbFuture not outlive database.
959+
#[allow(unused)]
960+
db: Database,
961+
}
962+
963+
impl NonTrxFuture {
964+
fn new(db: Database, f: *mut fdb::FDBFuture) -> Self {
965+
let inner = unsafe { FdbFuture::new(f) };
966+
Self { inner, db }
967+
}
968+
}
969+
970+
impl Future for NonTrxFuture {
971+
type Item = FdbFutureResult;
972+
type Error = FdbError;
973+
974+
fn poll(&mut self) -> std::result::Result<Async<Self::Item>, Self::Error> {
975+
self.inner.poll()
976+
}
977+
}
978+
943979
/// Abstraction over `fdb_transaction_on_err`.
944980
pub struct TrxErrFuture {
945-
err: Option<FdbError>,
946981
// A future from `fdb_transaction_on_err`. It resolves to `Ok(_)` after backoff interval if
947982
// undering transaction should be retried, and resolved to `Err(e)` if the error should be
948983
// reported to the user without retry.
949-
inner: FdbFuture,
984+
inner: NonTrxFuture,
985+
err: Option<FdbError>,
950986
}
951987
impl TrxErrFuture {
952988
fn new(trx: Transaction, err: FdbError) -> Self {
953-
let trx = trx.inner.inner;
954-
let inner = unsafe { fdb::fdb_transaction_on_error(trx, err.code()) };
955-
let inner = FdbFuture::new(inner);
989+
let inner = unsafe { fdb::fdb_transaction_on_error(trx.inner.inner, err.code()) };
956990

957991
Self {
992+
inner: NonTrxFuture::new(trx.into_database(), inner),
958993
err: Some(err),
959-
inner,
960994
}
961995
}
962996
}
997+
963998
impl Future for TrxErrFuture {
964999
type Item = FdbError;
9651000
type Error = FdbError;
@@ -979,16 +1014,19 @@ impl Future for TrxErrFuture {
9791014

9801015
/// Futures for transaction, which supports retry/backoff with `Database::transact`.
9811016
struct TrxFuture {
982-
trx: Option<Transaction>,
1017+
// Order of fields should not be changed, because Rust drops field top-to-bottom, and future
1018+
// should be dropped before transaction.
9831019
inner: FdbFuture,
1020+
trx: Option<Transaction>,
9841021
f_err: Option<TrxErrFuture>,
9851022
}
9861023

9871024
impl TrxFuture {
9881025
fn new(trx: Transaction, f: *mut fdb::FDBFuture) -> Self {
1026+
let inner = unsafe { FdbFuture::new(f) };
9891027
Self {
1028+
inner,
9901029
trx: Some(trx),
991-
inner: FdbFuture::new(f),
9921030
f_err: None,
9931031
}
9941032
}

0 commit comments

Comments
 (0)