From cb7383b6b69e6e5890b2cabc9519d9cf7f42c214 Mon Sep 17 00:00:00 2001
From: Pierre Krieger <pierre.krieger1708@gmail.com>
Date: Thu, 1 Aug 2019 09:53:53 +0200
Subject: [PATCH] Documentation and small cleanup in panic-handler (#3249)

* Documentation and small cleanup in panic-handler

* Apply suggestions from code review

Co-Authored-By: Sergei Pepyakin <s.pepyakin@gmail.com>
---
 .../core/executor/src/native_executor.rs      |  2 +-
 substrate/core/panic-handler/src/lib.rs       | 71 +++++++++++++++----
 substrate/core/state-machine/src/ext.rs       | 28 ++++----
 3 files changed, 72 insertions(+), 29 deletions(-)

diff --git a/substrate/core/executor/src/native_executor.rs b/substrate/core/executor/src/native_executor.rs
index 10010ef1663..f57c3316683 100644
--- a/substrate/core/executor/src/native_executor.rs
+++ b/substrate/core/executor/src/native_executor.rs
@@ -34,7 +34,7 @@ fn safe_call<F, U>(f: F) -> Result<U>
 	where F: UnwindSafe + FnOnce() -> U
 {
 	// Substrate uses custom panic hook that terminates process on panic. Disable termination for the native call.
-	let _guard = panic_handler::AbortGuard::new(false);
+	let _guard = panic_handler::AbortGuard::force_unwind();
 	::std::panic::catch_unwind(f).map_err(|_| Error::Runtime)
 }
 
diff --git a/substrate/core/panic-handler/src/lib.rs b/substrate/core/panic-handler/src/lib.rs
index b2fd7238e0d..287ff72a6aa 100644
--- a/substrate/core/panic-handler/src/lib.rs
+++ b/substrate/core/panic-handler/src/lib.rs
@@ -15,18 +15,31 @@
 // along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
 
 //! Custom panic hook with bug report link
+//!
+//! This crate provides the [`set`] function, which wraps around [`std::panic::set_hook`] and
+//! sets up a panic hook that prints a backtrace and invites the user to open an issue to the
+//! given URL.
+//!
+//! By default, the panic handler aborts the process by calling [`std::process::exit`]. This can
+//! temporarily be disabled by using an [`AbortGuard`].
 
 use backtrace::Backtrace;
 use std::io::{self, Write};
+use std::marker::PhantomData;
 use std::panic::{self, PanicInfo};
 use std::cell::Cell;
 use std::thread;
 
 thread_local! {
-	pub static ABORT: Cell<bool> = Cell::new(true);
+	static ABORT: Cell<bool> = Cell::new(true);
 }
 
-/// Set the panic hook
+/// Set the panic hook.
+///
+/// Calls [`std::panic::set_hook`] to set up the panic hook.
+///
+/// The `bug_url` parameter is an invitation for users to visit that URL to submit a bug report
+/// in the case where a panic happens.
 pub fn set(bug_url: &'static str) {
 	panic::set_hook(Box::new(move |c| panic_hook(c, bug_url)));
 }
@@ -39,7 +52,7 @@ This is a bug. Please report it at:
 ")}
 
 /// Set aborting flag. Returns previous value of the flag.
-pub fn set_abort(enabled: bool) -> bool {
+fn set_abort(enabled: bool) -> bool {
 	ABORT.with(|flag| {
 		let prev = flag.get();
 		flag.set(enabled);
@@ -47,22 +60,47 @@ pub fn set_abort(enabled: bool) -> bool {
 	})
 }
 
-/// Abort flag guard. Sets abort on construction and reverts to previous setting when dropped.
-pub struct AbortGuard(bool);
+/// RAII guard for whether panics in the current thread should unwind or abort.
+///
+/// Sets a thread-local abort flag on construction and reverts to the previous setting when dropped.
+/// Does not implement `Send` on purpose.
+///
+/// > **Note**: Because we restore the previous value when dropped, you are encouraged to leave
+/// > the `AbortGuard` on the stack and let it destroy itself naturally.
+pub struct AbortGuard {
+	/// Value that was in `ABORT` before we created this guard.
+	previous_val: bool,
+	/// Marker so that `AbortGuard` doesn't implement `Send`.
+	_not_send: PhantomData<std::rc::Rc<()>>
+}
 
 impl AbortGuard {
-	/// Create a new guard and set abort flag to specified value.
-	pub fn new(enable: bool) -> AbortGuard {
-		AbortGuard(set_abort(enable))
+	/// Create a new guard. While the guard is alive, panics that happen in the current thread will
+	/// unwind the stack (unless another guard is created afterwards).
+	pub fn force_unwind() -> AbortGuard {
+		AbortGuard {
+			previous_val: set_abort(false),
+			_not_send: PhantomData
+		}
+	}
+
+	/// Create a new guard. While the guard is alive, panics that happen in the current thread will
+	/// abort the process (unless another guard is created afterwards).
+	pub fn force_abort() -> AbortGuard {
+		AbortGuard {
+			previous_val: set_abort(true),
+			_not_send: PhantomData
+		}
 	}
 }
 
 impl Drop for AbortGuard {
 	fn drop(&mut self) {
-		set_abort(self.0);
+		set_abort(self.previous_val);
 	}
 }
 
+/// Function being called when a panic happens.
 fn panic_hook(info: &PanicInfo, report_url: &'static str) {
 	let location = info.location();
 	let file = location.as_ref().map(|l| l.file()).unwrap_or("<unknown>");
@@ -102,9 +140,14 @@ fn panic_hook(info: &PanicInfo, report_url: &'static str) {
 	})
 }
 
-#[test]
-fn does_not_abort() {
-	set("test");
-	let _guard = AbortGuard::new(false);
-	::std::panic::catch_unwind(|| panic!()).ok();
+#[cfg(test)]
+mod tests {
+	use super::*;
+
+	#[test]
+	fn does_not_abort() {
+		set("test");
+		let _guard = AbortGuard::force_unwind();
+		::std::panic::catch_unwind(|| panic!()).ok();
+	}
 }
diff --git a/substrate/core/state-machine/src/ext.rs b/substrate/core/state-machine/src/ext.rs
index f6369159ba4..b910ddcaf6b 100644
--- a/substrate/core/state-machine/src/ext.rs
+++ b/substrate/core/state-machine/src/ext.rs
@@ -173,35 +173,35 @@ where
 	N: crate::changes_trie::BlockNumber,
 {
 	fn storage(&self, key: &[u8]) -> Option<Vec<u8>> {
-		let _guard = panic_handler::AbortGuard::new(true);
+		let _guard = panic_handler::AbortGuard::force_abort();
 		self.overlay.storage(key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(||
 			self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL))
 	}
 
 	fn storage_hash(&self, key: &[u8]) -> Option<H::Out> {
-		let _guard = panic_handler::AbortGuard::new(true);
+		let _guard = panic_handler::AbortGuard::force_abort();
 		self.overlay.storage(key).map(|x| x.map(|x| H::hash(x))).unwrap_or_else(||
 			self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL))
 	}
 
 	fn original_storage(&self, key: &[u8]) -> Option<Vec<u8>> {
-		let _guard = panic_handler::AbortGuard::new(true);
+		let _guard = panic_handler::AbortGuard::force_abort();
 		self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL)
 	}
 
 	fn original_storage_hash(&self, key: &[u8]) -> Option<H::Out> {
-		let _guard = panic_handler::AbortGuard::new(true);
+		let _guard = panic_handler::AbortGuard::force_abort();
 		self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL)
 	}
 
 	fn child_storage(&self, storage_key: ChildStorageKey<H>, key: &[u8]) -> Option<Vec<u8>> {
-		let _guard = panic_handler::AbortGuard::new(true);
+		let _guard = panic_handler::AbortGuard::force_abort();
 		self.overlay.child_storage(storage_key.as_ref(), key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(||
 			self.backend.child_storage(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL))
 	}
 
 	fn exists_storage(&self, key: &[u8]) -> bool {
-		let _guard = panic_handler::AbortGuard::new(true);
+		let _guard = panic_handler::AbortGuard::force_abort();
 		match self.overlay.storage(key) {
 			Some(x) => x.is_some(),
 			_ => self.backend.exists_storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL),
@@ -209,7 +209,7 @@ where
 	}
 
 	fn exists_child_storage(&self, storage_key: ChildStorageKey<H>, key: &[u8]) -> bool {
-		let _guard = panic_handler::AbortGuard::new(true);
+		let _guard = panic_handler::AbortGuard::force_abort();
 
 		match self.overlay.child_storage(storage_key.as_ref(), key) {
 			Some(x) => x.is_some(),
@@ -218,7 +218,7 @@ where
 	}
 
 	fn place_storage(&mut self, key: Vec<u8>, value: Option<Vec<u8>>) {
-		let _guard = panic_handler::AbortGuard::new(true);
+		let _guard = panic_handler::AbortGuard::force_abort();
 		if is_child_storage_key(&key) {
 			warn!(target: "trie", "Refuse to directly set child storage key");
 			return;
@@ -229,14 +229,14 @@ where
 	}
 
 	fn place_child_storage(&mut self, storage_key: ChildStorageKey<H>, key: Vec<u8>, value: Option<Vec<u8>>) {
-		let _guard = panic_handler::AbortGuard::new(true);
+		let _guard = panic_handler::AbortGuard::force_abort();
 
 		self.mark_dirty();
 		self.overlay.set_child_storage(storage_key.into_owned(), key, value);
 	}
 
 	fn kill_child_storage(&mut self, storage_key: ChildStorageKey<H>) {
-		let _guard = panic_handler::AbortGuard::new(true);
+		let _guard = panic_handler::AbortGuard::force_abort();
 
 		self.mark_dirty();
 		self.overlay.clear_child_storage(storage_key.as_ref());
@@ -246,7 +246,7 @@ where
 	}
 
 	fn clear_prefix(&mut self, prefix: &[u8]) {
-		let _guard = panic_handler::AbortGuard::new(true);
+		let _guard = panic_handler::AbortGuard::force_abort();
 		if is_child_storage_key(prefix) {
 			warn!(target: "trie", "Refuse to directly clear prefix that is part of child storage key");
 			return;
@@ -264,7 +264,7 @@ where
 	}
 
 	fn storage_root(&mut self) -> H::Out {
-		let _guard = panic_handler::AbortGuard::new(true);
+		let _guard = panic_handler::AbortGuard::force_abort();
 		if let Some((_, ref root)) = self.storage_transaction {
 			return root.clone();
 		}
@@ -292,7 +292,7 @@ where
 	}
 
 	fn child_storage_root(&mut self, storage_key: ChildStorageKey<H>) -> Vec<u8> {
-		let _guard = panic_handler::AbortGuard::new(true);
+		let _guard = panic_handler::AbortGuard::force_abort();
 		if self.storage_transaction.is_some() {
 			self
 				.storage(storage_key.as_ref())
@@ -319,7 +319,7 @@ where
 	}
 
 	fn storage_changes_root(&mut self, parent_hash: H::Out) -> Result<Option<H::Out>, ()> {
-		let _guard = panic_handler::AbortGuard::new(true);
+		let _guard = panic_handler::AbortGuard::force_abort();
 		self.changes_trie_transaction = build_changes_trie::<_, T, H, N>(
 			self.backend,
 			self.changes_trie_storage.clone(),
-- 
GitLab