pw_assert: Add option to pw_assert_basic to call _Exit

Add option to exit the pw_assert_basic backend by calling std::_Exit to
exit the program without running atexit handlers or destructors.

This is needed in the following two cases:

In the case of using pw_build's `wrap_abort`, and the `pw_assert_basic`
default backend, assert failures would infinitely recurse calling abort.

In addition, when not using `wrap_abort` some C library implementations
will attempt to take mutexes out, which shouldn't be done in a crash
handler as it may deadlock.

Change-Id: I9839a9b1fb62b89192c019629ef7710beb80f905
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/127191
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Pigweed-Auto-Submit: Austin Foxley <afoxley@google.com>
diff --git a/pw_assert/public/pw_assert/config.h b/pw_assert/public/pw_assert/config.h
index e8a4ef9..6b39cec 100644
--- a/pw_assert/public/pw_assert/config.h
+++ b/pw_assert/public/pw_assert/config.h
@@ -34,11 +34,24 @@
 #define PW_ASSERT_CAPTURE_VALUES 1
 #endif  // !defined(PW_ASSERT_CAPTURE_VALUES)
 
-// If 1, call C's standard abort() function on assert failure.
-#ifndef PW_ASSERT_BASIC_ABORT
-#define PW_ASSERT_BASIC_ABORT 1
+// Modes available for how to end an assert failure for pw_assert_basic.
+#define PW_ASSERT_BASIC_ACTION_ABORT 100
+#define PW_ASSERT_BASIC_ACTION_EXIT 101
+#define PW_ASSERT_BASIC_ACTION_LOOP 102
+
+#ifdef PW_ASSERT_BASIC_ABORT
+#error PW_ASSERT_BASIC_ABORT is deprecated! Use PW_ASSERT_BASIC_ACTION instead.
 #endif  // PW_ASSERT_BASIC_ABORT
 
+// Set to one of the following to define how pw_basic_assert should act after an
+// assert failure:
+// - PW_ASSERT_BASIC_ACTION_ABORT: Call std::abort()
+// - PW_ASSERT_BASIC_ACTION_EXIT: Call std::_Exit(-1)
+// - PW_ASSERT_BASIC_ACTION_LOOP: Loop forever
+#ifndef PW_ASSERT_BASIC_ACTION
+#define PW_ASSERT_BASIC_ACTION PW_ASSERT_BASIC_ACTION_ABORT
+#endif  // PW_ASSERT_BASIC_ACTION
+
 // Whether to show the CRASH ASCII art banner.
 #ifndef PW_ASSERT_BASIC_SHOW_BANNER
 #define PW_ASSERT_BASIC_SHOW_BANNER 1
diff --git a/pw_assert_basic/basic_handler.cc b/pw_assert_basic/basic_handler.cc
index 5648a90..046b100 100644
--- a/pw_assert_basic/basic_handler.cc
+++ b/pw_assert_basic/basic_handler.cc
@@ -132,21 +132,27 @@
   // device. At some point we'll have a reboot BSP function or similar, but for
   // now this is acceptable since no one is using this basic backend.
   if (!PW_ASSERT_BASIC_DISABLE_NORETURN) {
-    if (PW_ASSERT_BASIC_ABORT) {
-      // abort() doesn't flush stderr/stdout, so manually flush them before
-      // aborting. abort() is preferred to exit(1) because debuggers catch it.
-      std::fflush(stderr);
-      std::fflush(stdout);
-      std::abort();
-    } else {
-      WriteLine("");
-      WriteLine(MAGENTA "  HANG TIME" RESET);
-      WriteLine("");
-      WriteLine(
-          "     ... until a debugger joins. System is waiting in a while(1)");
-      while (true) {
-      }
+#if (PW_ASSERT_BASIC_ACTION == PW_ASSERT_BASIC_ACTION_ABORT)
+    // abort() doesn't flush stderr/stdout, so manually flush them before
+    // aborting. abort() is preferred to exit(1) because debuggers catch it.
+    std::fflush(stderr);
+    std::fflush(stdout);
+    std::abort();
+#elif (PW_ASSERT_BASIC_ACTION == PW_ASSERT_BASIC_ACTION_EXIT)
+    // Use _Exit to not run destructors or atexit hooks in case they cause
+    // further crashes.
+    std::_Exit(-1);
+#elif (PW_ASSERT_BASIC_ACTION == PW_ASSERT_BASIC_ACTION_LOOP)
+    WriteLine("");
+    WriteLine(MAGENTA "  HANG TIME" RESET);
+    WriteLine("");
+    WriteLine(
+        "     ... until a debugger joins. System is waiting in a while(1)");
+    while (true) {
     }
+#else
+#error PW_ASSERT_BASIC_ACTION Must be set to valid option.
+#endif
     PW_UNREACHABLE;
   } else {
     WriteLine("");
diff --git a/pw_assert_basic/docs.rst b/pw_assert_basic/docs.rst
index a06627f..1f88859 100644
--- a/pw_assert_basic/docs.rst
+++ b/pw_assert_basic/docs.rst
@@ -20,6 +20,37 @@
   intended mostly for ease of initial bringup. We encourage teams to use
   tokenized asserts since they are much smaller both in terms of ROM and RAM.
 
+----------------------------
+Module Configuration Options
+----------------------------
+The following configurations can be adjusted via compile-time configuration of
+this module, see the
+:ref:`module documentation <module-structure-compile-time-configuration>` for
+more details.
+
+.. c:macro:: PW_ASSERT_BASIC_ACTION
+
+  Controls what happens after an assert failure. Should be set to one of the
+  following options:
+
+  - PW_ASSERT_BASIC_ACTION_ABORT: Call std::abort()
+  - PW_ASSERT_BASIC_ACTION_EXIT: Call std::_Exit(-1)
+  - PW_ASSERT_BASIC_ACTION_LOOP: Loop forever
+
+  Defaults to abort.
+
+.. c:macro:: PW_ASSERT_BASIC_SHOW_BANNER
+
+  Controls whether ASCII art banner is printed on assert failure.
+
+  This defaults to enabled.
+
+.. c:macro:: PW_ASSERT_BASIC_USE_COLORS
+
+  Controls whether colors are used in assert message printed to console.
+
+  This defaults to enabled.
+
 .. _module-pw_assert_basic-custom_handler:
 
 Custom handler backend example