pw_thread_threadx: adds yield, id, and sleep backends

Adds the ThreadX backend for pw_thread's yield, sleep, and id.

Change-Id: I3aba539632b9d4dbc90b2d1498365213dd44e7cf
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/31541
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/modules.gni b/modules.gni
index 4cd4d9b..28940d7 100644
--- a/modules.gni
+++ b/modules.gni
@@ -93,6 +93,7 @@
   dir_pw_thread = get_path_info("pw_thread", "abspath")
   dir_pw_thread_stl = get_path_info("pw_thread_stl", "abspath")
   dir_pw_thread_freertos = get_path_info("pw_thread_freertos", "abspath")
+  dir_pw_thread_threadx = get_path_info("pw_thread_threadx", "abspath")
   dir_pw_third_party = get_path_info("third_party", "abspath")
   dir_pw_tokenizer = get_path_info("pw_tokenizer", "abspath")
   dir_pw_toolchain = get_path_info("pw_toolchain", "abspath")
diff --git a/pw_thread/backend.gni b/pw_thread/backend.gni
index b74b899..98a2738 100644
--- a/pw_thread/backend.gni
+++ b/pw_thread/backend.gni
@@ -21,4 +21,9 @@
 
   # Backend for the pw_thread module's pw::thread::yield.
   pw_thread_YIELD_BACKEND = ""
+
+  # Whether the GN asserts should be silenced in ensuring that a compatible
+  # backend for pw_chrono_SYSTEM_CLOCK_BACKEND is chosen.
+  # Set to true to disable the asserts.
+  pw_thread_OVERRIDE_SYSTEM_CLOCK_BACKEND_CHECK = false
 }
