pw_sync_threadx: Add optimized ThreadNotification backend
Adds an optimized thread notification backend using tx_thread_sleep
and tx_thread_wait_abort.
Also fills out the missing pw_sync_threadx documentation and adds
an assert to the InterruptSpinLock backend to ensure it's not used
on SMP ports.
Change-Id: Iff89afb5aad9d16751d03b297620dd7530092791
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/67301
Pigweed-Auto-Submit: Ewout van Bekkum <ewout@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/pw_sync_threadx/BUILD.bazel b/pw_sync_threadx/BUILD.bazel
index 96b3abf..3cf1a1e 100644
--- a/pw_sync_threadx/BUILD.bazel
+++ b/pw_sync_threadx/BUILD.bazel
@@ -37,8 +37,8 @@
"//pw_build/constraints/rtos:threadx",
],
deps = [
- # TODO: This should depend on ThreadX but our third parties currently
- # do not have Bazel support.
+ # TODO(pwbug/317): This should depend on ThreadX but our third parties
+ # currently do not have Bazel support.
"//pw_chrono:system_clock",
],
)
@@ -75,7 +75,8 @@
"//pw_build/constraints/rtos:threadx",
],
deps = [
- # TODO: This should depend on ThreadX but our third parties currently
+ # TODO(pwbug/317): This should depend on ThreadX but our third parties
+ # currently do not have Bazel support.
# do not have Bazel support.
"//pw_chrono:system_clock",
],
@@ -113,7 +114,7 @@
"//pw_build/constraints/rtos:threadx",
],
deps = [
- # TODO: This should depend on ThreadX but our third parties currently
+ # TODO(pwbug/317): This should depend on ThreadX but our third parties currently
# do not have Bazel support.
"//pw_sync:mutex_facade",
],
@@ -131,6 +132,85 @@
)
pw_cc_library(
+ name = "thread_notification_headers",
+ hdrs = [
+ "public/pw_sync_threadx/config.h",
+ "public/pw_sync_threadx/thread_notification_inline.h",
+ "public/pw_sync_threadx/thread_notification_native.h",
+ "public_overrides/thread_notification/pw_sync_backend/thread_notification_inline.h",
+ "public_overrides/thread_notification/pw_sync_backend/thread_notification_native.h",
+ ],
+ includes = [
+ "public",
+ "public_overrides/thread_notification",
+ ],
+ target_compatible_with = [
+ "//pw_build/constraints/rtos:threadx",
+ ],
+ deps = [
+ # TODO(pwbug/317): This should depend on ThreadX but our third parties
+ # currently do not have Bazel support.
+ "//pw_assert",
+ "//pw_interrupt:context",
+ "//pw_sync:interrupt_spin_lock",
+ ],
+)
+
+pw_cc_library(
+ name = "thread_notification",
+ srcs = [
+ "thread_notification.cc",
+ ],
+ target_compatible_with = [
+ "//pw_build/constraints/rtos:threadx",
+ ],
+ deps = [
+ ":thread_notification_headers",
+ "//pw_assert",
+ "//pw_interrupt:context",
+ "//pw_sync:thread_notification_facade",
+ ],
+)
+
+pw_cc_library(
+ name = "timed_thread_notification_headers",
+ hdrs = [
+ "public/pw_sync_threadx/timed_thread_notification_inline.h",
+ "public_overrides/timed_thread_notification/pw_sync_backend/timed_thread_notification_inline.h",
+ ],
+ includes = [
+ "public",
+ "public_overrides/timed_thread_notification",
+ ],
+ target_compatible_with = [
+ "//pw_build/constraints/rtos:threadx",
+ ],
+ deps = [
+ # TODO(pwbug/317): This should depend on ThreadX but our third parties
+ # currently do not have Bazel support.
+ "//pw_chrono:system_clock",
+ "//pw_sync:timed_thread_notification_facade",
+ ],
+)
+
+pw_cc_library(
+ name = "timed_thread_notification",
+ srcs = [
+ "timed_thread_notification.cc",
+ ],
+ target_compatible_with = [
+ "//pw_build/constraints/rtos:threadx",
+ ],
+ deps = [
+ ":timed_thread_notification_headers",
+ "//pw_assert",
+ "//pw_chrono_threadx:system_clock_headers",
+ "//pw_interrupt:context",
+ "//pw_sync:timed_thread_notification_facade",
+ ],
+)
+
+pw_cc_library(
name = "timed_mutex_headers",
hdrs = [
"public/pw_sync_threadx/timed_mutex_inline.h",
@@ -144,7 +224,7 @@
"//pw_build/constraints/rtos:threadx",
],
deps = [
- # TODO: This should depend on ThreadX but our third parties currently
+ # TODO(pwbug/317): This should depend on ThreadX but our third parties currently
# do not have Bazel support.
"//pw_chrono:system_clock",
"//pw_sync:timed_mutex_facade",
@@ -179,7 +259,7 @@
"public",
"public_overrides",
],
- # TODO: This should depend on ThreadX but our third parties currently
+ # TODO(pwbug/317): This should depend on ThreadX but our third parties currently
# do not have Bazel support.
target_compatible_with = [
"//pw_build/constraints/rtos:threadx",
diff --git a/pw_sync_threadx/BUILD.gn b/pw_sync_threadx/BUILD.gn
index afeebe3..a258940 100644
--- a/pw_sync_threadx/BUILD.gn
+++ b/pw_sync_threadx/BUILD.gn
@@ -49,6 +49,34 @@
]
}
+config("public_overrides_thread_notification_include_path") {
+ include_dirs = [ "public_overrides/thread_notification" ]
+ visibility = [ ":thread_notification" ]
+}
+
+# This target provides the backend for pw::sync::ThreadNotification based on
+# tx_thread_sleep and tx_thread_wait_abort.
+pw_source_set("thread_notification") {
+ public_configs = [
+ ":public_include_path",
+ ":public_overrides_thread_notification_include_path",
+ ]
+ public = [
+ "public/pw_sync_threadx/thread_notification_inline.h",
+ "public/pw_sync_threadx/thread_notification_native.h",
+ "public_overrides/thread_notification/pw_sync_backend/thread_notification_inline.h",
+ "public_overrides/thread_notification/pw_sync_backend/thread_notification_native.h",
+ ]
+ public_deps = [
+ "$dir_pw_assert",
+ "$dir_pw_interrupt:context",
+ "$dir_pw_sync:interrupt_spin_lock",
+ "$dir_pw_sync:thread_notification.facade",
+ "$dir_pw_third_party/threadx",
+ ]
+ sources = [ "thread_notification.cc" ]
+}
+
if (pw_chrono_SYSTEM_CLOCK_BACKEND != "") {
# This target provides the backend for pw::sync::BinarySemaphore.
pw_source_set("binary_semaphore") {
@@ -111,6 +139,41 @@
"the ThreadX pw::chrono::SystemClock backend.")
}
+ config("public_overrides_timed_thread_notification_include_path") {
+ include_dirs = [ "public_overrides/timed_thread_notification" ]
+ visibility = [ ":timed_thread_notification" ]
+ }
+
+ # This target provides the backend for pw::sync::TimedThreadNotification based
+ # tx_thread_sleep and tx_thread_wait_abort.
+ pw_source_set("timed_thread_notification") {
+ public_configs = [
+ ":public_include_path",
+ ":public_overrides_timed_thread_notification_include_path",
+ ]
+ public = [
+ "public/pw_sync_threadx/timed_thread_notification_inline.h",
+ "public_overrides/timed_thread_notification/pw_sync_backend/timed_thread_notification_inline.h",
+ ]
+ public_deps = [
+ ":thread_notification",
+ "$dir_pw_chrono:system_clock",
+ "$dir_pw_sync:timed_thread_notification.facade",
+ ]
+ sources = [ "timed_thread_notification.cc" ]
+ deps = [
+ "$dir_pw_assert",
+ "$dir_pw_interrupt:context",
+ "$dir_pw_third_party/threadx",
+ pw_chrono_SYSTEM_CLOCK_BACKEND,
+ ]
+ assert(pw_sync_OVERRIDE_SYSTEM_CLOCK_BACKEND_CHECK ||
+ pw_chrono_SYSTEM_CLOCK_BACKEND ==
+ "$dir_pw_chrono_threadx:system_clock",
+ "The ThreadX pw::sync::TimedMutex backend only works with the " +
+ "ThreadX pw::chrono::SystemClock backend.")
+ }
+
# This target provides the backend for pw::sync::TimedMutex.
pw_source_set("timed_mutex") {
public_configs = [
diff --git a/pw_sync_threadx/docs.rst b/pw_sync_threadx/docs.rst
index b6fac08..1a3daa8 100644
--- a/pw_sync_threadx/docs.rst
+++ b/pw_sync_threadx/docs.rst
@@ -1,13 +1,72 @@
.. _module-pw_sync_threadx:
----------------
+===============
pw_sync_threadx
----------------
-This is a set of backends for pw_sync based on ThreadX. It is not ready for use,
-and is under construction.
+===============
+This is a set of backends for pw_sync based on ThreadX.
It is possible, if necessary, to use pw_sync_threadx without using the Pigweed
provided pw_chrono_threadx in case the ThreadX time API (``tx_time_get()``)) is
not available (i.e. ``TX_NO_TIMER`` is set). You are responsible for ensuring
that the chrono backend provided has counts which match the ThreadX tick based
API.
+
+--------------------------------
+Critical Section Lock Primitives
+--------------------------------
+
+Mutex & TimedMutex
+==================
+The ThreadX backend for the Mutex and TimedMutex use ``TX_MUTEX`` as the
+underlying type. It is created using ``tx_mutex_create`` as part of the
+constructors and cleaned up using ``tx_mutex_delete`` in the destructors.
+
+InterruptSpinLock
+=================
+The ThreadX backend for InterruptSpinLock is backed by an ``enum class`` and
+two ``UINT`` which permits these objects to detect accidental recursive locking
+and unlocking contexts.
+
+This object uses ``tx_interrupt_control`` to enable critical sections. In
+addition, ``tx_thread_preemption_change`` is used to prevent accidental thread
+context switches while the InterruptSpinLock is held by a thread.
+
+.. Warning::
+ This backend does not support SMP yet as there's no internal lock to spin on.
+
+--------------------
+Signaling Primitives
+--------------------
+
+ThreadNotification & TimedThreadNotification
+============================================
+An optimized ThreadX backend for the ThreadNotification and
+TimedThreadNotification is provided using ``tx_thread_sleep`` and
+``tx_thread_wait_abort``. It is backed by a ``TX_THREAD*`` and a ``bool`` which
+permits these objects to track the notification value per instance. In addition,
+this backend relies on a global singleton InterruptSpinLock to provide thread
+safety in a way which is SMP compatible.
+
+Design Notes
+------------
+Because ThreadX can support SMP systems, we need a lock which permits spinning
+to safely mutate the notification value and whether the thread is blocked.
+This could be allocated per ThreadNotification instance, however this cost
+quickly adds up with a large number of instances. In addition, the critical
+sections are reasonably short.
+
+For those reasons, we opted to go with a single global interrupt spin lock. This
+is the minimal memory footprint solution, perfect for uniprocessor systems. In
+addition, given that we do not expect any serious contention for most of our
+SMP users, we believe this is a good trade-off.
+
+On very large SMP systems which heavily rely on ThreadNotifications, one could
+consider moving the InterruptSpinLock to the ThreadNotification instance if
+contention on this global lock becomes a problem.
+
+BinarySemaphore & CountingSemaphore
+===================================
+The ThreadX backends for the BinarySemaphore and CountingSemaphore use
+``TX_SEMAPHORE`` as the underlying type. It is created using
+``tx_semaphore_create`` as part of the constructor and cleaned up using
+``tx_semaphore_delete`` in the destructor.
diff --git a/pw_sync_threadx/public/pw_sync_threadx/interrupt_spin_lock_native.h b/pw_sync_threadx/public/pw_sync_threadx/interrupt_spin_lock_native.h
index 01e07ab..a522eb3 100644
--- a/pw_sync_threadx/public/pw_sync_threadx/interrupt_spin_lock_native.h
+++ b/pw_sync_threadx/public/pw_sync_threadx/interrupt_spin_lock_native.h
@@ -17,6 +17,11 @@
namespace pw::sync::backend {
+// This currently does not support SMP as there's no actual lock to spin on
+// internally, see the pw_sync_threadx docs for more details.
+#ifdef TX_SMP_CORE_ID
+#error "This backend does not support SMP versions of ThreadX yet!"
+#endif // defined(TX_SMP_CORE_ID)
struct NativeInterruptSpinLock {
enum class State {
kUnlocked = 0, // This must be 0 to ensure it is bss eligible.
diff --git a/pw_sync_threadx/public/pw_sync_threadx/thread_notification_inline.h b/pw_sync_threadx/public/pw_sync_threadx/thread_notification_inline.h
new file mode 100644
index 0000000..2631a23
--- /dev/null
+++ b/pw_sync_threadx/public/pw_sync_threadx/thread_notification_inline.h
@@ -0,0 +1,57 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <mutex>
+
+#include "pw_assert/assert.h"
+#include "pw_interrupt/context.h"
+#include "pw_sync/thread_notification.h"
+#include "tx_api.h"
+
+namespace pw::sync {
+
+inline ThreadNotification::ThreadNotification()
+ : native_type_{
+ .blocked_thread = nullptr,
+ .notified = false,
+ } {}
+
+inline ThreadNotification::~ThreadNotification() = default;
+
+inline bool ThreadNotification::try_acquire() {
+ PW_DASSERT(!interrupt::InInterruptContext());
+ {
+ std::lock_guard lock(backend::thread_notification_isl);
+ const bool notified = native_type_.notified;
+ native_type_.notified = false;
+ return notified;
+ }
+}
+
+inline void ThreadNotification::release() {
+ std::lock_guard lock(backend::thread_notification_isl);
+ if (native_type_.blocked_thread != nullptr) {
+ PW_ASSERT(tx_thread_wait_abort(native_type_.blocked_thread) == TX_SUCCESS);
+ native_type_.blocked_thread = nullptr;
+ }
+ native_type_.notified = true;
+}
+
+inline ThreadNotification::native_handle_type
+ThreadNotification::native_handle() {
+ return native_type_;
+}
+
+} // namespace pw::sync
diff --git a/pw_sync_threadx/public/pw_sync_threadx/thread_notification_native.h b/pw_sync_threadx/public/pw_sync_threadx/thread_notification_native.h
new file mode 100644
index 0000000..fc04b01
--- /dev/null
+++ b/pw_sync_threadx/public/pw_sync_threadx/thread_notification_native.h
@@ -0,0 +1,29 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_sync/interrupt_spin_lock.h"
+#include "tx_api.h"
+
+namespace pw::sync::backend {
+
+extern InterruptSpinLock thread_notification_isl;
+
+struct NativeThreadNotification {
+ TX_THREAD* blocked_thread;
+ bool notified;
+};
+using NativeThreadNotificationHandle = NativeThreadNotification&;
+
+} // namespace pw::sync::backend
diff --git a/pw_sync_threadx/public/pw_sync_threadx/timed_thread_notification_inline.h b/pw_sync_threadx/public/pw_sync_threadx/timed_thread_notification_inline.h
new file mode 100644
index 0000000..543fac8
--- /dev/null
+++ b/pw_sync_threadx/public/pw_sync_threadx/timed_thread_notification_inline.h
@@ -0,0 +1,29 @@
+
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_chrono/system_clock.h"
+#include "pw_sync/timed_thread_notification.h"
+
+namespace pw::sync {
+
+inline bool TimedThreadNotification::try_acquire_until(
+ chrono::SystemClock::time_point deadline) {
+ // Note that if this deadline is in the future, it will get rounded up by
+ // one whole tick due to how try_lock_for is implemented.
+ return try_acquire_for(deadline - chrono::SystemClock::now());
+}
+
+} // namespace pw::sync
diff --git a/pw_sync_threadx/public_overrides/thread_notification/pw_sync_backend/thread_notification_inline.h b/pw_sync_threadx/public_overrides/thread_notification/pw_sync_backend/thread_notification_inline.h
new file mode 100644
index 0000000..cba88e3
--- /dev/null
+++ b/pw_sync_threadx/public_overrides/thread_notification/pw_sync_backend/thread_notification_inline.h
@@ -0,0 +1,16 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_sync_threadx/thread_notification_inline.h"
diff --git a/pw_sync_threadx/public_overrides/thread_notification/pw_sync_backend/thread_notification_native.h b/pw_sync_threadx/public_overrides/thread_notification/pw_sync_backend/thread_notification_native.h
new file mode 100644
index 0000000..7df0e9a
--- /dev/null
+++ b/pw_sync_threadx/public_overrides/thread_notification/pw_sync_backend/thread_notification_native.h
@@ -0,0 +1,16 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_sync_threadx/thread_notification_native.h"
diff --git a/pw_sync_threadx/public_overrides/timed_thread_notification/pw_sync_backend/timed_thread_notification_inline.h b/pw_sync_threadx/public_overrides/timed_thread_notification/pw_sync_backend/timed_thread_notification_inline.h
new file mode 100644
index 0000000..ff4a908
--- /dev/null
+++ b/pw_sync_threadx/public_overrides/timed_thread_notification/pw_sync_backend/timed_thread_notification_inline.h
@@ -0,0 +1,16 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_sync_threadx/timed_thread_notification_inline.h"
diff --git a/pw_sync_threadx/thread_notification.cc b/pw_sync_threadx/thread_notification.cc
new file mode 100644
index 0000000..96f07fa
--- /dev/null
+++ b/pw_sync_threadx/thread_notification.cc
@@ -0,0 +1,56 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_sync/thread_notification.h"
+
+#include <mutex>
+
+#include "pw_assert/check.h"
+#include "pw_interrupt/context.h"
+#include "pw_sync/interrupt_spin_lock.h"
+#include "tx_api.h"
+
+namespace pw::sync {
+namespace backend {
+
+InterruptSpinLock thread_notification_isl;
+
+} // namespace backend
+
+void ThreadNotification::acquire() {
+ PW_DCHECK(!interrupt::InInterruptContext());
+ PW_DCHECK(native_type_.blocked_thread == nullptr);
+
+ {
+ std::lock_guard lock(backend::thread_notification_isl);
+ if (native_type_.notified) {
+ native_type_.notified = false;
+ return;
+ }
+ // Not notified yet, register the thread for a one-time notification.
+ native_type_.blocked_thread = tx_thread_identify();
+ }
+
+ PW_CHECK_UINT_EQ(tx_thread_sleep(TX_WAIT_FOREVER), TX_WAIT_ABORTED);
+ {
+ // The thread pointer was cleared by the notifier.
+ // Note that this may hide another notification, however this is considered
+ // a form of notification saturation just like as if this happened before
+ // acquire() was invoked.
+ std::lock_guard lock(backend::thread_notification_isl);
+ native_type_.notified = false;
+ }
+}
+
+} // namespace pw::sync
diff --git a/pw_sync_threadx/timed_thread_notification.cc b/pw_sync_threadx/timed_thread_notification.cc
new file mode 100644
index 0000000..fbf5ba4
--- /dev/null
+++ b/pw_sync_threadx/timed_thread_notification.cc
@@ -0,0 +1,93 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_sync/timed_thread_notification.h"
+
+#include <mutex>
+
+#include "pw_assert/check.h"
+#include "pw_chrono/system_clock.h"
+#include "pw_chrono_threadx/system_clock_constants.h"
+#include "pw_interrupt/context.h"
+#include "tx_api.h"
+
+namespace pw::sync {
+namespace {
+
+using pw::chrono::SystemClock;
+
+} // namespace
+
+bool TimedThreadNotification::try_acquire_for(SystemClock::duration timeout) {
+ PW_DCHECK(!interrupt::InInterruptContext());
+ PW_DCHECK(native_handle().blocked_thread == nullptr);
+ {
+ std::lock_guard lock(backend::thread_notification_isl);
+ const bool notified = native_handle().notified;
+ // Don't block for negative or zero length durations.
+ if (notified || timeout <= SystemClock::duration::zero()) {
+ native_handle().notified = false;
+ return notified;
+ }
+ // Not notified yet, register the thread for a one-time notification.
+ native_handle().blocked_thread = tx_thread_identify();
+ }
+
+ const bool notified = [&]() {
+ // On a tick based kernel we cannot tell how far along we are on the current
+ // tick, ergo we add one whole tick to the final duration.
+ constexpr SystemClock::duration kMaxTimeoutMinusOne =
+ pw::chrono::threadx::kMaxTimeout - SystemClock::duration(1);
+ // In case the timeout is too long for us to express through the native
+ // ThreadX API, we repeatedly wait with shorter durations.
+ while (timeout > kMaxTimeoutMinusOne) {
+ const UINT result =
+ tx_thread_sleep(static_cast<ULONG>(kMaxTimeoutMinusOne.count()));
+ if (result != TX_SUCCESS) {
+ PW_CHECK_UINT_EQ(TX_WAIT_ABORTED, result);
+ return true;
+ }
+ timeout -= kMaxTimeoutMinusOne;
+ }
+
+ const UINT result =
+ tx_thread_sleep(static_cast<ULONG>(timeout.count() + 1));
+ if (result == TX_SUCCESS) {
+ return false;
+ }
+ PW_CHECK_UINT_EQ(TX_WAIT_ABORTED, result);
+ return true;
+ }();
+
+ {
+ std::lock_guard lock(backend::thread_notification_isl);
+ if (notified) {
+ // Note that this may hide another notification, however this is
+ // considered form of notification saturation just like as if this
+ // happened before acquire() was invoked.
+ native_handle().notified = false;
+ // The thread pointer was cleared by the notifier.
+ } else {
+ // Note that we do NOT want to clear the notified value so the next call
+ // can detect the notification which came after we timed out but before
+ // this critical section.
+ //
+ // However, we do need to clear the thread pointer if we weren't notified.
+ native_handle().blocked_thread = nullptr;
+ }
+ }
+ return notified;
+}
+
+} // namespace pw::sync