pw_thread_threadx: Keep walking tasks on error

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

Change-Id: I6ef8a0565165c0720f2c47d6982ff0031e33757f
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/53661
Commit-Queue: Armando Montanez <amontanez@google.com>
Pigweed-Auto-Submit: Armando Montanez <amontanez@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
diff --git a/pw_thread_threadx/BUILD.gn b/pw_thread_threadx/BUILD.gn
index dd7d7b3..1eba6dc 100644
--- a/pw_thread_threadx/BUILD.gn
+++ b/pw_thread_threadx/BUILD.gn
@@ -158,6 +158,7 @@
     "$dir_pw_thread:snapshot",
     dir_pw_bytes,
     dir_pw_function,
+    dir_pw_log,
     dir_pw_protobuf,
     dir_pw_status,
   ]
diff --git a/pw_thread_threadx/docs.rst b/pw_thread_threadx/docs.rst
index 5cfd84f..df27220 100644
--- a/pw_thread_threadx/docs.rst
+++ b/pw_thread_threadx/docs.rst
@@ -33,6 +33,15 @@
 ``ForEachThread()`` can be used to iterate over all the created thread TCBs.
 Note that it's only safe to use this while the scheduler is disabled.
 
+An ``Aborted`` error status is returned if the provided callback returns
+``false`` to request an early termination of thread iteration.
+
+Return values
+=============
+
+* ``Aborted``: The callback requested an early-termination of thread iteration.
+* ``OkStatus``: The callback has been successfully run with every thread.
+
 --------------------
 Snapshot integration
 --------------------
@@ -42,7 +51,7 @@
 SnapshotThread()/SnapshotThreads()
 ==================================
 ``SnapshotThread()`` captures the thread name, state, and stack information for
-the provided RTX TCB to a ``pw::thread::Thread`` protobuf encoder. To ensure
+the provided ThreadX TCB to a ``pw::thread::Thread`` protobuf encoder. To ensure
 the most up-to-date information is captured, the stack pointer for the currently
 running thread must be provided for cases where the running thread is being
 captured. For ARM Cortex-M CPUs, you can do something like this:
diff --git a/pw_thread_threadx/public/pw_thread_threadx/util.h b/pw_thread_threadx/public/pw_thread_threadx/util.h
index ed0c1e1..34c4299 100644
--- a/pw_thread_threadx/public/pw_thread_threadx/util.h
+++ b/pw_thread_threadx/public/pw_thread_threadx/util.h
@@ -19,12 +19,18 @@
 
 namespace pw::thread::threadx {
 
-// A callback that is executed for each thread when using ForEachThread().
-using ThreadCallback = pw::Function<Status(const TX_THREAD&)>;
+// 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 TX_THREAD&)>;
 
 // 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.
+//
+// Returns:
+//   Aborted - The callback requested an early-termination of thread iteration.
+//   OkStatus - Successfully iterated over all threads.
 //
 // Warning: This is only safe to use when the scheduler is disabled.
 Status ForEachThread(ThreadCallback& cb);
diff --git a/pw_thread_threadx/snapshot.cc b/pw_thread_threadx/snapshot.cc
index 0333714..48d0f4f 100644
--- a/pw_thread_threadx/snapshot.cc
+++ b/pw_thread_threadx/snapshot.cc
@@ -16,6 +16,7 @@
 #include <string_view>
 
 #include "pw_function/function.h"
+#include "pw_log/log.h"
 #include "pw_protobuf/encoder.h"
 #include "pw_status/status.h"
 #include "pw_thread/snapshot.h"
@@ -79,20 +80,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 TX_THREAD& thread) -> Status {
+  ThreadCallback thread_capture_cb([&ctx](const TX_THREAD& 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 TX_THREAD& thread,
diff --git a/pw_thread_threadx/util.cc b/pw_thread_threadx/util.cc
index 0d9c5e5..078a51a 100644
--- a/pw_thread_threadx/util.cc
+++ b/pw_thread_threadx/util.cc
@@ -15,7 +15,6 @@
 
 #include "pw_function/function.h"
 #include "pw_status/status.h"
-#include "pw_status/try.h"
 #include "tx_api.h"
 #include "tx_thread.h"
 
@@ -24,11 +23,14 @@
 namespace internal {
 
 // Iterates through all threads that haven't been deleted, calling the provided
-// call
+// callback.
 Status ForEachThread(const TX_THREAD& starting_thread, ThreadCallback& cb) {
   const TX_THREAD* thread = &starting_thread;
   do {
-    PW_TRY(cb(*thread));
+    if (!cb(*thread)) {
+      // Early-terminate iteration if requested by the callback.
+      return Status::Aborted();
+    }
     thread = thread->tx_thread_created_next;
   } while (thread != &starting_thread);