sync_cell.rs 6.58 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of pDSL.
//
// pDSL is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// pDSL is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with pDSL.  If not, see <http://www.gnu.org/licenses/>.

17
18
19
use crate::{
	storage::{
		cell::TypedCell,
20
		Allocator,
21
22
23
	},
};

24
use core::cell::RefCell;
25

26
/// A synchronized cell.
27
28
29
30
31
32
33
34
35
///
/// Provides interpreted, read-optimized and inplace-mutable
/// access to the associated constract storage slot.
///
/// # Guarantees
///
/// - `Owned`, `Typed`, `Avoid Reads`, `Mutable`
///
/// Read more about kinds of guarantees and their effect [here](../index.html#guarantees).
Hero Bird's avatar
Hero Bird committed
36
#[derive(Debug)]
37
pub struct SyncCell<T> {
38
39
	/// The underlying typed cell.
	cell: TypedCell<T>,
Hero Bird's avatar
Hero Bird committed
40
41
	/// The cache for the synchronized value.
	cache: Cache<T>,
42
43
}

44
/// A cache entry storing the value if synchronized.
Hero Bird's avatar
Hero Bird committed
45
46
#[derive(Debug)]
pub enum CacheEntry<T> {
47
	/// The cache is desychronized with the contract storage.
48
	Desync,
49
	/// The cache is in sync with the contract storage.
50
51
52
	Sync(Option<T>),
}

Hero Bird's avatar
Hero Bird committed
53
54
55
56
57
58
59
60
61
62
63
64
65
#[derive(Debug)]
pub struct Cache<T> {
	/// The cached value.
	entry: RefCell<CacheEntry<T>>,
}

impl<T> Default for Cache<T> {
	fn default() -> Self {
		Self{ entry: RefCell::new(CacheEntry::Desync) }
	}
}

impl<T> CacheEntry<T> {
66
	/// Returns `true` if the cache is in sync.
67
68
	pub fn is_synced(&self) -> bool {
		match self {
Hero Bird's avatar
Hero Bird committed
69
			CacheEntry::Sync(_) => true,
70
71
72
73
			_ => false,
		}
	}

74
75
76
77
78
	/// Unwraps an immutable reference to the synchronized value.
	///
	/// # Panics
	///
	/// Panics if the cache is in a desynchronized state.
Hero Bird's avatar
Hero Bird committed
79
	pub fn unwrap_get(&self) -> Option<&T> {
80
		match self {
Hero Bird's avatar
Hero Bird committed
81
82
83
84
85
86
87
			CacheEntry::Sync(opt_elem) => opt_elem.into(),
			CacheEntry::Desync => {
				panic!(
					"[pdsl_core::sync_cell::CacheEntry::unwrap] Error: \
					 tried to unwrap a desynchronized value"
				)
			}
88
89
		}
	}
Hero Bird's avatar
Hero Bird committed
90
}
91

Hero Bird's avatar
Hero Bird committed
92
impl<T> Cache<T> {
93
	/// Returns `true` if the cache is in sync.
Hero Bird's avatar
Hero Bird committed
94
95
	pub fn is_synced(&self) -> bool {
		self.entry.borrow().is_synced()
96
97
	}

98
99
100
101
102
	/// Updates the synchronized value.
	///
	/// # Note
	///
	/// The cache will be in sync after this operation.
Hero Bird's avatar
Hero Bird committed
103
104
105
106
107
108
	pub fn update(&self, new_val: Option<T>) {
		self.entry.replace(
			CacheEntry::Sync(new_val)
		);
	}

109
	/// Returns an immutable reference to the cache entry.
Hero Bird's avatar
Hero Bird committed
110
111
112
113
	pub fn get(&self) -> &CacheEntry<T> {
		unsafe{ &*self.entry.as_ptr() }
	}

114
115
116
117
	/// Mutates the synchronized value if any.
	///
	/// Returns an immutable reference to the result if
	/// a mutation happened, otherwise `None` is returned.
Hero Bird's avatar
Hero Bird committed
118
119
120
121
122
123
124
125
126
127
128
129
130
131
	pub fn mutate_with<F>(&mut self, f: F) -> Option<&T>
	where
		F: FnOnce(&mut T)
	{
		match self.entry.get_mut() {
			CacheEntry::Desync => None,
			CacheEntry::Sync(opt_val) => {
				if let Some(val) = opt_val {
					f(val);
					Some(&*val)
				} else {
					None
				}
			}
132
133
134
135
		}
	}
}

136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
impl<T> parity_codec::Encode for SyncCell<T> {
	fn encode_to<W: parity_codec::Output>(&self, dest: &mut W) {
		self.cell.encode_to(dest)
	}
}

impl<T> parity_codec::Decode for SyncCell<T> {
	fn decode<I: parity_codec::Input>(input: &mut I) -> Option<Self> {
		TypedCell::decode(input)
			.map(|typed_cell| Self{
				cell: typed_cell,
				cache: Cache::default()
			})
	}
}

