1use std::{any::type_name, sync::Arc};
2
3use anyhow::Result;
4use either::Either;
5use smallvec::SmallVec;
6use turbo_tasks::{TaskId, backend::CachedTaskType};
7
8use crate::{
9 backend::{AnyOperation, TaskDataCategory},
10 data::CachedDataItem,
11 utils::chunked_vec::ChunkedVec,
12};
13
14pub trait BackingStorage: BackingStorageSealed {
24 fn invalidate(&self, reason_code: &str) -> Result<()>;
36}
37
38pub trait BackingStorageSealed: 'static + Send + Sync {
44 type ReadTransaction<'l>;
45 fn next_free_task_id(&self) -> Result<TaskId>;
46 fn uncompleted_operations(&self) -> Result<Vec<AnyOperation>>;
47 #[allow(clippy::ptr_arg)]
48 fn serialize(&self, task: TaskId, data: &Vec<CachedDataItem>) -> Result<SmallVec<[u8; 16]>>;
49 fn save_snapshot<I>(
50 &self,
51 operations: Vec<Arc<AnyOperation>>,
52 task_cache_updates: Vec<ChunkedVec<(Arc<CachedTaskType>, TaskId)>>,
53 snapshots: Vec<I>,
54 ) -> Result<()>
55 where
56 I: Iterator<
57 Item = (
58 TaskId,
59 Option<SmallVec<[u8; 16]>>,
60 Option<SmallVec<[u8; 16]>>,
61 ),
62 > + Send
63 + Sync;
64 fn start_read_transaction(&self) -> Option<Self::ReadTransaction<'_>>;
65 unsafe fn forward_lookup_task_cache(
69 &self,
70 tx: Option<&Self::ReadTransaction<'_>>,
71 key: &CachedTaskType,
72 ) -> Result<Option<TaskId>>;
73 unsafe fn reverse_lookup_task_cache(
77 &self,
78 tx: Option<&Self::ReadTransaction<'_>>,
79 task_id: TaskId,
80 ) -> Result<Option<Arc<CachedTaskType>>>;
81 unsafe fn lookup_data(
85 &self,
86 tx: Option<&Self::ReadTransaction<'_>>,
87 task_id: TaskId,
88 category: TaskDataCategory,
89 ) -> Result<Vec<CachedDataItem>>;
90
91 fn shutdown(&self) -> Result<()> {
92 Ok(())
93 }
94}
95
96impl<L, R> BackingStorage for Either<L, R>
97where
98 L: BackingStorage,
99 R: BackingStorage,
100{
101 fn invalidate(&self, reason_code: &str) -> Result<()> {
102 either::for_both!(self, this => this.invalidate(reason_code))
103 }
104}
105
106impl<L, R> BackingStorageSealed for Either<L, R>
107where
108 L: BackingStorageSealed,
109 R: BackingStorageSealed,
110{
111 type ReadTransaction<'l> = Either<L::ReadTransaction<'l>, R::ReadTransaction<'l>>;
112
113 fn next_free_task_id(&self) -> Result<TaskId> {
114 either::for_both!(self, this => this.next_free_task_id())
115 }
116
117 fn uncompleted_operations(&self) -> Result<Vec<AnyOperation>> {
118 either::for_both!(self, this => this.uncompleted_operations())
119 }
120
121 fn serialize(&self, task: TaskId, data: &Vec<CachedDataItem>) -> Result<SmallVec<[u8; 16]>> {
122 either::for_both!(self, this => this.serialize(task, data))
123 }
124
125 fn save_snapshot<I>(
126 &self,
127 operations: Vec<Arc<AnyOperation>>,
128 task_cache_updates: Vec<ChunkedVec<(Arc<CachedTaskType>, TaskId)>>,
129 snapshots: Vec<I>,
130 ) -> Result<()>
131 where
132 I: Iterator<
133 Item = (
134 TaskId,
135 Option<SmallVec<[u8; 16]>>,
136 Option<SmallVec<[u8; 16]>>,
137 ),
138 > + Send
139 + Sync,
140 {
141 either::for_both!(self, this => this.save_snapshot(
142 operations,
143 task_cache_updates,
144 snapshots,
145 ))
146 }
147
148 fn start_read_transaction(&self) -> Option<Self::ReadTransaction<'_>> {
149 Some(match self {
150 Either::Left(this) => Either::Left(this.start_read_transaction()?),
151 Either::Right(this) => Either::Right(this.start_read_transaction()?),
152 })
153 }
154
155 unsafe fn forward_lookup_task_cache(
156 &self,
157 tx: Option<&Self::ReadTransaction<'_>>,
158 key: &CachedTaskType,
159 ) -> Result<Option<TaskId>> {
160 match self {
161 Either::Left(this) => {
162 let tx = tx.map(|tx| read_transaction_left_or_panic(tx.as_ref()));
163 unsafe { this.forward_lookup_task_cache(tx, key) }
164 }
165 Either::Right(this) => {
166 let tx = tx.map(|tx| read_transaction_right_or_panic(tx.as_ref()));
167 unsafe { this.forward_lookup_task_cache(tx, key) }
168 }
169 }
170 }
171
172 unsafe fn reverse_lookup_task_cache(
173 &self,
174 tx: Option<&Self::ReadTransaction<'_>>,
175 task_id: TaskId,
176 ) -> Result<Option<Arc<CachedTaskType>>> {
177 match self {
178 Either::Left(this) => {
179 let tx = tx.map(|tx| read_transaction_left_or_panic(tx.as_ref()));
180 unsafe { this.reverse_lookup_task_cache(tx, task_id) }
181 }
182 Either::Right(this) => {
183 let tx = tx.map(|tx| read_transaction_right_or_panic(tx.as_ref()));
184 unsafe { this.reverse_lookup_task_cache(tx, task_id) }
185 }
186 }
187 }
188
189 unsafe fn lookup_data(
190 &self,
191 tx: Option<&Self::ReadTransaction<'_>>,
192 task_id: TaskId,
193 category: TaskDataCategory,
194 ) -> Result<Vec<CachedDataItem>> {
195 match self {
196 Either::Left(this) => {
197 let tx = tx.map(|tx| read_transaction_left_or_panic(tx.as_ref()));
198 unsafe { this.lookup_data(tx, task_id, category) }
199 }
200 Either::Right(this) => {
201 let tx = tx.map(|tx| read_transaction_right_or_panic(tx.as_ref()));
202 unsafe { this.lookup_data(tx, task_id, category) }
203 }
204 }
205 }
206
207 fn shutdown(&self) -> Result<()> {
208 either::for_both!(self, this => this.shutdown())
209 }
210}
211
212fn read_transaction_left_or_panic<L, R>(either: Either<L, R>) -> L {
214 match either {
215 Either::Left(l) => l,
216 Either::Right(_) => panic!(
217 "expected ReadTransaction of Either::Left containing {}, received Either::Right type \
218 of {}",
219 type_name::<L>(),
220 type_name::<R>(),
221 ),
222 }
223}
224
225fn read_transaction_right_or_panic<L, R>(either: Either<L, R>) -> R {
227 match either {
228 Either::Left(_) => panic!(
229 "expected ReadTransaction of Either::Right containing {}, received Either::Left type \
230 of {}",
231 type_name::<R>(),
232 type_name::<L>(),
233 ),
234 Either::Right(r) => r,
235 }
236}