6

StateHTTPリクエストに渡すロケットを使用しています。この構造体にはMutex<DatastoreInstance>、SQLite データベースへのアクセスを提供する が含まれており、読み取りと書き込みを安全にするためにミューテックスでロックされています。

pub struct DatastoreInstance {
    conn: Connection,
}

構造体がこのように見えたとき、DatastoreInstanceSQLite 接続のみですべてが正常に機能しましたが、この構造体内にトランザクション オブジェクトも追加したいと考えました。

pub struct DatastoreInstance {
    conn: Connection,
    events_transaction: Transaction,
}

これはコンパイルされませんでした。これは、オブジェクトが認識している有効期間を持つオブジェクトTransactionを参照する必要があるためです。私が使用している rusqlite 内ConnectionのオブジェクトConnectionとオブジェクトは次のように定義されています。Transaction

pub struct Connection {
    db: RefCell<InnerConnection>,
    cache: StatementCache,
    path: Option<PathBuf>,
}

pub struct Transaction<'conn> {
    conn: &'conn Connection,
    drop_behavior: DropBehavior,
}

有効期間の問題を解決するには、これらの有効期間パラメーターを追加して機能させる必要がありました。

pub struct DatastoreInstance<'a> {
    conn: Connection,
    events_transaction: Transaction<'a>,
}

これは結果であり、ライフタイムとミューテックスの両方の理解に従って動作するはずでしたが、次のようなコンパイラ エラーが表示されます。

`std::cell::RefCell<lru_cache::LruCache<std::string::String, rusqlite::raw_statement::RawStatement>>` cannot be shared between threads safely
    |                                                                                                            
    = help: within `rusqlite::Connection`, the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<lru_cache::LruCache<std::string::String, rusqlite::raw_statement::RawStatement>>`
    = note: required because it appears within the type `rusqlite::cache::StatementCache`                        
    = note: required because it appears within the type `rusqlite::Connection`                                   
    = note: required because of the requirements on the impl of `std::marker::Send` for `&rusqlite::Connection`  
    = note: required because it appears within the type `datastore::DatastoreInstance<'_>`                       
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<datastore::DatastoreInstance<'_>>`
    = note: required because it appears within the type `endpoints::ServerState<'_>`                             
    = note: required by `rocket::State`

DatastoreInstanceミューテックスに関する私の理解によれば、構造体全体が にラップされているため、このコードは有効である必要がありますMutex。これにより、一度に 1 つのスレッドのみがこのオブジェクトを参照することが保証されます。

私は何が欠けていますか?

a 内だけではなく、a 内で参照RefCellされている内にいると、コンパイラがもう安全であると判断しないのはなぜですか?ConnectionTransactionConnection

ミューテックスの仕組みをよく理解していませんか? 私のライフタイムは無効で、何らかの形で読み取り/書き込みの安全性が損なわれていますか? Connectionandを同じ構造内に持つ設計は、Transaction読み取り/書き込みの安全性を損なう悪い設計ですか? これを安全にするために、何らかの形でデータ構造を再設計する必要がありますか? それとも、非常に明白な何かが欠けているだけですか?

4

1 に答える 1

6

AMutexのみSend、またはSync 含まれる値がそれ自体である場合Send:

impl<T: ?Sized + Send> Send for Mutex<T>    
impl<T: ?Sized + Send> Sync for Mutex<T>

Aはがの場合&Tのみ:Send TSync

impl<'a, T> Send for &'a T
where
    T: Sync + ?Sized, 

そして aRefCell は決してSync

impl<T> !Sync for RefCell<T>
where
    T: ?Sized, 

エラー メッセージが示すように、トランザクションには への参照が含まれていますRefCell。ミューテックスが存在することは問題ではありません。スレッド間で共有することは本質的にメモリセーフではありません。簡単な再現:

use std::{cell::RefCell, sync::Mutex};

struct Connection(RefCell<i32>);
struct Transaction<'a>(&'a Connection);

fn is_send<T: Send>(_: T) {}

fn main() {
    let c = Connection(RefCell::new(42));
    let t = Transaction(&c);
    let m = Mutex::new(t);

    is_send(m);
}
error[E0277]: `std::cell::RefCell<i32>` cannot be shared between threads safely
  --> src/main.rs:13:5
   |
13 |     is_send(m);
   |     ^^^^^^^ `std::cell::RefCell<i32>` cannot be shared between threads safely
   |
   = help: within `Connection`, the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
   = note: required because it appears within the type `Connection`
   = note: required because of the requirements on the impl of `std::marker::Send` for `&Connection`
   = note: required because it appears within the type `Transaction<'_>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<Transaction<'_>>`
note: required by `is_send`
  --> src/main.rs:6:1
   |
6  | fn is_send<T: Send>(_: T) {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^

a 内だけではなく、a 内で参照RefCellされている内にいると、コンパイラがもう安全であると判断しないのはなぜですか?ConnectionTransactionConnection

RefCell問題ありません。そうでないのはa への参照RefCellです。

Connectionandを同じ構造内に持つ設計はTransaction悪い設計ですか [...] データ構造を再設計する必要がありますか?

はい。

于 2018-10-14T14:08:46.740 に答える