152
impl<T> SyncCell<T> {
153
154
155
156
157
158
159
160
161
162
163
164
165
	/// Allocates a new sync cell using the given storage allocator.
	///
	/// # Safety
	///
	/// The is unsafe because it does not check if the associated storage
	/// does not alias with storage allocated by other storage allocators.
	pub unsafe fn new_using_alloc<A>(alloc: &mut A) -> Self
	where
		A: Allocator
	{
		Self{
			cell: TypedCell::new_using_alloc(alloc),
			cache: Default::default(),
166
167
		}
	}
168
169
170
171

	/// Removes the value from the cell.
	pub fn clear(&mut self) {
		self.cell.clear();
Hero Bird's avatar
Hero Bird committed
172
		self.cache.update(None);
173
	}
174
175
}

176
impl<T> SyncCell<T>
177
178
179
where
	T: parity_codec::Decode
{
180
	/// Returns the value of the cell if any.
181
	pub fn get(&self) -> Option<&T> {
Hero Bird's avatar
Hero Bird committed
182
183
184
185
186
187
188
		match self.cache.get() {
			CacheEntry::Desync => {
				self.load()
			}
			CacheEntry::Sync(opt_elem) => {
				opt_elem.into()
			}
189
190
191
192
193
194
195
		}
	}

	/// Returns an immutable reference to the entity if any.
	///
	/// # Note
	///
196
197
198
	/// Prefer using [`get`](struct.SyncCell.html#method.get)
	/// to avoid unnecesary contract storage accesses.
	fn load(&self) -> Option<&T> {
Hero Bird's avatar
Hero Bird committed
199
200
201
202
203
		self.cache.update(self.cell.load());
		// Now cache is certainly synchronized
		// so we can safely unwrap the cached value.
		debug_assert!(self.cache.is_synced());
		self.cache.get().unwrap_get()
204
205
206
	}
}

207
impl<T> SyncCell<T>
208
209
210
where
	T: parity_codec::Encode
{
211
	/// Sets the value of the cell.
212
213
	pub fn set(&mut self, val: T) {
		self.cell.store(&val);
Hero Bird's avatar
Hero Bird committed
214
		self.cache.update(Some(val))
215
	}
216
}
217

218
219
220
221
impl<T> SyncCell<T>
where
	T: parity_codec::Codec
{
222
	/// Mutates the value stored in the cell.
223
224
225
226
	///
	/// Returns an immutable reference to the result if
	/// a mutation happened, otherwise `None` is returned.
	pub fn mutate_with<F>(&mut self, f: F) -> Option<&T>
227
228
229
	where
		F: FnOnce(&mut T)
	{
Hero Bird's avatar
Hero Bird committed
230
		if !self.cache.is_synced() {
231
232
			self.load();
		}
Hero Bird's avatar
Hero Bird committed
233
234
235
236
		debug_assert!(self.cache.is_synced());
		match self.cache.mutate_with(f) {
			Some(res) => {
				self.cell.store(res);
237
				Some(&*res)
Hero Bird's avatar
Hero Bird committed
238
			}
239
			None => None
240
241
242
243
244
245
246
		}
	}
}

#[cfg(all(test, feature = "test-env"))]
mod tests {
	use super::*;
247
	use crate::storage::Key;
248

249
250
251
252
	use crate::{
		test_utils::run_test,
		env::TestEnv,
	};
253

254
255
256
257
258
259
260
261
262
	fn dummy_cell() -> SyncCell<i32> {
		unsafe {
			let mut alloc = crate::storage::alloc::ForwardAlloc::from_raw_parts(
				Key([0x0; 32])
			);
			SyncCell::new_using_alloc(&mut alloc)
		}
	}

263
264
	#[test]
	fn simple() {
265
		run_test(|| {
266
			let mut cell = dummy_cell();
267
268
269
270
271
272
273
274
			assert_eq!(cell.get(), None);
			cell.set(5);
			assert_eq!(cell.get(), Some(&5));
			assert_eq!(cell.mutate_with(|val| *val += 10), Some(&15));
			assert_eq!(cell.get(), Some(&15));
			cell.clear();
			assert_eq!(cell.get(), None);
		})
275
276
277
278
	}

	#[test]
	fn count_reads() {
279
		run_test(|| {
280
			let mut cell = dummy_cell();
281
282
283
284
285
286
287
			assert_eq!(TestEnv::total_reads(), 0);
			cell.get();
			assert_eq!(TestEnv::total_reads(), 1);
			cell.get();
			cell.get();
			assert_eq!(TestEnv::total_reads(), 1);
		})
288
289
290
291
	}

	#[test]
	fn count_writes() {
292
		run_test(|| {
293
			let mut cell = dummy_cell();
294
295
296
297
298
299
300
			assert_eq!(TestEnv::total_writes(), 0);
			cell.set(1);
			assert_eq!(TestEnv::total_writes(), 1);
			cell.set(2);
			cell.set(3);
			assert_eq!(TestEnv::total_writes(), 3);
		})
301
302
	}
}