settings_storage/
storage_factory.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::device_storage::DeviceStorage;
6use super::fidl_storage::FidlStorage;
7use crate::private::Sealed;
8use crate::stash_logger::StashInspectLogger;
9use anyhow::{format_err, Error};
10use fidl::endpoints::create_proxy;
11use fidl_fuchsia_io::DirectoryProxy;
12use fidl_fuchsia_stash::StoreProxy;
13use futures::lock::Mutex;
14use futures::Future;
15use std::any::Any;
16use std::collections::HashMap;
17use std::rc::Rc;
18
19pub trait DefaultLoader {
20    type Result;
21    fn default_value(&self) -> Self::Result;
22}
23impl<T> Sealed for T where T: DefaultLoader {}
24
25// Use when no loader is needed.
26pub struct NoneT;
27impl Sealed for NoneT {}
28
29/// `DeviceStorageFactory` abstracts over how to initialize and retrieve the `DeviceStorage`
30/// instance.
31pub trait StorageFactory {
32    /// The storage type used to manage persisted data.
33    type Storage;
34
35    /// Initialize the storage to be able to manage storage for objects of type T.
36    /// This will return an Error once `get_store` is called the first time.
37    fn initialize<T>(&self) -> impl Future<Output = Result<(), Error>>
38    where
39        T: StorageAccess<Storage = Self::Storage>;
40
41    /// Initialize the storage to be able to manage storage for objects of type T.
42    /// This will return an Error once `get_store` is called the first time.
43    fn initialize_with_loader<T, L>(&self, loader: L) -> impl Future<Output = Result<(), Error>>
44    where
45        T: StorageAccess<Storage = Self::Storage>,
46        L: DefaultLoader<Result = T::Data> + 'static;
47
48    /// Retrieve the store singleton instance.
49    fn get_store(&self) -> impl Future<Output = Rc<Self::Storage>>;
50}
51
52/// A trait for describing which storages an item needs access to.
53/// See [StashDeviceStorageFactory::initialize] for usage.
54/// # Example
55///
56/// ```
57/// # struct SomeItem;
58/// # struct StorageItem;
59///
60/// impl DeviceStorageCompatible for StorageItem {
61///    # fn default_value() -> Self { StorageItem }
62///    // ...
63///    const KEY: &'static str = "some_key";
64/// }
65///
66/// impl StorageAccess for SomeItem {
67///     type Storage = DeviceStorage;
68///     const STORAGE_KEY: &'static str = StorageItem::KEY;
69/// }
70/// ```
71pub trait StorageAccess {
72    type Storage;
73    type Data;
74
75    /// This field should be populated with the key used by the corresponding storage mechanism.
76    const STORAGE_KEY: &'static str;
77}
78
79/// The state of the factory. Only one state can be active at a time because once
80/// the [`DeviceStorage`] is created, there's no way to change the keys, so there's
81/// no need to keep the set of keys anymore.
82pub enum InitializationState<T, U = ()> {
83    /// This represents the state of the factory before the first request to get
84    /// [`DeviceStorage`]. It maintains a list of all keys that might be used for
85    /// storage.
86    Initializing(HashMap<&'static str, Option<Box<dyn Any>>>, U),
87    /// A temporary state used to help in the conversion from [Initializing] to [Initialized]. This
88    /// value is never intended to be read, but is necessary to keep the memory valid while
89    /// ownership is taken of the values in [Initializing], but before the values in [Initialized]
90    /// are ready.
91    Partial,
92    /// This represents the initialized state. When this is active, it is no longer
93    /// possible to add new storage keys to [`DeviceStorage`].
94    Initialized(Rc<T>),
95}
96
97impl<T> InitializationState<T, ()> {
98    /// Construct the default `InitializationState`.
99    pub fn new() -> Self {
100        Self::Initializing(HashMap::new(), ())
101    }
102}
103
104impl<T, U> Default for InitializationState<T, U>
105where
106    U: Default,
107{
108    fn default() -> Self {
109        Self::Initializing(Default::default(), Default::default())
110    }
111}
112
113impl<T> InitializationState<T, DirectoryProxy> {
114    /// Construct the default `InitializationState`.
115    pub fn with_storage_dir(storage_dir: DirectoryProxy) -> Self {
116        Self::Initializing(HashMap::new(), storage_dir)
117    }
118}
119
120/// Factory that vends out storage.
121pub struct StashDeviceStorageFactory {
122    store: StoreProxy,
123    device_storage_cache: Mutex<InitializationState<DeviceStorage>>,
124    inspect_handle: Rc<Mutex<StashInspectLogger>>,
125}
126
127impl StashDeviceStorageFactory {
128    /// Construct a new instance of `StashDeviceStorageFactory`.
129    pub fn new(
130        store: StoreProxy,
131        inspect_handle: Rc<Mutex<StashInspectLogger>>,
132    ) -> StashDeviceStorageFactory {
133        StashDeviceStorageFactory {
134            store,
135            device_storage_cache: Mutex::new(InitializationState::new()),
136            inspect_handle,
137        }
138    }
139
140    // Speeds up compilation by not needing to monomorphize this code for all T's.
141    async fn initialize_storage(&self, key: &'static str) -> Result<(), Error> {
142        match &mut *self.device_storage_cache.lock().await {
143            InitializationState::Initializing(initial_keys, ()) => {
144                let _ = initial_keys.insert(key, None);
145                Ok(())
146            }
147            InitializationState::Initialized(_) => {
148                Err(format_err!("Cannot initialize an already accessed device storage"))
149            }
150            _ => unreachable!(),
151        }
152    }
153
154    // Speeds up compilation by not needing to monomorphize this code for all T's.
155    async fn initialize_storage_with_loader(
156        &self,
157        key: &'static str,
158        loader: Box<dyn Any>,
159    ) -> Result<(), Error> {
160        match &mut *self.device_storage_cache.lock().await {
161            InitializationState::Initializing(initial_keys, ()) => {
162                let _ = initial_keys.insert(key, Some(loader));
163                Ok(())
164            }
165            InitializationState::Initialized(_) => {
166                Err(format_err!("Cannot initialize an already accessed device storage"))
167            }
168            _ => unreachable!(),
169        }
170    }
171}
172
173impl StorageFactory for StashDeviceStorageFactory {
174    type Storage = DeviceStorage;
175
176    async fn initialize<T>(&self) -> Result<(), Error>
177    where
178        T: StorageAccess<Storage = DeviceStorage>,
179    {
180        self.initialize_storage(T::STORAGE_KEY).await
181    }
182
183    async fn initialize_with_loader<T, L>(&self, loader: L) -> Result<(), Error>
184    where
185        T: StorageAccess<Storage = DeviceStorage>,
186        L: DefaultLoader<Result = T::Data> + 'static,
187    {
188        self.initialize_storage_with_loader(T::STORAGE_KEY, Box::new(loader) as Box<dyn Any>).await
189    }
190
191    async fn get_store(&self) -> Rc<DeviceStorage> {
192        let initialization = &mut *self.device_storage_cache.lock().await;
193        match initialization {
194            InitializationState::Initializing(initial_keys, ()) => {
195                let device_storage = Rc::new(DeviceStorage::with_stash_proxy(
196                    initial_keys.drain(),
197                    || {
198                        let (accessor_proxy, server_end) = create_proxy();
199                        self.store
200                            .create_accessor(false, server_end)
201                            .expect("failed to create accessor for stash");
202                        accessor_proxy
203                    },
204                    Rc::clone(&self.inspect_handle),
205                ));
206                *initialization = InitializationState::Initialized(Rc::clone(&device_storage));
207                device_storage
208            }
209            InitializationState::Initialized(device_storage) => Rc::clone(device_storage),
210            _ => unreachable!(),
211        }
212    }
213}
214
215/// Factory that vends out storage.
216pub struct FidlStorageFactory {
217    migration_id: u64,
218    device_storage_cache: Mutex<InitializationState<FidlStorage, DirectoryProxy>>,
219}
220
221impl FidlStorageFactory {
222    /// Construct a new instance of `FidlStorageFactory`.
223    pub fn new(migration_id: u64, storage_dir: DirectoryProxy) -> FidlStorageFactory {
224        FidlStorageFactory {
225            migration_id,
226            device_storage_cache: Mutex::new(InitializationState::with_storage_dir(storage_dir)),
227        }
228    }
229
230    // Speeds up compilation by not needing to monomorphize this code for all T's.
231    async fn initialize_storage(&self, key: &'static str) -> Result<(), Error> {
232        match &mut *self.device_storage_cache.lock().await {
233            InitializationState::Initializing(initial_keys, _) => {
234                let _ = initial_keys.insert(key, None);
235                Ok(())
236            }
237            InitializationState::Initialized(_) => {
238                Err(format_err!("Cannot initialize an already accessed device storage"))
239            }
240            _ => unreachable!(),
241        }
242    }
243
244    // Speeds up compilation by not needing to monomorphize this code for all T's.
245    async fn initialize_storage_with_loader(
246        &self,
247        key: &'static str,
248        loader: Box<dyn Any>,
249    ) -> Result<(), Error> {
250        match &mut *self.device_storage_cache.lock().await {
251            InitializationState::Initializing(initial_keys, _) => {
252                let _ = initial_keys.insert(key, Some(loader));
253                Ok(())
254            }
255            InitializationState::Initialized(_) => {
256                Err(format_err!("Cannot initialize an already accessed device storage"))
257            }
258            _ => unreachable!(),
259        }
260    }
261}
262
263impl StorageFactory for FidlStorageFactory {
264    type Storage = FidlStorage;
265
266    async fn initialize<T>(&self) -> Result<(), Error>
267    where
268        T: StorageAccess<Storage = FidlStorage>,
269    {
270        self.initialize_storage(T::STORAGE_KEY).await
271    }
272
273    async fn initialize_with_loader<T, L>(&self, loader: L) -> Result<(), Error>
274    where
275        T: StorageAccess<Storage = FidlStorage>,
276        L: DefaultLoader<Result = T::Data> + 'static,
277    {
278        self.initialize_storage_with_loader(T::STORAGE_KEY, Box::new(loader) as Box<dyn Any>).await
279    }
280
281    async fn get_store(&self) -> Rc<FidlStorage> {
282        let initialization = &mut *self.device_storage_cache.lock().await;
283        match initialization {
284            InitializationState::Initializing(..) => {
285                let (initial_keys, storage_dir) =
286                    match std::mem::replace(initialization, InitializationState::Partial) {
287                        InitializationState::Initializing(initial_keys, storage_dir) => {
288                            (initial_keys, storage_dir)
289                        }
290                        _ => unreachable!(),
291                    };
292                let migration_id = self.migration_id;
293                let (device_storage, sync_tasks) =
294                    FidlStorage::with_file_proxy(initial_keys, storage_dir, move |key| {
295                        let temp_file_name = format!("{key}.tmp");
296                        let file_name = format!("{key}_{migration_id}.pfidl");
297                        Ok((temp_file_name, file_name))
298                    })
299                    .await
300                    .expect("failed to get storage");
301                for task in sync_tasks {
302                    task.detach();
303                }
304
305                let device_storage = Rc::new(device_storage);
306                *initialization = InitializationState::Initialized(Rc::clone(&device_storage));
307                device_storage
308            }
309            InitializationState::Initialized(device_storage) => Rc::clone(device_storage),
310            _ => unreachable!(),
311        }
312    }
313}