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);
}