pw_thread: add explicit yield requirement to sleep

Extends the pw::this_thread::sleep_{for,until} API contract to
require explicit yielding if the effective sleep duration is 0
(e.g. sleep_for(0) or sleep_until(not_in_the_future)).

Updates the FreeRTOS and STL backends accordingly.

Change-Id: I3fb09e4fda45382ee14a081b10a384fa41fb6f5c
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/31540
Pigweed-Auto-Submit: Ewout van Bekkum <ewout@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
diff --git a/pw_thread/public/pw_thread/sleep.h b/pw_thread/public/pw_thread/sleep.h
index 93e8320..4e1fb48 100644
--- a/pw_thread/public/pw_thread/sleep.h
+++ b/pw_thread/public/pw_thread/sleep.h
@@ -20,9 +20,25 @@
 
 namespace pw::this_thread {
 
+// Blocks the execution of the current thread for at least the specified
+// duration. This function may block for longer due to scheduling or resource
+// contention delays.
+//
+// A sleep duration of 0 will at minimum yield, meaning it will provide a hint
+// to the implementation to reschedule the execution of threads, allowing other
+// threads to run.
+//
 // This can only be called from a thread, meaning the scheduler is running.
 void sleep_for(chrono::SystemClock::duration for_at_least);
 
+// Blocks the execution of the current thread until at least the specified
+// deadline. This function may block for longer due to scheduling or resource
+// contention delays.
+//
+// A sleep deadline in the past up to the current time will at minimum yield
+// meaning it will provide a hint to the implementation to reschedule the
+// execution of threads, allowing other threads to run.
+//
 // This can only be called from a thread, meaning the scheduler is running.
 void sleep_until(chrono::SystemClock::time_point until_at_least);
 
diff --git a/pw_thread_freertos/sleep.cc b/pw_thread_freertos/sleep.cc
index 4dc6655..88b7a5b 100644
--- a/pw_thread_freertos/sleep.cc
+++ b/pw_thread_freertos/sleep.cc
@@ -30,7 +30,7 @@
 void sleep_for(chrono::SystemClock::duration for_at_least) {
   PW_DCHECK(get_id() != thread::Id());
 
-  // Clamp negative durations to be 0 which maps to non-blocking.
+  // Clamp negative durations to be 0 which maps to yield with vTaskDelay.
   for_at_least = std::max(for_at_least, chrono::SystemClock::duration::zero());
 
   while (for_at_least > kMaxTimeout) {
diff --git a/pw_thread_stl/public/pw_thread_stl/sleep_inline.h b/pw_thread_stl/public/pw_thread_stl/sleep_inline.h
index ae9ecaa..2b0a914 100644
--- a/pw_thread_stl/public/pw_thread_stl/sleep_inline.h
+++ b/pw_thread_stl/public/pw_thread_stl/sleep_inline.h
@@ -21,10 +21,21 @@
 namespace pw::this_thread {
 
 inline void sleep_for(chrono::SystemClock::duration for_at_least) {
+  for_at_least = std::max(for_at_least, chrono::SystemClock::duration::zero());
+  // Although many implementations do yield with sleep_for(0), it is not
+  // required, ergo we explicitly add handling.
+  if (for_at_least == chrono::SystemClock::duration::zero()) {
+    return std::this_thread::yield();
+  }
   return std::this_thread::sleep_for(for_at_least);
 }
 
 inline void sleep_until(chrono::SystemClock::time_point until_at_least) {
+  // Although many implementations do yield with deadlines in the past until
+  // the current time, it is not required, ergo we explicitly add handling.
+  if (chrono::SystemClock::now() >= until_at_least) {
+    return std::this_thread::yield();
+  }
   return std::this_thread::sleep_until(until_at_least);
 }