pub struct StorageValueRef<'a> { /* private fields */ }
Expand description

An abstraction over local storage value.

Implementations§

Create a new reference to a value in the persistent local storage.

Examples found in repository?
src/offchain/storage_lock.rs (line 270)
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
	pub fn with_lockable(key: &'a [u8], lockable: L) -> Self {
		Self { value_ref: StorageValueRef::<'a>::persistent(key), lockable }
	}

	/// Extend active lock's deadline
	fn extend_active_lock(&mut self) -> Result<<L as Lockable>::Deadline, ()> {
		let res = self.value_ref.mutate(
			|s: Result<Option<L::Deadline>, StorageRetrievalError>| -> Result<<L as Lockable>::Deadline, ()> {
			match s {
				// lock is present and is still active, extend the lock.
				Ok(Some(deadline)) if !<L as Lockable>::has_expired(&deadline) =>
					Ok(self.lockable.deadline()),
				// other cases
				_ => Err(()),
			}
		});
		match res {
			Ok(deadline) => Ok(deadline),
			Err(MutateStorageError::ConcurrentModification(_)) => Err(()),
			Err(MutateStorageError::ValueFunctionFailed(e)) => Err(e),
		}
	}

	/// Internal lock helper to avoid lifetime conflicts.
	fn try_lock_inner(
		&mut self,
		new_deadline: L::Deadline,
	) -> Result<(), <L as Lockable>::Deadline> {
		let res = self.value_ref.mutate(
			|s: Result<Option<L::Deadline>, StorageRetrievalError>|
			-> Result<<L as Lockable>::Deadline, <L as Lockable>::Deadline> {
				match s {
					// no lock set, we can safely acquire it
					Ok(None) => Ok(new_deadline),
					// write was good, but read failed
					Err(_) => Ok(new_deadline),
					// lock is set, but it is expired. We can re-acquire it.
					Ok(Some(deadline)) if <L as Lockable>::has_expired(&deadline) =>
						Ok(new_deadline),
					// lock is present and is still active
					Ok(Some(deadline)) => Err(deadline),
				}
			},
		);
		match res {
			Ok(_) => Ok(()),
			Err(MutateStorageError::ConcurrentModification(deadline)) => Err(deadline),
			Err(MutateStorageError::ValueFunctionFailed(e)) => Err(e),
		}
	}

	/// A single attempt to lock using the storage entry.
	///
	/// Returns a lock guard on success, otherwise an error containing the
	/// `<Self::Lockable>::Deadline` in for the currently active lock
	/// by another task `Err(<L as Lockable>::Deadline)`.
	pub fn try_lock(&mut self) -> Result<StorageLockGuard<'a, '_, L>, <L as Lockable>::Deadline> {
		self.try_lock_inner(self.lockable.deadline())?;
		Ok(StorageLockGuard::<'a, '_> { lock: Some(self) })
	}

	/// Repeated lock attempts until the lock is successfully acquired.
	///
	/// If one uses `fn forget(..)`, it is highly likely `fn try_lock(..)`
	/// is the correct API to use instead of `fn lock(..)`, since that might
	/// never unlock in the anticipated span i.e. when used with `BlockAndTime`
	/// during a certain block number span.
	pub fn lock(&mut self) -> StorageLockGuard<'a, '_, L> {
		while let Err(deadline) = self.try_lock_inner(self.lockable.deadline()) {
			L::snooze(&deadline);
		}
		StorageLockGuard::<'a, '_, L> { lock: Some(self) }
	}

	/// Explicitly unlock the lock.
	fn unlock(&mut self) {
		self.value_ref.clear();
	}
}

/// RAII style guard for a lock.
pub struct StorageLockGuard<'a, 'b, L: Lockable> {
	lock: Option<&'b mut StorageLock<'a, L>>,
}

impl<'a, 'b, L: Lockable> StorageLockGuard<'a, 'b, L> {
	/// Consume the guard but **do not** unlock the underlying lock.
	///
	/// Can be used to implement a grace period after doing some
	/// heavy computations and sending a transaction to be included
	/// on-chain. By forgetting the lock, it will stay locked until
	/// its expiration deadline is reached while the off-chain worker
	/// can already exit.
	pub fn forget(mut self) {
		let _ = self.lock.take();
	}

	/// Extend the lock by guard deadline if it already exists.
	///
	/// i.e. large sets of items for which it is hard to calculate a
	/// meaning full conservative deadline which does not block for a
	/// very long time on node termination.
	pub fn extend_lock(&mut self) -> Result<<L as Lockable>::Deadline, ()> {
		if let Some(ref mut lock) = self.lock {
			lock.extend_active_lock()
		} else {
			Err(())
		}
	}
}

impl<'a, 'b, L: Lockable> Drop for StorageLockGuard<'a, 'b, L> {
	fn drop(&mut self) {
		if let Some(lock) = self.lock.take() {
			lock.unlock();
		}
	}
}