diff --git a/pw_thread_threadx/BUILD b/pw_thread_threadx/BUILD
new file mode 100644
index 0000000..bcb8a1f
--- /dev/null
+++ b/pw_thread_threadx/BUILD
@@ -0,0 +1,99 @@
+# 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.
+
+load(
+    "//pw_build:pigweed.bzl",
+    "pw_cc_library",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])  # Apache License 2.0
+
+pw_cc_library(
+    name = "id_headers",
+    hdrs = [
+        "public/pw_thread_threadx/id_inline.h",
+        "public/pw_thread_threadx/id_native.h",
+        "public_overrides/pw_thread_backend/id_inline.h",
+        "public_overrides/pw_thread_backend/id_native.h",
+    ],
+    includes = [
+        "public",
+        "public_overrides",
+    ],
+)
+
+pw_cc_library(
+    name = "id",
+    deps = [
+        ":id_headers",
+        "//pw_thread:id_facade",
+    ],
+    # TODO(pwbug/317): This should depend on ThreadX but our third parties
+		# currently do not have Bazel support.
+)
+
+pw_cc_library(
+    name = "sleep_headers",
+    hdrs = [
+        "public/pw_thread_threadx/sleep_inline.h",
+        "public_overrides/pw_thread_backend/sleep_inline.h",
+    ],
+    includes = [
+        "public",
+        "public_overrides",
+    ],
+    deps = [
+        "//pw_chrono:system_clock",
+    ],
+)
+
+pw_cc_library(
+    name = "sleep",
+    srcs = [
+        "sleep.cc",
+    ],
+    deps = [
+        ":sleep_headers",
+        "//pw_chrono_threadx:system_clock_headers",
+        "//pw_assert",
+        "//pw_chrono:system_clock",
+        "//pw_thread:sleep_facade",
+    ],
+    # TODO(pwbug/317): This should depend on ThreadX but our third parties
+		# currently do not have Bazel support.
+)
+
+pw_cc_library(
+    name = "yield_headers",
+    hdrs = [
+        "public/pw_thread_threadx/yield_inline.h",
+        "public_overrides/pw_thread_backend/yield_inline.h",
+    ],
+    includes = [
+        "public",
+        "public_overrides",
+    ],
+    # TODO(pwbug/317): This should depend on ThreadX but our third parties
+		# currently do not have Bazel support.
+)
+
+pw_cc_library(
+    name = "yield",
+    deps = [
+        ":yield_headers",
+        "//pw_thread:yield_facade",
+    ],
+)
diff --git a/pw_thread_threadx/BUILD.gn b/pw_thread_threadx/BUILD.gn
new file mode 100644
index 0000000..954615f
--- /dev/null
+++ b/pw_thread_threadx/BUILD.gn
@@ -0,0 +1,100 @@
+# 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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_chrono/backend.gni")
+import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_thread/backend.gni")
+
+config("public_include_path") {
+  include_dirs = [ "public" ]
+  visibility = [ ":*" ]
+}
+
+config("backend_config") {
+  include_dirs = [ "public_overrides" ]
+  visibility = [ ":*" ]
+}
+
+# This target provides the backend for pw::thread::Id.
+pw_source_set("id") {
+  public_configs = [
+    ":public_include_path",
+    ":backend_config",
+  ]
+  public_deps = [
+    "$dir_pw_assert",
+    "$dir_pw_interrupt:context",
+    "$dir_pw_third_party/threadx",
+  ]
+  public = [
+    "public/pw_thread_threadx/id_inline.h",
+    "public/pw_thread_threadx/id_native.h",
+    "public_overrides/pw_thread_backend/id_inline.h",
+    "public_overrides/pw_thread_backend/id_native.h",
+  ]
+  deps = [ "$dir_pw_thread:id.facade" ]
+}
+
+if (pw_chrono_SYSTEM_CLOCK_BACKEND != "" && pw_thread_SLEEP_BACKEND != "") {
+  # This target provides the backend for pw::thread::sleep_{for,until}.
+  pw_source_set("sleep") {
+    public_configs = [
+      ":public_include_path",
+      ":backend_config",
+    ]
+    public = [
+      "public/pw_thread_threadx/sleep_inline.h",
+      "public_overrides/pw_thread_backend/sleep_inline.h",
+    ]
+    public_deps = [ "$dir_pw_chrono:system_clock" ]
+    sources = [ "sleep.cc" ]
+    deps = [
+      "$dir_pw_assert",
+      "$dir_pw_chrono_threadx:system_clock",
+      "$dir_pw_third_party/threadx",
+      "$dir_pw_thread:id",
+    ]
+    assert(
+        pw_thread_OVERRIDE_SYSTEM_CLOCK_BACKEND_CHECK ||
+            pw_chrono_SYSTEM_CLOCK_BACKEND ==
+                "$dir_pw_chrono_threadx:system_clock",
+        "The ThreadX pw::thread::sleep_{for,until} backend only works with " +
+            "the ThreadX pw::chrono::SystemClock backend.")
+  }
+}
+
+# This target provides the backend for pw::thread::yield.
+pw_source_set("yield") {
+  public_configs = [
+    ":public_include_path",
+    ":backend_config",
+  ]
+  public = [
+    "public/pw_thread_threadx/yield_inline.h",
+    "public_overrides/pw_thread_backend/yield_inline.h",
+  ]
+  public_deps = [
+    "$dir_pw_assert",
+    "$dir_pw_third_party/threadx",
+    "$dir_pw_thread:id",
+  ]
+  deps = [ "$dir_pw_thread:yield.facade" ]
+}
+
+pw_doc_group("docs") {
+  sources = [ "docs.rst" ]
+}
diff --git a/pw_thread_threadx/docs.rst b/pw_thread_threadx/docs.rst
new file mode 100644
index 0000000..a457978
--- /dev/null
+++ b/pw_thread_threadx/docs.rst
@@ -0,0 +1,8 @@
+.. _module-pw_thread_threadx:
+
+-----------------
+pw_thread_threadx
+-----------------
+This is a set of backends for pw_thread based on ThreadX. It is not ready for
+use, and is under construction.
+
diff --git a/pw_thread_threadx/public/pw_thread_threadx/id_inline.h b/pw_thread_threadx/public/pw_thread_threadx/id_inline.h
new file mode 100644
index 0000000..7004996
--- /dev/null
+++ b/pw_thread_threadx/public/pw_thread_threadx/id_inline.h
@@ -0,0 +1,33 @@
+// 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.
+#pragma once
+
+#include "pw_assert/light.h"
+#include "pw_thread/id.h"
+#include "tx_api.h"
+// Prior to ThreadX 6.1, this contained TX_THREAD_GET_SYSTEM_STATE().
+#include "tx_thread.h"
+
+namespace pw::this_thread {
+
+inline thread::Id get_id() {
+  // When this value is 0, a thread is executing or the system is idle.
+  // Other values indicate that interrupt or initialization processing is
+  // active.
+  PW_DASSERT(TX_THREAD_GET_SYSTEM_STATE() == 0);
+
+  return thread::Id(tx_thread_identify());
+}
+
+}  // namespace pw::this_thread
diff --git a/pw_thread_threadx/public/pw_thread_threadx/id_native.h b/pw_thread_threadx/public/pw_thread_threadx/id_native.h
new file mode 100644
index 0000000..72c1e7f
--- /dev/null
+++ b/pw_thread_threadx/public/pw_thread_threadx/id_native.h
@@ -0,0 +1,52 @@
+// 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.
+#pragma once
+
+#include "tx_api.h"
+
+namespace pw::thread::backend {
+
+// Instead of using a pw::thread::threadx specific identifier, the ThreadX
+// thread pointer is used as this means pw::this_thread::id works correctly on
+// threads started with the native ThreadX APIs as well as those started
+// using the pw::thread APIs.
+class NativeId {
+ public:
+  constexpr NativeId(TX_THREAD* thread_ptr = nullptr)
+      : thread_ptr_(thread_ptr) {}
+
+  constexpr bool operator==(NativeId other) const {
+    return thread_ptr_ == other.thread_ptr_;
+  }
+  constexpr bool operator!=(NativeId other) const {
+    return thread_ptr_ != other.thread_ptr_;
+  }
+  constexpr bool operator<(NativeId other) const {
+    return thread_ptr_ < other.thread_ptr_;
+  }
+  constexpr bool operator<=(NativeId other) const {
+    return thread_ptr_ <= other.thread_ptr_;
+  }
+  constexpr bool operator>(NativeId other) const {
+    return thread_ptr_ > other.thread_ptr_;
+  }
+  constexpr bool operator>=(NativeId other) const {
+    return thread_ptr_ >= other.thread_ptr_;
+  }
+
+ private:
+  TX_THREAD* thread_ptr_;
+};
+
+}  // namespace pw::thread::backend
diff --git a/pw_thread_threadx/public/pw_thread_threadx/sleep_inline.h b/pw_thread_threadx/public/pw_thread_threadx/sleep_inline.h
new file mode 100644
index 0000000..1fc3df6
--- /dev/null
+++ b/pw_thread_threadx/public/pw_thread_threadx/sleep_inline.h
@@ -0,0 +1,24 @@
+// 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.
+#pragma once
+
+#include "pw_chrono/system_clock.h"
+
+namespace pw::this_thread {
+
+inline void sleep_until(chrono::SystemClock::time_point until_at_least) {
+  return sleep_for(until_at_least - chrono::SystemClock::now());
+}
+
+}  // namespace pw::this_thread
diff --git a/pw_thread_threadx/public/pw_thread_threadx/yield_inline.h b/pw_thread_threadx/public/pw_thread_threadx/yield_inline.h
new file mode 100644
index 0000000..32ce45c
--- /dev/null
+++ b/pw_thread_threadx/public/pw_thread_threadx/yield_inline.h
@@ -0,0 +1,27 @@
+// 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.
+#pragma once
+
+#include "pw_assert/light.h"
+#include "pw_thread/id.h"
+#include "tx_api.h"
+
+namespace pw::this_thread {
+
+inline void yield() {
+  PW_DASSERT(get_id() != thread::Id());
+  tx_thread_relinquish();
+}
+
+}  // namespace pw::this_thread
diff --git a/pw_thread_threadx/public_overrides/pw_thread_backend/id_inline.h b/pw_thread_threadx/public_overrides/pw_thread_backend/id_inline.h
new file mode 100644
index 0000000..046e842
--- /dev/null
+++ b/pw_thread_threadx/public_overrides/pw_thread_backend/id_inline.h
@@ -0,0 +1,16 @@
+// 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.
+#pragma once
+
+#include "pw_thread_threadx/id_inline.h"
diff --git a/pw_thread_threadx/public_overrides/pw_thread_backend/id_native.h b/pw_thread_threadx/public_overrides/pw_thread_backend/id_native.h
new file mode 100644
index 0000000..bf144dd
--- /dev/null
+++ b/pw_thread_threadx/public_overrides/pw_thread_backend/id_native.h
@@ -0,0 +1,16 @@
+// 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.
+#pragma once
+
+#include "pw_thread_threadx/id_native.h"
diff --git a/pw_thread_threadx/public_overrides/pw_thread_backend/sleep_inline.h b/pw_thread_threadx/public_overrides/pw_thread_backend/sleep_inline.h
new file mode 100644
index 0000000..7f1cea9
--- /dev/null
+++ b/pw_thread_threadx/public_overrides/pw_thread_backend/sleep_inline.h
@@ -0,0 +1,16 @@
+// 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.
+#pragma once
+
+#include "pw_thread_threadx/sleep_inline.h"
diff --git a/pw_thread_threadx/public_overrides/pw_thread_backend/yield_inline.h b/pw_thread_threadx/public_overrides/pw_thread_backend/yield_inline.h
new file mode 100644
index 0000000..c97d259
--- /dev/null
+++ b/pw_thread_threadx/public_overrides/pw_thread_backend/yield_inline.h
@@ -0,0 +1,16 @@
+// 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.
+#pragma once
+
+#include "pw_thread_threadx/yield_inline.h"
diff --git a/pw_thread_threadx/sleep.cc b/pw_thread_threadx/sleep.cc
new file mode 100644
index 0000000..48fdcaa
--- /dev/null
+++ b/pw_thread_threadx/sleep.cc
@@ -0,0 +1,52 @@
+// 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_thread/sleep.h"
+
+#include <algorithm>
+
+#include "pw_assert/assert.h"
+#include "pw_chrono/system_clock.h"
+#include "pw_chrono_threadx/system_clock_constants.h"
+#include "pw_thread/id.h"
+#include "tx_api.h"
+
+using pw::chrono::threadx::kMaxTimeout;
+
+namespace pw::this_thread {
+
+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.
+  for_at_least = std::max(for_at_least, chrono::SystemClock::duration::zero());
+
+  // The pw::sleep_{for,until} API contract is to yield if we attempt to sleep
+  // for a duration of 0. ThreadX's tx_thread_sleep does a no-op if 0 is passed,
+  // ergo we explicitly check for the yield condition if the duration is 0.
+  if (for_at_least == chrono::SystemClock::duration::zero()) {
+    tx_thread_relinquish();  // Direct API is used to reduce overhead.
+    return;
+  }
+
+  while (for_at_least > kMaxTimeout) {
+    const UINT result = tx_thread_sleep(kMaxTimeout.count());
+    PW_CHECK_UINT_EQ(TX_SUCCESS, result);
+    for_at_least -= kMaxTimeout;
+  }
+  const UINT result = tx_thread_sleep(for_at_least.count());
+  PW_CHECK_UINT_EQ(TX_SUCCESS, result);
+}
+
+}  // namespace pw::this_thread