storage: Introduce storage coordination thread and infrastructure for parallel next-generation IndexedDB implementation (#40661)

This PR introduces a new storage coordination thread, intended to serve
as the central point for managing all current and future storage
endpoints in Servo.

In addition to the new coordination thread, this PR also lays the
infrastructure required to develop a parallel, next-generation IndexedDB
implementation under the indexeddb_next feature flag living on a
separate branch.

Testing: Unit and WPT tests continue to pass

---------

Signed-off-by: Jan Varga <jvarga@igalia.com>
This commit is contained in:
Jan Varga
2025-11-28 11:40:02 +01:00
committed by GitHub
parent 516cdbd7d0
commit 1d0540810e
27 changed files with 186 additions and 54 deletions

View File

@@ -167,7 +167,8 @@ use serde::{Deserialize, Serialize};
use servo_config::{opts, pref};
use servo_url::{Host, ImmutableOrigin, ServoUrl};
use storage_traits::StorageThreads;
use storage_traits::indexeddb_thread::{IndexedDBThreadMsg, SyncOperation};
use storage_traits::client_storage::ClientStorageThreadMessage;
use storage_traits::indexeddb::{IndexedDBThreadMsg, SyncOperation};
use storage_traits::webstorage_thread::{StorageType, WebStorageThreadMsg};
use style::global_style_data::StyleThreadPool;
#[cfg(feature = "webgpu")]
@@ -2592,10 +2593,12 @@ where
// Channels to receive signals when threads are done exiting.
let (core_ipc_sender, core_ipc_receiver) =
ipc::channel().expect("Failed to create IPC channel!");
let (storage_ipc_sender, storage_ipc_receiver) =
generic_channel::channel().expect("Failed to create IPC channel!");
let (client_storage_generic_sender, client_storage_generic_receiver) =
generic_channel::channel().expect("Failed to create generic channel!");
let (indexeddb_ipc_sender, indexeddb_ipc_receiver) =
ipc::channel().expect("Failed to create IPC channel!");
let (web_storage_generic_sender, web_storage_generic_receiver) =
generic_channel::channel().expect("Failed to create generic channel!");
debug!("Exiting core resource threads.");
if let Err(e) = self
@@ -2613,14 +2616,13 @@ where
}
}
debug!("Exiting storage resource threads.");
debug!("Exiting client storage thread.");
if let Err(e) = generic_channel::GenericSend::send(
&self.public_storage_threads,
WebStorageThreadMsg::Exit(storage_ipc_sender),
ClientStorageThreadMessage::Exit(client_storage_generic_sender),
) {
warn!("Exit storage thread failed ({})", e);
warn!("Exit client storage thread failed ({})", e);
}
debug!("Exiting indexeddb resource threads.");
if let Err(e) =
self.public_storage_threads
@@ -2630,6 +2632,13 @@ where
{
warn!("Exit indexeddb thread failed ({})", e);
}
debug!("Exiting web storage thread.");
if let Err(e) = generic_channel::GenericSend::send(
&self.public_storage_threads,
WebStorageThreadMsg::Exit(web_storage_generic_sender),
) {
warn!("Exit web storage thread failed ({})", e);
}
#[cfg(feature = "bluetooth")]
{
@@ -2698,12 +2707,15 @@ where
if let Err(e) = core_ipc_receiver.recv() {
warn!("Exit resource thread failed ({:?})", e);
}
if let Err(e) = storage_ipc_receiver.recv() {
warn!("Exit storage thread failed ({:?})", e);
if let Err(e) = client_storage_generic_receiver.recv() {
warn!("Exit client storage thread failed ({:?})", e);
}
if let Err(e) = indexeddb_ipc_receiver.recv() {
warn!("Exit indexeddb thread failed ({:?})", e);
}
if let Err(e) = web_storage_generic_receiver.recv() {
warn!("Exit web storage thread failed ({:?})", e);
}
debug!("Shutting-down IPC router thread in constellation.");
ROUTER.shutdown();

View File

@@ -286,7 +286,7 @@ pub(crate) struct GlobalScope {
resource_threads: ResourceThreads,
/// Associated resource threads for use by DOM objects like XMLHttpRequest,
/// including indexeddb_thread and storage_thread
/// including indexeddb thread and storage_thread
#[no_trace]
storage_threads: StorageThreads,

View File

@@ -8,7 +8,7 @@ use dom_struct::dom_struct;
use js::jsapi::Heap;
use js::jsval::{JSVal, UndefinedValue};
use js::rust::MutableHandleValue;
use storage_traits::indexeddb_thread::{IndexedDBKeyRange, IndexedDBKeyType, IndexedDBRecord};
use storage_traits::indexeddb::{IndexedDBKeyRange, IndexedDBKeyType, IndexedDBRecord};
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::IDBCursorBinding::{
@@ -25,7 +25,7 @@ use crate::dom::indexeddb::idbindex::IDBIndex;
use crate::dom::indexeddb::idbobjectstore::IDBObjectStore;
use crate::dom::indexeddb::idbrequest::IDBRequest;
use crate::dom::indexeddb::idbtransaction::IDBTransaction;
use crate::indexed_db::key_type_to_jsval;
use crate::indexeddb::key_type_to_jsval;
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
#[derive(JSTraceable, MallocSizeOf)]

View File

@@ -4,7 +4,7 @@
use dom_struct::dom_struct;
use js::rust::MutableHandleValue;
use storage_traits::indexeddb_thread::IndexedDBKeyRange;
use storage_traits::indexeddb::IndexedDBKeyRange;
use crate::dom::bindings::codegen::Bindings::IDBCursorBinding::IDBCursorDirection;
use crate::dom::bindings::codegen::Bindings::IDBCursorWithValueBinding::IDBCursorWithValueMethods;

View File

@@ -8,7 +8,7 @@ use base::IpcSend;
use dom_struct::dom_struct;
use ipc_channel::ipc::IpcSender;
use profile_traits::ipc;
use storage_traits::indexeddb_thread::{IndexedDBThreadMsg, KeyPath, SyncOperation};
use storage_traits::indexeddb::{IndexedDBThreadMsg, KeyPath, SyncOperation};
use stylo_atoms::Atom;
use crate::dom::bindings::cell::DomRefCell;
@@ -30,7 +30,7 @@ use crate::dom::globalscope::GlobalScope;
use crate::dom::indexeddb::idbobjectstore::IDBObjectStore;
use crate::dom::indexeddb::idbtransaction::IDBTransaction;
use crate::dom::indexeddb::idbversionchangeevent::IDBVersionChangeEvent;
use crate::indexed_db::is_valid_key_path;
use crate::indexeddb::is_valid_key_path;
use crate::script_runtime::CanGc;
#[dom_struct]

View File

@@ -13,7 +13,7 @@ use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use crate::dom::indexeddb::idbopendbrequest::IDBOpenDBRequest;
use crate::indexed_db::convert_value_to_key;
use crate::indexeddb::convert_value_to_key;
use crate::script_runtime::CanGc;
#[dom_struct]

View File

@@ -8,13 +8,13 @@ use js::rust::HandleValue;
use script_bindings::codegen::GenericBindings::IDBKeyRangeBinding::IDBKeyRangeMethods;
use script_bindings::root::DomRoot;
use script_bindings::script_runtime::CanGc;
use storage_traits::indexeddb_thread::IndexedDBKeyRange;
use storage_traits::indexeddb::IndexedDBKeyRange;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::import::module::SafeJSContext;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::globalscope::GlobalScope;
use crate::indexed_db::{convert_value_to_key, key_type_to_jsval};
use crate::indexeddb::{convert_value_to_key, key_type_to_jsval};
#[dom_struct]
pub struct IDBKeyRange {

View File

@@ -10,7 +10,7 @@ use js::rust::HandleValue;
use profile_traits::ipc;
use script_bindings::conversions::SafeToJSValConvertible;
use script_bindings::error::ErrorResult;
use storage_traits::indexeddb_thread::{
use storage_traits::indexeddb::{
AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, IndexedDBKeyType,
IndexedDBThreadMsg, SyncOperation,
};
@@ -36,7 +36,7 @@ use crate::dom::indexeddb::idbcursor::{IDBCursor, IterationParam, ObjectStoreOrI
use crate::dom::indexeddb::idbcursorwithvalue::IDBCursorWithValue;
use crate::dom::indexeddb::idbrequest::IDBRequest;
use crate::dom::indexeddb::idbtransaction::IDBTransaction;
use crate::indexed_db::{
use crate::indexeddb::{
self, ExtractionResult, convert_value_to_key, convert_value_to_key_range, extract_key,
};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
@@ -256,7 +256,7 @@ impl IDBObjectStore {
return Err(Error::InvalidState(None));
};
let (sender, receiver) = indexed_db::create_channel(self.global());
let (sender, receiver) = indexeddb::create_channel(self.global());
// Step 12. Let operation be an algorithm to run store a record into an object store with store, clone, key, and no-overwrite flag.
// Step 13. Return the result (an IDBRequest) of running asynchronously execute a request with handle and operation.
@@ -341,7 +341,7 @@ impl IDBObjectStore {
primary_key: None,
count: None,
};
let (sender, receiver) = indexed_db::create_channel(self.global());
let (sender, receiver) = indexeddb::create_channel(self.global());
IDBRequest::execute_async(
self,
AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Iterate {
@@ -393,7 +393,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
let serialized_query = convert_value_to_key_range(cx, query, Some(true));
// Step 7. Let operation be an algorithm to run delete records from an object store with store and range.
// Step 8. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
let (sender, receiver) = indexed_db::create_channel(self.global());
let (sender, receiver) = indexeddb::create_channel(self.global());
serialized_query.and_then(|key_range| {
IDBRequest::execute_async(
self,
@@ -422,7 +422,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
// Step 6. Let operation be an algorithm to run clear an object store with store.
// Step 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
let (sender, receiver) = indexed_db::create_channel(self.global());
let (sender, receiver) = indexeddb::create_channel(self.global());
IDBRequest::execute_async(
self,
@@ -449,7 +449,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
// Step 6. Let operation be an algorithm to run retrieve a value from an object store with the current Realm record, store, and range.
// Step 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
let (sender, receiver) = indexed_db::create_channel(self.global());
let (sender, receiver) = indexeddb::create_channel(self.global());
serialized_query.and_then(|q| {
IDBRequest::execute_async(
self,
@@ -481,7 +481,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
// Step 6. Run the steps to asynchronously execute a request and return the IDBRequest created by these steps.
// The steps are run with this object store handle as source and the steps to retrieve a key from an object
// store as operation, using store and range.
let (sender, receiver) = indexed_db::create_channel(self.global());
let (sender, receiver) = indexeddb::create_channel(self.global());
serialized_query.and_then(|q| {
IDBRequest::execute_async(
self,
@@ -518,7 +518,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
// Step 6. Run the steps to asynchronously execute a request and return the IDBRequest created by these steps.
// The steps are run with this object store handle as source and the steps to retrieve a key from an object
// store as operation, using store and range.
let (sender, receiver) = indexed_db::create_channel(self.global());
let (sender, receiver) = indexeddb::create_channel(self.global());
serialized_query.and_then(|q| {
IDBRequest::execute_async(
self,
@@ -556,7 +556,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
// Step 6. Run the steps to asynchronously execute a request and return the IDBRequest created by these steps.
// The steps are run with this object store handle as source and the steps to retrieve a key from an object
// store as operation, using store and range.
let (sender, receiver) = indexed_db::create_channel(self.global());
let (sender, receiver) = indexeddb::create_channel(self.global());
serialized_query.and_then(|q| {
IDBRequest::execute_async(
self,
@@ -588,7 +588,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
// Step 6. Let operation be an algorithm to run count the records in a range with store and range.
// Step 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
let (sender, receiver) = indexed_db::create_channel(self.global());
let (sender, receiver) = indexeddb::create_channel(self.global());
serialized_query.and_then(|q| {
IDBRequest::execute_async(
self,

View File

@@ -9,7 +9,7 @@ use js::jsval::UndefinedValue;
use js::rust::HandleValue;
use profile_traits::ipc;
use script_bindings::conversions::SafeToJSValConvertible;
use storage_traits::indexeddb_thread::{BackendResult, IndexedDBThreadMsg, SyncOperation};
use storage_traits::indexeddb::{BackendResult, IndexedDBThreadMsg, SyncOperation};
use stylo_atoms::Atom;
use crate::dom::bindings::codegen::Bindings::IDBOpenDBRequestBinding::IDBOpenDBRequestMethods;

View File

@@ -13,7 +13,7 @@ use js::rust::HandleValue;
use profile_traits::ipc::IpcReceiver;
use script_bindings::conversions::SafeToJSValConvertible;
use serde::{Deserialize, Serialize};
use storage_traits::indexeddb_thread::{
use storage_traits::indexeddb::{
AsyncOperation, AsyncReadOnlyOperation, BackendError, BackendResult, IndexedDBKeyType,
IndexedDBRecord, IndexedDBThreadMsg, IndexedDBTxnMode, PutItemResult,
};
@@ -37,7 +37,7 @@ use crate::dom::indexeddb::idbcursor::{IterationParam, iterate_cursor};
use crate::dom::indexeddb::idbcursorwithvalue::IDBCursorWithValue;
use crate::dom::indexeddb::idbobjectstore::IDBObjectStore;
use crate::dom::indexeddb::idbtransaction::IDBTransaction;
use crate::indexed_db::key_type_to_jsval;
use crate::indexeddb::key_type_to_jsval;
use crate::realms::enter_realm;
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};

View File

@@ -10,7 +10,7 @@ use dom_struct::dom_struct;
use ipc_channel::ipc::IpcSender;
use profile_traits::ipc;
use script_bindings::codegen::GenericUnionTypes::StringOrStringSequence;
use storage_traits::indexeddb_thread::{IndexedDBThreadMsg, KeyPath, SyncOperation};
use storage_traits::indexeddb::{IndexedDBThreadMsg, KeyPath, SyncOperation};
use stylo_atoms::Atom;
use crate::dom::bindings::cell::DomRefCell;

View File

@@ -154,7 +154,7 @@ use crate::dom::hashchangeevent::HashChangeEvent;
use crate::dom::history::History;
use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
use crate::dom::html::htmliframeelement::HTMLIFrameElement;
use crate::dom::indexeddb::idbfactory::IDBFactory;
use crate::dom::idbfactory::IDBFactory;
use crate::dom::inputevent::HitTestResult;
use crate::dom::location::Location;
use crate::dom::medialist::MediaList;

View File

@@ -67,7 +67,7 @@ use crate::dom::dedicatedworkerglobalscope::{
};
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlscriptelement::{SCRIPT_JS_MIMES, ScriptOrigin, ScriptType};
use crate::dom::indexeddb::idbfactory::IDBFactory;
use crate::dom::idbfactory::IDBFactory;
use crate::dom::performance::performance::Performance;
use crate::dom::performance::performanceresourcetiming::InitiatorType;
use crate::dom::promise::Promise;

View File

@@ -20,7 +20,7 @@ use profile_traits::ipc;
use profile_traits::ipc::IpcReceiver;
use script_bindings::script_runtime::CanGc;
use serde::{Deserialize, Serialize};
use storage_traits::indexeddb_thread::{BackendResult, IndexedDBKeyRange, IndexedDBKeyType};
use storage_traits::indexeddb::{BackendResult, IndexedDBKeyRange, IndexedDBKeyType};
use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
use crate::dom::bindings::codegen::Bindings::FileBinding::FileMethods;
@@ -37,8 +37,8 @@ use crate::dom::bindings::utils::set_dictionary_property;
use crate::dom::blob::Blob;
use crate::dom::file::File;
use crate::dom::globalscope::GlobalScope;
use crate::dom::indexeddb::idbkeyrange::IDBKeyRange;
use crate::dom::indexeddb::idbobjectstore::KeyPath;
use crate::dom::idbkeyrange::IDBKeyRange;
use crate::dom::idbobjectstore::KeyPath;
pub fn create_channel<T>(
global: DomRoot<GlobalScope>,

View File

@@ -34,7 +34,7 @@ pub(crate) mod document_loader;
mod dom;
pub(crate) use dom::canvas_context;
pub(crate) mod fetch;
pub(crate) mod indexed_db;
pub(crate) mod indexeddb;
mod init;
mod layout_image;

View File

@@ -0,0 +1,12 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use base::generic_channel::GenericSender;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
pub enum ClientStorageThreadMessage {
/// Send a reply when done cleaning up thread resources and then shut it down
Exit(GenericSender<()>),
}

View File

@@ -8,30 +8,45 @@ use ipc_channel::ipc::{IpcError, IpcSender};
use malloc_size_of::malloc_size_of_is_0;
use serde::{Deserialize, Serialize};
use crate::indexeddb_thread::IndexedDBThreadMsg;
use crate::client_storage::ClientStorageThreadMessage;
use crate::indexeddb::IndexedDBThreadMsg;
use crate::webstorage_thread::WebStorageThreadMsg;
pub mod indexeddb_thread;
pub mod client_storage;
pub mod indexeddb;
pub mod webstorage_thread;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct StorageThreads {
web_storage_thread: GenericSender<WebStorageThreadMsg>,
client_storage_thread: GenericSender<ClientStorageThreadMessage>,
idb_thread: IpcSender<IndexedDBThreadMsg>,
web_storage_thread: GenericSender<WebStorageThreadMsg>,
}
impl StorageThreads {
pub fn new(
web_storage_thread: GenericSender<WebStorageThreadMsg>,
client_storage_thread: GenericSender<ClientStorageThreadMessage>,
idb_thread: IpcSender<IndexedDBThreadMsg>,
web_storage_thread: GenericSender<WebStorageThreadMsg>,
) -> StorageThreads {
StorageThreads {
web_storage_thread,
client_storage_thread,
idb_thread,
web_storage_thread,
}
}
}
impl GenericSend<ClientStorageThreadMessage> for StorageThreads {
fn send(&self, msg: ClientStorageThreadMessage) -> SendResult {
self.client_storage_thread.send(msg)
}
fn sender(&self) -> GenericSender<ClientStorageThreadMessage> {
self.client_storage_thread.clone()
}
}
impl IpcSend<IndexedDBThreadMsg> for StorageThreads {
fn send(&self, msg: IndexedDBThreadMsg) -> IpcSendResult {
self.idb_thread.send(msg).map_err(IpcError::Bincode)

View File

@@ -0,0 +1,66 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::path::PathBuf;
use std::thread;
use base::generic_channel::{self, GenericReceiver, GenericSender};
use storage_traits::client_storage::ClientStorageThreadMessage;
pub trait ClientStorageThreadFactory {
fn new(config_dir: Option<PathBuf>) -> Self;
}
impl ClientStorageThreadFactory for GenericSender<ClientStorageThreadMessage> {
fn new(config_dir: Option<PathBuf>) -> GenericSender<ClientStorageThreadMessage> {
let (generic_sender, generic_receiver) = generic_channel::channel().unwrap();
let generic_sender_clone = generic_sender.clone();
thread::Builder::new()
.name("ClientStorageThread".to_owned())
.spawn(move || {
ClientStorageThread::new(config_dir, generic_sender, generic_receiver).start();
})
.expect("Thread spawning failed");
generic_sender_clone
}
}
pub struct ClientStorageThread {
_base_dir: PathBuf,
_generic_sender: GenericSender<ClientStorageThreadMessage>,
generic_receiver: GenericReceiver<ClientStorageThreadMessage>,
}
impl ClientStorageThread {
pub fn new(
config_dir: Option<PathBuf>,
generic_sender: GenericSender<ClientStorageThreadMessage>,
generic_receiver: GenericReceiver<ClientStorageThreadMessage>,
) -> ClientStorageThread {
let base_dir = config_dir
.unwrap_or_else(|| PathBuf::from("."))
.join("clientstorage");
ClientStorageThread {
_base_dir: base_dir,
_generic_sender: generic_sender,
generic_receiver,
}
}
pub fn start(&mut self) {
#[allow(clippy::never_loop)]
loop {
match self.generic_receiver.recv().unwrap() {
ClientStorageThreadMessage::Exit(sender) => {
let _ = sender.send(());
return;
},
}
}
}
}

View File

@@ -4,9 +4,7 @@
use std::collections::VecDeque;
use storage_traits::indexeddb_thread::{
AsyncOperation, CreateObjectResult, IndexedDBTxnMode, KeyPath,
};
use storage_traits::indexeddb::{AsyncOperation, CreateObjectResult, IndexedDBTxnMode, KeyPath};
use tokio::sync::oneshot;
pub use self::sqlite::SqliteEngine;

View File

@@ -11,7 +11,7 @@ use rusqlite::{Connection, Error, OptionalExtension, params};
use sea_query::{Condition, Expr, ExprTrait, IntoCondition, SqliteQueryBuilder};
use sea_query_rusqlite::RusqliteBinder;
use serde::Serialize;
use storage_traits::indexeddb_thread::{
use storage_traits::indexeddb::{
AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, BackendError, BackendResult,
CreateObjectResult, IndexedDBKeyRange, IndexedDBKeyType, IndexedDBRecord, IndexedDBTxnMode,
KeyPath, PutItemResult,
@@ -672,7 +672,7 @@ mod tests {
use base::threadpool::ThreadPool;
use serde::{Deserialize, Serialize};
use servo_url::ImmutableOrigin;
use storage_traits::indexeddb_thread::{
use storage_traits::indexeddb::{
AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, CreateObjectResult,
IndexedDBKeyRange, IndexedDBKeyType, IndexedDBTxnMode, KeyPath, PutItemResult,
};

View File

@@ -17,7 +17,7 @@ use log::{debug, warn};
use rustc_hash::FxHashMap;
use servo_config::pref;
use servo_url::origin::ImmutableOrigin;
use storage_traits::indexeddb_thread::{
use storage_traits::indexeddb::{
AsyncOperation, BackendError, BackendResult, CreateObjectResult, DbResult, IndexedDBThreadMsg,
IndexedDBTxnMode, KeyPath, SyncOperation,
};

View File

@@ -2,11 +2,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
pub mod client_storage;
mod indexeddb;
pub(crate) mod shared;
mod storage_thread;
mod webstorage;
pub use client_storage::ClientStorageThreadFactory;
pub(crate) use indexeddb::IndexedDBThreadFactory;
pub use storage_thread::new_storage_threads;
pub(crate) use webstorage::WebStorageThreadFactory;

View File

@@ -8,21 +8,24 @@ use base::generic_channel::GenericSender;
use ipc_channel::ipc::IpcSender;
use profile_traits::mem::ProfilerChan as MemProfilerChan;
use storage_traits::StorageThreads;
use storage_traits::indexeddb_thread::IndexedDBThreadMsg;
use storage_traits::client_storage::ClientStorageThreadMessage;
use storage_traits::indexeddb::IndexedDBThreadMsg;
use storage_traits::webstorage_thread::WebStorageThreadMsg;
use crate::{IndexedDBThreadFactory, WebStorageThreadFactory};
use crate::{ClientStorageThreadFactory, IndexedDBThreadFactory, WebStorageThreadFactory};
#[allow(clippy::too_many_arguments)]
pub fn new_storage_threads(
mem_profiler_chan: MemProfilerChan,
config_dir: Option<PathBuf>,
) -> (StorageThreads, StorageThreads) {
let client_storage: GenericSender<ClientStorageThreadMessage> =
ClientStorageThreadFactory::new(config_dir.clone());
let idb: IpcSender<IndexedDBThreadMsg> = IndexedDBThreadFactory::new(config_dir.clone());
let web_storage: GenericSender<WebStorageThreadMsg> =
WebStorageThreadFactory::new(config_dir, mem_profiler_chan);
(
StorageThreads::new(web_storage.clone(), idb.clone()),
StorageThreads::new(web_storage, idb),
StorageThreads::new(client_storage.clone(), idb.clone(), web_storage.clone()),
StorageThreads::new(client_storage, idb, web_storage),
)
}

View File

@@ -0,0 +1,22 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use base::generic_channel::{self, GenericSender};
use storage::ClientStorageThreadFactory;
use storage_traits::client_storage::ClientStorageThreadMessage;
#[test]
fn test_exit() {
let thread: GenericSender<ClientStorageThreadMessage> = ClientStorageThreadFactory::new(None);
let (sender, receiver) = generic_channel::channel().unwrap();
thread
.send(ClientStorageThreadMessage::Exit(sender))
.unwrap();
receiver.recv().unwrap();
// Workaround for https://github.com/servo/servo/issues/32912
#[cfg(windows)]
std::thread::sleep(std::time::Duration::from_millis(1000));
}

View File

@@ -2,4 +2,5 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
mod client_storage;
mod webstorage;

View File

@@ -233,6 +233,7 @@ class MachCommands(CommandBase):
"servoshell",
"servo_url",
"storage",
"storage_traits",
"xpath",
]
if not packages: