fix(wallet): allow PersistedWallet to be Send + Sync#1874
Conversation
a9ef2af to
14152c0
Compare
|
ACK, i run build, test, clippy and fmt. All looks goods |
14152c0 to
ee4e2c8
Compare
oleonardolima
left a comment
There was a problem hiding this comment.
I'm quite divided on this one, still taking a look in SQLite docs, but it doesn't feel right to override the Send + Sync implementation for any given persister.
I think there's no need for this change and it should inherit the Send + Sync implementation as it's patterns and use a Mutex instead, there's a few issues/discussions on rusqlite about this and it seems their decision is related on how sqlite flags are used, see rusqlite/rusqlite#188.
|
@oleonardolima when you call Without this change both the Example without this change #[derive(Debug, uniffi::Object)]
pub struct Wallet {
pub id: WalletId,
pub network: Network,
pub bdk: Mutex<bdk_wallet::PersistedWallet<Connection>>,
pub metadata: WalletMetadata,
db: Mutex<Connection>,
}Example with this change: #[derive(Debug, uniffi::Object)]
pub struct Wallet {
pub id: WalletId,
pub network: Network,
pub bdk: bdk_wallet::PersistedWallet<Connection>,
pub metadata: WalletMetadata,
db: Mutex<Connection>,
}There isn't a safety issue here because the wallet doesn't actually store the |
| pub struct PersistedWallet<P> { | ||
| inner: Wallet, | ||
| marker: PhantomData<P>, | ||
| _marker: PhantomData<fn(P) -> P>, |
There was a problem hiding this comment.
| _marker: PhantomData<fn(P) -> P>, | |
| _marker: PhantomData<*const P>, |
According to @LLFourn
There was a problem hiding this comment.
I tried this approach and get this error running test_thread_safety:
error[E0277]: `*const Connection` cannot be sent between threads safely
--> crates/wallet/tests/wallet.rs:4215:19
|
4215 | thread_safe::<PersistedWallet<bdk_chain::rusqlite::Connection>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const Connection` cannot be sent between threads safely
|
= help: within `PersistedWallet<Connection>`, the trait `Send` is not implemented for `*const Connection`
= help: the trait `Send` is implemented for `Connection`
note: required because it appears within the type `PhantomData<*const Connection>`
--> /Users/steve/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/marker.rs:757:12
|
757 | pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `PersistedWallet<Connection>`
--> /Users/steve/git/notmandatory/bdk/crates/wallet/src/wallet/persisted.rs:121:12
|
121 | pub struct PersistedWallet<P> {
| ^^^^^^^^^^^^^^^
note: required by a bound in `thread_safe`
--> crates/wallet/tests/wallet.rs:4212:23
|
4212 | fn thread_safe<T: Send + Sync>() {}
| ^^^^ required by this bound in `thread_safe`
There was a problem hiding this comment.
PhantomData<*const P> makes it !Sync: https://doc.rust-lang.org/nomicon/phantom-data.html#table-of-phantomdata-patterns
There was a problem hiding this comment.
@notmandatory Sorry yeah my suggestion does the complete opposite of what we need.
|
According to "In this case, you want to ensure that your struct remains Send and Sync, even if the functions implemented on it have a &mut T parameter and T is not Sync. To achieve this, you can use PhantomData<fn(&mut T)> instead of PhantomData<fn(T)>. This tells Rust that your struct is a "consumer" of &mut T, but it does not own T and therefore should not be tied to its lifetime." |
ee4e2c8 to
09e3178
Compare
|
I updated the code per comment above and also included some of the explanation in the commit message. |
b525a92 to
3c2fc86
Compare
|
@notmandatory Perplexity seems to agree too
|
|
I pushed another commit to fix the CI issue. Ring just (4hrs ago) bumped their MSRV from 1.63 to 1.66 with a patch release 😞 (see briansmith/ring#2449). |
|
@notmandatory I think in our case since we aren't dealing with lifetimes of any unsafe things, I think all three are practically equal, all of this compiles. I think the AIs are wrong. PhantomData<fn(P) -> P>
PhantomData<fn(&mut P)>
PhantomData<fn(P)>use std::marker::PhantomData;
use rusqlite::Connection;
pub struct PersistedWallet<P> {
_marker: PhantomData<fn(&mut P)>,
}
impl<P> PersistedWallet<P> {
pub fn new() -> Self {
Self {
_marker: PhantomData,
}
}
pub fn persist(&self, _wallet: &mut P) {}
pub fn persist_2(&self, _wallet: P) {}
}
pub struct PersistedWallet2<P> {
_marker: PhantomData<fn(P)>,
}
impl<P> PersistedWallet2<P> {
pub fn new() -> Self {
Self {
_marker: PhantomData,
}
}
pub fn persist(&self, _wallet: &mut P) {}
pub fn persist_2(&self, _wallet: P) {}
}
pub struct PersistedWallet3<P> {
_marker: PhantomData<fn(P) -> P>,
}
impl<P> PersistedWallet3<P> {
pub fn new() -> Self {
Self {
_marker: PhantomData,
}
}
pub fn persist(&self, _wallet: &mut P) {}
pub fn persist_2(&self, _wallet: P) {}
}
pub type Wallet = PersistedWallet<Connection>;
pub type Wallet2 = PersistedWallet2<Connection>;
pub type Wallet3 = PersistedWallet3<Connection>;
fn thead_safe<T>(_wallet: T)
where
T: Sync + Sync,
{
}
fn main() {
thead_safe(Wallet::new());
thead_safe(Wallet2::new());
thead_safe(Wallet3::new());
} |
|
If all are safe and enable the same functionality then I think the one suggested by the AIs is still the best since it communicates that PersistWallet "consumes" the &mut P type in it's functions without owning P. But happy to change it to one of the other options if there's some compelling reason to. |
|
That makes sense to me |
evanlinjin
left a comment
There was a problem hiding this comment.
ACK cdba39d
TIL something about PhantomData.
ValuedMammal
left a comment
There was a problem hiding this comment.
ACK, needs a rebase now that #1876 is merged
cdba39d to
25559e2
Compare
…!Sync The goal of this change is to ensure that PersistWallet<P> remains Send and Sync, even if the functions implemented on it have a &mut P parameter and P is not Sync. The reason to change PersistWallet<P>'s _marker field type from PhatonData<P> to PhantomData<fn(&mut P)> is to tell the Rust compiler that this struct is a "consumer" of &mut P, but it does not own P and therefore should not be tied to its lifetime.
25559e2 to
14251a4
Compare
|
@ValuedMammal I rebased this PR, OK to merge now? |

Description
fixes #1873 based on solution proposed by @praveenperera .
Notes to the reviewers
This is not a breaking change since it's only changing the internal
_markerfield's type.Changelog notice
Checklists
All Submissions:
cargo fmtandcargo clippybefore committingBugfixes: