pw_{sync,chrono}_threadx: Add initial ThreadX support

Adds initial ThreadX support through a set of backends for pw_sync
through pw_sync_threadx and pw_chrono through pw_chrono_threadx.

Change-Id: I3e310323cfce3d4bf07557273c2a86f91f6479d7
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/24900
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Ewout van Bekkum <ewout@google.com>
diff --git a/modules.gni b/modules.gni
index 5b9ad3a..056b425 100644
--- a/modules.gni
+++ b/modules.gni
@@ -31,6 +31,7 @@
   dir_pw_checksum = get_path_info("pw_checksum", "abspath")
   dir_pw_chrono = get_path_info("pw_chrono", "abspath")
   dir_pw_chrono_stl = get_path_info("pw_chrono_stl", "abspath")
+  dir_pw_chrono_threadx = get_path_info("pw_chrono_threadx", "abspath")
   dir_pw_cli = get_path_info("pw_cli", "abspath")
   dir_pw_containers = get_path_info("pw_containers", "abspath")
   dir_pw_cpu_exception = get_path_info("pw_cpu_exception", "abspath")
diff --git a/pw_chrono_threadx/BUILD b/pw_chrono_threadx/BUILD
new file mode 100644
index 0000000..0920cfb
--- /dev/null
+++ b/pw_chrono_threadx/BUILD
@@ -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.
+
+load(
+    "//pw_build:pigweed.bzl",
+    "pw_cc_library",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])  # Apache License 2.0
+
+pw_cc_library(
+    name = "system_clock_headers",
+    hdrs = [
+        "public/pw_chrono_threadx/config.h",
+        "public/pw_chrono_threadx/system_clock_config.h",
+        "public/pw_chrono_threadx/system_clock_constants.h",
+        "public_overrides/pw_chrono_backend/system_clock_config.h",
+    ],
+    includes = [
+        "public",
+        "public_overrides",
+    ],
+    deps = [
+        "//pw_chrono:epoch",
+    ],
+)
+
+pw_cc_library(
+    name = "system_clock",
+    srcs = [
+        "system_clock.cc",
+    ],
+    deps = [
+        ":system_clock_headers",
+        "//pw_chrono:system_clock_facade",
+        # TODO: This should depend on ThreadX but our third parties currently
+        # do not have Bazel support.
+    ],
+)
diff --git a/pw_chrono_threadx/BUILD.gn b/pw_chrono_threadx/BUILD.gn
new file mode 100644
index 0000000..d7bdace
--- /dev/null
+++ b/pw_chrono_threadx/BUILD.gn
@@ -0,0 +1,73 @@
+# 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/module_config.gni")
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_docgen/docs.gni")
+
+declare_args() {
+  # The build target that overrides the default configuration options for this
+  # module. This should point to a source set that provides defines through a
+  # public config (which may -include a file or add defines directly).
+  pw_chrono_threadx_CONFIG = pw_build_DEFAULT_MODULE_CONFIG
+}
+
+config("public_include_path") {
+  include_dirs = [ "public" ]
+  visibility = [ ":*" ]
+}
+
+config("backend_config") {
+  include_dirs = [ "public_overrides" ]
+  visibility = [ ":*" ]
+}
+
+pw_source_set("config") {
+  public = [ "public/pw_chrono_threadx/config.h" ]
+  public_configs = [ ":public_include_path" ]
+  public_deps = [
+    "$dir_pw_third_party/threadx",
+    pw_chrono_threadx_CONFIG,
+  ]
+}
+
+# This target provides the backend for pw::chrono::SystemClock.
+pw_source_set("system_clock") {
+  public_configs = [
+    ":public_include_path",
+    ":backend_config",
+  ]
+  public = [
+    "public/pw_chrono_threadx/system_clock_config.h",
+    "public/pw_chrono_threadx/system_clock_constants.h",
+    "public_overrides/pw_chrono_backend/system_clock_config.h",
+  ]
+  public_deps = [
+    ":config",
+    "$dir_pw_chrono:epoch",
+    "$dir_pw_chrono:system_clock.facade",
+    "$dir_pw_third_party/threadx",
+  ]
+  sources = [ "system_clock.cc" ]
+  deps = [
+    "$dir_pw_chrono:system_clock.facade",
+    "$dir_pw_sync:spin_lock",
+  ]
+}
+
+pw_doc_group("docs") {
+  sources = [ "docs.rst" ]
+}
diff --git a/pw_chrono_threadx/docs.rst b/pw_chrono_threadx/docs.rst
new file mode 100644
index 0000000..9913eff
--- /dev/null
+++ b/pw_chrono_threadx/docs.rst
@@ -0,0 +1,34 @@
+.. _module-pw_chrono_threadx:
+
+-=---------------
+pw_chrono_threadx
+-----------------
+``pw_chrono_threadx`` is a collection of ``pw_chrono`` backends that are
+implemented using ThreadX.
+
+.. warning::
+  This module is under construction, not ready for use, and the documentation
+  is incomplete.
+
+SystemClock backend
+-------------------
+The ThreadX based ``system_clock`` backend implements the
+``pw_chrono:system_clock`` facade by using ``tx_time_get()``. Before the global
+singleton SystemClock's SpinLock is constructed the raw result is returned,
+after the overflows are managed in a thread and IRQ safe manner to produce a
+signed 64 bit timestamp.
+
+The ``SystemClock::now()`` must be used more than once per overflow of the
+native ThreadX ``tx_time_get()`` overflow. Note that this duration may vary if
+``tx_time_set()`` is used.
+
+.. warning::
+Note that this is not compatible with TX_NO_TIMER as this disables
+``tx_time_get()``.
+
+Build targets
+-------------
+The GN build for ``pw_chrono_threadx`` has one target: ``system_clock``.
+The ``system_clock`` target provides the
+``pw_chrono_backend/system_clock_config.h`` and ``pw_chrono_threadx/config.h``
+headers and the backend for the ``pw_chrono:system_clock``.
diff --git a/pw_chrono_threadx/public/pw_chrono_threadx/config.h b/pw_chrono_threadx/public/pw_chrono_threadx/config.h
new file mode 100644
index 0000000..a4681c5
--- /dev/null
+++ b/pw_chrono_threadx/public/pw_chrono_threadx/config.h
@@ -0,0 +1,46 @@
+// 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.
+// Configuration macros for the tokenizer module.
+#pragma once
+
+#include <assert.h>
+
+#include "tx_api.h"
+
+// For the ThreadX backend of the SystemClock, there is no way to determine the
+// system timer's tick interval/frequency. By default most ports happen to be at
+// 10ms intervals (i.e. 100Hz), however this can be tuned for different
+// applications.
+// The resulting clock period is defined in seconds = numerator / denominator.
+// For example the default is configured to 1/100 seconds per clock tick.
+#ifndef PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_NUMERATOR
+#define PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_NUMERATOR 1
+#endif  // PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_NUMERATOR
+#ifndef PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_DENOMINATOR
+#define PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_DENOMINATOR 100
+#endif  // PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_DENOMINATOR
+
+static_assert(PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_NUMERATOR >= 1);
+static_assert(PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_DENOMINATOR >= 1);
+
+// Because the SystemClock::now() implementation requires the user to invoke it
+// more than once per overflow period, the max timeout is set to ensure that
+// blocking indefinitely on a single primitive will meet this constraint with
+// margin (i.e. more than twice per overflow).
+#ifndef PW_CHRONO_THREADX_CFG_MAX_TIMEOUT
+#define PW_CHRONO_THREADX_CFG_MAX_TIMEOUT ((TX_WAIT_FOREVER - 1) / 3)
+#endif  // PW_CHRONO_THREADX_CFG_MAX_TIMEOUT
+
+static_assert((PW_CHRONO_THREADX_CFG_MAX_TIMEOUT > 0) &&
+              (PW_CHRONO_THREADX_CFG_MAX_TIMEOUT <= (TX_WAIT_FOREVER - 1)));
diff --git a/pw_chrono_threadx/public/pw_chrono_threadx/system_clock_config.h b/pw_chrono_threadx/public/pw_chrono_threadx/system_clock_config.h
new file mode 100644
index 0000000..3e8a676
--- /dev/null
+++ b/pw_chrono_threadx/public/pw_chrono_threadx/system_clock_config.h
@@ -0,0 +1,39 @@
+// 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 <ratio>
+
+#include "pw_chrono/epoch.h"
+#include "pw_chrono_threadx/config.h"
+
+namespace pw::chrono::backend {
+
+// ThreadX does not have an API to determine the tick rate/period, instead
+// require the user to specify this through the configuration.
+using SystemClockPeriodSecondsRatio =
+    std::ratio<PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_NUMERATOR,
+               PW_CHRONO_THREADX_CFG_CLOCK_PERIOD_SECONDS_DENOMINATOR>;
+
+// The ThreadX clock starts at zero during initialization, approximately the
+// time since boot.
+constexpr inline Epoch kSystemClockEpoch = pw::chrono::Epoch::kTimeSinceBoot;
+
+// The current backend implementation is not NMI safe.
+constexpr inline bool kSystemClockNmiSafe = false;
+
+// The ThreadX clock halts when the systick interrupt is masked.
+constexpr inline bool kSystemClockFreeRunning = false;
+
+}  // namespace pw::chrono::backend
diff --git a/pw_chrono_threadx/public/pw_chrono_threadx/system_clock_constants.h b/pw_chrono_threadx/public/pw_chrono_threadx/system_clock_constants.h
new file mode 100644
index 0000000..5de6fa3
--- /dev/null
+++ b/pw_chrono_threadx/public/pw_chrono_threadx/system_clock_constants.h
@@ -0,0 +1,26 @@
+// 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"
+#include "pw_chrono_threadx/config.h"
+
+namespace pw::chrono::threadx {
+
+// Max timeout to be used by users of the ThreadX's pw::chrono::SystemClock
+// backend provided by this module.
+inline constexpr SystemClock::duration kMaxTimeout =
+    SystemClock::duration(PW_CHRONO_THREADX_CFG_MAX_TIMEOUT);
+
+}  // namespace pw::chrono::threadx
diff --git a/pw_chrono_threadx/public_overrides/pw_chrono_backend/system_clock_config.h b/pw_chrono_threadx/public_overrides/pw_chrono_backend/system_clock_config.h
new file mode 100644
index 0000000..3cd5c64
--- /dev/null
+++ b/pw_chrono_threadx/public_overrides/pw_chrono_backend/system_clock_config.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_chrono_threadx/system_clock_config.h"
diff --git a/pw_chrono_threadx/system_clock.cc b/pw_chrono_threadx/system_clock.cc
new file mode 100644
index 0000000..972fc29
--- /dev/null
+++ b/pw_chrono_threadx/system_clock.cc
@@ -0,0 +1,80 @@
+// 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_chrono/system_clock.h"
+
+#include <atomic>
+#include <chrono>
+#include <limits>
+#include <mutex>
+
+#include "pw_sync/spin_lock.h"
+#include "tx_api.h"
+
+namespace pw::chrono::backend {
+namespace {
+
+#if defined(TX_NO_TIMER) && TX_NO_TIMER
+#error "This backend is not compatible with TX_NO_TIMER"
+#endif  // defined(TX_NO_TIMER) && TX_NO_TIMER
+
+// Extension wrapping pw::SpinLock to allow an atomic to be used to determine
+// whether it has been constructed and is ready to be used.
+class ConstructionSignalingSpinLock : public sync::SpinLock {
+ public:
+  ConstructionSignalingSpinLock(std::atomic<bool>& constructed_signal)
+      : sync::SpinLock() {
+    // Note that the memory order is relaxed because C++ global static
+    // construction is a single threaded environment.
+    constructed_signal.store(true, std::memory_order_relaxed);
+  };
+};
+
+// This is POD, meaning this atomic is available before static C++ global
+// constructors are run.
+std::atomic<bool> system_clock_spin_lock_constructed = {false};
+
+ConstructionSignalingSpinLock system_clock_spin_lock(
+    system_clock_spin_lock_constructed);
+
+int64_t overflow_tick_count = 0;
+ULONG native_tick_count = 0;
+static_assert(!SystemClock::is_nmi_safe,
+              "global state is not atomic nor double buferred");
+
+// The tick count resets to 0, ergo the overflow count is the max count + 1.
+constexpr int64_t kNativeOverflowTickCount =
+    static_cast<int64_t>(std::numeric_limits<ULONG>::max()) + 1;
+
+}  // namespace
+
+int64_t GetSystemClockTickCount() {
+  // Note that the memory order is relaxed because C++ global static
+  // construction is a single threaded environment.
+  if (!system_clock_spin_lock_constructed.load(std::memory_order_relaxed)) {
+    return tx_time_get();
+  }
+
+  std::lock_guard lock(system_clock_spin_lock);
+  const ULONG new_native_tick_count = tx_time_get();
+  // WARNING: This must be called more than once per overflow period!
+  if (new_native_tick_count < native_tick_count) {
+    // Native tick count overflow detected!
+    overflow_tick_count += kNativeOverflowTickCount;
+  }
+  native_tick_count = new_native_tick_count;
+  return overflow_tick_count + native_tick_count;
+}
+
+}  // namespace pw::chrono::backend
diff --git a/pw_sync_threadx/BUILD b/pw_sync_threadx/BUILD
index 54dff01..7b25c93 100644
--- a/pw_sync_threadx/BUILD
+++ b/pw_sync_threadx/BUILD
@@ -22,6 +22,102 @@
 licenses(["notice"])  # Apache License 2.0
 
 pw_cc_library(
+    name = "binary_semaphore_headers",
+    hdrs = [
+        "public/pw_sync_threadx/binary_semaphore_inline.h",
+        "public/pw_sync_threadx/binary_semaphore_native.h",
+        "public_overrides/pw_sync_backend/binary_semaphore_inline.h",
+        "public_overrides/pw_sync_backend/binary_semaphore_native.h",
+    ],
+    includes = [
+        "public",
+        "public_overrides",
+    ],
+    deps = [
+        # TODO: This should depend on ThreadX but our third parties currently
+        # do not have Bazel support.
+        "//pw_chrono:system_clock",
+    ],
+)
+
+pw_cc_library(
+    name = "binary_semaphore",
+    srcs = [
+        "binary_semaphore.cc",
+    ],
+    deps = [
+        ":binary_semaphore_headers",
+        "//pw_chrono_threadx:system_clock_headers",
+        "//pw_interrupt:context",
+        "//pw_sync:binary_semaphore_facade",
+    ],
+)
+
+pw_cc_library(
+    name = "counting_semaphore_headers",
+    hdrs = [
+        "public/pw_sync_threadx/counting_semaphore_inline.h",
+        "public/pw_sync_threadx/counting_semaphore_native.h",
+        "public_overrides/pw_sync_backend/counting_semaphore_inline.h",
+        "public_overrides/pw_sync_backend/counting_semaphore_native.h",
+    ],
+    includes = [
+        "public",
+        "public_overrides",
+    ],
+    deps = [
+        # TODO: This should depend on ThreadX but our third parties currently
+        # do not have Bazel support.
+        "//pw_chrono:system_clock",
+    ],
+)
+
+pw_cc_library(
+    name = "counting_semaphore",
+    srcs = [
+        "counting_semaphore.cc",
+    ],
+    deps = [
+        ":counting_semaphore_headers",
+        "//pw_chrono_threadx:system_clock_headers",
+        "//pw_interrupt:context",
+        "//pw_sync:counting_semaphore_facade",
+    ],
+)
+
+pw_cc_library(
+    name = "mutex_headers",
+    hdrs = [
+        "public/pw_sync_threadx/mutex_inline.h",
+        "public/pw_sync_threadx/mutex_native.h",
+        "public_overrides/pw_sync_backend/mutex_inline.h",
+        "public_overrides/pw_sync_backend/mutex_native.h",
+    ],
+    includes = [
+        "public",
+        "public_overrides",
+    ],
+    deps = [
+        # TODO: This should depend on ThreadX but our third parties currently
+        # do not have Bazel support.
+        "//pw_chrono:system_clock",
+    ],
+)
+
+pw_cc_library(
+    name = "mutex",
+    srcs = [
+        "mutex.cc",
+    ],
+    deps = [
+        ":mutex_headers",
+        "//pw_chrono_threadx:system_clock_headers",
+        "//pw_interrupt:context",
+        "//pw_sync:mutex_facade",
+    ],
+)
+
+pw_cc_library(
     name = "spin_lock_headers",
     hdrs = [
         "public/pw_sync_threadx/spin_lock_inline.h",
diff --git a/pw_sync_threadx/BUILD.gn b/pw_sync_threadx/BUILD.gn
index 7f80bd5..dc82a5a 100644
--- a/pw_sync_threadx/BUILD.gn
+++ b/pw_sync_threadx/BUILD.gn
@@ -15,6 +15,7 @@
 import("//build_overrides/pigweed.gni")
 
 import("$dir_pw_build/target_types.gni")
+import("$dir_pw_chrono/backend.gni")
 import("$dir_pw_docgen/docs.gni")
 
 config("public_include_path") {
@@ -27,6 +28,96 @@
   visibility = [ ":*" ]
 }
 
+# This target provides the backend for pw::sync::BinarySemaphore.
+pw_source_set("binary_semaphore") {
+  public_configs = [
+    ":public_include_path",
+    ":backend_config",
+  ]
+  public = [
+    "public/pw_sync_threadx/binary_semaphore_inline.h",
+    "public/pw_sync_threadx/binary_semaphore_native.h",
+    "public_overrides/pw_sync_backend/binary_semaphore_inline.h",
+    "public_overrides/pw_sync_backend/binary_semaphore_native.h",
+  ]
+  public_deps = [
+    "$dir_pw_assert",
+    "$dir_pw_chrono:system_clock",
+    "$dir_pw_interrupt:context",
+    "$dir_pw_third_party/threadx",
+  ]
+  sources = [ "binary_semaphore.cc" ]
+  deps = [
+    "$dir_pw_chrono_threadx:system_clock",
+    "$dir_pw_sync:binary_semaphore.facade",
+  ]
+  assert(pw_chrono_SYSTEM_CLOCK_BACKEND == "" ||
+             pw_chrono_SYSTEM_CLOCK_BACKEND ==
+                 "$dir_pw_chrono_threadx:system_clock",
+         "The ThreadX pw::sync::BinarySemaphore backend only works with the " +
+             "ThreadX pw::chrono::SystemClock backend.")
+}
+
+# This target provides the backend for pw::sync::CountingSemaphore.
+pw_source_set("counting_semaphore") {
+  public_configs = [
+    ":public_include_path",
+    ":backend_config",
+  ]
+  public = [
+    "public/pw_sync_threadx/counting_semaphore_inline.h",
+    "public/pw_sync_threadx/counting_semaphore_native.h",
+    "public_overrides/pw_sync_backend/counting_semaphore_inline.h",
+    "public_overrides/pw_sync_backend/counting_semaphore_native.h",
+  ]
+  public_deps = [
+    "$dir_pw_assert",
+    "$dir_pw_chrono:system_clock",
+    "$dir_pw_interrupt:context",
+    "$dir_pw_third_party/threadx",
+  ]
+  sources = [ "counting_semaphore.cc" ]
+  deps = [
+    "$dir_pw_chrono_threadx:system_clock",
+    "$dir_pw_sync:counting_semaphore.facade",
+  ]
+  assert(pw_chrono_SYSTEM_CLOCK_BACKEND == "" ||
+             pw_chrono_SYSTEM_CLOCK_BACKEND ==
+                 "$dir_pw_chrono_threadx:system_clock",
+         "The ThreadX pw::sync::CountingSemaphore backend only works with " +
+             "the ThreadX pw::chrono::SystemClock backend.")
+}
+
+# This target provides the backend for pw::sync::Mutex.
+pw_source_set("mutex") {
+  public_configs = [
+    ":public_include_path",
+    ":backend_config",
+  ]
+  public = [
+    "public/pw_sync_threadx/mutex_inline.h",
+    "public/pw_sync_threadx/mutex_native.h",
+    "public_overrides/pw_sync_backend/mutex_inline.h",
+    "public_overrides/pw_sync_backend/mutex_native.h",
+  ]
+  public_deps = [
+    "$dir_pw_assert",
+    "$dir_pw_chrono:system_clock",
+    "$dir_pw_interrupt:context",
+    "$dir_pw_third_party/threadx",
+  ]
+  sources = [ "mutex.cc" ]
+  deps = [
+    "$dir_pw_chrono_threadx:system_clock",
+    "$dir_pw_sync:mutex.facade",
+  ]
+  assert(pw_chrono_SYSTEM_CLOCK_BACKEND == "" ||
+             pw_chrono_SYSTEM_CLOCK_BACKEND ==
+                 "$dir_pw_chrono_threadx:system_clock",
+         "The ThreadX pw::sync::Mutex backend only works with the ThreadX " +
+             "pw::chrono::SystemClock backend.")
+}
+
 # This target provides the backend for pw::sync::SpinLock, note that this
 # implementation does NOT support ThreadX w/ SMP.
 pw_source_set("spin_lock") {
diff --git a/pw_sync_threadx/binary_semaphore.cc b/pw_sync_threadx/binary_semaphore.cc
new file mode 100644
index 0000000..b902966
--- /dev/null
+++ b/pw_sync_threadx/binary_semaphore.cc
@@ -0,0 +1,55 @@
+// 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/binary_semaphore.h"
+
+#include <algorithm>
+
+#include "pw_assert/assert.h"
+#include "pw_chrono/system_clock.h"
+#include "pw_chrono_threadx/system_clock_constants.h"
+#include "pw_interrupt/context.h"
+#include "tx_api.h"
+
+using pw::chrono::SystemClock;
+using pw::chrono::threadx::kMaxTimeout;
+
+namespace pw::sync {
+
+bool BinarySemaphore::try_acquire_for(SystemClock::duration for_at_least) {
+  // Enforce the pw::sync::BinarySemaphore IRQ contract.
+  PW_DCHECK(!interrupt::InInterruptContext());
+
+  // Clamp negative durations to be 0 which maps to non-blocking.
+  for_at_least = std::max(for_at_least, SystemClock::duration::zero());
+
+  while (for_at_least > kMaxTimeout) {
+    const UINT result = tx_semaphore_get(&native_type_, kMaxTimeout.count());
+    if (result != TX_NO_INSTANCE) {
+      // If we didn't time out (TX_NO_INSTANCE), then we should have succeeded.
+      PW_CHECK_UINT_EQ(TX_SUCCESS, result);
+      return true;
+    }
+    for_at_least -= kMaxTimeout;
+  }
+
+  const UINT result = tx_semaphore_get(&native_type_, for_at_least.count());
+  if (result == TX_NO_INSTANCE) {
+    return false;  // We timed out, there's still no token.
+  }
+  PW_CHECK_UINT_EQ(TX_SUCCESS, result);
+  return true;
+}
+
+}  // namespace pw::sync
diff --git a/pw_sync_threadx/counting_semaphore.cc b/pw_sync_threadx/counting_semaphore.cc
new file mode 100644
index 0000000..30184c1
--- /dev/null
+++ b/pw_sync_threadx/counting_semaphore.cc
@@ -0,0 +1,54 @@
+// 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/counting_semaphore.h"
+
+#include <algorithm>
+
+#include "pw_assert/assert.h"
+#include "pw_chrono/system_clock.h"
+#include "pw_chrono_threadx/system_clock_constants.h"
+#include "pw_interrupt/context.h"
+#include "tx_api.h"
+
+using pw::chrono::SystemClock;
+using pw::chrono::threadx::kMaxTimeout;
+
+namespace pw::sync {
+
+bool CountingSemaphore::try_acquire_for(SystemClock::duration for_at_least) {
+  // Enforce the pw::sync::CountingSemaphore IRQ contract.
+  PW_DCHECK(!interrupt::InInterruptContext());
+
+  // Clamp negative durations to be 0 which maps to non-blocking.
+  for_at_least = std::max(for_at_least, SystemClock::duration::zero());
+
+  while (for_at_least > kMaxTimeout) {
+    const UINT result = tx_semaphore_get(&native_type_, kMaxTimeout.count());
+    if (result != TX_NO_INSTANCE) {
+      // If we didn't time out (TX_NO_INSTANCE), then we should have succeeded.
+      PW_CHECK_UINT_EQ(TX_SUCCESS, result);
+      return true;
+    }
+    for_at_least -= kMaxTimeout;
+  }
+  const UINT result = tx_semaphore_get(&native_type_, for_at_least.count());
+  if (result == TX_NO_INSTANCE) {
+    return false;  // We timed out, there's still no token.
+  }
+  PW_CHECK_UINT_EQ(TX_SUCCESS, result);
+  return true;
+}
+
+}  // namespace pw::sync
diff --git a/pw_sync_threadx/docs.rst b/pw_sync_threadx/docs.rst
index 6699742..f7b0eaa 100644
--- a/pw_sync_threadx/docs.rst
+++ b/pw_sync_threadx/docs.rst
@@ -3,6 +3,6 @@
 ---------------
 pw_sync_threadx
 ---------------
-This is a set of backends for pw_sync based on the ThreadX RTOS. It is not ready
-for use, and is under construction.
+This is a set of backends for pw_sync based on ThreadX. It is not ready for use,
+and is under construction.
 
diff --git a/pw_sync_threadx/mutex.cc b/pw_sync_threadx/mutex.cc
new file mode 100644
index 0000000..0e8fab9
--- /dev/null
+++ b/pw_sync_threadx/mutex.cc
@@ -0,0 +1,53 @@
+// 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/mutex.h"
+
+#include <algorithm>
+
+#include "pw_assert/assert.h"
+#include "pw_chrono/system_clock.h"
+#include "pw_chrono_threadx/system_clock_constants.h"
+#include "pw_interrupt/context.h"
+#include "tx_api.h"
+
+using pw::chrono::SystemClock;
+using pw::chrono::threadx::kMaxTimeout;
+
+namespace pw::sync {
+
+bool Mutex::try_lock_for(SystemClock::duration for_at_least) {
+  // Enforce the pw::sync::Mutex IRQ contract.
+  PW_DCHECK(!interrupt::InInterruptContext());
+
+  // Clamp negative durations to be 0 which maps to non-blocking.
+  for_at_least = std::max(for_at_least, SystemClock::duration::zero());
+
+  while (for_at_least > kMaxTimeout) {
+    const UINT result = tx_mutex_get(&native_type_, kMaxTimeout.count());
+    if (result != TX_NOT_AVAILABLE) {
+      PW_CHECK_UINT_EQ(TX_SUCCESS, result);
+      return true;
+    }
+    for_at_least -= kMaxTimeout;
+  }
+  const UINT result = tx_mutex_get(&native_type_, for_at_least.count());
+  if (result == TX_NOT_AVAILABLE) {
+    return false;
+  }
+  PW_CHECK_UINT_EQ(TX_SUCCESS, result);
+  return true;
+}
+
+}  // namespace pw::sync
diff --git a/pw_sync_threadx/public/pw_sync_threadx/binary_semaphore_inline.h b/pw_sync_threadx/public/pw_sync_threadx/binary_semaphore_inline.h
new file mode 100644
index 0000000..f0df039
--- /dev/null
+++ b/pw_sync_threadx/public/pw_sync_threadx/binary_semaphore_inline.h
@@ -0,0 +1,71 @@
+// 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_chrono/system_clock.h"
+#include "pw_interrupt/context.h"
+#include "pw_sync/binary_semaphore.h"
+#include "tx_api.h"
+
+namespace pw::sync {
+namespace backend {
+
+inline constexpr char kBinarySemaphoreName[] = "pw::BinarySemaphore";
+
+}  // namespace backend
+
+inline BinarySemaphore::BinarySemaphore() : native_type_() {
+  PW_ASSERT(
+      tx_semaphore_create(&native_type_,
+                          const_cast<char*>(backend::kBinarySemaphoreName),
+                          0) == TX_SUCCESS);
+}
+
+inline BinarySemaphore::~BinarySemaphore() {
+  PW_ASSERT(tx_semaphore_delete(&native_type_) == TX_SUCCESS);
+}
+
+inline void BinarySemaphore::release() {
+  // Give at most 1 token.
+  const UINT result = tx_semaphore_ceiling_put(&native_type_, 1);
+  PW_ASSERT(result == TX_SUCCESS || result == TX_CEILING_EXCEEDED);
+}
+
+inline void BinarySemaphore::acquire() {
+  // Enforce the pw::sync::BinarySemaphore IRQ contract.
+  PW_DASSERT(!interrupt::InInterruptContext());
+  const UINT result = tx_semaphore_get(&native_type_, TX_WAIT_FOREVER);
+  PW_ASSERT(result == TX_SUCCESS);
+}
+
+inline bool BinarySemaphore::try_acquire() {
+  const UINT result = tx_semaphore_get(&native_type_, TX_NO_WAIT);
+  if (result == TX_NO_INSTANCE) {
+    return false;
+  }
+  PW_ASSERT(result == TX_SUCCESS);
+  return true;
+}
+
+inline bool BinarySemaphore::try_acquire_until(
+    chrono::SystemClock::time_point until_at_least) {
+  return try_acquire_for(until_at_least - chrono::SystemClock::now());
+}
+
+inline BinarySemaphore::native_handle_type BinarySemaphore::native_handle() {
+  return native_type_;
+}
+
+}  // namespace pw::sync
diff --git a/pw_sync_threadx/public/pw_sync_threadx/binary_semaphore_native.h b/pw_sync_threadx/public/pw_sync_threadx/binary_semaphore_native.h
new file mode 100644
index 0000000..e433435
--- /dev/null
+++ b/pw_sync_threadx/public/pw_sync_threadx/binary_semaphore_native.h
@@ -0,0 +1,28 @@
+// 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 <limits>
+
+#include "tx_api.h"
+
+namespace pw::sync::backend {
+
+using NativeBinarySemaphore = TX_SEMAPHORE;
+using NativeBinarySemaphoreHandle = NativeBinarySemaphore&;
+
+inline constexpr ptrdiff_t kBinarySemaphoreMaxValue =
+    std::numeric_limits<ULONG>::max();
+
+}  // namespace pw::sync::backend
diff --git a/pw_sync_threadx/public/pw_sync_threadx/counting_semaphore_inline.h b/pw_sync_threadx/public/pw_sync_threadx/counting_semaphore_inline.h
new file mode 100644
index 0000000..763baee
--- /dev/null
+++ b/pw_sync_threadx/public/pw_sync_threadx/counting_semaphore_inline.h
@@ -0,0 +1,73 @@
+// 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 <algorithm>
+
+#include "pw_assert/light.h"
+#include "pw_chrono/system_clock.h"
+#include "pw_interrupt/context.h"
+#include "pw_sync/counting_semaphore.h"
+#include "tx_api.h"
+
+namespace pw::sync {
+namespace backend {
+
+inline constexpr char kCountingSemaphoreName[] = "pw::CountingSemaphore";
+
+}  // namespace backend
+
+inline CountingSemaphore::CountingSemaphore() : native_type_() {
+  PW_ASSERT(
+      tx_semaphore_create(&native_type_,
+                          const_cast<char*>(backend::kCountingSemaphoreName),
+                          0) == TX_SUCCESS);
+}
+
+inline CountingSemaphore::~CountingSemaphore() {
+  PW_ASSERT(tx_semaphore_delete(&native_type_) == TX_SUCCESS);
+}
+
+inline void CountingSemaphore::release(ptrdiff_t update) {
+  for (; update > 0; --update) {
+    PW_ASSERT(tx_semaphore_put(&native_type_) == TX_SUCCESS);
+  }
+}
+
+inline void CountingSemaphore::acquire() {
+  // Enforce the pw::sync::CountingSemaphore IRQ contract.
+  PW_DASSERT(!interrupt::InInterruptContext());
+  PW_ASSERT(tx_semaphore_get(&native_type_, TX_WAIT_FOREVER) == TX_SUCCESS);
+}
+
+inline bool CountingSemaphore::try_acquire() {
+  const UINT result = tx_semaphore_get(&native_type_, TX_NO_WAIT);
+  if (result == TX_NO_INSTANCE) {
+    return false;
+  }
+  PW_ASSERT(result == TX_SUCCESS);
+  return true;
+}
+
+inline bool CountingSemaphore::try_acquire_until(
+    chrono::SystemClock::time_point until_at_least) {
+  return try_acquire_for(until_at_least - chrono::SystemClock::now());
+}
+
+inline CountingSemaphore::native_handle_type
+CountingSemaphore::native_handle() {
+  return native_type_;
+}
+
+}  // namespace pw::sync
diff --git a/pw_sync_threadx/public/pw_sync_threadx/counting_semaphore_native.h b/pw_sync_threadx/public/pw_sync_threadx/counting_semaphore_native.h
new file mode 100644
index 0000000..d322a53
--- /dev/null
+++ b/pw_sync_threadx/public/pw_sync_threadx/counting_semaphore_native.h
@@ -0,0 +1,28 @@
+// 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 <limits>
+
+#include "tx_api.h"
+
+namespace pw::sync::backend {
+
+using NativeCountingSemaphore = TX_SEMAPHORE;
+using NativeCountingSemaphoreHandle = NativeCountingSemaphore&;
+
+inline constexpr ptrdiff_t kCountingSemaphoreMaxValue =
+    std::numeric_limits<ULONG>::max();
+
+}  // namespace pw::sync::backend
diff --git a/pw_sync_threadx/public/pw_sync_threadx/mutex_inline.h b/pw_sync_threadx/public/pw_sync_threadx/mutex_inline.h
new file mode 100644
index 0000000..388e6b9
--- /dev/null
+++ b/pw_sync_threadx/public/pw_sync_threadx/mutex_inline.h
@@ -0,0 +1,69 @@
+// 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_chrono/system_clock.h"
+#include "pw_interrupt/context.h"
+#include "pw_sync/mutex.h"
+#include "tx_api.h"
+
+namespace pw::sync {
+namespace backend {
+
+inline constexpr char kMutexName[] = "pw::Mutex";
+
+}  // namespace backend
+
+inline Mutex::Mutex() : native_type_() {
+  PW_ASSERT(tx_mutex_create(&native_type_,
+                            const_cast<char*>(backend::kMutexName),
+                            TX_INHERIT) == TX_SUCCESS);
+}
+
+inline Mutex::~Mutex() {
+  PW_ASSERT(tx_mutex_delete(&native_type_) == TX_SUCCESS);
+}
+
+inline void Mutex::lock() {
+  // Enforce the pw::sync::Mutex IRQ contract.
+  PW_ASSERT(!interrupt::InInterruptContext());
+  PW_ASSERT(tx_mutex_get(&native_type_, TX_WAIT_FOREVER) == TX_SUCCESS);
+}
+
+inline bool Mutex::try_lock() {
+  // Enforce the pw::sync::Mutex IRQ contract.
+  PW_ASSERT(!interrupt::InInterruptContext());
+  const UINT result = tx_mutex_get(&native_type_, TX_NO_WAIT);
+  if (result == TX_NOT_AVAILABLE) {
+    return false;
+  }
+  PW_ASSERT(result == TX_SUCCESS);
+  return true;
+}
+
+inline bool Mutex::try_lock_until(
+    chrono::SystemClock::time_point until_at_least) {
+  return try_lock_for(until_at_least - chrono::SystemClock::now());
+}
+
+inline void Mutex::unlock() {
+  // Enforce the pw::sync::Mutex IRQ contract.
+  PW_ASSERT(!interrupt::InInterruptContext());
+  PW_ASSERT(tx_mutex_put(&native_type_) == TX_SUCCESS);
+}
+
+inline Mutex::native_handle_type Mutex::native_handle() { return native_type_; }
+
+}  // namespace pw::sync
diff --git a/pw_sync_threadx/public/pw_sync_threadx/mutex_native.h b/pw_sync_threadx/public/pw_sync_threadx/mutex_native.h
new file mode 100644
index 0000000..e38e3eb
--- /dev/null
+++ b/pw_sync_threadx/public/pw_sync_threadx/mutex_native.h
@@ -0,0 +1,23 @@
+// 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::sync::backend {
+
+using NativeMutex = TX_MUTEX;
+using NativeMutexHandle = NativeMutex&;
+
+}  // namespace pw::sync::backend
diff --git a/pw_sync_threadx/public/pw_sync_threadx/spin_lock_inline.h b/pw_sync_threadx/public/pw_sync_threadx/spin_lock_inline.h
index 67be0dc..af81d78 100644
--- a/pw_sync_threadx/public/pw_sync_threadx/spin_lock_inline.h
+++ b/pw_sync_threadx/public/pw_sync_threadx/spin_lock_inline.h
@@ -14,7 +14,6 @@
 #pragma once
 
 #include "pw_sync/spin_lock.h"
-#include "pw_sync/yield_core.h"
 
 namespace pw::sync {
 
diff --git a/pw_sync_threadx/public_overrides/pw_sync_backend/binary_semaphore_inline.h b/pw_sync_threadx/public_overrides/pw_sync_backend/binary_semaphore_inline.h
new file mode 100644
index 0000000..2d6969d
--- /dev/null
+++ b/pw_sync_threadx/public_overrides/pw_sync_backend/binary_semaphore_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_sync_threadx/binary_semaphore_inline.h"
diff --git a/pw_sync_threadx/public_overrides/pw_sync_backend/binary_semaphore_native.h b/pw_sync_threadx/public_overrides/pw_sync_backend/binary_semaphore_native.h
new file mode 100644
index 0000000..9270618
--- /dev/null
+++ b/pw_sync_threadx/public_overrides/pw_sync_backend/binary_semaphore_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_sync_threadx/binary_semaphore_native.h"
diff --git a/pw_sync_threadx/public_overrides/pw_sync_backend/counting_semaphore_inline.h b/pw_sync_threadx/public_overrides/pw_sync_backend/counting_semaphore_inline.h
new file mode 100644
index 0000000..19ba3f7
--- /dev/null
+++ b/pw_sync_threadx/public_overrides/pw_sync_backend/counting_semaphore_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_sync_threadx/counting_semaphore_inline.h"
diff --git a/pw_sync_threadx/public_overrides/pw_sync_backend/counting_semaphore_native.h b/pw_sync_threadx/public_overrides/pw_sync_backend/counting_semaphore_native.h
new file mode 100644
index 0000000..b85e21f
--- /dev/null
+++ b/pw_sync_threadx/public_overrides/pw_sync_backend/counting_semaphore_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_sync_threadx/counting_semaphore_native.h"
diff --git a/pw_sync_threadx/public_overrides/pw_sync_backend/mutex_inline.h b/pw_sync_threadx/public_overrides/pw_sync_backend/mutex_inline.h
new file mode 100644
index 0000000..2dae3d2
--- /dev/null
+++ b/pw_sync_threadx/public_overrides/pw_sync_backend/mutex_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_sync_threadx/mutex_inline.h"
diff --git a/pw_sync_threadx/public_overrides/pw_sync_backend/mutex_native.h b/pw_sync_threadx/public_overrides/pw_sync_backend/mutex_native.h
new file mode 100644
index 0000000..02f78a0
--- /dev/null
+++ b/pw_sync_threadx/public_overrides/pw_sync_backend/mutex_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_sync_threadx/mutex_native.h"