From 97cac4ce8b75272c3f5cf8468b4fe1b3f6b505e0 Mon Sep 17 00:00:00 2001
From: wangjj9219 <183318287@qq.com>
Date: Mon, 15 Jun 2020 23:05:17 +0800
Subject: [PATCH] add extend_lock for StorageLock (#6323)

* add extend_lock for StorageLock

* changes

* changes
---
 .../runtime/src/offchain/storage_lock.rs      | 78 +++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/substrate/primitives/runtime/src/offchain/storage_lock.rs b/substrate/primitives/runtime/src/offchain/storage_lock.rs
index f8defa42245..4718d2e3dde 100644
--- a/substrate/primitives/runtime/src/offchain/storage_lock.rs
+++ b/substrate/primitives/runtime/src/offchain/storage_lock.rs
@@ -264,6 +264,24 @@ impl<'a, L: Lockable> StorageLock<'a, L> {
 		}
 	}
 
+	/// Extend active lock's deadline
+	fn extend_active_lock(&mut self) -> Result<<L as Lockable>::Deadline, ()> {
+		let res = self.value_ref.mutate(|s: Option<Option<L::Deadline>>| -> Result<<L as Lockable>::Deadline, ()> {
+			match s {
+				// lock is present and is still active, extend the lock.
+				Some(Some(deadline)) if !<L as Lockable>::has_expired(&deadline) =>
+					Ok(self.lockable.deadline()),
+				// other cases
+				_ => Err(()),
+			}
+		});
+		match res {
+			Ok(Ok(deadline)) => Ok(deadline),
+			Ok(Err(_)) => Err(()),
+			Err(e) => Err(e),
+		}
+	}
+
 	/// Internal lock helper to avoid lifetime conflicts.
 	fn try_lock_inner(
 		&mut self,
@@ -337,6 +355,19 @@ impl<'a, 'b, L: Lockable> StorageLockGuard<'a, 'b, L> {
 	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> {
@@ -512,4 +543,51 @@ mod tests {
 		let opt = state.read().persistent_storage.get(b"", b"lock_3");
 		assert!(opt.is_some());
 	}
+
+	#[test]
+	fn extend_active_lock() {
+		let (offchain, state) = testing::TestOffchainExt::new();
+		let mut t = TestExternalities::default();
+		t.register_extension(OffchainExt::new(offchain));
+
+		t.execute_with(|| {
+			let lock_expiration = Duration::from_millis(300);
+
+			let mut lock = StorageLock::<'_, Time>::with_deadline(b"lock_4", lock_expiration);
+			let mut guard = lock.lock();
+
+			// sleep_until < lock_expiration
+			offchain::sleep_until(offchain::timestamp().add(Duration::from_millis(200)));
+
+			// the lock is still active, extend it successfully
+            assert_eq!(guard.extend_lock().is_ok(), true);
+
+            // sleep_until < deadline
+            offchain::sleep_until(offchain::timestamp().add(Duration::from_millis(200)));
+
+            // the lock is still active, try_lock will fail
+            let mut lock = StorageLock::<'_, Time>::with_deadline(b"lock_4", lock_expiration);
+            let res = lock.try_lock();
+            assert_eq!(res.is_ok(), false);
+
+            // sleep again untill sleep_until > deadline
+            offchain::sleep_until(offchain::timestamp().add(Duration::from_millis(200)));
+
+            // the lock has expired, failed to extend it
+            assert_eq!(guard.extend_lock().is_ok(), false);
+            guard.forget();
+
+            // try_lock will succeed
+            let mut lock = StorageLock::<'_, Time>::with_deadline(b"lock_4", lock_expiration);
+            let res = lock.try_lock();
+            assert!(res.is_ok());
+            let guard = res.unwrap();
+
+            guard.forget();
+        });
+
+        // lock must have been cleared at this point
+        let opt = state.read().persistent_storage.get(b"", b"lock_4");
+        assert_eq!(opt.unwrap(), vec![132_u8, 3u8, 0, 0, 0, 0, 0, 0]); // 132 + 256 * 3 = 900
+	}
 }
-- 
GitLab