From 382c6b9c3d1f4757339fb35c8f4a193a8ed24a99 Mon Sep 17 00:00:00 2001 From: David Drysdale Date: Thu, 2 Apr 2020 15:17:50 +0100 Subject: [PATCH] runtime: remove now-unused C++ functionality Slim down the C++ runtime now that the Rust runtime handles messaging and Wasm nodes. --- oak/server/BUILD | 155 +------- oak/server/base_runtime.h | 41 --- oak/server/channel.cc | 161 -------- oak/server/channel.h | 236 ------------ oak/server/channel_test.cc | 158 -------- oak/server/grpc_client_node.cc | 6 +- oak/server/grpc_client_node.h | 8 +- oak/server/invocation.cc | 3 +- oak/server/loader/oak_loader.cc | 9 +- oak/server/loader/oak_runner_main.cc | 1 + oak/server/logging_node.cc | 66 ---- oak/server/logging_node.h | 41 --- oak/server/module_invocation.cc | 3 +- oak/server/module_invocation.h | 1 - oak/server/node_thread.cc | 60 --- oak/server/node_thread.h | 56 --- oak/server/notification.cc | 40 -- oak/server/notification.h | 47 --- oak/server/notification_test.cc | 79 ---- oak/server/oak_grpc_node.cc | 23 +- oak/server/oak_grpc_node.h | 21 +- oak/server/oak_node.cc | 317 +++------------- oak/server/oak_node.h | 79 +--- oak/server/oak_runtime.cc | 176 ++------- oak/server/oak_runtime.h | 66 +--- oak/server/storage/BUILD | 2 +- oak/server/storage/storage_node.cc | 6 +- oak/server/storage/storage_node.h | 7 +- oak/server/wabt.bzl | 40 -- oak/server/wabt_output.cc | 40 -- oak/server/wabt_output.h | 31 -- oak/server/wabt_output_test.cc | 49 --- oak/server/wasm_node.cc | 530 --------------------------- oak/server/wasm_node.h | 82 ----- oak/server/wasm_node_fuzzer.cc | 29 -- oak/server/wasm_node_test.cc | 75 ---- 36 files changed, 147 insertions(+), 2597 deletions(-) delete mode 100644 oak/server/base_runtime.h delete mode 100644 oak/server/channel.cc delete mode 100644 oak/server/channel.h delete mode 100644 oak/server/channel_test.cc delete mode 100644 oak/server/logging_node.cc delete mode 100644 oak/server/logging_node.h delete mode 100644 oak/server/node_thread.cc delete mode 100644 oak/server/node_thread.h delete mode 100644 oak/server/notification.cc delete mode 100644 oak/server/notification.h delete mode 100644 oak/server/notification_test.cc delete mode 100644 oak/server/wabt.bzl delete mode 100644 oak/server/wabt_output.cc delete mode 100644 oak/server/wabt_output.h delete mode 100644 oak/server/wabt_output_test.cc delete mode 100644 oak/server/wasm_node.cc delete mode 100644 oak/server/wasm_node.h delete mode 100644 oak/server/wasm_node_fuzzer.cc delete mode 100644 oak/server/wasm_node_test.cc diff --git a/oak/server/BUILD b/oak/server/BUILD index cc686847153..bc49312dc48 100644 --- a/oak/server/BUILD +++ b/oak/server/BUILD @@ -14,9 +14,7 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") -load("//oak/common:fuzzer.bzl", "oak_fuzzer") -load("//oak/server:wabt.bzl", "wasm_group") +load("@rules_cc//cc:defs.bzl", "cc_library") package( default_visibility = ["//oak/server:__subpackages__"], @@ -28,10 +26,10 @@ cc_library( srcs = ["oak_node.cc"], hdrs = ["oak_node.h"], deps = [ - ":base_runtime", - ":channel", "//oak/common:handles", "//oak/common:logging", + "//oak/proto:oak_api_cc_proto", + "//oak/proto:policy_cc_proto", "//oak/server/rust/oak_glue:oak_glue_wrapper", "@com_google_absl//absl/base:endian", "@com_google_absl//absl/memory", @@ -45,66 +43,6 @@ cc_library( deps = [":oak_node"], ) -cc_library( - name = "wasm_node", - srcs = [ - "wabt_output.cc", - "wasm_node.cc", - ], - hdrs = [ - "wabt_output.h", - "wasm_node.h", - ], - deps = [ - "//oak/common:handles", - "//oak/common:logging", - "//oak/proto:grpc_encap_cc_proto", - "//oak/proto:oak_api_cc_proto", - "//oak/server:node_thread", - "@com_google_absl//absl/base:endian", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/time", - "@com_google_absl//absl/types:span", - "@wabt", - ], -) - -oak_fuzzer( - name = "wasm_node_fuzz", - srcs = [ - "wasm_node_fuzzer.cc", - ], - tags = ["manual"], # requires main() from fuzzer - deps = [ - ":wasm_node", - ], -) - -cc_test( - name = "wasm_node_test", - srcs = [ - "wabt_output_test.cc", - "wasm_node_test.cc", - ], - data = [ - ":testdata", - ], - deps = [ - ":wasm_node", - "@gtest//:gtest_main", - ], -) - -wasm_group( - name = "testdata", - srcs = [ - "testdata/empty.wat", - "testdata/minimal.wat", - "testdata/missing.wat", - "testdata/wrong.wat", - ], -) - # This should be the only target that depends on @com_github_grpc_grpc. cc_library( name = "oak_grpc_node", @@ -118,7 +56,6 @@ cc_library( ], deps = [ ":oak_node", - "//oak/common:app_config", "//oak/common:handles", "//oak/common:logging", "//oak/common:policy", @@ -136,14 +73,12 @@ cc_library( "oak_runtime.h", ], deps = [ - ":base_runtime", ":grpc_client_node", ":oak_grpc_node", - ":wasm_node", + ":oak_node", "//oak/common:app_config", "//oak/common:logging", "//oak/proto:oak_api_cc_proto", - "//oak/server:logging_node", "//oak/server/rust/oak_glue:oak_glue_wrapper", "//oak/server/storage:storage_node", "@com_google_absl//absl/base", @@ -153,63 +88,6 @@ cc_library( ], ) -cc_library( - name = "base_runtime", - hdrs = ["base_runtime.h"], - deps = [ - ":channel", - ], -) - -cc_library( - name = "channel", - srcs = ["channel.cc"], - hdrs = ["channel.h"], - deps = [ - ":notification", - "//oak/common:logging", - "//oak/proto:oak_api_cc_proto", - "//oak/proto:policy_cc_proto", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/types:variant", - ], -) - -cc_test( - name = "channel_test", - srcs = ["channel_test.cc"], - deps = [ - ":channel", - "@com_google_absl//absl/memory", - "@gtest//:gtest_main", - ], -) - -cc_library( - name = "node_thread", - srcs = ["node_thread.cc"], - hdrs = ["node_thread.h"], - deps = [ - ":oak_node", - "//oak/common:logging", - ], -) - -cc_library( - name = "logging_node", - srcs = ["logging_node.cc"], - hdrs = ["logging_node.h"], - deps = [ - ":handle_closer", - ":node_thread", - "//oak/common:handles", - "//oak/common:logging", - "//oak/proto:log_cc_proto", - "@com_google_absl//absl/memory", - ], -) - cc_library( name = "invocation", srcs = ["invocation.cc"], @@ -228,9 +106,8 @@ cc_library( srcs = ["grpc_client_node.cc"], hdrs = ["grpc_client_node.h"], deps = [ - ":base_runtime", ":invocation", - ":node_thread", + ":oak_node", "//oak/common:handles", "//oak/common:logging", "//oak/proto:grpc_encap_cc_proto", @@ -238,25 +115,3 @@ cc_library( "@com_google_absl//absl/memory", ], ) - -cc_library( - name = "notification", - srcs = ["notification.cc"], - hdrs = ["notification.h"], - deps = [ - "@com_google_absl//absl/memory", - "@com_google_absl//absl/synchronization", - ], -) - -cc_test( - name = "notification_test", - srcs = ["notification_test.cc"], - deps = [ - ":channel", - ":notification", - "//oak/common:logging", - "@com_google_absl//absl/memory", - "@gtest//:gtest_main", - ], -) diff --git a/oak/server/base_runtime.h b/oak/server/base_runtime.h deleted file mode 100644 index 0518545a52b..00000000000 --- a/oak/server/base_runtime.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2019 The Project Oak 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 - * - * http://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. - */ - -#ifndef OAK_SERVER_BASE_RUNTIME_H_ -#define OAK_SERVER_BASE_RUNTIME_H_ - -#include -#include - -#include "oak/server/channel.h" - -namespace oak { - -// BaseRuntime is a abstract base class that describes Oak Runtime functionality -// that is available for Oak Nodes to use; it effectively allows an OakNode -// object to hold a reference to the OakRuntime that owns it, without requiring -// a dependency cycle. -class BaseRuntime { - public: - virtual ~BaseRuntime() {} - virtual bool CreateAndRunNode(const std::string& config_name, const std::string& entrypoint_name, - std::unique_ptr half, std::string* node_name) = 0; - virtual bool TerminationPending() = 0; -}; // class BaseRuntime - -} // namespace oak - -#endif // OAK_SERVER_BASE_RUNTIME_H_ diff --git a/oak/server/channel.cc b/oak/server/channel.cc deleted file mode 100644 index 6caa003a1ff..00000000000 --- a/oak/server/channel.cc +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2019 The Project Oak 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 - * - * http://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 "oak/server/channel.h" - -#include "absl/memory/memory.h" -#include "oak/common/logging.h" - -namespace oak { - -MessageChannel::ChannelHalves MessageChannel::Create() { - // (MessageChannel constructor is private so std::make_shared is unavailable) - std::shared_ptr channel(new MessageChannel()); - return ChannelHalves{absl::make_unique(channel), - absl::make_unique(channel)}; -} - -namespace { -ABSL_CONST_INIT absl::Mutex channel_count_mu(absl::kConstInit); -int channel_count; -} // namespace - -MessageChannel::MessageChannel() : reader_count_(0), writer_count_(0) { - absl::MutexLock lock(&channel_count_mu); - channel_count++; - OAK_LOG(INFO) << "Channel created, extant count now " << channel_count; -} - -MessageChannel::~MessageChannel() { - absl::MutexLock lock(&channel_count_mu); - channel_count--; - OAK_LOG(INFO) << "Channel destroyed, extant count now " << channel_count; -} - -ChannelReadStatus MessageChannel::ReadStatus(std::weak_ptr notify) { - absl::MutexLock lock(&mu_); - if (msgs_.size() > 0) { - return ChannelReadStatus::READ_READY; - } else if (writer_count_ == 0) { - return ChannelReadStatus::ORPHANED; - } else { - notifies_.push_back(notify); - return ChannelReadStatus::NOT_READY; - } -} - -size_t MessageChannel::Count() const { - absl::ReaderMutexLock lock(&mu_); - return msgs_.size(); -} - -void MessageChannel::Write(std::unique_ptr msg) { - if (msg == nullptr) { - OAK_LOG(WARNING) << "Ignoring attempt to write null message"; - return; - } - { - absl::MutexLock lock(&mu_); - OAK_LOG(INFO) << "Add message with data size " << msg->data.size() << " and " - << msg->channels.size() << " channels"; - msgs_.push_back(std::move(msg)); - } - TriggerNotifications(); -} - -ReadResult MessageChannel::Read(uint32_t max_size, uint32_t max_channels) { - absl::MutexLock lock(&mu_); - if (msgs_.empty()) { - ReadResult result(OakStatus::OK); - return result; - } - return ReadLocked(max_size, max_channels); -} - -ReadResult MessageChannel::ReadLocked(uint32_t max_size, uint32_t max_channels) { - Message* next_msg = msgs_.front().get(); - size_t actual_size = next_msg->data.size(); - size_t actual_count = next_msg->channels.size(); - if (actual_size > max_size) { - OAK_LOG(INFO) << "Next message of size " << actual_size << ", read limited to size " - << max_size; - ReadResult result(OakStatus::ERR_BUFFER_TOO_SMALL); - result.required_size = actual_size; - result.required_channels = actual_count; - return result; - } - if (actual_count > max_channels) { - OAK_LOG(INFO) << "Next message with " << actual_count << " handles, read limited to " - << max_channels << " handles"; - ReadResult result(OakStatus::ERR_HANDLE_SPACE_TOO_SMALL); - result.required_size = actual_size; - result.required_channels = actual_count; - return result; - } - ReadResult result(OakStatus::OK); - result.msg = std::move(msgs_.front()); - msgs_.pop_front(); - OAK_LOG(INFO) << "Read message of size " << result.msg->data.size() << " with " << actual_count - << " channels from channel"; - return result; -} - -ReadResult MessageChannel::BlockingRead(uint32_t max_size, uint32_t max_channels) { - absl::MutexLock lock(&mu_); - mu_.Await(absl::Condition( - +[](std::deque>* msgs) { return msgs->size() > 0; }, &msgs_)); - return ReadLocked(max_size, max_channels); -} - -void MessageChannel::Await() { - absl::MutexLock lock(&mu_); - mu_.Await(absl::Condition( - +[](std::deque>* msgs) { return msgs->size() > 0; }, &msgs_)); -} - -void MessageChannel::TriggerNotifications() { - // Trigger any notifications waiting on this channel, but not while holding - // the lock. - std::vector> notifies; - { - absl::MutexLock lock(&mu_); - std::swap(notifies, notifies_); - } - - for (auto& possible_notify : notifies) { - std::shared_ptr notify = possible_notify.lock(); - if (notify != nullptr) { - notify->Notify(); - } - } -} - -namespace { -// At namespace scope because local classes may not have member templates. -struct CloneVariant { - template - std::unique_ptr operator()(const T& h) const { - return absl::make_unique(h->Clone()); - } -}; -} // namespace - -std::unique_ptr CloneChannelHalf(ChannelHalf* half) { - CloneVariant visitor; - return absl::visit(visitor, *half); -} - -} // namespace oak diff --git a/oak/server/channel.h b/oak/server/channel.h deleted file mode 100644 index df1df09f779..00000000000 --- a/oak/server/channel.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright 2019 The Project Oak 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 - * - * http://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. - */ - -#ifndef OAK_SERVER_CHANNEL_H_ -#define OAK_SERVER_CHANNEL_H_ - -#include -#include -#include -#include - -#include "absl/base/thread_annotations.h" -#include "absl/memory/memory.h" -#include "absl/synchronization/mutex.h" -#include "absl/types/variant.h" -#include "oak/proto/oak_api.pb.h" -#include "oak/proto/policy.pb.h" -#include "oak/server/notification.h" - -namespace oak { - -class MessageChannelWriteHalf; // forward declaration -class MessageChannelReadHalf; // forward declaration - -// Owning reference to either a read or write half of a channel. -using ChannelHalf = absl::variant, - std::unique_ptr>; - -// Each message transferred over a channel includes data and potentially -// also references to halves of channels. -// It also has a label attached to it, which is used for information flow. -struct Message { - std::vector data; - std::vector> channels; - // TODO(#603): Consider using a native struct here instead of the proto - // representation. - oak::policy::Label label; -}; - -// Result of a read operation. -struct ReadResult { - explicit ReadResult(OakStatus s) : status(s), required_size(0), required_channels(0) {} - OakStatus status; - // The following fields are filled in if the status is ERR_BUFFER_TOO_SMALL - // or ERR_HANDLE_SPACE_TOO_SMALL, indicating the required size and number - // of handles needed to read the message. - uint32_t required_size; - uint32_t required_channels; - // The following field is filled in if the status is OK. - std::unique_ptr msg; -}; - -// A channel, which holds an arbitrary number of queued messages in a -// uni-directional stream. Users do not access MessageChannel objects directly; -// all operations are performed via either a read or write half of the channel. -// -// No flow control is implemented at this level; application -// may decide to build some of these abstractions on top of the Channel -// interface. -// -// Each channel may be connected to a built-in component, or to a local or -// (in future) remote Oak Node. -// -// TODO(#753): Add a hard limit for message size. -class MessageChannel { - public: - struct ChannelHalves { - std::unique_ptr write; - std::unique_ptr read; - }; - - // Create a message channel and return references to both halves of it. - static ChannelHalves Create(); - ~MessageChannel(); - - private: - friend class MessageChannelReadHalf; - friend class MessageChannelWriteHalf; - - MessageChannel(); - - // Return the current readable status of the channel, and if NOT_READY add the - // provided Notification to pending notifies for the channel. - ChannelReadStatus ReadStatus(std::weak_ptr notify) LOCKS_EXCLUDED(mu_); - - // Count indicates the number of pending messages. - size_t Count() const LOCKS_EXCLUDED(mu_); - - // Read returns the first message on the channel, subject to size checks. - ReadResult Read(uint32_t max_size, uint32_t max_channels) LOCKS_EXCLUDED(mu_); - - // BlockingRead behaves like Read but blocks until a message is available. - ReadResult BlockingRead(uint32_t max_size, uint32_t max_channels) LOCKS_EXCLUDED(mu_); - - // Write passes ownership of a message to the channel. - void Write(std::unique_ptr msg) LOCKS_EXCLUDED(mu_); - - void Await() LOCKS_EXCLUDED(mu_); - - ReadResult ReadLocked(uint32_t max_size, uint32_t max_channels) EXCLUSIVE_LOCKS_REQUIRED(mu_); - - void TriggerNotifications() LOCKS_EXCLUDED(mu_); - - // Use a single Mutex to serialize acccess to all per-channel state for safety - // and simplicity; in particular, ReadStatus() needs synchronized access to - // all of the state. - mutable absl::Mutex mu_; // protects msgs_, notifies_, reader_count_, writer_count_ - - std::deque> msgs_ GUARDED_BY(mu_); - std::vector> notifies_ GUARDED_BY(mu_); - - // Keep track of the number of current read and write halves that refer to - // this channel, to allow detection of orphaned channels. - int reader_count_ GUARDED_BY(mu_); - int writer_count_ GUARDED_BY(mu_); -}; - -// Shared-ownership wrapper for the read half of a MessageChannel. -class MessageChannelReadHalf { - public: - MessageChannelReadHalf(std::shared_ptr channel) : channel_(channel) { - absl::MutexLock lock(&channel_->mu_); - channel_->reader_count_++; - } - - ~MessageChannelReadHalf() { - absl::MutexLock lock(&channel_->mu_); - channel_->reader_count_--; - } - - std::unique_ptr Clone() { - return absl::make_unique(channel_); - } - - // Read a message from the channel, as long as the amount of data and handles - // associated with the message fits within the specified limits. The caller - // owns any returned message, whose actual size / handle count will be less - // than or equal to the specified limits. - // - // If the next available message on the channel does not fit in the specified - // data/handle count limits, then no data will be returned and the ReadResult - // will indicate the required sizes. - // - // Note that (pure) C++ users of this method can typically pass INT_MAX for - // the limit parameters, as they receive ownership of the Message. - ReadResult Read(uint32_t max_size, uint32_t max_channels) { - return channel_->Read(max_size, max_channels); - } - - // BlockingRead behaves like Read but blocks until a message is available. - ReadResult BlockingRead(uint32_t max_size, uint32_t max_channels) { - return channel_->BlockingRead(max_size, max_channels); - } - - // Return the current readable status of the channel, and if NOT_READY add the - // provided Notification to pending notifies for the channel. - ChannelReadStatus ReadStatus(std::weak_ptr notify) { - return channel_->ReadStatus(notify); - } - - // Indicate whether a Read operation would return a message. - bool CanRead() { return channel_->Count() > 0; } - - // Indicate the number of pending messages available to read. - size_t Count() { return channel_->Count(); } - - // Await blocks until there is a message available to read on the channel. - void Await() { return channel_->Await(); } - - // Indicates whether the underlying channel has no associated write halves. - bool Orphaned() const { - absl::ReaderMutexLock lock(&channel_->mu_); - return (channel_->writer_count_ == 0); - } - - private: - std::shared_ptr channel_; -}; - -// Shared-ownership wrapper for the write half of a MessageChannel. -class MessageChannelWriteHalf { - public: - MessageChannelWriteHalf(std::shared_ptr channel) : channel_(channel) { - absl::MutexLock lock(&channel_->mu_); - channel_->writer_count_++; - } - - ~MessageChannelWriteHalf() { - absl::MutexLock lock(&channel_->mu_); - channel_->writer_count_--; - } - - std::unique_ptr Clone() { - return absl::make_unique(channel_); - } - - // Write the provided message to the Channel. - void Write(std::unique_ptr msg) { channel_->Write(std::move(msg)); } - - // Indicates whether the underlying channel has no associated read halves. - bool Orphaned() const { - absl::ReaderMutexLock lock(&channel_->mu_); - return (channel_->reader_count_ == 0); - } - - private: - std::shared_ptr channel_; -}; - -// Create a new channel half (of the same read/write end) that shares a -// reference to the same underlying MessageChannel. -std::unique_ptr CloneChannelHalf(ChannelHalf* half); - -// Current readable status of a channel. -struct ChannelStatus { - explicit ChannelStatus(uint64_t h) : handle(h), status(ChannelReadStatus::NOT_READY) {} - uint64_t handle; - ChannelReadStatus status; -}; - -} // namespace oak - -#endif // OAK_SERVER_CHANNEL_H_ diff --git a/oak/server/channel_test.cc b/oak/server/channel_test.cc deleted file mode 100644 index 1c44886d67a..00000000000 --- a/oak/server/channel_test.cc +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2019 The Project Oak 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 - * - * http://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 "oak/server/channel.h" - -#include - -#include "absl/memory/memory.h" -#include "gtest/gtest.h" - -namespace oak { - -static std::unique_ptr CreateMessage(int base) { - auto result = absl::make_unique(); - result->data.push_back(static_cast(base + 1)); - result->data.push_back(static_cast(base + 2)); - result->data.push_back(static_cast(base + 3)); - return result; -} - -TEST(MessageChannel, BasicOperation) { - MessageChannel::ChannelHalves halves = MessageChannel::Create(); - std::unique_ptr read_half(std::move(halves.read)); - std::unique_ptr write_half(std::move(halves.write)); - ASSERT_EQ(false, read_half->CanRead()); - - std::unique_ptr msg1 = CreateMessage(0x00); - write_half->Write(std::move(msg1)); - - ASSERT_EQ(true, read_half->CanRead()); - - ReadResult result1 = read_half->Read(1, 0); // too small - ASSERT_EQ(OakStatus::ERR_BUFFER_TOO_SMALL, result1.status); - ASSERT_EQ(3, result1.required_size); - ASSERT_EQ(nullptr, result1.msg); - - ReadResult result2 = read_half->Read(3, 0); // just right - ASSERT_EQ(OakStatus::OK, result2.status); - EXPECT_NE(result2.msg, nullptr); - ASSERT_EQ(3, result2.msg->data.size()); - ASSERT_EQ(0x01, (result2.msg->data)[0]); - - ASSERT_EQ(false, read_half->CanRead()); - - ReadResult result3 = read_half->Read(10000, 0); - ASSERT_EQ(OakStatus::OK, result3.status); - ASSERT_EQ(nullptr, result3.msg); - ASSERT_EQ(0, result3.required_size); - - std::unique_ptr msg2 = CreateMessage(0x10); - write_half->Write(std::move(msg2)); - std::unique_ptr msg3 = CreateMessage(0x20); - write_half->Write(std::move(msg3)); - - ASSERT_EQ(true, read_half->CanRead()); - - ReadResult result4 = read_half->Read(3000, 0); - ASSERT_EQ(OakStatus::OK, result4.status); - EXPECT_NE(result4.msg, nullptr); - ASSERT_EQ(3, result4.msg->data.size()); - ASSERT_EQ(0x11, (result4.msg->data)[0]); - - ReadResult result5 = read_half->Read(0, 0); - ASSERT_EQ(OakStatus::ERR_BUFFER_TOO_SMALL, result5.status); - ASSERT_EQ(3, result5.required_size); - ASSERT_EQ(nullptr, result5.msg); - - ReadResult result6 = read_half->Read(10, 0); - ASSERT_EQ(OakStatus::OK, result6.status); - EXPECT_NE(result6.msg, nullptr); - ASSERT_EQ(3, result6.msg->data.size()); - ASSERT_EQ(0x21, (result6.msg->data)[0]); - - ASSERT_EQ(false, read_half->CanRead()); -} - -TEST(MessageChannel, ChannelTransfer) { - MessageChannel::ChannelHalves halves = MessageChannel::Create(); - std::unique_ptr read_half(std::move(halves.read)); - std::unique_ptr write_half(std::move(halves.write)); - ASSERT_EQ(false, read_half->CanRead()); - - std::unique_ptr msg1 = CreateMessage(0x00); - msg1->channels.push_back(absl::make_unique(write_half->Clone())); - write_half->Write(std::move(msg1)); - - ASSERT_EQ(true, read_half->CanRead()); - - ReadResult result1 = read_half->Read(1000, 0); // no space for channels - ASSERT_EQ(OakStatus::ERR_HANDLE_SPACE_TOO_SMALL, result1.status); - ASSERT_EQ(3, result1.required_size); - ASSERT_EQ(1, result1.required_channels); - ASSERT_EQ(nullptr, result1.msg); - - ReadResult result2 = read_half->Read(1000, 1); // just right - ASSERT_EQ(OakStatus::OK, result2.status); - EXPECT_NE(result2.msg, nullptr); - ASSERT_EQ(3, result2.msg->data.size()); - ASSERT_EQ(0x01, (result2.msg->data)[0]); - ASSERT_EQ(1, result2.msg->channels.size()); - - ASSERT_EQ(false, read_half->CanRead()); - - ReadResult result3 = read_half->Read(10000, 10); - ASSERT_EQ(OakStatus::OK, result3.status); - ASSERT_EQ(nullptr, result3.msg); - ASSERT_EQ(0, result3.required_size); - ASSERT_EQ(0, result3.required_channels); - - std::unique_ptr msg2 = CreateMessage(0x10); - msg2->channels.push_back(absl::make_unique(write_half->Clone())); - msg2->channels.push_back(absl::make_unique(write_half->Clone())); - write_half->Write(std::move(msg2)); - std::unique_ptr msg3 = CreateMessage(0x20); - msg3->channels.push_back(absl::make_unique(write_half->Clone())); - msg3->channels.push_back(absl::make_unique(write_half->Clone())); - msg3->channels.push_back(absl::make_unique(write_half->Clone())); - write_half->Write(std::move(msg3)); - - ASSERT_EQ(true, read_half->CanRead()); - - ReadResult result4 = read_half->Read(3000, 10); - ASSERT_EQ(OakStatus::OK, result4.status); - EXPECT_NE(result4.msg, nullptr); - ASSERT_EQ(3, result4.msg->data.size()); - ASSERT_EQ(0x11, (result4.msg->data)[0]); - ASSERT_EQ(2, result4.msg->channels.size()); - - ReadResult result5 = read_half->Read(100, 0); - ASSERT_EQ(OakStatus::ERR_HANDLE_SPACE_TOO_SMALL, result5.status); - ASSERT_EQ(3, result5.required_size); - ASSERT_EQ(3, result5.required_channels); - ASSERT_EQ(nullptr, result5.msg); - - ReadResult result6 = read_half->Read(10, 10); - ASSERT_EQ(OakStatus::OK, result6.status); - EXPECT_NE(result6.msg, nullptr); - ASSERT_EQ(3, result6.msg->data.size()); - ASSERT_EQ(0x21, (result6.msg->data)[0]); - ASSERT_EQ(3, result6.msg->channels.size()); - - ASSERT_EQ(false, read_half->CanRead()); -} - -} // namespace oak diff --git a/oak/server/grpc_client_node.cc b/oak/server/grpc_client_node.cc index f5d0cabd743..f5a610ecf62 100644 --- a/oak/server/grpc_client_node.cc +++ b/oak/server/grpc_client_node.cc @@ -29,9 +29,9 @@ namespace { void* tag(int i) { return (void*)static_cast(i); } } // namespace -GrpcClientNode::GrpcClientNode(BaseRuntime* runtime, const std::string& name, NodeId node_id, +GrpcClientNode::GrpcClientNode(const std::string& name, NodeId node_id, const std::string& grpc_address) - : NodeThread(runtime, name, node_id), + : OakNode(name, node_id), channel_(grpc::CreateChannel(grpc_address, grpc::InsecureChannelCredentials())), stub_(new grpc::GenericStub(channel_)) { OAK_LOG(INFO) << "Created gRPC client node for " << grpc_address; @@ -46,7 +46,7 @@ bool GrpcClientNode::HandleInvocation(Handle invocation_handle) { // Expect to read a single request out of the request channel. // TODO(#97): support client-side streaming - NodeReadResult req_result = ChannelRead(invocation->req_handle.get(), INT_MAX, INT_MAX); + NodeReadResult req_result = ChannelRead(invocation->req_handle.get()); if (req_result.status != OakStatus::OK) { OAK_LOG(ERROR) << "Failed to read invocation message: " << req_result.status; return false; diff --git a/oak/server/grpc_client_node.h b/oak/server/grpc_client_node.h index 2ee07d02f2f..e3642d116d1 100644 --- a/oak/server/grpc_client_node.h +++ b/oak/server/grpc_client_node.h @@ -23,15 +23,13 @@ #include "grpcpp/generic/generic_stub.h" #include "include/grpcpp/grpcpp.h" #include "oak/common/handles.h" -#include "oak/server/base_runtime.h" -#include "oak/server/node_thread.h" +#include "oak/server/oak_node.h" namespace oak { -class GrpcClientNode final : public NodeThread { +class GrpcClientNode final : public OakNode { public: - GrpcClientNode(BaseRuntime* runtime, const std::string& name, NodeId node_id, - const std::string& grpc_address); + GrpcClientNode(const std::string& name, NodeId node_id, const std::string& grpc_address); private: bool HandleInvocation(Handle invocation_handle); diff --git a/oak/server/invocation.cc b/oak/server/invocation.cc index 3a4393228c5..c081870a009 100644 --- a/oak/server/invocation.cc +++ b/oak/server/invocation.cc @@ -16,6 +16,7 @@ #include "oak/server/invocation.h" +#include "absl/memory/memory.h" #include "oak/common/logging.h" namespace oak { @@ -27,7 +28,7 @@ std::unique_ptr Invocation::ReceiveFromChannel(OakNode* node, // as a GrpcRequest. // - Handle for the write half of a channel to send responses down, each // serialized as a GrpcResponse. - NodeReadResult invocation = node->ChannelRead(invocation_handle, INT_MAX, INT_MAX); + NodeReadResult invocation = node->ChannelRead(invocation_handle); if (invocation.status != OakStatus::OK) { OAK_LOG(ERROR) << "Failed to read invocation message: " << invocation.status; return nullptr; diff --git a/oak/server/loader/oak_loader.cc b/oak/server/loader/oak_loader.cc index 5869c8636fb..ce916d8a6f7 100644 --- a/oak/server/loader/oak_loader.cc +++ b/oak/server/loader/oak_loader.cc @@ -29,16 +29,15 @@ grpc::Status OakLoader::CreateApplication( std::shared_ptr grpc_credentials) { OAK_LOG(INFO) << "Creating an Oak application"; - auto runtime = absl::make_unique(); - auto status = runtime->Initialize(application_configuration, grpc_credentials); + runtime_ = absl::make_unique(); + auto status = runtime_->Initialize(application_configuration, grpc_credentials); if (!status.ok()) { return status; } // Start the runtime. - auto result = runtime->Start(); - runtime_ = std::move(runtime); - return result; + runtime_->Start(); + return grpc::Status::OK; } grpc::Status OakLoader::TerminateApplication() { diff --git a/oak/server/loader/oak_runner_main.cc b/oak/server/loader/oak_runner_main.cc index 5b0fafd4558..999e9413b22 100644 --- a/oak/server/loader/oak_runner_main.cc +++ b/oak/server/loader/oak_runner_main.cc @@ -21,6 +21,7 @@ #include "absl/flags/parse.h" #include "absl/synchronization/notification.h" #include "absl/time/time.h" +#include "oak/common/app_config.h" #include "oak/common/logging.h" #include "oak/common/utils.h" #include "oak/server/loader/oak_loader.h" diff --git a/oak/server/logging_node.cc b/oak/server/logging_node.cc deleted file mode 100644 index eba6a19064f..00000000000 --- a/oak/server/logging_node.cc +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2019 The Project Oak 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 - * - * http://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 "oak/server/logging_node.h" - -#include "absl/memory/memory.h" -#include "oak/common/logging.h" -#include "oak/proto/log.pb.h" -#include "oak/server/handle_closer.h" - -namespace oak { - -void LoggingNode::Run(Handle handle) { - HandleCloser handle_closer(this, handle); - std::vector> status; - status.push_back(absl::make_unique(handle)); - bool done = false; - while (!done) { - if (!WaitOnChannels(&status)) { - OAK_LOG(WARNING) << "{" << name_ << "} Node termination requested"; - done = true; - } - while (true) { - NodeReadResult result = ChannelRead(handle, INT_MAX, INT_MAX); - if (result.status == OakStatus::ERR_CHANNEL_EMPTY) { - break; - } - if (result.status != OakStatus::OK) { - OAK_LOG(ERROR) << "{" << name_ << "} Failed to read message: " << result.status; - return; - } - oak::log::LogMessage log_msg; - bool successful_parse = - log_msg.ParseFromArray(result.msg->data.data(), result.msg->data.size()); - if (successful_parse) { - // TODO(#630): when information flow control is implemented, this - // logging should be governed by that (rather than by the compile-time - // OAK_DEBUG flag). - OAK_LOG(INFO) << "{" << name_ << "} " - << "LOG: " << oak::log::Level_Name(log_msg.level()) << " " << log_msg.file() - << ":" << log_msg.line() << ": " << log_msg.message(); - } else { - OAK_LOG(ERROR) << "{" << name_ << "} Could not parse LogMessage."; - } - // Drop any handles that erroneously came along with the message. - for (Handle handle : result.msg->handles) { - ChannelClose(handle); - } - } - } -} - -} // namespace oak diff --git a/oak/server/logging_node.h b/oak/server/logging_node.h deleted file mode 100644 index 6f7cf70a3ee..00000000000 --- a/oak/server/logging_node.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2019 The Project Oak 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 - * - * http://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. - */ - -#ifndef OAK_SERVER_LOGGING_NODE_H_ -#define OAK_SERVER_LOGGING_NODE_H_ - -#include -#include - -#include "oak/common/handles.h" -#include "oak/server/channel.h" -#include "oak/server/node_thread.h" - -namespace oak { - -// Pseudo-node to perform logging. -class LoggingNode final : public NodeThread { - public: - explicit LoggingNode(BaseRuntime* runtime, const std::string& name, NodeId node_id) - : NodeThread(runtime, name, node_id) {} - - private: - void Run(Handle handle) override; -}; - -} // namespace oak - -#endif // OAK_SERVER_LOGGING_NODE_H_ diff --git a/oak/server/module_invocation.cc b/oak/server/module_invocation.cc index d1b0c932bd1..59cbc7da7dc 100644 --- a/oak/server/module_invocation.cc +++ b/oak/server/module_invocation.cc @@ -21,7 +21,6 @@ #include "oak/common/policy.h" #include "oak/proto/grpc_encap.pb.h" #include "oak/proto/policy.pb.h" -#include "oak/server/channel.h" namespace oak { @@ -187,7 +186,7 @@ void ModuleInvocation::BlockingSendResponse() { FinishAndCleanUp(grpc::Status(grpc::StatusCode::INTERNAL, "Message wait failed")); return; } - NodeReadResult rsp_result = grpc_node_->ChannelRead(rsp_handle_, INT_MAX, INT_MAX); + NodeReadResult rsp_result = grpc_node_->ChannelRead(rsp_handle_); if (rsp_result.status != OakStatus::OK) { OAK_LOG(ERROR) << "invocation#" << stream_id_ << " SendResponse: Failed to read message: " << rsp_result.status; diff --git a/oak/server/module_invocation.h b/oak/server/module_invocation.h index 38807acff2c..f799d70c77c 100644 --- a/oak/server/module_invocation.h +++ b/oak/server/module_invocation.h @@ -19,7 +19,6 @@ #include -#include "include/grpcpp/generic/async_generic_service.h" #include "include/grpcpp/grpcpp.h" #include "oak/common/handles.h" #include "oak/server/oak_grpc_node.h" diff --git a/oak/server/node_thread.cc b/oak/server/node_thread.cc deleted file mode 100644 index ce679775215..00000000000 --- a/oak/server/node_thread.cc +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2019 The Project Oak 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 - * - * http://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 "oak/server/node_thread.h" - -#include "oak/common/logging.h" - -namespace oak { - -NodeThread::~NodeThread() { StopThread(); } - -void NodeThread::Start(Handle handle) { - if (thread_.joinable()) { - OAK_LOG(ERROR) << "Attempt to Start() an already-running NodeThread"; - return; - } - if (TerminationPending()) { - OAK_LOG(ERROR) << "Attempt to Start() an already-terminated NodeThread"; - return; - } - - OAK_LOG(INFO) << "Executing new {" << name_ << "} node thread with handle " << handle; - thread_ = std::thread(&oak::NodeThread::RunAndCleanup, this, handle); - OAK_LOG(INFO) << "Started {" << name_ << "} node thread"; -} - -void NodeThread::Stop() { - OAK_LOG(INFO) << "Stopping node {" << name_ << "}"; - StopThread(); -} - -void NodeThread::StopThread() { - OAK_LOG(INFO) << "Termination pending for {" << name_ << "}"; - if (thread_.joinable()) { - OAK_LOG(INFO) << "Waiting for completion of {" << name_ << "} node thread"; - thread_.join(); - OAK_LOG(INFO) << "Completed {" << name_ << "} node thread"; - } -} - -void NodeThread::RunAndCleanup(Handle handle) { - Run(handle); - OAK_LOG(INFO) << "Node's Run() method completed, clear handle table"; - ClearHandles(); -} - -} // namespace oak diff --git a/oak/server/node_thread.h b/oak/server/node_thread.h deleted file mode 100644 index 601a60179dd..00000000000 --- a/oak/server/node_thread.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2019 The Project Oak 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 - * - * http://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. - */ - -#ifndef OAK_SERVER_NODE_THREAD_H_ -#define OAK_SERVER_NODE_THREAD_H_ - -#include -#include - -#include "oak/server/oak_node.h" - -namespace oak { - -// This class represents a node or pseudo-node that executes as a distinct -// thread. -class NodeThread : public OakNode { - public: - // Construct a thread, identified by the given name in diagnostic messages. - NodeThread(BaseRuntime* runtime, const std::string& name, NodeId node_id) - : OakNode(runtime, name, node_id) {} - virtual ~NodeThread(); - - // Start kicks off a separate thread that invokes the Run() method. - void Start(Handle handle) override; - - // Stop terminates the thread associated with the pseudo-node. - void Stop() override; - - protected: - // The Run() method is run on a new thread, and should respond to termination requests - // (indicated by termination_pending_.load()) in a timely manner. - virtual void Run(Handle handle) = 0; - - private: - void StopThread(); - void RunAndCleanup(Handle handle); - - std::thread thread_; -}; - -} // namespace oak - -#endif // OAK_SERVER_NODE_THREAD_H_ diff --git a/oak/server/notification.cc b/oak/server/notification.cc deleted file mode 100644 index 3579428fb9b..00000000000 --- a/oak/server/notification.cc +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2020 The Project Oak 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 - * - * http://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 "oak/server/notification.h" - -namespace oak { - -Notification::~Notification() { absl::MutexLock lock(&mu_); } - -void Notification::Notify() { - absl::MutexLock lock(&mu_); - count_++; -} - -void Notification::WaitForNotification() { - auto notified = [this] { return count_ > 0; }; - absl::MutexLock lock(&mu_); - mu_.Await(absl::Condition(¬ified)); -} - -bool Notification::WaitForNotificationWithTimeout(absl::Duration timeout) const { - auto notified = [this] { return count_ > 0; }; - absl::MutexLock lock(&mu_); - return mu_.AwaitWithTimeout(absl::Condition(¬ified), timeout); -} - -} // namespace oak diff --git a/oak/server/notification.h b/oak/server/notification.h deleted file mode 100644 index 677476a524d..00000000000 --- a/oak/server/notification.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2020 The Project Oak 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 - * - * http://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. - */ - -#ifndef OAK_SERVER_NOTIFICATION_H_ -#define OAK_SERVER_NOTIFICATION_H_ - -#include "absl/synchronization/mutex.h" -#include "absl/time/time.h" - -namespace oak { - -// A Notification allows threads to receive notification of an event. -class Notification { - public: - Notification() : count_(0) {} - Notification(const Notification&) = delete; - Notification& operator=(const Notification&) = delete; - ~Notification(); - - // Trigger the notification. May be called multiple times. - void Notify(); - - // Await one or more triggers of the notification. - void WaitForNotification(); - bool WaitForNotificationWithTimeout(absl::Duration timeout) const; - - private: - mutable absl::Mutex mu_; - int count_; -}; - -} // namespace oak - -#endif // OAK_SERVER_NOTIFICATION_H_ diff --git a/oak/server/notification_test.cc b/oak/server/notification_test.cc deleted file mode 100644 index ef9cf4ee856..00000000000 --- a/oak/server/notification_test.cc +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2020 The Project Oak 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 - * - * http://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 "oak/server/notification.h" - -#include -#include - -#include "gtest/gtest.h" -#include "oak/common/logging.h" -#include "oak/server/channel.h" - -namespace oak { - -TEST(Notification, MultipleNotify) { - const int kChannelCount = 5; - const int kWaiterCount = 4; - - std::vector channels; - for (int chan = 0; chan < kChannelCount; chan++) { - channels.push_back(MessageChannel::Create()); - } - - absl::Mutex mu; - int waiting_count = 0; - - std::vector waiters; - for (int wait = 0; wait < kWaiterCount; wait++) { - OAK_LOG(INFO) << "start new waiter thread " << wait; - waiters.push_back(std::thread([wait, &channels, &mu, &waiting_count] { - OAK_LOG(INFO) << "new waiter thread " << wait; - auto notify = std::make_shared(); - for (int chan = 0; chan < kChannelCount; chan++) { - OAK_LOG(INFO) << "register notification for waiter " << wait << " with channel " << chan; - channels[chan].read->ReadStatus(std::weak_ptr(notify)); - } - - mu.Lock(); - waiting_count++; - mu.Unlock(); - - OAK_LOG(INFO) << "waiter thread " << wait << " awaiting..."; - notify->WaitForNotification(); - OAK_LOG(INFO) << "waiter thread " << wait << " awaiting...done"; - })); - } - - // Make sure all waiters are up and running before writing notifications. - mu.Lock(); - auto all_up = [&waiting_count] { return waiting_count == kWaiterCount; }; - mu.Await(absl::Condition(&all_up)); - mu.Unlock(); - - for (int chan = 0; chan < kChannelCount; chan++) { - auto msg = absl::make_unique(); - channels[chan].write->Write(std::move(msg)); - } - - for (int wait = 0; wait < kWaiterCount; wait++) { - OAK_LOG(INFO) << "await completion of waiter thread " << wait; - waiters[wait].join(); - OAK_LOG(INFO) << "await completion of waiter thread " << wait << " done"; - } -} - -} // namespace oak diff --git a/oak/server/oak_grpc_node.cc b/oak/server/oak_grpc_node.cc index 22a74cdb4ee..64d8a503810 100644 --- a/oak/server/oak_grpc_node.cc +++ b/oak/server/oak_grpc_node.cc @@ -18,16 +18,15 @@ #include "absl/memory/memory.h" #include "include/grpcpp/grpcpp.h" -#include "oak/common/app_config.h" #include "oak/common/logging.h" #include "oak/server/module_invocation.h" namespace oak { std::unique_ptr OakGrpcNode::Create( - BaseRuntime* runtime, const std::string& name, NodeId node_id, + const std::string& name, NodeId node_id, std::shared_ptr grpc_credentials, const uint16_t port) { - std::unique_ptr node = absl::WrapUnique(new OakGrpcNode(runtime, name, node_id)); + std::unique_ptr node = absl::WrapUnique(new OakGrpcNode(name, node_id)); // Build Server grpc::ServerBuilder builder; @@ -54,14 +53,14 @@ std::unique_ptr OakGrpcNode::Create( } void OakGrpcNode::Start(Handle handle) { - handle_ = handle; - OAK_LOG(INFO) << "{" << name_ << "} Using handle " << handle_ << " for sending invocations"; - // Start a new thread to process the gRPC completion queue. - queue_thread_ = std::thread(&OakGrpcNode::CompletionQueueLoop, this); + OAK_LOG(INFO) << "{" << name_ << "} Using handle " << handle << " for sending invocations"; + // Start a new thread to do processing. + queue_thread_ = std::thread(&OakGrpcNode::Run, this, handle); } -void OakGrpcNode::CompletionQueueLoop() { - OAK_LOG(INFO) << "{" << name_ << "} Starting gRPC completion queue loop"; +void OakGrpcNode::Run(Handle handle) { + OAK_LOG(INFO) << "{" << name_ << "} Starting gRPC completion queue loop on handle " << handle; + handle_ = handle; // The stream object will delete itself when finished with the request, // after creating a new stream object for the next request. @@ -71,11 +70,7 @@ void OakGrpcNode::CompletionQueueLoop() { bool ok; void* tag; if (!completion_queue_->Next(&tag, &ok)) { - if (!TerminationPending()) { - OAK_LOG(FATAL) << "{" << name_ << "} Failure reading from completion queue"; - } - OAK_LOG(INFO) << "{" << name_ - << "} No Next event on completion queue, stopping gRPC completion queue loop"; + OAK_LOG(ERROR) << "{" << name_ << "} Failure reading from completion queue"; return; } auto callback = static_cast*>(tag); diff --git a/oak/server/oak_grpc_node.h b/oak/server/oak_grpc_node.h index e3c4a26127f..7f2f9358f1a 100644 --- a/oak/server/oak_grpc_node.h +++ b/oak/server/oak_grpc_node.h @@ -17,11 +17,12 @@ #ifndef OAK_SERVER_OAK_GRPC_NODE_H_ #define OAK_SERVER_OAK_GRPC_NODE_H_ +#include #include #include "absl/synchronization/mutex.h" +#include "include/grpcpp/generic/async_generic_service.h" #include "include/grpcpp/grpcpp.h" -#include "oak/common/app_config.h" #include "oak/server/oak_node.h" namespace oak { @@ -31,14 +32,15 @@ class OakGrpcNode final : public OakNode { // Create an Oak node with the `name` and gRPC `port`. // If `port` equals 0, then gRPC port is assigned automatically. static std::unique_ptr Create( - BaseRuntime* runtime, const std::string& name, NodeId node_id, + const std::string& name, NodeId node_id, std::shared_ptr grpc_credentials, const uint16_t port = 0); virtual ~OakGrpcNode(){}; - void Start(Handle handle) override; - void Stop() override; + void Run(Handle handle) override; - int GetPort() { return port_; }; + // Start() invokes Run in a separate thread. + void Start(Handle handle); + void Stop(); int32_t NextStreamID() LOCKS_EXCLUDED(id_mu_) { absl::MutexLock lock(&id_mu_); @@ -48,14 +50,11 @@ class OakGrpcNode final : public OakNode { private: friend class ModuleInvocation; - OakGrpcNode(BaseRuntime* runtime, const std::string& name, NodeId node_id) - : OakNode(runtime, name, node_id), next_stream_id_(1), handle_(kInvalidHandle) {} + OakGrpcNode(const std::string& name, NodeId node_id) + : OakNode(name, node_id), next_stream_id_(1), handle_(kInvalidHandle) {} OakGrpcNode(const OakGrpcNode&) = delete; OakGrpcNode& operator=(const OakGrpcNode&) = delete; - // Consumes gRPC events from the completion queue in an infinite loop. - void CompletionQueueLoop(); - int port_; std::unique_ptr<::grpc::Server> server_; @@ -69,7 +68,7 @@ class OakGrpcNode final : public OakNode { absl::Mutex id_mu_; // protects next_stream_id_ int32_t next_stream_id_ GUARDED_BY(id_mu_); - Handle handle_; // const after Start() + Handle handle_; // const after Run() begins. }; } // namespace oak diff --git a/oak/server/oak_node.cc b/oak/server/oak_node.cc index f8468b5577b..19c3e14ca71 100644 --- a/oak/server/oak_node.cc +++ b/oak/server/oak_node.cc @@ -19,299 +19,66 @@ #include "absl/base/internal/endian.h" #include "absl/memory/memory.h" #include "oak/common/logging.h" -#include "oak/server/notification.h" #include "oak/server/rust/oak_glue/oak_glue.h" namespace oak { -NodeReadResult OakNode::ChannelRead(Handle handle, uint32_t max_size, uint32_t max_channels) { - { - uint32_t actual_size; - uint32_t actual_count; - uint32_t status = - glue_channel_read(node_id_, handle, nullptr, 0, &actual_size, nullptr, 0, &actual_count); - if ((status != OakStatus::ERR_BUFFER_TOO_SMALL) && - (status != OakStatus::ERR_HANDLE_SPACE_TOO_SMALL)) { - return NodeReadResult(static_cast(status)); - } - NodeReadResult result(OakStatus::OK); - result.msg = absl::make_unique(); - result.msg->data.resize(actual_size); - result.msg->handles.resize(actual_count); - status = glue_channel_read(node_id_, handle, - reinterpret_cast(result.msg->data.data()), actual_size, - &actual_size, reinterpret_cast(result.msg->handles.data()), - actual_count, &actual_count); - result.status = static_cast(status); - return result; - } - - // Borrowing a reference to the channel is safe because the node is single - // threaded and so cannot invoke channel_close while channel_read is - // ongoing. - MessageChannelReadHalf* channel = BorrowReadChannel(handle); - if (channel == nullptr) { - OAK_LOG(WARNING) << "{" << name_ << "} Invalid channel handle: " << handle; - return NodeReadResult(OakStatus::ERR_BAD_HANDLE); - } - ReadResult result_internal = channel->Read(max_size, max_channels); - NodeReadResult result(result_internal.status); - result.required_size = result_internal.required_size; - result.required_channels = result_internal.required_channels; - if (result.status == OakStatus::OK) { - if (result_internal.msg != nullptr) { - // Move data and label across into Node-relative message. - result.msg = absl::make_unique(); - result.msg->data = std::move(result_internal.msg->data); - result.msg->label = std::move(result_internal.msg->label); - // Transmute channel references to handles in this Node's handle space. - for (size_t ii = 0; ii < result_internal.msg->channels.size(); ii++) { - Handle handle = AddChannel(std::move(result_internal.msg->channels[ii])); - OAK_LOG(INFO) << "{" << name_ << "} Transferred channel has new handle " << handle; - result.msg->handles.push_back(handle); - } - } else { - // Nothing available to read. - if (channel->Orphaned()) { - OAK_LOG(INFO) << "{" << name_ << "} channel_read[" << handle << "]: no writers left"; - result.status = OakStatus::ERR_CHANNEL_CLOSED; - } else { - result.status = OakStatus::ERR_CHANNEL_EMPTY; - } - } - } +NodeReadResult OakNode::ChannelRead(Handle handle) { + uint32_t actual_size; + uint32_t actual_count; + uint32_t status = + glue_channel_read(node_id_, handle, nullptr, 0, &actual_size, nullptr, 0, &actual_count); + if ((status != OakStatus::ERR_BUFFER_TOO_SMALL) && + (status != OakStatus::ERR_HANDLE_SPACE_TOO_SMALL)) { + return NodeReadResult(static_cast(status)); + } + NodeReadResult result(OakStatus::OK); + result.msg = absl::make_unique(); + result.msg->data.resize(actual_size); + result.msg->handles.resize(actual_count); + status = glue_channel_read(node_id_, handle, reinterpret_cast(result.msg->data.data()), + actual_size, &actual_size, + reinterpret_cast(result.msg->handles.data()), actual_count, + &actual_count); + result.status = static_cast(status); return result; } OakStatus OakNode::ChannelWrite(Handle handle, std::unique_ptr msg) { - { - uint32_t status = glue_channel_write( - node_id_, handle, reinterpret_cast(msg->data.data()), msg->data.size(), - reinterpret_cast(msg->handles.data()), msg->handles.size()); - return static_cast(status); - } - - // Borrowing a reference to the channel is safe because the Node is single - // threaded and so cannot invoke channel_close while channel_write is - // ongoing. - MessageChannelWriteHalf* channel = BorrowWriteChannel(handle); - if (channel == nullptr) { - OAK_LOG(WARNING) << "{" << name_ << "} Invalid channel handle: " << handle; - return OakStatus::ERR_BAD_HANDLE; - } - - if (channel->Orphaned()) { - OAK_LOG(INFO) << "{" << name_ << "} channel_write[" << handle << "]: no readers left"; - return OakStatus::ERR_CHANNEL_CLOSED; - } - auto msg_internal = absl::make_unique(); - msg_internal->data = std::move(msg->data); - msg_internal->label = std::move(msg->label); - for (Handle h : msg->handles) { - ChannelHalf* half = BorrowChannel(h); - if (half == nullptr) { - OAK_LOG(WARNING) << "{" << name_ << "} Invalid transferred channel handle: " << h; - return OakStatus::ERR_BAD_HANDLE; - } - msg_internal->channels.push_back(CloneChannelHalf(half)); - } - - channel->Write(std::move(msg_internal)); - return OakStatus::OK; + uint32_t status = glue_channel_write( + node_id_, handle, reinterpret_cast(msg->data.data()), msg->data.size(), + reinterpret_cast(msg->handles.data()), msg->handles.size()); + return static_cast(status); } std::pair OakNode::ChannelCreate() { - { - uint64_t write; - uint64_t read; - uint32_t status = glue_channel_create(node_id_, &write, &read); - if (static_cast(status) != OakStatus::OK) { - return std::pair(0, 0); - } - return std::pair(write, read); + uint64_t write; + uint64_t read; + uint32_t status = glue_channel_create(node_id_, &write, &read); + if (static_cast(status) != OakStatus::OK) { + return std::pair(0, 0); } - MessageChannel::ChannelHalves halves = MessageChannel::Create(); - Handle write_handle = AddChannel(absl::make_unique(std::move(halves.write))); - Handle read_handle = AddChannel(absl::make_unique(std::move(halves.read))); - OAK_LOG(INFO) << "{" << name_ << "} Created new channel with handles write=" << write_handle - << ", read=" << read_handle; - return std::pair(write_handle, read_handle); + return std::pair(write, read); } OakStatus OakNode::ChannelClose(Handle handle) { - { - uint32_t status = glue_channel_close(node_id_, handle); - return static_cast(status); - } - absl::MutexLock lock(&mu_); - auto it = channel_halves_.find(handle); - if (it == channel_halves_.end()) { - return OakStatus::ERR_BAD_HANDLE; - } - channel_halves_.erase(it); - return OakStatus::OK; -} - -OakStatus OakNode::NodeCreate(Handle handle, const std::string& config_name, - const std::string& entrypoint_name) { - // Check that the handle identifies the read half of a channel. - ChannelHalf* borrowed_half = BorrowChannel(handle); - if (borrowed_half == nullptr) { - OAK_LOG(WARNING) << "{" << name_ << "} Invalid channel handle: " << handle; - return OakStatus::ERR_BAD_HANDLE; - } - if (!absl::holds_alternative>(*borrowed_half)) { - OAK_LOG(WARNING) << "{" << name_ << "} Wrong direction channel handle: " << handle; - return OakStatus::ERR_BAD_HANDLE; - } - std::unique_ptr half = CloneChannelHalf(borrowed_half); - - OAK_LOG(INFO) << "Create a new node with config '" << config_name << "' and entrypoint '" - << entrypoint_name << "'"; - - std::string node_name; - if (!runtime_->CreateAndRunNode(config_name, entrypoint_name, std::move(half), &node_name)) { - return OakStatus::ERR_INVALID_ARGS; - } else { - OAK_LOG(INFO) << "Created new node named {" << node_name << "}"; - return OakStatus::OK; - } -} - -Handle OakNode::NextHandle() { - std::uniform_int_distribution distribution; - while (true) { - // Keep picking random Handle values until we find an unused (and valid) value. - Handle handle = distribution(prng_engine_); - if (handle == kInvalidHandle) { - continue; - } - if (channel_halves_.find(handle) == channel_halves_.end()) { - return handle; - } - } -} - -Handle OakNode::AddChannel(std::unique_ptr half) { - absl::MutexLock lock(&mu_); - Handle handle = NextHandle(); - channel_halves_[handle] = std::move(half); - return handle; -} - -ChannelHalf* OakNode::BorrowChannel(Handle handle) const { - absl::ReaderMutexLock lock(&mu_); - auto it = channel_halves_.find(handle); - if (it == channel_halves_.end()) { - return nullptr; - } - return it->second.get(); -} - -MessageChannelReadHalf* OakNode::BorrowReadChannel(Handle handle) const { - absl::ReaderMutexLock lock(&mu_); - auto it = channel_halves_.find(handle); - if (it == channel_halves_.end()) { - return nullptr; - } - ChannelHalf* half = it->second.get(); - auto value = absl::get_if>(half); - if (value == nullptr) { - return nullptr; - } - return value->get(); -} - -MessageChannelWriteHalf* OakNode::BorrowWriteChannel(Handle handle) const { - absl::ReaderMutexLock lock(&mu_); - auto it = channel_halves_.find(handle); - if (it == channel_halves_.end()) { - return nullptr; - } - ChannelHalf* half = it->second.get(); - auto value = absl::get_if>(half); - if (value == nullptr) { - return nullptr; - } - return value->get(); + uint32_t status = glue_channel_close(node_id_, handle); + return static_cast(status); } bool OakNode::WaitOnChannels(std::vector>* statuses) const { - { - int count = statuses->size(); - std::vector space(9 * count); - for (int ii = 0; ii < count; ii++) { - Handle handle = (*statuses)[ii]->handle; - OAK_LOG(INFO) << "{" << name_ << "} wait on " << handle; - absl::little_endian::Store64(space.data() + (9 * ii), handle); - } - uint32_t status = glue_wait_on_channels(node_id_, space.data(), count); - for (int ii = 0; ii < count; ii++) { - (*statuses)[ii]->status = static_cast(space[9 * ii + 8]); - } - return (status == OakStatus::OK); - } - while (true) { - bool found_ready = false; - bool found_readable = false; - auto notify = std::make_shared(); - for (uint32_t ii = 0; ii < statuses->size(); ii++) { - uint64_t handle = (*statuses)[ii]->handle; - MessageChannelReadHalf* channel = BorrowReadChannel(handle); - if (channel == nullptr) { - OAK_LOG(WARNING) << "{" << name_ << "} Waiting on non-existent read channel handle " - << handle; - (*statuses)[ii]->status = ChannelReadStatus::INVALID_CHANNEL; - continue; - } - - ChannelReadStatus status = channel->ReadStatus(std::weak_ptr(notify)); - (*statuses)[ii]->status = status; - switch (status) { - case ChannelReadStatus::READ_READY: - OAK_LOG(INFO) << "{" << name_ << "} Message available on handle " << handle; - found_ready = true; - break; - case ChannelReadStatus::ORPHANED: - OAK_LOG(INFO) << "{" << name_ << "} Handle " << handle - << " is orphaned (no extant writers)"; - break; - case ChannelReadStatus::NOT_READY: - found_readable = true; - break; - default: - OAK_LOG(ERROR) << "{" << name_ << "} Unexpected channel read status: " << status; - return false; - break; - } - } - - if (runtime_->TerminationPending()) { - OAK_LOG(WARNING) << "{" << name_ << "} Node is pending termination"; - return false; - } - if (found_ready) { - return true; - } - if (!found_readable) { - OAK_LOG(WARNING) << "{" << name_ << "} No read-capable channels found"; - return false; - } - - // Wait with a timeout to make end-of-day shutdown easier: this means that a - // node with no pending work will still check termination_pending_ - // occasionally. - notify->WaitForNotificationWithTimeout(absl::Seconds(1)); - - // The only shared_ptr to the Notification object will be dropped here, at - // which point any still-existing weak_ptr instances will no longer resolve. - } -} - -void OakNode::ClearHandles() { - absl::MutexLock lock(&mu_); - OAK_LOG(INFO) << "Dropping " << channel_halves_.size() << " handles for Node"; - channel_halves_.clear(); + int count = statuses->size(); + std::vector space(9 * count); + for (int ii = 0; ii < count; ii++) { + Handle handle = (*statuses)[ii]->handle; + OAK_LOG(INFO) << "{" << name_ << "} wait on " << handle; + absl::little_endian::Store64(space.data() + (9 * ii), handle); + } + uint32_t status = glue_wait_on_channels(node_id_, space.data(), count); + for (int ii = 0; ii < count; ii++) { + (*statuses)[ii]->status = static_cast(space[9 * ii + 8]); + } + return (status == OakStatus::OK); } } // namespace oak diff --git a/oak/server/oak_node.h b/oak/server/oak_node.h index a52811178d7..d4fc3290102 100644 --- a/oak/server/oak_node.h +++ b/oak/server/oak_node.h @@ -19,41 +19,34 @@ #include -#include #include -#include -#include -#include #include -#include "absl/base/thread_annotations.h" -#include "absl/synchronization/mutex.h" #include "oak/common/handles.h" -#include "oak/server/base_runtime.h" -#include "oak/server/channel.h" +#include "oak/proto/oak_api.pb.h" +#include "oak/proto/policy.pb.h" namespace oak { +// Current readable status of a channel. +struct ChannelStatus { + explicit ChannelStatus(uint64_t h) : handle(h), status(ChannelReadStatus::NOT_READY) {} + uint64_t handle; + ChannelReadStatus status; +}; + // Representation of a message transferred over a channel, relative to -// a particular Node. This is equivalent to the Message object, but -// using channel handles (which are relative to a particular OakNode) rather -// than raw channel references. +// a particular Node. struct NodeMessage { std::vector data; std::vector handles; oak::policy::Label label; }; -// Result of a read operation relative to a Node. Equivalent to ReadResult but -// holds a NodeMessage rather than a Message. +// Result of a read operation relative to a Node. struct NodeReadResult { - explicit NodeReadResult(OakStatus s) : status(s), required_size(0), required_channels(0) {} + explicit NodeReadResult(OakStatus s) : status(s) {} OakStatus status; - // The following fields are filled in if the status is ERR_BUFFER_TOO_SMALL - // or ERR_HANDLE_SPACE_TOO_SMALL, indicating the required size and number - // of handles needed to read the message. - uint32_t required_size; - uint32_t required_channels; // The following field is filled in if the status is OK. std::unique_ptr msg; }; @@ -62,16 +55,16 @@ using NodeId = uint64_t; class OakNode { public: - OakNode(BaseRuntime* runtime, const std::string& name, NodeId node_id) - : name_(name), node_id_(node_id), runtime_(runtime), prng_engine_() {} + OakNode(const std::string& name, NodeId node_id) : name_(name), node_id_(node_id) {} virtual ~OakNode() {} - virtual void Start(Handle handle) = 0; - virtual void Stop() = 0; + // The Run() method is run on a new thread, and should respond to termination + // requests (indicated by termination_pending_.load()) in a timely manner. + virtual void Run(Handle handle) = 0; // ChannelRead returns the first message on the channel identified by the - // handle, subject to size checks. - NodeReadResult ChannelRead(Handle handle, uint32_t max_size, uint32_t max_channels); + // handle. + NodeReadResult ChannelRead(Handle handle); // ChannelWrite passes ownership of a message to the channel identified by the // handle. @@ -81,11 +74,7 @@ class OakNode { std::pair ChannelCreate(); // Close the given channel half. - OakStatus ChannelClose(Handle handle) LOCKS_EXCLUDED(mu_); - - // Create a new Node. - OakStatus NodeCreate(Handle handle, const std::string& config_name, - const std::string& entrypoint_name); + OakStatus ChannelClose(Handle handle); // Wait on the given channel handles, modifying the contents of the passed-in // vector. Returns a boolean indicating whether the wait finished due to a @@ -96,42 +85,12 @@ class OakNode { bool WaitOnChannels(std::vector>* statuses) const; protected: - bool TerminationPending() const { return runtime_->TerminationPending(); } - - void ClearHandles() LOCKS_EXCLUDED(mu_); - const std::string name_; const NodeId node_id_; private: // Allow the Runtime to use internal methods. friend class OakRuntime; - - // Take ownership of the given channel half, returning a channel handle that - // the Node can use to refer to it in future. - Handle AddChannel(std::unique_ptr half) LOCKS_EXCLUDED(mu_); - - Handle NextHandle() EXCLUSIVE_LOCKS_REQUIRED(mu_); - - // Return a borrowed reference to the channel half identified by the given - // handle (or nullptr if the handle is not recognized). Caller is responsible - // for ensuring that the borrowed reference does not out-live the owned - // channel. - ChannelHalf* BorrowChannel(Handle handle) const LOCKS_EXCLUDED(mu_); - MessageChannelReadHalf* BorrowReadChannel(Handle handle) const LOCKS_EXCLUDED(mu_); - MessageChannelWriteHalf* BorrowWriteChannel(Handle handle) const LOCKS_EXCLUDED(mu_); - - using ChannelHalfTable = std::unordered_map>; - - // Runtime instance that owns this Node. - BaseRuntime* const runtime_; - - mutable absl::Mutex mu_; // protects prng_engine_, channel_halves_ - - std::random_device prng_engine_ GUARDED_BY(mu_); - - // Map from channel handles to channel half instances. - ChannelHalfTable channel_halves_ GUARDED_BY(mu_); }; } // namespace oak diff --git a/oak/server/oak_runtime.cc b/oak/server/oak_runtime.cc index afeafa6ed7f..2241a5f7e7d 100644 --- a/oak/server/oak_runtime.cc +++ b/oak/server/oak_runtime.cc @@ -27,17 +27,15 @@ #include "oak/common/app_config.h" #include "oak/common/logging.h" #include "oak/server/grpc_client_node.h" -#include "oak/server/logging_node.h" #include "oak/server/rust/oak_glue/oak_glue.h" #include "oak/server/storage/storage_node.h" -#include "oak/server/wasm_node.h" namespace oak { namespace { -// Name to use for the (sole) gRPC pseudo-Node. This will not clash with any -// dynamically created Node names because they are all of the form -// "--". +// Name to use for the (sole) gRPC server pseudo-Node. This will not clash with +// any dynamically created Node names because they are all of the form +// "-". constexpr char kGrpcNodeName[] = "grpc"; absl::once_flag glue_once; @@ -55,7 +53,6 @@ grpc::Status OakRuntime::Initialize(const ApplicationConfiguration& config, std::shared_ptr grpc_credentials) { absl::call_once(glue_once, &glue_init); - OAK_LOG(INFO) << "Initializing Oak Runtime"; if (!ValidApplicationConfig(config)) { return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Invalid configuration"); } @@ -66,24 +63,16 @@ grpc::Status OakRuntime::Initialize(const ApplicationConfiguration& config, OAK_LOG(ERROR) << "Failed to serialize ApplicationConfiguration"; return grpc::Status(grpc::StatusCode::INTERNAL, "Failed to serialize configuration"); } - uint64_t rust_handle = glue_start(reinterpret_cast(config_data.data()), - static_cast(config_data.size()), NodeFactory, - reinterpret_cast(this)); - OAK_LOG(INFO) << "Started Rust runtime, handle=" << rust_handle; - - absl::MutexLock lock(&mu_); + grpc_handle_ = glue_start(reinterpret_cast(config_data.data()), + static_cast(config_data.size()), NodeFactory, + reinterpret_cast(this)); + OAK_LOG(INFO) << "Started Rust runtime, handle=" << grpc_handle_; // Accumulate the various data structures indexed by config name. - wasm_config_.clear(); - log_config_.clear(); storage_config_.clear(); + grpc_client_config_.clear(); for (const auto& node_config : config.node_configs()) { - if (node_config.has_wasm_config()) { - wasm_config_[node_config.name()] = - absl::make_unique(node_config.wasm_config()); - } else if (node_config.has_log_config()) { - log_config_.insert(node_config.name()); - } else if (node_config.has_storage_config()) { + if (node_config.has_storage_config()) { const StorageProxyConfiguration& storage_config = node_config.storage_config(); storage_config_[node_config.name()] = absl::make_unique(storage_config.address()); @@ -94,164 +83,59 @@ grpc::Status OakRuntime::Initialize(const ApplicationConfiguration& config, } } - // Create a gRPC pseudo-Node. - NodeId grpc_node_id = NextNodeId(); - const std::string grpc_name = kGrpcNodeName; + // Create a gRPC pseudo-Node, with a node ID of zero (which indicates + // it acts as the controller of the Rust runtime). const uint16_t grpc_port = config.grpc_port(); - OAK_LOG(INFO) << "Create gRPC pseudo-Node named {" << grpc_name << "}"; - std::unique_ptr grpc_node = - OakGrpcNode::Create(this, grpc_name, grpc_node_id, grpc_credentials, grpc_port); - grpc_node_ = grpc_node.get(); // borrowed copy - nodes_[grpc_name] = std::move(grpc_node); - - grpc_handle_ = rust_handle; - return grpc::Status::OK; - - // Create the initial Application Node. - NodeId app_node_id = NextNodeId(); - std::string node_name; - app_node_ = CreateNode(config.initial_node_config_name(), config.initial_entrypoint_name(), - app_node_id, &node_name); - if (app_node_ == nullptr) { - return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Failed to create initial Oak Node"); - } - OAK_LOG(INFO) << "Created Wasm node named {" << node_name << "}"; - - // Create an initial channel from gRPC pseudo-Node to Application Node. - // Both of the initial nodes have exactly one registered handle. - MessageChannel::ChannelHalves halves = MessageChannel::Create(); - grpc_handle_ = grpc_node_->AddChannel(absl::make_unique(std::move(halves.write))); - app_handle_ = app_node_->AddChannel(absl::make_unique(std::move(halves.read))); - OAK_LOG(INFO) << "Created initial channel from Wasm node {" << grpc_name << "}." << grpc_handle_ - << " to {" << node_name << "}." << app_handle_; + OAK_LOG(INFO) << "Create gRPC pseudo-Node named {" << kGrpcNodeName << "}"; + grpc_node_ = OakGrpcNode::Create(kGrpcNodeName, 0, grpc_credentials, grpc_port); return grpc::Status::OK; } -// Generate a unique (per-Runtime) name for a new Node, running the given Node -// configuration and entrypoint. -std::string OakRuntime::NextNodeName(const std::string& config_name, - const std::string& entrypoint_name) { - int index = next_index_[config_name]++; - return absl::StrCat(config_name, "-", index, "-", entrypoint_name); -} - -NodeId OakRuntime::NextNodeId() { return next_node_id_++; } - // Create (but don't start) a new Node instance. Return a borrowed pointer to // the new Node (or nullptr on failure). -OakNode* OakRuntime::CreateNode(const std::string& config_name, const std::string& entrypoint_name, - NodeId node_id, std::string* node_name) { - std::string name = NextNodeName(config_name, entrypoint_name); - std::unique_ptr node; +std::unique_ptr OakRuntime::CreateNode(const std::string& config_name, NodeId node_id) { + std::string name = absl::StrCat(config_name, "-", node_id); - if (wasm_config_.count(config_name) > 0) { - OAK_LOG(INFO) << "Create Wasm node named {" << name << "}"; - const WebAssemblyConfiguration* wasm_cfg = wasm_config_[config_name].get(); - node = WasmNode::Create(this, name, node_id, wasm_cfg->module_bytes(), entrypoint_name); - } else if (log_config_.count(config_name) > 0) { - OAK_LOG(INFO) << "Create log node named {" << name << "}"; - node = absl::make_unique(this, name, node_id); - } else if (storage_config_.count(config_name) > 0) { + if (storage_config_.count(config_name) > 0) { std::string address = *(storage_config_[config_name].get()); OAK_LOG(INFO) << "Create storage proxy node named {" << name << "} connecting to " << address; - node = absl::make_unique(this, name, node_id, address); + return absl::make_unique(name, node_id, address); } else if (grpc_client_config_.count(config_name) > 0) { std::string address = *(grpc_client_config_[config_name].get()); OAK_LOG(INFO) << "Create gRPC client node named {" << name << "} connecting to " << address; - node = absl::make_unique(this, name, node_id, address); + return absl::make_unique(name, node_id, address); } else { OAK_LOG(ERROR) << "failed to find config with name " << config_name; return nullptr; } - - OakNode* result = node.get(); - if (node != nullptr) { - nodes_[name] = std::move(node); - *node_name = name; - } else { - OAK_LOG(ERROR) << "failed to create Node with config of name " << config_name; - } - return result; } void OakRuntime::CreateAndRunPseudoNode(const std::string& config_name, NodeId node_id, Handle handle) { - std::string node_name; - OakNode* node; - { - absl::MutexLock lock(&mu_); - node = CreateNode(config_name, "unused", node_id, &node_name); - } + std::unique_ptr node = CreateNode(config_name, node_id); if (node == nullptr) { OAK_LOG(FATAL) << "Failed to create pseudo-Node with config " << config_name; } - OAK_LOG(INFO) << "Start pseudo-node named {" << node_name << "} with initial handle " << handle; - node->Start(handle); - // Caller runs this on another thread, so OK to block here. - node->Stop(); -} - -bool OakRuntime::CreateAndRunNode(const std::string& config_name, - const std::string& entrypoint_name, - std::unique_ptr half, std::string* node_name) { - if (TerminationPending()) { - OAK_LOG(WARNING) << "Runtime is pending termination, fail node creation"; - return false; - } - - absl::MutexLock lock(&mu_); - NodeId node_id = NextNodeId(); - OakNode* node = CreateNode(config_name, entrypoint_name, node_id, node_name); - if (node == nullptr) { - return false; - } - - // Add the given channel as the Node's single available handle. - Handle handle = node->AddChannel(std::move(half)); - - OAK_LOG(INFO) << "Start node named {" << *node_name << "} with initial handle " << handle; - node->Start(handle); - return true; + OAK_LOG(INFO) << "Start pseudo-node of config '" << config_name << "' with initial handle " + << handle; + node->Run(handle); + OAK_LOG(INFO) << "Finished pseudo-node of config '" << config_name << "' with initial handle " + << handle; } -grpc::Status OakRuntime::Start() { +void OakRuntime::Start() { OAK_LOG(INFO) << "Starting runtime"; - - // Now all dependencies are running, start the initial pair of Nodes running. + // Start the initial gRPC Node running. grpc_node_->Start(grpc_handle_); - return grpc::Status::OK; - - app_node_->Start(app_handle_); - return grpc::Status::OK; } -int32_t OakRuntime::GetPort() { return grpc_node_->GetPort(); } - -grpc::Status OakRuntime::Stop() { - OAK_LOG(INFO) << "Stopping runtime..."; +void OakRuntime::Stop() { + OAK_LOG(INFO) << "Stopping gRPC server pseudo-Node..."; + grpc_node_->Stop(); + OAK_LOG(INFO) << "Stopping Rust runtime..."; glue_stop(); - termination_pending_ = true; - - // Take local ownership of all the nodes owned by the runtime. - std::map> nodes; - { - absl::MutexLock lock(&mu_); - grpc_node_ = nullptr; - nodes = std::move(nodes_); - nodes_.clear(); - } - - // Now stop all the nodes without holding the runtime lock, just - // in case any of the per-Node threads happens to try an operation - // (e.g. node_create) that needs the lock. - for (auto& named_node : nodes) { - OAK_LOG(INFO) << "Stopping node " << named_node.first; - named_node.second->Stop(); - } - - return grpc::Status::OK; } } // namespace oak diff --git a/oak/server/oak_runtime.h b/oak/server/oak_runtime.h index 9c33fa4ce71..2373aa73287 100644 --- a/oak/server/oak_runtime.h +++ b/oak/server/oak_runtime.h @@ -26,88 +26,44 @@ #include "include/grpcpp/server.h" #include "oak/proto/application.pb.h" #include "oak/proto/oak_api.pb.h" -#include "oak/server/base_runtime.h" #include "oak/server/oak_grpc_node.h" -#include "oak/server/storage/storage_node.h" +#include "oak/server/oak_node.h" namespace oak { -// OakRuntime contains the common runtime needed for an Oak System. The Runtime is responsible for -// Initializing and Running a gRPC server, creating the nodes and channels and keeping track of -// the connectivity. For now, it only supports one node. -// -// It can run in its own enclave, but this is optional. -class OakRuntime : public BaseRuntime { +// OakRuntime contains the common C++ parts of a runtime needed for an Oak +// System, but mostly acts as a proxy for the Rust runtime. +class OakRuntime { public: - OakRuntime() - : grpc_node_(nullptr), - grpc_handle_(kInvalidHandle), - app_node_(nullptr), - app_handle_(kInvalidHandle), - next_node_id_(0), - termination_pending_(false) {} + OakRuntime() : grpc_node_(nullptr), grpc_handle_(kInvalidHandle) {} virtual ~OakRuntime() = default; // Initializes an OakRuntime with a user-provided ApplicationConfiguration. This // method should be called exactly once, before Start(). grpc::Status Initialize(const ApplicationConfiguration& config, - std::shared_ptr grpc_credentials) - LOCKS_EXCLUDED(mu_); - grpc::Status Start(); - grpc::Status Stop(); + std::shared_ptr grpc_credentials); + void Start(); + void Stop(); void CreateAndRunPseudoNode(const std::string& config_name, NodeId node_id, Handle handle); - bool CreateAndRunNode(const std::string& config_name, const std::string& entrypoint_name, - std::unique_ptr half, std::string* node_name) override; - - bool TerminationPending() override { return termination_pending_.load(); } - - int32_t GetPort(); - private: OakRuntime& operator=(const OakRuntime& other) = delete; - std::string NextNodeName(const std::string& config_name, const std::string& entrypoint_name) - EXCLUSIVE_LOCKS_REQUIRED(mu_); - NodeId NextNodeId() EXCLUSIVE_LOCKS_REQUIRED(mu_); - - OakNode* CreateNode(const std::string& config_name, const std::string& entrypoint_name, - NodeId node_id, std::string* node_name) EXCLUSIVE_LOCKS_REQUIRED(mu_); + std::unique_ptr CreateNode(const std::string& config_name, NodeId node_id); // Information derived from ApplicationConfiguration; const after Initialize() called: - // Collection of Wasm configuration info indexed by config name. - std::map> wasm_config_; - // Config names that refer to a logging node. - std::set log_config_; // Config names that refer to a storage proxy node. std::map> storage_config_; // Config names that refer to a gRPC client node. std::map> grpc_client_config_; - // Convenience (non-owning) reference to gRPC pseudo-node. - OakGrpcNode* grpc_node_; + // gRPC pseudo-node. + std::unique_ptr grpc_node_; // Handle for the write half of the gRPC server notification channel, relative // to the gRPC server pseudo-Node Handle grpc_handle_; - // Convenience (non-owning) reference to initial Application Wasm node; - OakNode* app_node_; - // Handle for the read half of the gRPC server notification channel, relative - // to the initial Application Wasm Node. - Handle app_handle_; - - // Next indexes for node name/ID generation. - mutable absl::Mutex mu_; // protects nodes_, next_index_, next_node_id_; - std::map next_index_ GUARDED_BY(mu_); - NodeId next_node_id_ GUARDED_BY(mu_); - - // Collection of running Nodes indexed by Node name. Note that Node name is - // unique but is not visible to the running Application in any way. - std::map> nodes_ GUARDED_BY(mu_); - - std::atomic_bool termination_pending_; - }; // class OakRuntime } // namespace oak diff --git a/oak/server/storage/BUILD b/oak/server/storage/BUILD index 49483566786..15595449969 100644 --- a/oak/server/storage/BUILD +++ b/oak/server/storage/BUILD @@ -53,7 +53,7 @@ cc_library( "//oak/proto:grpc_encap_cc_proto", "//oak/proto:storage_channel_cc_proto", "//oak/server:invocation", - "//oak/server:node_thread", + "//oak/server:oak_node", "//third_party/asylo:statusor", "@com_github_grpc_grpc//:grpc++", "@com_google_absl//absl/memory", diff --git a/oak/server/storage/storage_node.cc b/oak/server/storage/storage_node.cc index 063ef318502..4d8089a89d3 100644 --- a/oak/server/storage/storage_node.cc +++ b/oak/server/storage/storage_node.cc @@ -27,9 +27,9 @@ namespace oak { -StorageNode::StorageNode(BaseRuntime* runtime, const std::string& name, NodeId node_id, +StorageNode::StorageNode(const std::string& name, NodeId node_id, const std::string& storage_address) - : NodeThread(runtime, name, node_id), storage_processor_(storage_address) {} + : OakNode(name, node_id), storage_processor_(storage_address) {} void StorageNode::Run(Handle invocation_handle) { std::vector> channel_status; @@ -47,7 +47,7 @@ void StorageNode::Run(Handle invocation_handle) { } // Expect to read a single request out of the request channel. - NodeReadResult req_result = ChannelRead(invocation->req_handle.get(), INT_MAX, INT_MAX); + NodeReadResult req_result = ChannelRead(invocation->req_handle.get()); if (req_result.status != OakStatus::OK) { OAK_LOG(ERROR) << "Failed to read message: " << req_result.status; return; diff --git a/oak/server/storage/storage_node.h b/oak/server/storage/storage_node.h index e109b86abc8..e8839c77789 100644 --- a/oak/server/storage/storage_node.h +++ b/oak/server/storage/storage_node.h @@ -22,16 +22,15 @@ #include #include "oak/proto/grpc_encap.pb.h" -#include "oak/server/node_thread.h" +#include "oak/server/oak_node.h" #include "oak/server/storage/storage_processor.h" #include "third_party/asylo/statusor.h" namespace oak { -class StorageNode final : public NodeThread { +class StorageNode final : public OakNode { public: - StorageNode(BaseRuntime* runtime, const std::string& name, NodeId node_id, - const std::string& storage_address); + StorageNode(const std::string& name, NodeId node_id, const std::string& storage_address); private: void Run(Handle handle) override; diff --git a/oak/server/wabt.bzl b/oak/server/wabt.bzl deleted file mode 100644 index 6c1196c5f11..00000000000 --- a/oak/server/wabt.bzl +++ /dev/null @@ -1,40 +0,0 @@ -# -# Copyright 2019 The Project Oak 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 -# -# http://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. -# - -"""Utilities for handling WebAssembly files.""" - -def wasm_group(name, srcs): - """Compiles WebAssembly files from textual to binary representation. - - Args: - name: Name for build target. - srcs: Source WebAssembly text files (.wat extension). - """ - outs = [] - for src in srcs: - out = src.replace(".wat", ".wasm") - outs.append(out) - native.genrule( - name = name + "_" + src + "_generate_wasm", - tools = ["@wabt//:wat2wasm"], - srcs = [src], - outs = [out], - cmd = "$(location @wabt//:wat2wasm) $< --output $@", - ) - native.filegroup( - name = name, - srcs = outs, - ) diff --git a/oak/server/wabt_output.cc b/oak/server/wabt_output.cc deleted file mode 100644 index aec991605aa..00000000000 --- a/oak/server/wabt_output.cc +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2019 The Project Oak 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 - * - * http://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 "oak/server/wabt_output.h" - -std::ostream& operator<<(std::ostream& os, const wabt::ExternalKind& k) { - return os << wabt::GetKindName(k); -} - -std::ostream& operator<<(std::ostream& os, const wabt::Type& t) { - return os << wabt::GetTypeName(t); -} - -std::ostream& operator<<(std::ostream& os, const std::vector& vt) { - os << "("; - for (size_t ii = 0; ii < vt.size(); ii++) { - if (ii > 0) { - os << ", "; - } - os << vt[ii]; - } - return os << ")"; -} - -std::ostream& operator<<(std::ostream& os, const wabt::interp::FuncSignature& sig) { - return os << sig.param_types << " -> " << sig.result_types; -} diff --git a/oak/server/wabt_output.h b/oak/server/wabt_output.h deleted file mode 100644 index 279544c0fd2..00000000000 --- a/oak/server/wabt_output.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2019 The Project Oak 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 - * - * http://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. - */ - -#ifndef OAK_SERVER_WABT_OUTPUT_H_ -#define OAK_SERVER_WABT_OUTPUT_H_ - -#include - -#include "src/interp/interp.h" - -/* Output helpers for wabt:: types */ - -std::ostream& operator<<(std::ostream& os, const wabt::ExternalKind& k); -std::ostream& operator<<(std::ostream& os, const wabt::Type& t); -std::ostream& operator<<(std::ostream& os, const std::vector& vt); -std::ostream& operator<<(std::ostream& os, const wabt::interp::FuncSignature& sig); - -#endif // OAK_SERVER_WABT_OUTPUT_H_ diff --git a/oak/server/wabt_output_test.cc b/oak/server/wabt_output_test.cc deleted file mode 100644 index 37c2254f449..00000000000 --- a/oak/server/wabt_output_test.cc +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2019 The Project Oak 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 - * - * http://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 "oak/server/wabt_output.h" - -#include "gtest/gtest.h" - -TEST(WabtOutput, KindName) { - std::stringstream ss; - ss << wabt::ExternalKind::Func; - EXPECT_EQ("func", ss.str()); -} - -TEST(WabtOutput, TypeName) { - std::stringstream ss; - ss << wabt::Type::F32; - EXPECT_EQ("f32", ss.str()); -} - -TEST(WabtOutput, TypeVector) { - std::stringstream ss; - std::vector vec0 = {}; - std::vector vec1 = {wabt::Type::I32}; - std::vector vec3 = {wabt::Type::Func, wabt::Type::I32, wabt::Type::F32}; - ss << vec0 << "|" << vec1 << "|" << vec3; - EXPECT_EQ("()|(i32)|(func, i32, f32)", ss.str()); -} - -TEST(WabtOutput, FuncSignature) { - std::stringstream ss; - wabt::interp::FuncSignature sig0; - wabt::interp::FuncSignature sig1{{wabt::Type::I32}, {wabt::Type::I32}}; - wabt::interp::FuncSignature sig2{{wabt::Type::I32, wabt::Type::F32}, {wabt::Type::I32}}; - ss << sig0 << "|" << sig1 << "|" << sig2; - EXPECT_EQ("() -> ()|(i32) -> (i32)|(i32, f32) -> (i32)", ss.str()); -} diff --git a/oak/server/wasm_node.cc b/oak/server/wasm_node.cc deleted file mode 100644 index 6f82ca1fe9f..00000000000 --- a/oak/server/wasm_node.cc +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright 2018 The Project Oak 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 - * - * http://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 "oak/server/wasm_node.h" - -#include -#include -#include -#include - -#include "absl/base/internal/endian.h" -#include "absl/memory/memory.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "absl/types/span.h" -#include "oak/common/logging.h" -#include "oak/proto/oak_api.pb.h" -#include "oak/server/base_runtime.h" -#include "oak/server/wabt_output.h" -#include "src/binary-reader.h" -#include "src/error-formatter.h" -#include "src/error.h" -#include "src/interp/binary-reader-interp.h" - -namespace oak { - -// Alias for types used for linear memory addressing. This should be the only -// thing that needs changing for any future 64-bit version of Wasm. -static wabt::Type wabtUsizeType = wabt::Type::I32; - -// From https://github.com/WebAssembly/wabt/blob/master/src/tools/wasm-interp.cc . - -static std::unique_ptr s_log_stream = wabt::FileStream::CreateStdout(); -static std::unique_ptr s_stdout_stream = wabt::FileStream::CreateStdout(); - -static bool MemoryAvailable(wabt::interp::Environment* env, const uint32_t offset, - const uint32_t size) { - return ((offset + size) <= env->GetMemory(0)->data.size()); -} - -static absl::Span ReadMemory(wabt::interp::Environment* env, const uint32_t offset, - const uint32_t size) { - return absl::MakeConstSpan(env->GetMemory(0)->data).subspan(offset, size); -} - -static void WriteMemory(wabt::interp::Environment* env, const uint32_t offset, - const absl::Span data) { - std::copy(data.cbegin(), data.cend(), env->GetMemory(0)->data.begin() + offset); -} - -static void WriteI32(wabt::interp::Environment* env, const uint32_t offset, const int32_t value) { - absl::little_endian::Store32(env->GetMemory(0)->data.data() + offset, value); -} - -static void WriteU64(wabt::interp::Environment* env, const uint32_t offset, const uint64_t v) { - absl::little_endian::Store64(env->GetMemory(0)->data.data() + offset, v); -} - -static uint64_t ReadU64(wabt::interp::Environment* env, const uint32_t offset) { - return absl::little_endian::Load64(env->GetMemory(0)->data.data() + offset); -} - -static void LogHostFunctionCall(const std::string& node_name, const wabt::interp::HostFunc* func, - const wabt::interp::TypedValues& args) { - std::stringstream params; - bool first = true; - for (auto const& arg : args) { - params << std::string(first ? "" : ", ") << wabt::interp::TypedValueToString(arg); - first = false; - } - OAK_LOG(INFO) << "{" << node_name << "} Called host function: " << func->module_name << "." - << func->field_name << "(" << params.str() << ")"; -} - -static wabt::Result ReadModule(const std::string& module_bytes, wabt::interp::Environment* env, - wabt::Errors* errors) { - OAK_LOG(INFO) << "Reading module"; - wabt::Result result; - - wabt::Features s_features; - const bool kReadDebugNames = true; - const bool kStopOnFirstError = true; - const bool kFailOnCustomSectionError = true; - wabt::Stream* log_stream = nullptr; - wabt::ReadBinaryOptions options(s_features, log_stream, kReadDebugNames, kStopOnFirstError, - kFailOnCustomSectionError); - - wabt::interp::DefinedModule* module = nullptr; - return wabt::ReadBinaryInterp(env, module_bytes.data(), module_bytes.size(), options, errors, - &module); -} - -} // namespace oak - -namespace oak { - -const wabt::interp::FuncSignature kRequiredExport(wabt::interp::FuncSignature( - std::vector{wabt::Type::I64}, std::vector{})); - -// Check module exports the required function with the correct signatures, -// returning true if so. -static bool CheckModuleExport(wabt::interp::Environment* env, wabt::interp::Module* module, - const std::string& main_entrypoint) { - OAK_LOG(INFO) << "check for " << main_entrypoint; - wabt::interp::Export* exp = module->GetExport(main_entrypoint); - if (exp == nullptr) { - OAK_LOG(WARNING) << "Could not find required export '" << main_entrypoint << "' in module"; - return false; - } - if (exp->kind != wabt::ExternalKind::Func) { - OAK_LOG(WARNING) << "Required export of kind " << exp->kind << " not func in module"; - return false; - } - OAK_LOG(INFO) << "check signature of function #" << exp->index; - wabt::interp::Func* func = env->GetFunc(exp->index); - if (func == nullptr) { - OAK_LOG(WARNING) << "failed to retrieve function #" << exp->index; - return false; - } - if (func->sig_index >= env->GetFuncSignatureCount()) { - OAK_LOG(WARNING) << "Function #" << func->sig_index << " beyond range of signature types"; - return false; - } - wabt::interp::FuncSignature* sig = env->GetFuncSignature(func->sig_index); - if (sig == nullptr) { - OAK_LOG(WARNING) << "Could not find signature for function #" << exp->index; - return false; - } - OAK_LOG(INFO) << "function #" << exp->index << " has type #" << func->sig_index - << " with signature " << *sig; - if ((sig->param_types != kRequiredExport.param_types) || - (sig->result_types != kRequiredExport.result_types)) { - OAK_LOG(WARNING) << "Function signature mismatch for " << main_entrypoint << ": got " << *sig - << ", want " << kRequiredExport; - return false; - } - return true; -} - -WasmNode::WasmNode(BaseRuntime* runtime, const std::string& name, NodeId node_id, - const std::string& main_entrypoint) - : NodeThread(runtime, name, node_id), main_entrypoint_(main_entrypoint), prng_engine_() {} - -std::unique_ptr WasmNode::Create(BaseRuntime* runtime, const std::string& name, - NodeId node_id, const std::string& module, - const std::string& main_entrypoint) { - OAK_LOG(INFO) << "Creating Wasm Node"; - - std::unique_ptr node = - absl::WrapUnique(new WasmNode(runtime, name, node_id, main_entrypoint)); - node->InitEnvironment(&node->env_); - OAK_LOG(INFO) << "Host func count: " << node->env_.GetFuncCount(); - - wabt::Errors errors; - OAK_LOG(INFO) << "Reading module"; - wabt::Result result = ReadModule(module, &node->env_, &errors); - if (!wabt::Succeeded(result)) { - OAK_LOG(WARNING) << "Could not read module: " << result; - OAK_LOG(WARNING) << "Errors: " - << wabt::FormatErrorsToString(errors, wabt::Location::Type::Binary); - return nullptr; - } - - OAK_LOG(INFO) << "Reading module done"; - if (!CheckModuleExport(&node->env_, node->Module(), main_entrypoint)) { - OAK_LOG(WARNING) << "Failed to validate module"; - return nullptr; - } - - return node; -} - -// Register all available host functions so that they are available to the Oak Module at runtime. -void WasmNode::InitEnvironment(wabt::interp::Environment* env) { - wabt::interp::HostModule* oak_module = env->AppendHostModule("oak"); - oak_module->AppendFuncExport( - "channel_read", - wabt::interp::FuncSignature( - std::vector{wabt::Type::I64, wabtUsizeType, wabtUsizeType, wabtUsizeType, - wabtUsizeType, wabt::Type::I32, wabtUsizeType}, - std::vector{wabt::Type::I32}), - this->OakChannelRead(env)); - oak_module->AppendFuncExport( - "channel_write", - wabt::interp::FuncSignature( - std::vector{wabt::Type::I64, wabtUsizeType, wabtUsizeType, wabtUsizeType, - wabt::Type::I32}, - std::vector{wabt::Type::I32}), - this->OakChannelWrite(env)); - - oak_module->AppendFuncExport( - "wait_on_channels", - wabt::interp::FuncSignature(std::vector{wabtUsizeType, wabt::Type::I32}, - std::vector{wabt::Type::I32}), - this->OakWaitOnChannels(env)); - oak_module->AppendFuncExport( - "channel_create", - wabt::interp::FuncSignature(std::vector{wabtUsizeType, wabtUsizeType}, - std::vector{wabt::Type::I32}), - this->OakChannelCreate(env)); - oak_module->AppendFuncExport( - "channel_close", - wabt::interp::FuncSignature(std::vector{wabt::Type::I64}, - std::vector{wabt::Type::I32}), - this->OakChannelClose(env)); - oak_module->AppendFuncExport( - "node_create", - wabt::interp::FuncSignature( - std::vector{wabtUsizeType, wabtUsizeType, wabtUsizeType, wabtUsizeType, - wabt::Type::I64}, - std::vector{wabt::Type::I32}), - this->OakNodeCreate(env)); - oak_module->AppendFuncExport( - "random_get", - wabt::interp::FuncSignature(std::vector{wabtUsizeType, wabtUsizeType}, - std::vector{wabt::Type::I32}), - this->OakRandomGet(env)); - - // These exported functions are used to run applications that have been ported to Wasm - // without code refactoring, and thus may require several WASI functions to be imported. - // WASI is an interface that provides access to OS features (e.g. filesystems, sockets, etc.): - // https://wasi.dev/ - // WASI functions are imported from a `wasi_snapshot_preview1` module: - // https://github.com/CraneStation/wasi-libc/blob/12f5832b45c7450f8320db271334081247191d58/libc-bottom-half/headers/public/wasi/api.h#L1584 - // These particular functions are required by TensorFlow Lite for Microcontrollers. - wabt::interp::HostModule* wasi_module = env->AppendHostModule("wasi_snapshot_preview1"); - wasi_module->AppendFuncExport( - "proc_exit", - wabt::interp::FuncSignature(std::vector{wabt::Type::I32}, - std::vector{}), - this->WasiPlaceholder()); - wasi_module->AppendFuncExport( - "fd_write", - wabt::interp::FuncSignature(std::vector{wabt::Type::I32, wabt::Type::I32, - wabt::Type::I32, wabt::Type::I32}, - std::vector{wabt::Type::I32}), - this->WasiPlaceholder()); - wasi_module->AppendFuncExport( - "fd_seek", - wabt::interp::FuncSignature(std::vector{wabt::Type::I32, wabt::Type::I64, - wabt::Type::I32, wabt::Type::I32}, - std::vector{wabt::Type::I32}), - this->WasiPlaceholder()); - wasi_module->AppendFuncExport( - "fd_close", - wabt::interp::FuncSignature(std::vector{wabt::Type::I32}, - std::vector{wabt::Type::I32}), - this->WasiPlaceholder()); - wasi_module->AppendFuncExport( - "environ_sizes_get", - wabt::interp::FuncSignature(std::vector{wabt::Type::I32, wabt::Type::I32}, - std::vector{wabt::Type::I32}), - this->WasiPlaceholder()); - wasi_module->AppendFuncExport( - "environ_get", - wabt::interp::FuncSignature(std::vector{wabt::Type::I32, wabt::Type::I32}, - std::vector{wabt::Type::I32}), - this->WasiPlaceholder()); -} - -void WasmNode::Run(Handle handle) { - wabt::interp::Thread::Options thread_options; - wabt::Stream* trace_stream = nullptr; - wabt::interp::Executor executor(&env_, trace_stream, thread_options); - - OAK_LOG(INFO) << "{" << name_ << "} module execution thread: run " << main_entrypoint_ << "(" - << handle << ")"; - wabt::interp::TypedValues args = { - wabt::interp::TypedValue(wabt::Type::I64, wabt::interp::Value{.i64 = handle})}; - wabt::interp::ExecResult exec_result = executor.RunExportByName(Module(), main_entrypoint_, args); - - if (exec_result.result != wabt::interp::Result::Ok) { - OAK_LOG(ERROR) << "{" << name_ - << "} execution failure: " << wabt::interp::ResultToString(exec_result.result); - return; - } - OAK_LOG(WARNING) << "{" << name_ << "} module execution terminated"; -} - -wabt::interp::HostFunc::Callback WasmNode::OakChannelRead(wabt::interp::Environment* env) { - return [this, env](const wabt::interp::HostFunc* func, const wabt::interp::FuncSignature*, - const wabt::interp::TypedValues& args, wabt::interp::TypedValues& results) { - LogHostFunctionCall(name_, func, args); - - Handle channel_handle = args[0].get_i64(); - uint32_t offset = args[1].get_i32(); - uint32_t size = args[2].get_i32(); - uint32_t size_offset = args[3].get_i32(); - uint32_t handle_space_offset = args[4].get_i32(); - uint32_t handle_space_count = args[5].get_i32(); - uint32_t handle_count_offset = args[6].get_i32(); - - // Check all provided linear memory is accessible. - if (!MemoryAvailable(env, offset, size) || !MemoryAvailable(env, size_offset, 4) || - !MemoryAvailable(env, handle_space_offset, handle_space_count * sizeof(Handle)) || - !MemoryAvailable(env, handle_count_offset, 4)) { - OAK_LOG(WARNING) << "{" << name_ << "} Node provided invalid memory offset+size"; - results[0].set_i32(OakStatus::ERR_INVALID_ARGS); - return wabt::interp::Result::Ok; - } - - NodeReadResult result = ChannelRead(channel_handle, size, handle_space_count); - OAK_LOG(INFO) << "{" << name_ << "} channel_read[" << channel_handle - << "]: gives status: " << result.status << " with required size " - << result.required_size << ", count " << result.required_channels; - WriteI32(env, size_offset, result.required_size); - WriteI32(env, handle_count_offset, result.required_channels); - results[0].set_i32(result.status); - - if ((result.status == OakStatus::OK) && (result.msg != nullptr)) { - // Transfer message and handles to Node. - OAK_LOG(INFO) << "{" << name_ << "} channel_read[" << channel_handle - << "]: read message of size " << result.msg->data.size() << " with " - << result.msg->handles.size() << " attached handles"; - WriteI32(env, size_offset, result.msg->data.size()); - WriteMemory(env, offset, absl::Span(result.msg->data.data(), result.msg->data.size())); - - WriteI32(env, handle_count_offset, result.msg->handles.size()); - for (size_t ii = 0; ii < result.msg->handles.size(); ii++) { - Handle handle = result.msg->handles[ii]; - OAK_LOG(INFO) << "{" << name_ << "} Transferred new handle " << handle; - WriteU64(env, handle_space_offset + ii * sizeof(Handle), handle); - } - } - return wabt::interp::Result::Ok; - }; -} - -wabt::interp::HostFunc::Callback WasmNode::OakChannelWrite(wabt::interp::Environment* env) { - return [this, env](const wabt::interp::HostFunc* func, const wabt::interp::FuncSignature*, - const wabt::interp::TypedValues& args, wabt::interp::TypedValues& results) { - LogHostFunctionCall(name_, func, args); - - Handle channel_handle = args[0].get_i64(); - uint32_t offset = args[1].get_i32(); - uint32_t size = args[2].get_i32(); - uint32_t handle_offset = args[3].get_i32(); - uint32_t handle_count = args[4].get_i32(); - - // Check all provided linear memory is accessible. - if (!MemoryAvailable(env, offset, size) || - !MemoryAvailable(env, handle_offset, handle_count * sizeof(Handle))) { - OAK_LOG(WARNING) << "{" << name_ << "} Node provided invalid memory offset+size"; - results[0].set_i32(OakStatus::ERR_INVALID_ARGS); - return wabt::interp::Result::Ok; - } - - // Copy the data from the Wasm linear memory. - absl::Span origin = ReadMemory(env, offset, size); - auto msg = absl::make_unique(); - msg->data.insert(msg->data.end(), origin.begin(), origin.end()); - OAK_LOG(INFO) << "{" << name_ << "} channel_write[" << channel_handle - << "]: write message of size " << size; - - // Find any handles and clone the corresponding channels. - std::vector handles; - handles.reserve(handle_count); - for (uint32_t ii = 0; ii < handle_count; ii++) { - Handle handle = ReadU64(env, handle_offset + (ii * sizeof(Handle))); - OAK_LOG(INFO) << "{" << name_ << "} Transfer channel handle " << handle; - msg->handles.push_back(handle); - } - OakStatus status = ChannelWrite(channel_handle, std::move(msg)); - results[0].set_i32(status); - return wabt::interp::Result::Ok; - }; -} - -wabt::interp::HostFunc::Callback WasmNode::OakWaitOnChannels(wabt::interp::Environment* env) { - return [this, env](const wabt::interp::HostFunc* func, const wabt::interp::FuncSignature*, - const wabt::interp::TypedValues& args, wabt::interp::TypedValues& results) { - LogHostFunctionCall(name_, func, args); - - uint32_t offset = args[0].get_i32(); - uint32_t count = args[1].get_i32(); - - // Check all provided linear memory is accessible. - if (!MemoryAvailable(env, offset, count * 9)) { - OAK_LOG(WARNING) << "{" << name_ << "} Node provided invalid memory offset+size"; - results[0].set_i32(OakStatus::ERR_INVALID_ARGS); - return wabt::interp::Result::Ok; - } - - if (count == 0) { - OAK_LOG(INFO) << "{" << name_ << "} Waiting on no channels, return immediately"; - results[0].set_i32(OakStatus::ERR_INVALID_ARGS); - return wabt::interp::Result::Ok; - } - - std::vector> statuses; - statuses.reserve(count); - for (uint32_t ii = 0; ii < count; ii++) { - uint64_t handle = ReadU64(env, offset + (9 * ii)); - statuses.push_back(absl::make_unique(handle)); - } - auto space = env->GetMemory(0)->data.begin() + offset; - bool wait_success = WaitOnChannels(&statuses); - // Transcribe the status byte into the notification space regardless of - // result. - for (uint32_t ii = 0; ii < count; ii++) { - auto base = space + (9 * ii); - base[8] = statuses[ii]->status; - } - if (wait_success) { - results[0].set_i32(OakStatus::OK); - } else if (TerminationPending()) { - results[0].set_i32(OakStatus::ERR_TERMINATED); - } else { - results[0].set_i32(OakStatus::ERR_BAD_HANDLE); - } - return wabt::interp::Result::Ok; - }; -} - -wabt::interp::HostFunc::Callback WasmNode::OakChannelCreate(wabt::interp::Environment* env) { - return [this, env](const wabt::interp::HostFunc* func, const wabt::interp::FuncSignature*, - const wabt::interp::TypedValues& args, wabt::interp::TypedValues& results) { - LogHostFunctionCall(name_, func, args); - - uint32_t write_half_offset = args[0].get_i32(); - uint32_t read_half_offset = args[1].get_i32(); - if (!MemoryAvailable(env, write_half_offset, sizeof(Handle)) || - !MemoryAvailable(env, read_half_offset, sizeof(Handle))) { - OAK_LOG(WARNING) << "{" << name_ << "} Node provided invalid memory offset+size"; - results[0].set_i32(OakStatus::ERR_INVALID_ARGS); - return wabt::interp::Result::Ok; - } - - std::pair handles = ChannelCreate(); - WriteU64(env, write_half_offset, handles.first); - WriteU64(env, read_half_offset, handles.second); - - results[0].set_i32(OakStatus::OK); - return wabt::interp::Result::Ok; - }; -} - -wabt::interp::HostFunc::Callback WasmNode::OakChannelClose(wabt::interp::Environment*) { - return [this](const wabt::interp::HostFunc* func, const wabt::interp::FuncSignature*, - const wabt::interp::TypedValues& args, wabt::interp::TypedValues& results) { - LogHostFunctionCall(name_, func, args); - - Handle channel_handle = args[0].get_i64(); - OakStatus status = ChannelClose(channel_handle); - OAK_LOG(INFO) << "{" << name_ << "} Close channel handle " << channel_handle << " status " - << status; - results[0].set_i32(status); - return wabt::interp::Result::Ok; - }; -} - -wabt::interp::HostFunc::Callback WasmNode::OakNodeCreate(wabt::interp::Environment* env) { - return [this, env](const wabt::interp::HostFunc* func, const wabt::interp::FuncSignature*, - const wabt::interp::TypedValues& args, wabt::interp::TypedValues& results) { - LogHostFunctionCall(name_, func, args); - - uint64_t config_offset = args[0].get_i32(); - uint32_t config_size = args[1].get_i32(); - uint64_t entrypoint_offset = args[2].get_i32(); - uint32_t entrypoint_size = args[3].get_i32(); - Handle channel_handle = args[4].get_i64(); - if (!MemoryAvailable(env, config_offset, config_size)) { - OAK_LOG(WARNING) << "Node provided invalid memory offset+size for config"; - results[0].set_i32(OakStatus::ERR_INVALID_ARGS); - return wabt::interp::Result::Ok; - } - if (!MemoryAvailable(env, entrypoint_offset, entrypoint_size)) { - OAK_LOG(WARNING) << "Node provided invalid memory offset+size for entrypoint"; - results[0].set_i32(OakStatus::ERR_INVALID_ARGS); - return wabt::interp::Result::Ok; - } - auto config_base = env->GetMemory(0)->data.begin() + config_offset; - std::string config_name(config_base, config_base + config_size); - auto entrypoint_base = env->GetMemory(0)->data.begin() + entrypoint_offset; - std::string entrypoint_name(entrypoint_base, entrypoint_base + entrypoint_size); - - OakStatus status = NodeCreate(channel_handle, config_name, entrypoint_name); - results[0].set_i32(status); - return wabt::interp::Result::Ok; - }; -} - -wabt::interp::HostFunc::Callback WasmNode::OakRandomGet(wabt::interp::Environment* env) { - return [this, env](const wabt::interp::HostFunc* func, const wabt::interp::FuncSignature*, - const wabt::interp::TypedValues& args, wabt::interp::TypedValues& results) { - LogHostFunctionCall(name_, func, args); - - uint32_t offset = args[0].get_i32(); - uint32_t size = args[1].get_i32(); - if (!MemoryAvailable(env, offset, size)) { - OAK_LOG(WARNING) << "Node provided invalid memory offset+size"; - results[0].set_i32(OakStatus::ERR_INVALID_ARGS); - return wabt::interp::Result::Ok; - } - - std::uniform_int_distribution distribution; - auto base = env->GetMemory(0)->data.begin() + offset; - for (uint32_t i = 0; i < size; i++) { - base[i] = distribution(prng_engine_); - } - - results[0].set_i32(OakStatus::OK); - return wabt::interp::Result::Ok; - }; -} - -wabt::interp::HostFunc::Callback WasmNode::WasiPlaceholder() { - return [this](const wabt::interp::HostFunc* func, const wabt::interp::FuncSignature*, - const wabt::interp::TypedValues& args, wabt::interp::TypedValues& results) { - LogHostFunctionCall(name_, func, args); - OAK_LOG(FATAL) << "{" << name_ << "} WASI is not implemented"; - results[0].set_i32(OakStatus::ERR_INTERNAL); - return wabt::interp::Result::TrapUnreachable; - }; -} - -} // namespace oak diff --git a/oak/server/wasm_node.h b/oak/server/wasm_node.h deleted file mode 100644 index 50ffafe6c96..00000000000 --- a/oak/server/wasm_node.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2019 The Project Oak 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 - * - * http://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. - */ - -#ifndef OAK_SERVER_WASM_NODE_H_ -#define OAK_SERVER_WASM_NODE_H_ - -#include -#include - -#include "oak/server/node_thread.h" -#include "src/interp/interp.h" - -namespace oak { - -class WasmNode final : public NodeThread { - public: - // Creates a Wasm Node by loading the Wasm module code. - static std::unique_ptr Create(BaseRuntime* runtime, const std::string& name, - NodeId node_id, const std::string& module, - const std::string& main_entrypoint); - - private: - // Clients should construct WasmNode instances with Create() (which can fail). - WasmNode(BaseRuntime* runtime, const std::string& name, NodeId node_id, - const std::string& main_entrypoint); - - void InitEnvironment(wabt::interp::Environment* env); - - // Return a (borrowed) pointer to the Web Assembly module for the Node. - wabt::interp::Module* Module() { return env_.GetLastModule(); } - - void Run(Handle handle) override; - - // Native implementation of the `oak.channel_read` host function. - wabt::interp::HostFunc::Callback OakChannelRead(wabt::interp::Environment* env); - - // Native implementation of the `oak.channel_write` host function. - wabt::interp::HostFunc::Callback OakChannelWrite(wabt::interp::Environment* env); - - // Native implementation of the `oak.wait_on_channels` host function. - wabt::interp::HostFunc::Callback OakWaitOnChannels(wabt::interp::Environment* env); - - // Native implementation of the `oak.channel_create` host function. - wabt::interp::HostFunc::Callback OakChannelCreate(wabt::interp::Environment* env); - - // Native implementation of the `oak.channel_close` host function. - wabt::interp::HostFunc::Callback OakChannelClose(wabt::interp::Environment* env); - - // Native implementation of the `oak.node_create` host function. - wabt::interp::HostFunc::Callback OakNodeCreate(wabt::interp::Environment* env); - - // Native implementation of the `oak.random_get` host function. - wabt::interp::HostFunc::Callback OakRandomGet(wabt::interp::Environment* env); - - // Placeholder for WASI functions. - // Since WASI functions are currently not supported by Oak, this function - // will always log, return an error and terminate the program. - wabt::interp::HostFunc::Callback WasiPlaceholder(); - - const std::string main_entrypoint_; - - wabt::interp::Environment env_; - - std::random_device prng_engine_; -}; - -} // namespace oak - -#endif // OAK_SERVER_WASM_NODE_H_ diff --git a/oak/server/wasm_node_fuzzer.cc b/oak/server/wasm_node_fuzzer.cc deleted file mode 100644 index 5e3386a087c..00000000000 --- a/oak/server/wasm_node_fuzzer.cc +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2019 The Project Oak 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 - * - * http://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 - -#include "oak/server/wasm_node.h" - -// See https://llvm.org/docs/LibFuzzer.html#fuzz-target. -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - std::string module(reinterpret_cast(data), size); - // Build a WasmNode instance treating the fuzzing input as Wasm module code. - // No need to pass in an owning runtime instance (first argument) since the - // built Node is never run. - std::unique_ptr node = oak::WasmNode::Create(nullptr, "test", module, "oak_main"); - return 0; -} diff --git a/oak/server/wasm_node_test.cc b/oak/server/wasm_node_test.cc deleted file mode 100644 index 32f8fe6c77c..00000000000 --- a/oak/server/wasm_node_test.cc +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2019 The Project Oak 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 - * - * http://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 "oak/server/wasm_node.h" - -#include - -#include "gtest/gtest.h" - -namespace oak { - -namespace { - -std::string DataFrom(const std::string& filename) { - std::ifstream ifs(filename.c_str(), std::ios::in | std::ios::binary); - EXPECT_TRUE(ifs.is_open()) << "failed to open " << filename; - std::stringstream ss; - ss << ifs.rdbuf(); - ifs.close(); - return ss.str(); -} - -} // namespace - -TEST(WasmNode, MalformedFailure) { - // No magic. - ASSERT_EQ(nullptr, WasmNode::Create(nullptr, "test", "", "oak_main")); - // Wrong magic. - ASSERT_EQ(nullptr, - WasmNode::Create(nullptr, "test", std::string("\x00\x61\x73\x6b\x01\x00\x00\x00", 8), - "oak_main")); - // Wrong version. - ASSERT_EQ(nullptr, - WasmNode::Create(nullptr, "test", std::string("\x00\x61\x73\x6d\x09\x00\x00\x00", 8), - "oak_main")); - // Right magic+version, no contents. - ASSERT_EQ(nullptr, WasmNode::Create(nullptr, "test", DataFrom("oak/server/testdata/empty.wasm"), - "oak_main")); -} - -TEST(WasmNode, MinimalSuccess) { - std::unique_ptr node = - WasmNode::Create(nullptr, "test", DataFrom("oak/server/testdata/minimal.wasm"), "oak_main"); - EXPECT_NE(nullptr, node); -} - -TEST(WasmNode, MissingExport) { - ASSERT_EQ(nullptr, WasmNode::Create(nullptr, "test", DataFrom("oak/server/testdata/missing.wasm"), - "oak_main")); -} - -TEST(WasmNode, WrongExport) { - ASSERT_EQ(nullptr, WasmNode::Create(nullptr, "test", DataFrom("oak/server/testdata/minimal.wasm"), - "oak_other_main")); -} - -TEST(WasmNode, WrongSignature) { - ASSERT_EQ(nullptr, WasmNode::Create(nullptr, "test", DataFrom("oak/server/testdata/wrong.wasm"), - "oak_main")); -} - -} // namespace oak