pw_rpc: Add synchronized channel output
This adds an channel output wrapper which synchronizes a ChannelOutput's
operations with a sync::Mutex, and updates the host target's system RPC server
to use it.
Change-Id: I3d2407a8c81a8f0370fd714ded850293d7030209
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/28540
Commit-Queue: Alexei Frolov <frolv@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9a84842..ad9e3be 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,6 +24,8 @@
add_subdirectory(pw_build EXCLUDE_FROM_ALL)
add_subdirectory(pw_bytes EXCLUDE_FROM_ALL)
add_subdirectory(pw_checksum EXCLUDE_FROM_ALL)
+add_subdirectory(pw_chrono EXCLUDE_FROM_ALL)
+add_subdirectory(pw_chrono_stl EXCLUDE_FROM_ALL)
add_subdirectory(pw_containers EXCLUDE_FROM_ALL)
add_subdirectory(pw_cpu_exception EXCLUDE_FROM_ALL)
add_subdirectory(pw_cpu_exception_armv7m EXCLUDE_FROM_ALL)
@@ -43,6 +45,8 @@
add_subdirectory(pw_status EXCLUDE_FROM_ALL)
add_subdirectory(pw_stream EXCLUDE_FROM_ALL)
add_subdirectory(pw_string EXCLUDE_FROM_ALL)
+add_subdirectory(pw_sync EXCLUDE_FROM_ALL)
+add_subdirectory(pw_sync_stl EXCLUDE_FROM_ALL)
add_subdirectory(pw_sys_io EXCLUDE_FROM_ALL)
add_subdirectory(pw_sys_io_stdio EXCLUDE_FROM_ALL)
add_subdirectory(pw_tokenizer EXCLUDE_FROM_ALL)
diff --git a/pw_chrono/CMakeLists.txt b/pw_chrono/CMakeLists.txt
new file mode 100644
index 0000000..d6cc918
--- /dev/null
+++ b/pw_chrono/CMakeLists.txt
@@ -0,0 +1,22 @@
+# 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($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_add_facade(pw_chrono.system_clock
+ SOURCES
+ system_clock.cc
+ PUBLIC_DEPS
+ pw_preprocessor
+)
diff --git a/pw_chrono_stl/CMakeLists.txt b/pw_chrono_stl/CMakeLists.txt
new file mode 100644
index 0000000..f0b6197
--- /dev/null
+++ b/pw_chrono_stl/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_add_module_library(pw_chrono_stl.system_clock
+ IMPLEMENTS_FACADES
+ pw_chrono.system_clock
+)
diff --git a/pw_hdlc_lite/BUILD b/pw_hdlc_lite/BUILD
index ef4e127..f80de90 100644
--- a/pw_hdlc_lite/BUILD
+++ b/pw_hdlc_lite/BUILD
@@ -46,12 +46,19 @@
)
pw_cc_library(
+ name = "rpc_channel_output",
+ hdrs = ["public/pw_hdlc_lite/rpc_channel.h"],
+ includes = ["public"],
+ deps = [
+ ":pw_hdlc_lite",
+ "//pw_rpc:server",
+ ],
+)
+
+pw_cc_library(
name = "pw_rpc",
srcs = ["rpc_packets.cc"],
- hdrs = [
- "public/pw_hdlc_lite/rpc_channel.h",
- "public/pw_hdlc_lite/rpc_packets.h",
- ],
+ hdrs = ["public/pw_hdlc_lite/rpc_packets.h"],
includes = ["public"],
deps = [
":pw_hdlc_lite",
diff --git a/pw_hdlc_lite/public/pw_hdlc_lite/rpc_channel.h b/pw_hdlc_lite/public/pw_hdlc_lite/rpc_channel.h
index 03ecd44..049b1f7 100644
--- a/pw_hdlc_lite/public/pw_hdlc_lite/rpc_channel.h
+++ b/pw_hdlc_lite/public/pw_hdlc_lite/rpc_channel.h
@@ -26,8 +26,8 @@
// Custom HDLC ChannelOutput class to write and read data through serial using
// the HDLC-Lite protocol.
//
-// WARNING: This ChannelOutput is not thread-safe.
-// TODO(frolv): Update this to use OS locking primitives.
+// WARNING: This ChannelOutput is not thread-safe. If thread-safety is required,
+// wrap this in a pw::rpc::SynchronizedChannelOutput.
class RpcChannelOutput : public rpc::ChannelOutput {
public:
// The RpcChannelOutput class does not own the buffer it uses to store the
@@ -60,8 +60,8 @@
// RpcChannelOutput with its own buffer.
//
-// WARNING: This ChannelOutput is not thread-safe.
-// TODO(frolv): Update this to use OS locking primitives.
+// WARNING: This ChannelOutput is not thread-safe. If thread-safety is required,
+// wrap this in a pw::rpc::SynchronizedChannelOutput.
template <size_t buffer_size>
class RpcChannelOutputBuffer : public rpc::ChannelOutput {
public:
diff --git a/pw_rpc/BUILD b/pw_rpc/BUILD
index bccb767..aef8d1c 100644
--- a/pw_rpc/BUILD
+++ b/pw_rpc/BUILD
@@ -85,6 +85,16 @@
)
pw_cc_library(
+ name = "synchronized_channel_output",
+ hdrs = ["public/pw_rpc/synchronized_channel_output.h"],
+ includes = ["public"],
+ deps = [
+ ":common",
+ "//pw_sync:mutex",
+ ],
+)
+
+pw_cc_library(
name = "internal_test_utils",
hdrs = [
"public/pw_rpc/internal/test_method.h",
diff --git a/pw_rpc/BUILD.gn b/pw_rpc/BUILD.gn
index 939f074..80db934 100644
--- a/pw_rpc/BUILD.gn
+++ b/pw_rpc/BUILD.gn
@@ -103,6 +103,15 @@
friend = [ "./*" ]
}
+pw_source_set("synchronized_channel_output") {
+ public_configs = [ ":public_include_path" ]
+ public_deps = [
+ ":common",
+ "$dir_pw_sync:mutex",
+ ]
+ public = [ "public/pw_rpc/synchronized_channel_output.h" ]
+}
+
pw_source_set("test_utils") {
public = [
"public/pw_rpc/internal/test_method.h",
diff --git a/pw_rpc/CMakeLists.txt b/pw_rpc/CMakeLists.txt
index 50d9ab0..5a892d1 100644
--- a/pw_rpc/CMakeLists.txt
+++ b/pw_rpc/CMakeLists.txt
@@ -58,6 +58,12 @@
pw_log
)
+pw_add_module_library(pw_rpc.synchronized_channel_output
+ PUBLIC_DEPS
+ pw_rpc.common
+ pw_sync.mutex
+)
+
add_library(pw_rpc.test_utils INTERFACE)
target_include_directories(pw_rpc.test_utils INTERFACE .)
diff --git a/pw_rpc/public/pw_rpc/synchronized_channel_output.h b/pw_rpc/public/pw_rpc/synchronized_channel_output.h
new file mode 100644
index 0000000..348fde9
--- /dev/null
+++ b/pw_rpc/public/pw_rpc/synchronized_channel_output.h
@@ -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.
+#pragma once
+
+#include <algorithm>
+
+#include "pw_rpc/channel.h"
+#include "pw_sync/mutex.h"
+
+namespace pw::rpc {
+
+// Wraps an RPC ChannelOutput implementation with a mutex to synchronize its
+// acquire and release buffer operations. This can be used to allow a simple
+// ChannelOutput implementation to run in multi-threaded contexts. More complex
+// implementations may want to roll their own synchronization.
+template <typename BaseChannelOutput>
+class SynchronizedChannelOutput final : public BaseChannelOutput {
+ public:
+ template <typename... Args>
+ constexpr SynchronizedChannelOutput(sync::Mutex& mutex, Args&&... args)
+ : BaseChannelOutput(std::forward<Args>(args)...), mutex_(mutex) {}
+
+ std::span<std::byte> AcquireBuffer() final {
+ mutex_.lock();
+ return BaseChannelOutput::AcquireBuffer();
+ }
+
+ Status SendAndReleaseBuffer(std::span<const std::byte> buffer) final {
+ Status status = BaseChannelOutput::SendAndReleaseBuffer(buffer);
+ mutex_.unlock();
+ return status;
+ }
+
+ private:
+ sync::Mutex& mutex_;
+};
+
+} // namespace pw::rpc
diff --git a/pw_sync/CMakeLists.txt b/pw_sync/CMakeLists.txt
new file mode 100644
index 0000000..69c553e
--- /dev/null
+++ b/pw_sync/CMakeLists.txt
@@ -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.
+
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_add_facade(pw_sync.mutex
+ SOURCES
+ mutex.cc
+ PUBLIC_DEPS
+ pw_chrono.system_clock
+ pw_preprocessor
+)
diff --git a/pw_sync_stl/CMakeLists.txt b/pw_sync_stl/CMakeLists.txt
new file mode 100644
index 0000000..bf8d343
--- /dev/null
+++ b/pw_sync_stl/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_add_module_library(pw_sync_stl.mutex_backend
+ IMPLEMENTS_FACADES
+ pw_sync.mutex
+)
diff --git a/pw_toolchain/host_clang/toolchain.cmake b/pw_toolchain/host_clang/toolchain.cmake
index 8c0f866..9f4dd6a 100644
--- a/pw_toolchain/host_clang/toolchain.cmake
+++ b/pw_toolchain/host_clang/toolchain.cmake
@@ -14,9 +14,11 @@
include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
-pw_set_backend(pw_log pw_log_basic)
pw_set_backend(pw_assert pw_assert_log)
+pw_set_backend(pw_chrono.system_clock pw_chrono_stl.system_clock)
+pw_set_backend(pw_log pw_log_basic)
pw_set_backend(pw_rpc.system_server targets.host.system_rpc_server)
+pw_set_backend(pw_sync.mutex pw_sync_stl.mutex_backend)
pw_set_backend(pw_sys_io pw_sys_io_stdio)
set(CMAKE_C_COMPILER clang)
diff --git a/pw_toolchain/host_gcc/toolchain.cmake b/pw_toolchain/host_gcc/toolchain.cmake
index e2ce85f..cf87958 100644
--- a/pw_toolchain/host_gcc/toolchain.cmake
+++ b/pw_toolchain/host_gcc/toolchain.cmake
@@ -14,9 +14,11 @@
include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
-pw_set_backend(pw_log pw_log_basic)
pw_set_backend(pw_assert pw_assert_log)
+pw_set_backend(pw_chrono.system_clock pw_chrono_stl.system_clock)
+pw_set_backend(pw_log pw_log_basic)
pw_set_backend(pw_rpc.system_server targets.host.system_rpc_server)
+pw_set_backend(pw_sync.mutex pw_sync_stl.mutex_backend)
pw_set_backend(pw_sys_io pw_sys_io_stdio)
set(CMAKE_C_COMPILER gcc)
diff --git a/targets/host/BUILD.gn b/targets/host/BUILD.gn
index cbb53ca..4f35eef 100644
--- a/targets/host/BUILD.gn
+++ b/targets/host/BUILD.gn
@@ -31,6 +31,7 @@
deps = [
"$dir_pw_hdlc_lite:pw_rpc",
"$dir_pw_hdlc_lite:rpc_channel_output",
+ "$dir_pw_rpc:synchronized_channel_output",
"$dir_pw_rpc/system_server:facade",
"$dir_pw_stream:socket_stream",
dir_pw_log,
diff --git a/targets/host/CMakeLists.txt b/targets/host/CMakeLists.txt
index 345ae92..a05e237 100644
--- a/targets/host/CMakeLists.txt
+++ b/targets/host/CMakeLists.txt
@@ -15,13 +15,13 @@
include("$ENV{PW_ROOT}/pw_build/pigweed.cmake")
pw_add_module_library(targets.host.system_rpc_server
- IMPLEMENTS_FACADE
+ IMPLEMENTS_FACADES
pw_rpc.system_server
SOURCES
system_rpc_server.cc
PRIVATE_DEPS
- pw_rpc.system_server.facade
pw_hdlc_lite
pw_rpc.server
+ pw_rpc.synchronized_channel_output
pw_stream.socket_stream
)
diff --git a/targets/host/system_rpc_server.cc b/targets/host/system_rpc_server.cc
index bc8ec37..203d2a9 100644
--- a/targets/host/system_rpc_server.cc
+++ b/targets/host/system_rpc_server.cc
@@ -18,6 +18,7 @@
#include "pw_hdlc_lite/rpc_channel.h"
#include "pw_hdlc_lite/rpc_packets.h"
#include "pw_log/log.h"
+#include "pw_rpc/synchronized_channel_output.h"
#include "pw_rpc_system_server/rpc_server.h"
#include "pw_stream/socket_stream.h"
@@ -28,8 +29,13 @@
constexpr uint16_t kSocketPort = 33000;
stream::SocketStream socket_stream;
-hdlc_lite::RpcChannelOutputBuffer<kMaxTransmissionUnit> hdlc_channel_output(
- socket_stream, hdlc_lite::kDefaultRpcAddress, "HDLC channel");
+sync::Mutex channel_output_mutex;
+rpc::SynchronizedChannelOutput<
+ hdlc_lite::RpcChannelOutputBuffer<kMaxTransmissionUnit>>
+ hdlc_channel_output(channel_output_mutex,
+ socket_stream,
+ hdlc_lite::kDefaultRpcAddress,
+ "HDLC channel");
Channel channels[] = {rpc::Channel::Create<1>(&hdlc_channel_output)};
rpc::Server server(channels);