pw_thread_embos: Keep walking tasks on error

Changes behavior of the thread iterator to continue iteration over all
threads when an error is encountered.

Change-Id: I9a4dae78f277d8de69a59dc67212a1184ff33e64
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/53660
Pigweed-Auto-Submit: Armando Montanez <amontanez@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
diff --git a/pw_thread_embos/docs.rst b/pw_thread_embos/docs.rst
index abd599a..2328ef3 100644
--- a/pw_thread_embos/docs.rst
+++ b/pw_thread_embos/docs.rst
@@ -161,14 +161,16 @@
 the scheduler has started is non-fatal, but will result in no action and a
 ``FailedPrecondition`` error code.
 
+An ``Aborted`` error status is returned if the provided callback returns
+``false`` to request an early termination of thread iteration.
+
 Return values
 -------------
 
 * ``FailedPrecondition``: Returned when ``ForEachThread()`` is run before the OS
   has been initialized.
+* ``Aborted``: The callback requested an early-termination of thread iteration.
 * ``OkStatus``: The callback has been successfully run with every thread.
-* Other: The callback returned an error status on a thread, triggering an early
-  abort.
 
 --------------------
 Snapshot Integration
diff --git a/pw_thread_embos/public/pw_thread_embos/util.h b/pw_thread_embos/public/pw_thread_embos/util.h
index 7d5cb95..8fdfd90 100644
--- a/pw_thread_embos/public/pw_thread_embos/util.h
+++ b/pw_thread_embos/public/pw_thread_embos/util.h
@@ -19,20 +19,22 @@
 
 namespace pw::thread::embos {
 
-// A callback that is executed for each thread when using ForEachThread().
-using ThreadCallback = pw::Function<Status(const OS_TASK&)>;
+// A callback that is executed for each thread when using ForEachThread(). The
+// callback should return true if thread iteration should continue. When this
+// callback returns false, ForEachThread() will cease iteration of threads and
+// return an `Aborted` error code.
+using ThreadCallback = pw::Function<bool(const OS_TASK&)>;
 
 // Iterates through all threads that haven't been deleted, calling the provided
-// callback on each thread. If the callback fails on one thread, the iteration
-// stops.
+// callback on each thread.
 //
 // Precondition:
 //   OS_Start() must be called prior to using this function.
 //
 // Returns:
 //   FailedPrecondition - The scheduler has not yet been initialized.
+//   Aborted - The callback requested an early-termination of thread iteration.
 //   OkStatus - Successfully iterated over all threads.
-//   Other statuses may be returned by the ThreadCallback.
 //
 // Warning: This is only safe to use when the scheduler is disabled.
 Status ForEachThread(ThreadCallback& cb);
diff --git a/pw_thread_embos/snapshot.cc b/pw_thread_embos/snapshot.cc
index 4e2c037..3bf1268 100644
--- a/pw_thread_embos/snapshot.cc
+++ b/pw_thread_embos/snapshot.cc
@@ -72,20 +72,29 @@
     void* running_thread_stack_pointer;
     SnapshotThreadInfo::StreamEncoder* encoder;
     ProcessThreadStackCallback* stack_dumper;
+    Status thread_capture_status;
   } ctx;
   ctx.running_thread_stack_pointer = running_thread_stack_pointer;
   ctx.encoder = &encoder;
   ctx.stack_dumper = &stack_dumper;
 
-  ThreadCallback thread_capture_cb([&ctx](const OS_TASK& thread) -> Status {
+  ThreadCallback thread_capture_cb([&ctx](const OS_TASK& thread) -> bool {
     Thread::StreamEncoder thread_encoder = ctx.encoder->GetThreadsEncoder();
-    return SnapshotThread(thread,
-                          ctx.running_thread_stack_pointer,
-                          thread_encoder,
-                          *ctx.stack_dumper);
+    ctx.thread_capture_status.Update(
+        SnapshotThread(thread,
+                       ctx.running_thread_stack_pointer,
+                       thread_encoder,
+                       *ctx.stack_dumper));
+    // Always iterate all threads.
+    return true;
   });
 
-  return ForEachThread(thread_capture_cb);
+  if (Status status = ForEachThread(thread_capture_cb); !status.ok()) {
+    PW_LOG_ERROR("Failed to iterate threads during snapshot capture: %d",
+                 static_cast<int>(status.code()));
+  }
+
+  return ctx.thread_capture_status;
 }
 
 Status SnapshotThread(const OS_TASK& thread,
diff --git a/pw_thread_embos/util.cc b/pw_thread_embos/util.cc
index c565460..31b0713 100644
--- a/pw_thread_embos/util.cc
+++ b/pw_thread_embos/util.cc
@@ -16,7 +16,6 @@
 #include "RTOS.h"
 #include "pw_function/function.h"
 #include "pw_status/status.h"
-#include "pw_status/try.h"
 
 namespace pw::thread::embos {
 
@@ -31,7 +30,10 @@
 
   const OS_TASK* thread = &starting_thread;
   while (thread != nullptr) {
-    PW_TRY(cb(*thread));
+    if (!cb(*thread)) {
+      // Early-terminate iteration if requested by the callback.
+      return Status::Aborted();
+    }
     thread = thread->pNext;
   }