pw_sync_threadx: Add pw::sync::SpinLock backend
Adds initial ThreadX support through a SpinLock backend for
pw_sync. Note that this first backend does not support ThreadX
with SMP enabled, taking a shortcut to detect SpinLock recursion
instead.
Change-Id: I6cb4d1edfe78bc97fc632cfb353589e53f87f2c7
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/25803
Commit-Queue: Ewout van Bekkum <ewout@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/docs/BUILD.gn b/docs/BUILD.gn
index 6f38d3c..4ad9ea4 100644
--- a/docs/BUILD.gn
+++ b/docs/BUILD.gn
@@ -96,6 +96,7 @@
"$dir_pw_string:docs",
"$dir_pw_sync:docs",
"$dir_pw_sync_stl:docs",
+ "$dir_pw_sync_threadx:docs",
"$dir_pw_sys_io:docs",
"$dir_pw_sys_io_arduino:docs",
"$dir_pw_sys_io_baremetal_stm32f429:docs",
diff --git a/modules.gni b/modules.gni
index 2576da3..b079b63 100644
--- a/modules.gni
+++ b/modules.gni
@@ -68,6 +68,7 @@
dir_pw_string = get_path_info("pw_string", "abspath")
dir_pw_sync = get_path_info("pw_sync", "abspath")
dir_pw_sync_stl = get_path_info("pw_sync_stl", "abspath")
+ dir_pw_sync_threadx = get_path_info("pw_sync_threadx", "abspath")
dir_pw_sys_io = get_path_info("pw_sys_io", "abspath")
dir_pw_sys_io_baremetal_lm3s6965evb =
get_path_info("pw_sys_io_baremetal_lm3s6965evb", "abspath")
diff --git a/pw_sync_threadx/BUILD b/pw_sync_threadx/BUILD
new file mode 100644
index 0000000..54dff01
--- /dev/null
+++ b/pw_sync_threadx/BUILD
@@ -0,0 +1,49 @@
+# 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 = "spin_lock_headers",
+ hdrs = [
+ "public/pw_sync_threadx/spin_lock_inline.h",
+ "public/pw_sync_threadx/spin_lock_native.h",
+ "public_overrides/pw_sync_backend/spin_lock_inline.h",
+ "public_overrides/pw_sync_backend/spin_lock_native.h",
+ ],
+ includes = [
+ "public",
+ "public_overrides",
+ ],
+ # TODO: This should depend on ThreadX but our third parties currently
+ # do not have Bazel support.
+)
+
+pw_cc_library(
+ name = "spin_lock",
+ srcs = [
+ "spin_lock.cc",
+ ],
+ deps = [
+ ":spin_lock_headers",
+ "//pw_sync:spin_lock_facade",
+ ],
+)
diff --git a/pw_sync_threadx/BUILD.gn b/pw_sync_threadx/BUILD.gn
new file mode 100644
index 0000000..7f80bd5
--- /dev/null
+++ b/pw_sync_threadx/BUILD.gn
@@ -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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_docgen/docs.gni")
+
+config("public_include_path") {
+ include_dirs = [ "public" ]
+ visibility = [ ":*" ]
+}
+
+config("backend_config") {
+ include_dirs = [ "public_overrides" ]
+ visibility = [ ":*" ]
+}
+
+# This target provides the backend for pw::sync::SpinLock, note that this
+# implementation does NOT support ThreadX w/ SMP.
+pw_source_set("spin_lock") {
+ public_configs = [
+ ":public_include_path",
+ ":backend_config",
+ ]
+ public = [
+ "public/pw_sync_threadx/spin_lock_inline.h",
+ "public/pw_sync_threadx/spin_lock_native.h",
+ "public_overrides/pw_sync_backend/spin_lock_inline.h",
+ "public_overrides/pw_sync_backend/spin_lock_native.h",
+ ]
+ public_deps = [ "$dir_pw_third_party/threadx" ]
+ sources = [ "spin_lock.cc" ]
+ deps = [
+ "$dir_pw_assert",
+ "$dir_pw_sync:spin_lock.facade",
+ ]
+}
+
+pw_doc_group("docs") {
+ sources = [ "docs.rst" ]
+}
diff --git a/pw_sync_threadx/docs.rst b/pw_sync_threadx/docs.rst
new file mode 100644
index 0000000..6699742
--- /dev/null
+++ b/pw_sync_threadx/docs.rst
@@ -0,0 +1,8 @@
+.. _module-pw_sync_threadx:
+
+---------------
+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.
+
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
new file mode 100644
index 0000000..67be0dc
--- /dev/null
+++ b/pw_sync_threadx/public/pw_sync_threadx/spin_lock_inline.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 "pw_sync/spin_lock.h"
+#include "pw_sync/yield_core.h"
+
+namespace pw::sync {
+
+inline SpinLock::SpinLock()
+ : native_type_{.locked = false, .saved_interrupt_mask = 0} {}
+
+inline SpinLock::native_handle_type SpinLock::native_handle() {
+ return native_type_;
+}
+
+} // namespace pw::sync
diff --git a/pw_sync_threadx/public/pw_sync_threadx/spin_lock_native.h b/pw_sync_threadx/public/pw_sync_threadx/spin_lock_native.h
new file mode 100644
index 0000000..7756abc
--- /dev/null
+++ b/pw_sync_threadx/public/pw_sync_threadx/spin_lock_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 <atomic>
+
+#include "tx_api.h"
+
+namespace pw::sync::backend {
+
+struct NativeSpinLock {
+ std::atomic<bool> locked; // Used to detect recursion.
+ UINT saved_interrupt_mask;
+};
+using NativeSpinLockHandle = NativeSpinLock&;
+
+} // namespace pw::sync::backend
diff --git a/pw_sync_threadx/public_overrides/pw_sync_backend/spin_lock_inline.h b/pw_sync_threadx/public_overrides/pw_sync_backend/spin_lock_inline.h
new file mode 100644
index 0000000..6040c43
--- /dev/null
+++ b/pw_sync_threadx/public_overrides/pw_sync_backend/spin_lock_inline.h
@@ -0,0 +1,19 @@
+// 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.
+
+// This override header includes the main tokenized logging header and defines
+// the PW_LOG macro as the tokenized logging macro.
+#pragma once
+
+#include "pw_sync_threadx/spin_lock_inline.h"
diff --git a/pw_sync_threadx/public_overrides/pw_sync_backend/spin_lock_native.h b/pw_sync_threadx/public_overrides/pw_sync_backend/spin_lock_native.h
new file mode 100644
index 0000000..dcf41fe
--- /dev/null
+++ b/pw_sync_threadx/public_overrides/pw_sync_backend/spin_lock_native.h
@@ -0,0 +1,19 @@
+// 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.
+
+// This override header includes the main tokenized logging header and defines
+// the PW_LOG macro as the tokenized logging macro.
+#pragma once
+
+#include "pw_sync_threadx/spin_lock_native.h"
diff --git a/pw_sync_threadx/spin_lock.cc b/pw_sync_threadx/spin_lock.cc
new file mode 100644
index 0000000..6d8f277
--- /dev/null
+++ b/pw_sync_threadx/spin_lock.cc
@@ -0,0 +1,57 @@
+// 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/spin_lock.h"
+
+#include "pw_assert/assert.h"
+#include "tx_api.h"
+
+namespace pw::sync {
+
+void SpinLock::lock() {
+ // In order to be pw::sync::SpinLock compliant, mask the interrupts
+ // before attempting to grab the internal spin lock.
+ native_type_.saved_interrupt_mask = tx_interrupt_control(TX_INT_DISABLE);
+
+ // This implementation is not set up to support SMP, meaning we cannot
+ // deadlock here due to the global interrupt lock, so we crash on recursion
+ // on a specific spinlock instead.
+ PW_CHECK(!native_type_.locked.load(std::memory_order_relaxed),
+ "Recursive SpinLock::lock() detected");
+
+ native_type_.locked.store(true, std::memory_order_relaxed);
+}
+
+bool SpinLock::try_lock() {
+ // In order to be pw::sync::SpinLock compliant, mask the interrupts
+ // before attempting to grab the internal spin lock.
+ UINT saved_interrupt_mask = tx_interrupt_control(TX_INT_DISABLE);
+
+ if (native_type_.locked.load(std::memory_order_relaxed)) {
+ // Already locked, restore interrupts and bail out.
+ tx_interrupt_control(saved_interrupt_mask);
+ return false;
+ }
+
+ native_type_.saved_interrupt_mask = saved_interrupt_mask;
+ native_type_.locked.store(true, std::memory_order_relaxed);
+ return true;
+}
+
+void SpinLock::unlock() {
+ native_type_.locked.store(false, std::memory_order_relaxed);
+ tx_interrupt_control(native_type_.saved_interrupt_mask);
+}
+
+} // namespace pw::sync
diff --git a/third_party/threadx/BUILD.gn b/third_party/threadx/BUILD.gn
new file mode 100644
index 0000000..e400f06
--- /dev/null
+++ b/third_party/threadx/BUILD.gn
@@ -0,0 +1,51 @@
+# 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")
+
+declare_args() {
+ # If compiling backends with ThreadX, this variable is set to the path to the
+ # ThreadX include directory. When set, a pw_source_set for the threadx library
+ # is created at "$dir_pw_third_party/threadx".
+ dir_pw_third_party_threadx_include = ""
+
+ # The pw_source_set which provides the port specific includes and sources.
+ pw_third_party_threadx_PORT = ""
+}
+
+# This file defines a GN source_set for an external installation of ThreadX.
+# To use, checkout the threadx source into a directory, then set the build arg
+# dir_pw_third_party_threadx_include to point to that directory. The ThreadX
+# library will be available in GN at "$dir_pw_third_party/threadx".
+if (dir_pw_third_party_threadx_include != "") {
+ config("public_includes") {
+ include_dirs = [ "$dir_pw_third_party_threadx_include" ]
+ visibility = [ ":*" ]
+ }
+
+ pw_source_set("threadx") {
+ public_configs = [ ":public_includes" ]
+ allow_circular_includes_from = [ pw_third_party_threadx_PORT ]
+ public_deps = [ pw_third_party_threadx_PORT ]
+ public = [
+ "$dir_pw_third_party_threadx_include/tx_api.h",
+ "$dir_pw_third_party_threadx_include/tx_port.h",
+ ]
+ }
+} else {
+ group("threadx") {
+ }
+}