Merge "Detect log files without a header earlier"
diff --git a/aos/BUILD b/aos/BUILD
index 04c0548..04d77f3 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -274,6 +274,7 @@
],
visibility = ["//visibility:public"],
deps = [
+ ":thread_local",
"@com_github_google_glog//:glog",
],
)
@@ -543,3 +544,15 @@
],
visibility = ["//visibility:public"],
)
+
+cc_test(
+ name = "realtime_test",
+ srcs = [
+ "realtime_test.cc",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":realtime",
+ "//aos/testing:googletest",
+ ],
+)
diff --git a/aos/configuration.fbs b/aos/configuration.fbs
index e8a8050..cdf21b8 100644
--- a/aos/configuration.fbs
+++ b/aos/configuration.fbs
@@ -153,6 +153,10 @@
//
// Don't specify a hostname in multiple nodes in the same configuration.
hostnames:[string] (id: 3);
+
+ // An arbitrary list of strings representing properties of each node. These
+ // can be used to store information about roles.
+ tags:[string] (id: 4);
}
// Overall configuration datastructure for the pubsub.
diff --git a/aos/configuration.h b/aos/configuration.h
index f1bcece..5579334 100644
--- a/aos/configuration.h
+++ b/aos/configuration.h
@@ -8,7 +8,7 @@
#include <string_view>
-#include "aos/configuration_generated.h"
+#include "aos/configuration_generated.h" // IWYU pragma: export
#include "aos/flatbuffers.h"
namespace aos {
diff --git a/aos/events/BUILD b/aos/events/BUILD
index 59968c3..7c1abd0 100644
--- a/aos/events/BUILD
+++ b/aos/events/BUILD
@@ -260,6 +260,7 @@
deps = [
":event_loop",
":test_message_fbs",
+ "//aos:realtime",
"//aos/testing:googletest",
],
)
@@ -308,6 +309,7 @@
":aos_logging",
":event_loop",
":simple_channel",
+ "//aos:realtime",
"//aos/events/logging:logger_fbs",
"//aos/ipc_lib:index",
"//aos/network:message_bridge_client_status",
diff --git a/aos/events/event_loop.h b/aos/events/event_loop.h
index 6ffb0fe..dc458e7 100644
--- a/aos/events/event_loop.h
+++ b/aos/events/event_loop.h
@@ -753,6 +753,6 @@
} // namespace aos
-#include "aos/events/event_loop_tmpl.h"
+#include "aos/events/event_loop_tmpl.h" // IWYU pragma: export
#endif // AOS_EVENTS_EVENT_LOOP_H
diff --git a/aos/events/event_loop_param_test.cc b/aos/events/event_loop_param_test.cc
index 248dcd3..d92927b 100644
--- a/aos/events/event_loop_param_test.cc
+++ b/aos/events/event_loop_param_test.cc
@@ -5,6 +5,7 @@
#include <unordered_set>
#include "aos/flatbuffer_merge.h"
+#include "aos/realtime.h"
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -1949,6 +1950,120 @@
aos::Sender<TestMessage> sender = loop1->MakeSender<TestMessage>("/test");
}
+// Tests that a non-realtime event loop timer is marked non-realtime.
+TEST_P(AbstractEventLoopTest, NonRealtimeEventLoopTimer) {
+ auto loop1 = MakePrimary();
+
+ // Add a timer to actually quit.
+ auto test_timer = loop1->AddTimer([this]() {
+ CheckNotRealtime();
+ this->Exit();
+ });
+
+ loop1->OnRun([&test_timer, &loop1]() {
+ CheckNotRealtime();
+ test_timer->Setup(loop1->monotonic_now(), ::std::chrono::milliseconds(100));
+ });
+
+ Run();
+}
+
+// Tests that a realtime event loop timer is marked realtime.
+TEST_P(AbstractEventLoopTest, RealtimeEventLoopTimer) {
+ auto loop1 = MakePrimary();
+
+ loop1->SetRuntimeRealtimePriority(1);
+
+ // Add a timer to actually quit.
+ auto test_timer = loop1->AddTimer([this]() {
+ CheckRealtime();
+ this->Exit();
+ });
+
+ loop1->OnRun([&test_timer, &loop1]() {
+ CheckRealtime();
+ test_timer->Setup(loop1->monotonic_now(), ::std::chrono::milliseconds(100));
+ });
+
+ Run();
+}
+
+// Tests that a non-realtime event loop phased loop is marked non-realtime.
+TEST_P(AbstractEventLoopTest, NonRealtimeEventLoopPhasedLoop) {
+ auto loop1 = MakePrimary();
+
+ // Add a timer to actually quit.
+ loop1->AddPhasedLoop(
+ [this](int) {
+ CheckNotRealtime();
+ this->Exit();
+ },
+ chrono::seconds(1), chrono::seconds(0));
+
+ Run();
+}
+
+// Tests that a realtime event loop phased loop is marked realtime.
+TEST_P(AbstractEventLoopTest, RealtimeEventLoopPhasedLoop) {
+ auto loop1 = MakePrimary();
+
+ loop1->SetRuntimeRealtimePriority(1);
+
+ // Add a timer to actually quit.
+ loop1->AddPhasedLoop(
+ [this](int) {
+ CheckRealtime();
+ this->Exit();
+ },
+ chrono::seconds(1), chrono::seconds(0));
+
+ Run();
+}
+
+// Tests that a non-realtime event loop watcher is marked non-realtime.
+TEST_P(AbstractEventLoopTest, NonRealtimeEventLoopWatcher) {
+ auto loop1 = MakePrimary();
+ auto loop2 = Make();
+
+ aos::Sender<TestMessage> sender = loop2->MakeSender<TestMessage>("/test");
+
+ loop1->OnRun([&]() {
+ aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+ TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+ ASSERT_TRUE(msg.Send(builder.Finish()));
+ });
+
+ loop1->MakeWatcher("/test", [&](const TestMessage &) {
+ CheckNotRealtime();
+ this->Exit();
+ });
+
+ Run();
+}
+
+// Tests that a realtime event loop watcher is marked realtime.
+TEST_P(AbstractEventLoopTest, RealtimeEventLoopWatcher) {
+ auto loop1 = MakePrimary();
+ auto loop2 = Make();
+
+ loop1->SetRuntimeRealtimePriority(1);
+
+ aos::Sender<TestMessage> sender = loop2->MakeSender<TestMessage>("/test");
+
+ loop1->OnRun([&]() {
+ aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+ TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+ ASSERT_TRUE(msg.Send(builder.Finish()));
+ });
+
+ loop1->MakeWatcher("/test", [&](const TestMessage &) {
+ CheckRealtime();
+ this->Exit();
+ });
+
+ Run();
+}
+
// Tests that watchers fail when created on the wrong node.
TEST_P(AbstractEventLoopDeathTest, NodeWatcher) {
EnableNodes("them");
diff --git a/aos/events/logging/log_namer.cc b/aos/events/logging/log_namer.cc
index 474386e..411e666 100644
--- a/aos/events/logging/log_namer.cc
+++ b/aos/events/logging/log_namer.cc
@@ -36,7 +36,8 @@
}
DetachedBufferWriter *LocalLogNamer::MakeWriter(const Channel *channel) {
- CHECK(configuration::ChannelIsSendableOnNode(channel, node()));
+ CHECK(configuration::ChannelIsSendableOnNode(channel, node()))
+ << ": " << configuration::CleanedChannelToString(channel);
return data_writer_.get();
}
diff --git a/aos/events/logging/logfile_sorting.cc b/aos/events/logging/logfile_sorting.cc
index f8f08ba..d3f7f38 100644
--- a/aos/events/logging/logfile_sorting.cc
+++ b/aos/events/logging/logfile_sorting.cc
@@ -30,9 +30,20 @@
std::vector<std::pair<std::string, int>> parts;
};
+ // Struct to hold both the node, and the parts associated with it.
+ struct UnsortedLogPartsMap {
+ std::string logger_node;
+ aos::monotonic_clock::time_point monotonic_start_time =
+ aos::monotonic_clock::min_time;
+ aos::realtime_clock::time_point realtime_start_time =
+ aos::realtime_clock::min_time;
+
+ std::map<std::string, UnsortedLogParts> unsorted_parts;
+ };
+
// Map holding the log_event_uuid -> second map. The second map holds the
// parts_uuid -> list of parts for sorting.
- std::map<std::string, std::map<std::string, UnsortedLogParts>> parts_list;
+ std::map<std::string, UnsortedLogPartsMap> parts_list;
// Sort part files without UUIDs and part indexes as well. Extract everything
// useful from the log in the first pass, then sort later.
@@ -64,6 +75,11 @@
? log_header.message().node()->name()->string_view()
: "";
+ const std::string_view logger_node =
+ log_header.message().has_logger_node()
+ ? log_header.message().logger_node()->name()->string_view()
+ : "";
+
// Looks like an old log. No UUID, index, and also single node. We have
// little to no multi-node log files in the wild without part UUIDs and
// indexes which we care much about.
@@ -99,6 +115,9 @@
CHECK(log_header.message().has_parts_uuid());
CHECK(log_header.message().has_parts_index());
+ CHECK_EQ(log_header.message().has_logger_node(),
+ log_header.message().has_node());
+
const std::string log_event_uuid =
log_header.message().log_event_uuid()->str();
const std::string parts_uuid = log_header.message().parts_uuid()->str();
@@ -108,14 +127,28 @@
if (log_it == parts_list.end()) {
log_it =
parts_list
- .insert(std::make_pair(log_event_uuid,
- std::map<std::string, UnsortedLogParts>()))
+ .insert(std::make_pair(log_event_uuid, UnsortedLogPartsMap()))
.first;
+ log_it->second.logger_node = logger_node;
+ } else {
+ CHECK_EQ(log_it->second.logger_node, logger_node);
}
- auto it = log_it->second.find(parts_uuid);
- if (it == log_it->second.end()) {
- it = log_it->second.insert(std::make_pair(parts_uuid, UnsortedLogParts()))
+ if (node == log_it->second.logger_node) {
+ if (log_it->second.monotonic_start_time ==
+ aos::monotonic_clock::min_time) {
+ log_it->second.monotonic_start_time = monotonic_start_time;
+ log_it->second.realtime_start_time = realtime_start_time;
+ } else {
+ CHECK_EQ(log_it->second.monotonic_start_time, monotonic_start_time);
+ CHECK_EQ(log_it->second.realtime_start_time, realtime_start_time);
+ }
+ }
+
+ auto it = log_it->second.unsorted_parts.find(parts_uuid);
+ if (it == log_it->second.unsorted_parts.end()) {
+ it = log_it->second.unsorted_parts
+ .insert(std::make_pair(parts_uuid, UnsortedLogParts()))
.first;
it->second.monotonic_start_time = monotonic_start_time;
it->second.realtime_start_time = realtime_start_time;
@@ -157,6 +190,8 @@
p.parts.parts.emplace_back(std::move(f.second));
}
log_file.parts.emplace_back(std::move(p.parts));
+ log_file.monotonic_start_time = log_file.parts[0].monotonic_start_time;
+ log_file.realtime_start_time = log_file.parts[0].realtime_start_time;
result.emplace_back(std::move(log_file));
}
@@ -166,11 +201,14 @@
// Now, sort them and produce the final vector form.
std::vector<LogFile> result;
result.reserve(parts_list.size());
- for (std::pair<const std::string, std::map<std::string, UnsortedLogParts>>
- &logs : parts_list) {
+ for (std::pair<const std::string, UnsortedLogPartsMap> &logs : parts_list) {
LogFile new_file;
new_file.log_event_uuid = logs.first;
- for (std::pair<const std::string, UnsortedLogParts> &parts : logs.second) {
+ new_file.logger_node = logs.second.logger_node;
+ new_file.monotonic_start_time = logs.second.monotonic_start_time;
+ new_file.realtime_start_time = logs.second.realtime_start_time;
+ for (std::pair<const std::string, UnsortedLogParts> &parts :
+ logs.second.unsorted_parts) {
LogParts new_parts;
new_parts.monotonic_start_time = parts.second.monotonic_start_time;
new_parts.realtime_start_time = parts.second.realtime_start_time;
@@ -199,6 +237,11 @@
if (!file.log_event_uuid.empty()) {
stream << "\"log_event_uuid\": \"" << file.log_event_uuid << "\", ";
}
+ if (!file.logger_node.empty()) {
+ stream << "\"logger_node\": \"" << file.logger_node << "\", ";
+ }
+ stream << "\"monotonic_start_time\": " << file.monotonic_start_time
+ << ", \"realtime_start_time\": " << file.realtime_start_time << ", [";
stream << "\"parts\": [";
for (size_t i = 0; i < file.parts.size(); ++i) {
if (i != 0u) {
diff --git a/aos/events/logging/logfile_sorting.h b/aos/events/logging/logfile_sorting.h
index 8b59dff..50bfbd7 100644
--- a/aos/events/logging/logfile_sorting.h
+++ b/aos/events/logging/logfile_sorting.h
@@ -36,6 +36,13 @@
// The UUID tying them all together (if available)
std::string log_event_uuid;
+ // The node the logger was running on (if available)
+ std::string logger_node;
+
+ // The start time on the logger node.
+ aos::monotonic_clock::time_point monotonic_start_time;
+ aos::realtime_clock::time_point realtime_start_time;
+
// All the parts, unsorted.
std::vector<LogParts> parts;
};
diff --git a/aos/events/logging/logger_test.cc b/aos/events/logging/logger_test.cc
index 1affce9..57d274b 100644
--- a/aos/events/logging/logger_test.cc
+++ b/aos/events/logging/logger_test.cc
@@ -1127,9 +1127,11 @@
size_t missing_rt_count = 0;
+ std::vector<std::string> logger_nodes;
for (const LogFile &log_file : sorted_parts) {
EXPECT_FALSE(log_file.log_event_uuid.empty());
log_event_uuids.insert(log_file.log_event_uuid);
+ logger_nodes.emplace_back(log_file.logger_node);
both_uuids.insert(log_file.log_event_uuid);
for (const LogParts &part : log_file.parts) {
@@ -1160,6 +1162,16 @@
// (inner vectors all need to be in order, but outer one doesn't matter).
EXPECT_THAT(ToLogReaderVector(sorted_parts),
::testing::UnorderedElementsAreArray(structured_logfiles_));
+
+ EXPECT_THAT(logger_nodes, ::testing::UnorderedElementsAre("pi1", "pi2"));
+
+ EXPECT_NE(sorted_parts[0].realtime_start_time, aos::realtime_clock::min_time);
+ EXPECT_NE(sorted_parts[1].realtime_start_time, aos::realtime_clock::min_time);
+
+ EXPECT_NE(sorted_parts[0].monotonic_start_time,
+ aos::monotonic_clock::min_time);
+ EXPECT_NE(sorted_parts[1].monotonic_start_time,
+ aos::monotonic_clock::min_time);
}
// Tests that if we remap a remapped channel, it shows up correctly.
diff --git a/aos/events/simulated_event_loop.cc b/aos/events/simulated_event_loop.cc
index c429eef..3daabb2 100644
--- a/aos/events/simulated_event_loop.cc
+++ b/aos/events/simulated_event_loop.cc
@@ -9,6 +9,7 @@
#include "aos/events/aos_logging.h"
#include "aos/events/simulated_network_bridge.h"
#include "aos/json_to_flatbuffer.h"
+#include "aos/realtime.h"
#include "aos/util/phased_loop.h"
namespace aos {
@@ -19,6 +20,16 @@
namespace {
+class ScopedMarkRealtimeRestorer {
+ public:
+ ScopedMarkRealtimeRestorer(bool rt) : rt_(rt), prior_(MarkRealtime(rt)) {}
+ ~ScopedMarkRealtimeRestorer() { CHECK_EQ(rt_, MarkRealtime(prior_)); }
+
+ private:
+ const bool rt_;
+ const bool prior_;
+};
+
// Container for both a message, and the context for it for simulation. This
// makes tracking the timestamps associated with the data easy.
struct SimulatedMessage final {
@@ -544,7 +555,10 @@
}
void OnRun(::std::function<void()> on_run) override {
- scheduler_->ScheduleOnRun(on_run);
+ scheduler_->ScheduleOnRun([this, on_run = std::move(on_run)]() {
+ ScopedMarkRealtimeRestorer rt(priority() > 0);
+ on_run();
+ });
}
const Node *node() const override { return node_; }
@@ -737,7 +751,10 @@
context.realtime_remote_time = context.realtime_event_time;
}
- DoCallCallback([monotonic_now]() { return monotonic_now; }, context);
+ {
+ ScopedMarkRealtimeRestorer rt(simulated_event_loop_->priority() > 0);
+ DoCallCallback([monotonic_now]() { return monotonic_now; }, context);
+ }
msgs_.pop_front();
if (token_ != scheduler_->InvalidToken()) {
@@ -855,7 +872,10 @@
simulated_event_loop_->AddEvent(&event_);
}
- Call([monotonic_now]() { return monotonic_now; }, monotonic_now);
+ {
+ ScopedMarkRealtimeRestorer rt(simulated_event_loop_->priority() > 0);
+ Call([monotonic_now]() { return monotonic_now; }, monotonic_now);
+ }
}
void SimulatedTimerHandler::Disable() {
@@ -891,9 +911,14 @@
if (simulated_event_loop_->log_impl_) {
prev_logger.Swap(simulated_event_loop_->log_impl_);
}
- Call(
- [monotonic_now]() { return monotonic_now; },
- [this](monotonic_clock::time_point sleep_time) { Schedule(sleep_time); });
+
+ {
+ ScopedMarkRealtimeRestorer rt(simulated_event_loop_->priority() > 0);
+ Call([monotonic_now]() { return monotonic_now; },
+ [this](monotonic_clock::time_point sleep_time) {
+ Schedule(sleep_time);
+ });
+ }
}
void SimulatedPhasedLoopHandler::Schedule(
diff --git a/aos/flatbuffers.h b/aos/flatbuffers.h
index 5da7466..b619452 100644
--- a/aos/flatbuffers.h
+++ b/aos/flatbuffers.h
@@ -7,7 +7,7 @@
#include "absl/types/span.h"
#include "aos/containers/resizeable_buffer.h"
#include "aos/macros.h"
-#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/flatbuffers.h" // IWYU pragma: export
#include "glog/logging.h"
namespace aos {
@@ -264,9 +264,12 @@
}
void Reset() {
- CHECK(!allocator_.is_allocated()) << ": May not reset while building";
+ CHECK(!allocator_.is_allocated() || data_ != nullptr)
+ << ": May not reset while building";
fbb_ = flatbuffers::FlatBufferBuilder(Size, &allocator_);
fbb_.ForceDefaults(true);
+ data_ = nullptr;
+ size_ = 0;
}
flatbuffers::FlatBufferBuilder *fbb() {
diff --git a/aos/network/message_bridge_server_lib.cc b/aos/network/message_bridge_server_lib.cc
index b272c29..b80547d 100644
--- a/aos/network/message_bridge_server_lib.cc
+++ b/aos/network/message_bridge_server_lib.cc
@@ -248,6 +248,12 @@
const Channel *const timestamp_channel = configuration::GetChannel(
event_loop_->configuration(), "/aos", Timestamp::GetFullyQualifiedName(),
event_loop_->name(), event_loop_->node());
+ CHECK(timestamp_channel != nullptr)
+ << ": Failed to find timestamp channel {\"name\": \"/aos\", \"type\": \""
+ << Timestamp::GetFullyQualifiedName() << "\"}";
+ CHECK(configuration::ChannelIsSendableOnNode(timestamp_channel,
+ event_loop_->node()))
+ << ": Timestamp channel is not sendable on this node.";
for (const Channel *channel : *event_loop_->configuration()->channels()) {
CHECK(channel->has_source_node());
@@ -302,11 +308,17 @@
timestamp_state_ = state.get();
}
channels_.emplace_back(std::move(state));
+ } else if (channel == timestamp_channel) {
+ std::unique_ptr<ChannelState> state(
+ new ChannelState{channel, channel_index});
+ timestamp_state_ = state.get();
+ channels_.emplace_back(std::move(state));
} else {
channels_.emplace_back(nullptr);
}
++channel_index;
}
+ CHECK(timestamp_state_ != nullptr);
// Buffer up the max size a bit so everything fits nicely.
LOG(INFO) << "Max message size for all clients is " << max_size;
diff --git a/aos/realtime.cc b/aos/realtime.cc
index a41eb0d..9df7aca 100644
--- a/aos/realtime.cc
+++ b/aos/realtime.cc
@@ -1,18 +1,19 @@
#include "aos/realtime.h"
+#include <errno.h>
+#include <malloc.h>
+#include <sched.h>
+#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
-#include <errno.h>
-#include <sched.h>
+#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <unistd.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <sys/prctl.h>
-#include <malloc.h>
+#include "aos/thread_local.h"
#include "glog/logging.h"
namespace FLAG__namespace_do_not_use_directly_use_DECLARE_double_instead {
@@ -68,6 +69,7 @@
} // namespace
void LockAllMemory() {
+ CheckNotRealtime();
// Allow locking as much as we want into RAM.
SetSoftRLimit(RLIMIT_MEMLOCK, RLIM_INFINITY, SetLimitForRoot::kNo);
@@ -101,6 +103,7 @@
}
void InitRT() {
+ CheckNotRealtime();
LockAllMemory();
// Only let rt processes run for 3 seconds straight.
@@ -114,6 +117,7 @@
struct sched_param param;
param.sched_priority = 0;
PCHECK(sched_setscheduler(0, SCHED_OTHER, ¶m) == 0);
+ MarkRealtime(false);
}
void SetCurrentThreadAffinity(const cpu_set_t &cpuset) {
@@ -141,6 +145,7 @@
struct sched_param param;
param.sched_priority = priority;
+ MarkRealtime(true);
PCHECK(sched_setscheduler(0, SCHED_FIFO, ¶m) == 0)
<< ": changing to SCHED_FIFO with " << priority;
}
@@ -155,4 +160,20 @@
AllowSoftLimitDecrease::kNo);
}
+namespace {
+AOS_THREAD_LOCAL bool is_realtime = false;
+}
+
+bool MarkRealtime(bool realtime) {
+ const bool prior = is_realtime;
+ is_realtime = realtime;
+ return prior;
+}
+
+void CheckRealtime() { CHECK(is_realtime); }
+
+void CheckNotRealtime() { CHECK(!is_realtime); }
+
+ScopedRealtimeRestorer::ScopedRealtimeRestorer() : prior_(is_realtime) {}
+
} // namespace aos
diff --git a/aos/realtime.h b/aos/realtime.h
index 6e0a472..52db4a8 100644
--- a/aos/realtime.h
+++ b/aos/realtime.h
@@ -4,6 +4,8 @@
#include <sched.h>
#include <string_view>
+#include "glog/logging.h"
+
namespace aos {
// Locks everything into memory and sets the limits. This plus InitNRT are
@@ -34,6 +36,55 @@
void ExpandStackSize();
+// CHECKs that we are (or are not) running on the RT scheduler. Useful for
+// enforcing that operations which are or are not bounded shouldn't be run. This
+// works both in simulation and when running against the real target.
+void CheckRealtime();
+void CheckNotRealtime();
+
+// Marks that we are or are not running on the realtime scheduler. Returns the
+// previous state.
+//
+// Note: this shouldn't be used directly. The event loop primitives should be
+// used instead.
+bool MarkRealtime(bool realtime);
+
+// Class which restores the current RT state when destructed.
+class ScopedRealtimeRestorer {
+ public:
+ ScopedRealtimeRestorer();
+ ~ScopedRealtimeRestorer() { MarkRealtime(prior_); }
+
+ private:
+ const bool prior_;
+};
+
+// Class which marks us as on the RT scheduler until it goes out of scope.
+// Note: this shouldn't be needed for most applications.
+class ScopedRealtime {
+ public:
+ ScopedRealtime() : prior_(MarkRealtime(true)) {}
+ ~ScopedRealtime() {
+ CHECK(MarkRealtime(prior_)) << ": Priority was modified";
+ }
+
+ private:
+ const bool prior_;
+};
+
+// Class which marks us as not on the RT scheduler until it goes out of scope.
+// Note: this shouldn't be needed for most applications.
+class ScopedNotRealtime {
+ public:
+ ScopedNotRealtime() : prior_(MarkRealtime(false)) {}
+ ~ScopedNotRealtime() {
+ CHECK(!MarkRealtime(prior_)) << ": Priority was modified";
+ }
+
+ private:
+ const bool prior_;
+};
+
} // namespace aos
#endif // AOS_REALTIME_H_
diff --git a/aos/realtime_test.cc b/aos/realtime_test.cc
new file mode 100644
index 0000000..e77f140
--- /dev/null
+++ b/aos/realtime_test.cc
@@ -0,0 +1,76 @@
+#include "aos/realtime.h"
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace testing {
+
+// Tests that ScopedRealtime handles the simple case.
+TEST(RealtimeTest, ScopedRealtime) {
+ CheckNotRealtime();
+ {
+ ScopedRealtime rt;
+ CheckRealtime();
+ }
+ CheckNotRealtime();
+}
+
+// Tests that ScopedRealtime handles nesting.
+TEST(RealtimeTest, DoubleScopedRealtime) {
+ CheckNotRealtime();
+ {
+ ScopedRealtime rt;
+ CheckRealtime();
+ {
+ ScopedRealtime rt2;
+ CheckRealtime();
+ }
+ CheckRealtime();
+ }
+ CheckNotRealtime();
+}
+
+// Tests that ScopedRealtime handles nesting with ScopedNotRealtime.
+TEST(RealtimeTest, ScopedNotRealtime) {
+ CheckNotRealtime();
+ {
+ ScopedRealtime rt;
+ CheckRealtime();
+ {
+ ScopedNotRealtime nrt;
+ CheckNotRealtime();
+ }
+ CheckRealtime();
+ }
+ CheckNotRealtime();
+}
+
+// Tests that ScopedRealtimeRestorer works both when starting RT and nonrt.
+TEST(RealtimeTest, ScopedRealtimeRestorer) {
+ CheckNotRealtime();
+ {
+ ScopedRealtime rt;
+ CheckRealtime();
+ {
+ ScopedRealtimeRestorer restore;
+ CheckRealtime();
+
+ MarkRealtime(false);
+ CheckNotRealtime();
+ }
+ CheckRealtime();
+ }
+ CheckNotRealtime();
+
+ {
+ ScopedRealtimeRestorer restore;
+ CheckNotRealtime();
+
+ MarkRealtime(true);
+ CheckRealtime();
+ }
+ CheckNotRealtime();
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/third_party/gmp/BUILD b/third_party/gmp/BUILD
index b60c349..0ac8edc 100644
--- a/third_party/gmp/BUILD
+++ b/third_party/gmp/BUILD
@@ -47,6 +47,9 @@
],
}
+# gmp's tools leak memory on purpose. Just skip asan for them.
+tool_features = ["-asan"]
+
genrule(
name = "gmp_h_copy",
srcs = file_from_architecture(architecture_paths, "gmp.h"),
@@ -112,6 +115,7 @@
name = "gen-fac",
srcs = ["gen-fac.c"],
copts = copts,
+ features = tool_features,
deps = [":bootstrap"],
)
@@ -133,6 +137,7 @@
name = "gen-fib",
srcs = ["gen-fib.c"],
copts = copts,
+ features = tool_features,
deps = [":bootstrap"],
)
@@ -154,6 +159,7 @@
name = "gen-bases",
srcs = ["gen-bases.c"],
copts = copts,
+ features = tool_features,
deps = [":bootstrap"],
)
@@ -175,6 +181,7 @@
name = "gen-trialdivtab",
srcs = ["gen-trialdivtab.c"],
copts = copts,
+ features = tool_features,
deps = [":bootstrap"],
)