impl<'a> StorageLock<'a, Time> {
	/// Explicitly create a time based storage lock with a non-default
	/// expiration timeout.
	pub fn with_deadline(key: &'a [u8], expiration_duration: Duration) -> Self {
		Self {
			value_ref: StorageValueRef::<'a>::persistent(key),
			lockable: Time { expiration_duration },
		}
	}
}

impl<'a, B> StorageLock<'a, BlockAndTime<B>>
where
	B: BlockNumberProvider,
{
	/// Explicitly create a time and block number based storage lock with
	/// a non-default expiration duration and block number offset.
	pub fn with_block_and_time_deadline(
		key: &'a [u8],
		expiration_block_number_offset: u32,
		expiration_duration: Duration,
	) -> Self {
		Self {
			value_ref: StorageValueRef::<'a>::persistent(key),
			lockable: BlockAndTime::<B> {
				expiration_block_number_offset,
				expiration_duration,
				_phantom: core::marker::PhantomData,
			},
		}
	}

	/// Explicitly create a time and block number based storage lock with
	/// the default expiration duration and a non-default block number offset.
	pub fn with_block_deadline(key: &'a [u8], expiration_block_number_offset: u32) -> Self {
		Self {
			value_ref: StorageValueRef::<'a>::persistent(key),
			lockable: BlockAndTime::<B> {
				expiration_block_number_offset,
				expiration_duration: STORAGE_LOCK_DEFAULT_EXPIRY_DURATION,
				_phantom: core::marker::PhantomData,
			},
		}
	}

Create a new reference to a value in the fork-aware local storage.

Set the value of the storage to encoding of given parameter.

Note that the storage may be accessed by workers running concurrently, if you happen to write a get-check-set pattern you should most likely be using mutate instead.

Remove the associated value from the storage.

Examples found in repository?
src/offchain/storage_lock.rs (line 345)
344
345
346
	fn unlock(&mut self) {
		self.value_ref.clear();
	}

Retrieve & decode the value from storage.

Note that if you want to do some checks based on the value and write changes after that, you should rather be using mutate.

Returns the value if stored. Returns an error if the value could not be decoded.

Retrieve & decode the current value and set it to a new value atomically.

Function mutate_val takes as input the current value and should return a new value that is attempted to be written to storage.

This function returns:

  1. Ok(T) in case the value has been successfully set.
  2. Err(MutateStorageError::ConcurrentModification(T)) in case the value was calculated by the passed closure mutate_val, but it could not be stored.
  3. Err(MutateStorageError::ValueFunctionFailed(_)) in case mutate_val returns an error.
Examples found in repository?
src/offchain/storage_lock.rs (lines 275-284)
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
	fn extend_active_lock(&mut self) -> Result<<L as Lockable>::Deadline, ()> {
		let res = self.value_ref.mutate(
			|s: Result<Option<L::Deadline>, StorageRetrievalError>| -> Result<<L as Lockable>::Deadline, ()> {
			match s {
				// lock is present and is still active, extend the lock.
				Ok(Some(deadline)) if !<L as Lockable>::has_expired(&deadline) =>
					Ok(self.lockable.deadline()),
				// other cases
				_ => Err(()),
			}
		});
		match res {
			Ok(deadline) => Ok(deadline),
			Err(MutateStorageError::ConcurrentModification(_)) => Err(()),
			Err(MutateStorageError::ValueFunctionFailed(e)) => Err(e),
		}
	}

	/// Internal lock helper to avoid lifetime conflicts.
	fn try_lock_inner(
		&mut self,
		new_deadline: L::Deadline,
	) -> Result<(), <L as Lockable>::Deadline> {
		let res = self.value_ref.mutate(
			|s: Result<Option<L::Deadline>, StorageRetrievalError>|
			-> Result<<L as Lockable>::Deadline, <L as Lockable>::Deadline> {
				match s {
					// no lock set, we can safely acquire it
					Ok(None) => Ok(new_deadline),
					// write was good, but read failed
					Err(_) => Ok(new_deadline),
					// lock is set, but it is expired. We can re-acquire it.
					Ok(Some(deadline)) if <L as Lockable>::has_expired(&deadline) =>
						Ok(new_deadline),
					// lock is present and is still active
					Ok(Some(deadline)) => Err(deadline),
				}
			},
		);
		match res {
			Ok(_) => Ok(()),
			Err(MutateStorageError::ConcurrentModification(deadline)) => Err(deadline),
			Err(MutateStorageError::ValueFunctionFailed(e)) => Err(e),
		}
	}

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more
Convert from a value of T into an equivalent instance of Option<Self>. Read more
Consume self to return Some equivalent value of Option<T>. Read more
Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.

Returns the argument unchanged.

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Instruments this type with the current Span, returning an Instrumented wrapper. Read more

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Get a reference to the inner from the outer.

Get a mutable reference to the inner from the outer.

Should always be Self
Convert from a value of T into an equivalent instance of Self. Read more
Consume self to return an equivalent value of T. Read more
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.
The counterpart to unchecked_from.
Consume self to return an equivalent value of T.
Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more