Merge "Fixed failing 2015 bot3 test."
diff --git a/aos/build/aos.gyp b/aos/build/aos.gyp
index 2c44ed6..bbdb2c9 100644
--- a/aos/build/aos.gyp
+++ b/aos/build/aos.gyp
@@ -16,6 +16,7 @@
['OS=="linux"', {
'sources': [
'<(AOS)/linux_code/logging/linux_interface.cc',
+ '<(AOS)/common/logging/context.cc',
],
'dependencies': [
'<(AOS)/linux_code/linux_code.gyp:complex_thread_local',
diff --git a/aos/build/aos_all.gyp b/aos/build/aos_all.gyp
index 195997a..b546087 100644
--- a/aos/build/aos_all.gyp
+++ b/aos/build/aos_all.gyp
@@ -37,6 +37,7 @@
'<(AOS)/common/common.gyp:once_test',
'<(AOS)/common/common.gyp:event_test',
'<(AOS)/common/common.gyp:queue_testutils_test',
+ '<(AOS)/common/common.gyp:transaction_test',
'<(AOS)/common/logging/logging.gyp:logging_impl_test',
'<(AOS)/common/util/util.gyp:options_test',
'<(AOS)/common/common.gyp:queue_test',
diff --git a/aos/common/BUILD b/aos/common/BUILD
index 1691e22..4262b60 100644
--- a/aos/common/BUILD
+++ b/aos/common/BUILD
@@ -89,12 +89,23 @@
)
cc_library(
- name = 'event',
+ name = 'real_event',
+ visibility = ['//aos/linux_code/ipc_lib:__pkg__'],
hdrs = [
'event.h',
],
deps = [
'//aos/linux_code/ipc_lib:aos_sync',
+ ':time',
+ ],
+)
+
+cc_library(
+ # Use this one!
+ # TODO(Brian): Remove this mess...
+ name = 'event',
+ deps = [
+ '//aos/linux_code/ipc_lib:event',
],
)
@@ -301,19 +312,29 @@
],
deps = [
'//aos/testing:googletest',
- '//aos/linux_code/ipc_lib:event',
+ ':event',
':queue_testutils',
':time',
],
)
cc_library(
- name = 'condition',
+ name = 'real_condition',
+ visibility = ['//aos/linux_code/ipc_lib:__pkg__'],
hdrs = [
'condition.h',
],
)
+cc_library(
+ # Use this one!
+ # TODO(Brian): Remove this mess...
+ name = 'condition',
+ deps = [
+ '//aos/linux_code/ipc_lib:condition',
+ ],
+)
+
cc_test(
name = 'condition_test',
srcs = [
@@ -321,7 +342,7 @@
],
deps = [
'//aos/testing:googletest',
- '//aos/linux_code/ipc_lib:condition',
+ ':condition',
'//aos/common/util:thread',
':time',
'//aos/linux_code/ipc_lib:mutex',
@@ -379,3 +400,27 @@
':die',
],
)
+
+cc_library(
+ name = 'transaction',
+ hdrs = [
+ 'transaction.h',
+ ],
+ deps = [
+ '//aos/common/logging:logging_interface',
+ '//aos/common/util:compiler_memory_barrier',
+ ],
+)
+
+cc_test(
+ name = 'transaction_test',
+ srcs = [
+ 'transaction_test.cc',
+ ],
+ deps = [
+ ':transaction',
+ '//aos/testing:googletest',
+ '//aos/common/logging',
+ '//aos/common/util:death_test_log_implementation',
+ ],
+)
diff --git a/aos/common/actions/BUILD b/aos/common/actions/BUILD
index 6669ef1..de13464 100644
--- a/aos/common/actions/BUILD
+++ b/aos/common/actions/BUILD
@@ -54,6 +54,6 @@
'//aos/common:queues',
'//aos/common:time',
':action_queue',
- '//aos/linux_code/ipc_lib:event',
+ '//aos/common:event',
],
)
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
index c9e1b6a..14619ad 100644
--- a/aos/common/common.gyp
+++ b/aos/common/common.gyp
@@ -253,10 +253,12 @@
],
'dependencies': [
'<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+ 'time',
'<(AOS)/build/aos.gyp:logging_interface',
],
'export_dependent_settings': [
'<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+ 'time',
],
},
{
@@ -361,5 +363,31 @@
'die',
],
},
+ {
+ 'target_name': 'transaction',
+ 'type': 'static_library',
+ 'sources': [
+ #'transaction.h',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging_interface',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/build/aos.gyp:logging_interface',
+ ],
+ },
+ {
+ 'target_name': 'transaction_test',
+ 'type': 'executable',
+ 'sources': [
+ 'transaction_test.cc',
+ ],
+ 'dependencies': [
+ 'transaction',
+ '<(EXTERNALS):gtest',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/util/util.gyp:death_test_log_implementation',
+ ],
+ },
],
}
diff --git a/aos/common/event.h b/aos/common/event.h
index 9839d41..b3ee87b 100644
--- a/aos/common/event.h
+++ b/aos/common/event.h
@@ -1,6 +1,8 @@
#ifndef AOS_COMMON_EVENT_H_
#define AOS_COMMON_EVENT_H_
+#include "aos/common/time.h"
+
#include "aos/linux_code/ipc_lib/aos_sync.h"
namespace aos {
@@ -37,6 +39,11 @@
// Waits for the event to be set. Returns immediately if it is already set.
void Wait();
+ // Waits for the event to be set or until timeout has elapsed. Returns
+ // immediately if it is already set.
+ // Returns true if the event was Set or false if the timeout expired.
+ bool WaitTimeout(const ::aos::time::Time &timeout);
+
// Wakes up all Wait()ers and sets the event (atomically).
void Set();
// Unsets the event so future Wait() callers will block instead of returning
diff --git a/aos/common/event_test.cc b/aos/common/event_test.cc
index d8671eb..7331309 100644
--- a/aos/common/event_test.cc
+++ b/aos/common/event_test.cc
@@ -12,7 +12,7 @@
class EventTest : public ::testing::Test {
public:
- Event test_event;
+ Event test_event_;
protected:
void SetUp() override {
@@ -22,13 +22,13 @@
// Makes sure that basic operations with no blocking or anything work.
TEST_F(EventTest, Basic) {
- EXPECT_FALSE(test_event.Clear());
- EXPECT_FALSE(test_event.Clear());
+ EXPECT_FALSE(test_event_.Clear());
+ EXPECT_FALSE(test_event_.Clear());
- test_event.Set();
- test_event.Wait();
- EXPECT_TRUE(test_event.Clear());
- EXPECT_FALSE(test_event.Clear());
+ test_event_.Set();
+ test_event_.Wait();
+ EXPECT_TRUE(test_event_.Clear());
+ EXPECT_FALSE(test_event_.Clear());
}
// Tests that tsan understands that events establish a happens-before
@@ -36,13 +36,13 @@
TEST_F(EventTest, ThreadSanitizer) {
for (int i = 0; i < 3000; ++i) {
int variable = 0;
- test_event.Clear();
+ test_event_.Clear();
::std::thread thread([this, &variable]() {
- test_event.Wait();
+ test_event_.Wait();
--variable;
});
++variable;
- test_event.Set();
+ test_event_.Set();
thread.join();
EXPECT_EQ(0, variable);
}
@@ -56,13 +56,33 @@
::std::thread thread([this, &start_time, &finish_time, &started]() {
start_time = time::Time::Now();
started.Set();
- test_event.Wait();
+ test_event_.Wait();
finish_time = time::Time::Now();
});
static const time::Time kWaitTime = time::Time::InSeconds(0.05);
started.Wait();
time::SleepFor(kWaitTime);
- test_event.Set();
+ test_event_.Set();
+ thread.join();
+ EXPECT_GE(finish_time - start_time, kWaitTime);
+}
+
+TEST_F(EventTest, WaitTimeout) {
+ EXPECT_FALSE(test_event_.WaitTimeout(time::Time::InSeconds(0.05)));
+
+ time::Time start_time, finish_time;
+ // Without this, it sometimes manages to fail under tsan.
+ Event started;
+ ::std::thread thread([this, &start_time, &finish_time, &started]() {
+ start_time = time::Time::Now();
+ started.Set();
+ EXPECT_TRUE(test_event_.WaitTimeout(time::Time::InSeconds(0.5)));
+ finish_time = time::Time::Now();
+ });
+ static const time::Time kWaitTime = time::Time::InSeconds(0.05);
+ started.Wait();
+ time::SleepFor(kWaitTime);
+ test_event_.Set();
thread.join();
EXPECT_GE(finish_time - start_time, kWaitTime);
}
diff --git a/aos/common/logging/BUILD b/aos/common/logging/BUILD
index 9223c44..78d1cbd 100644
--- a/aos/common/logging/BUILD
+++ b/aos/common/logging/BUILD
@@ -71,14 +71,11 @@
srcs = [
'logging_interface.cc',
],
- copts = [
- # TODO(austin): This is wrong.
- '-Wno-error=format-nonliteral'
- ],
deps = [
'//aos/common:die',
'//aos/common/libc:aos_strerror',
'//aos/linux_code/logging:linux_interface',
+ '//aos/common:macros',
],
)
diff --git a/aos/common/logging/logging_impl.h b/aos/common/logging/logging_impl.h
index ab4b728..5fdeec7 100644
--- a/aos/common/logging/logging_impl.h
+++ b/aos/common/logging/logging_impl.h
@@ -106,18 +106,6 @@
return LOG_UNKNOWN;
}
-// Takes a message and logs it. It will set everything up and then call DoLog
-// for the current LogImplementation.
-void VLog(log_level level, const char *format, va_list ap)
- __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 2, 0)));
-// Adds to the saved up message.
-void VCork(int line, const char *function, const char *format, va_list ap)
- __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0)));
-// Actually logs the saved up message.
-void VUnCork(int line, const char *function, log_level level, const char *file,
- const char *format, va_list ap)
- __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 5, 0)));
-
// Will call VLog with the given arguments for the next logger in the chain.
void LogNext(log_level level, const char *format, ...)
__attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 2, 3)));
@@ -217,18 +205,6 @@
// Prints message to output.
void PrintMessage(FILE *output, const LogMessage &message);
-// Prints format (with ap) into output and correctly deals with the result
-// being too long etc.
-size_t ExecuteFormat(char *output, size_t output_size, const char *format,
- va_list ap)
- __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0)));
-
-// Runs the given function with the current LogImplementation (handles switching
-// it out while running function etc).
-// levels is how many LogImplementations to not use off the stack.
-void RunWithCurrentImplementation(
- int levels, ::std::function<void(LogImplementation *)> function);
-
} // namespace internal
} // namespace logging
} // namespace aos
diff --git a/aos/common/logging/logging_interface.h b/aos/common/logging/logging_interface.h
index af591ee..97587bf 100644
--- a/aos/common/logging/logging_interface.h
+++ b/aos/common/logging/logging_interface.h
@@ -7,6 +7,7 @@
#include <functional>
#include "aos/common/logging/logging.h"
+#include "aos/common/macros.h"
namespace aos {
@@ -17,6 +18,18 @@
namespace aos {
namespace logging {
+// Takes a message and logs it. It will set everything up and then call DoLog
+// for the current LogImplementation.
+void VLog(log_level level, const char *format, va_list ap)
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 2, 0)));
+// Adds to the saved up message.
+void VCork(int line, const char *function, const char *format, va_list ap)
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0)));
+// Actually logs the saved up message.
+void VUnCork(int line, const char *function, log_level level, const char *file,
+ const char *format, va_list ap)
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 5, 0)));
+
// Represents a system that can actually take log messages and do something
// useful with them.
// All of the code (transitively too!) in the DoLog here can make
@@ -90,6 +103,21 @@
LogImplementation *next_;
};
+namespace internal {
+
+// Prints format (with ap) into output and correctly deals with the result
+// being too long etc.
+size_t ExecuteFormat(char *output, size_t output_size, const char *format,
+ va_list ap)
+ __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0)));
+
+// Runs the given function with the current LogImplementation (handles switching
+// it out while running function etc).
+// levels is how many LogImplementations to not use off the stack.
+void RunWithCurrentImplementation(
+ int levels, ::std::function<void(LogImplementation *)> function);
+
+} // namespace internal
} // namespace logging
} // namespace aos
diff --git a/aos/common/time.cc b/aos/common/time.cc
index 7318afb..d1985b5 100644
--- a/aos/common/time.cc
+++ b/aos/common/time.cc
@@ -38,7 +38,8 @@
static_cast<uintmax_t>(clock), &temp);
}
- const timespec offset = (global_core == nullptr)
+ const timespec offset = (&global_core == nullptr || global_core == nullptr ||
+ global_core->mem_struct == nullptr)
? timespec{0, 0}
: global_core->mem_struct->time_offset;
return Time(temp) + Time(offset);
@@ -223,7 +224,9 @@
}
void OffsetToNow(const Time &now) {
+ CHECK_NOTNULL(&global_core);
CHECK_NOTNULL(global_core);
+ CHECK_NOTNULL(global_core->mem_struct);
global_core->mem_struct->time_offset.tv_nsec = 0;
global_core->mem_struct->time_offset.tv_sec = 0;
global_core->mem_struct->time_offset = (now - Time::Now()).ToTimespec();
diff --git a/aos/common/transaction.h b/aos/common/transaction.h
new file mode 100644
index 0000000..e6e9e41
--- /dev/null
+++ b/aos/common/transaction.h
@@ -0,0 +1,103 @@
+#ifndef AOS_COMMON_TRANSACTION_H_
+#define AOS_COMMON_TRANSACTION_H_
+
+#include <stdint.h>
+
+#include <array>
+
+#include "aos/common/util/compiler_memory_barrier.h"
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+namespace transaction {
+
+// Manages a LIFO stack of Work objects. Designed to help implement transactions
+// by providing a safe way to undo things etc.
+//
+// number_works Work objects are created statically and then Create is called on
+// each as it is added to the stack. When the work should do whatever it does,
+// DoWork() will be called. The work objects get no notification when they are
+// dropped off of the stack.
+//
+// Work::DoWork() must be idempotent because it may get called multiple times if
+// CompleteWork() is interrupted part of the way through.
+//
+// This class handles compiler memory barriers etc to make sure only fully
+// created works are ever invoked, and each work will be fully created by the
+// time AddWork returns. This does not mean it's safe for multiple threads to
+// interact with an instance of this class at the same time.
+template <class Work, int number_works>
+class WorkStack {
+ public:
+ // Calls DoWork() on all the works that have been added and then removes them
+ // all from the stack.
+ void CompleteWork() {
+ int current = stack_index_;
+ while (current > 0) {
+ stack_.at(--current).DoWork();
+ }
+ aos_compiler_memory_barrier();
+ stack_index_ = 0;
+ aos_compiler_memory_barrier();
+ }
+
+ // Drops all works that have been added.
+ void DropWork() {
+ stack_index_ = 0;
+ aos_compiler_memory_barrier();
+ }
+
+ // Returns true if we have any works to complete right now.
+ bool HasWork() const { return stack_index_ != 0; }
+
+ // Forwards all of its arguments to Work::Create, which it calls on the next
+ // work to be added.
+ template <class... A>
+ void AddWork(A &&... a) {
+ if (stack_index_ >= number_works) {
+ LOG(FATAL, "too many works\n");
+ }
+ stack_.at(stack_index_).Create(::std::forward<A>(a)...);
+ aos_compiler_memory_barrier();
+ ++stack_index_;
+ aos_compiler_memory_barrier();
+ }
+
+ private:
+ // The next index into stack_ for a new work to be added.
+ int stack_index_ = 0;
+ ::std::array<Work, number_works> stack_;
+};
+
+// When invoked, sets *pointer to the value it had when this work was Created.
+template <class T>
+class RestoreValueWork {
+ public:
+ void Create(T *pointer) {
+ pointer_ = pointer;
+ value_ = *pointer;
+ }
+ void DoWork() {
+ *pointer_ = value_;
+ }
+
+ private:
+ T *pointer_;
+ T value_;
+};
+
+// Handles the casting necessary to restore any kind of pointer.
+class RestorePointerWork : public RestoreValueWork<void *> {
+ public:
+ template <class T>
+ void Create(T **pointer) {
+ static_assert(sizeof(T *) == sizeof(void *),
+ "that's a weird pointer");
+ RestoreValueWork<void *>::Create(reinterpret_cast<void **>(pointer));
+ }
+};
+
+} // namespace transaction
+} // namespace aos
+
+#endif // AOS_COMMON_TRANSACTION_H_
diff --git a/aos/common/transaction_test.cc b/aos/common/transaction_test.cc
new file mode 100644
index 0000000..e74fd69
--- /dev/null
+++ b/aos/common/transaction_test.cc
@@ -0,0 +1,106 @@
+#include "aos/common/transaction.h"
+
+#include <vector>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/util/death_test_log_implementation.h"
+
+namespace aos {
+namespace transaction {
+namespace testing {
+
+class WorkStackTest : public ::testing::Test {
+ public:
+ // Contains an index which it adds to the created_works and invoked_works
+ // vectors of its containing WorkStackTest.
+ class TestWork {
+ public:
+ void Create(WorkStackTest *test, int i) {
+ test->created_works()->push_back(i);
+ i_ = i;
+ test_ = test;
+ }
+ void DoWork() {
+ test_->invoked_works()->push_back(i_);
+ }
+
+ int i() const { return i_; }
+
+ private:
+ int i_;
+ WorkStackTest *test_;
+ };
+
+ ::std::vector<int> *created_works() { return &created_works_; }
+ ::std::vector<int> *invoked_works() { return &invoked_works_; }
+ WorkStack<TestWork, 20> *work_stack() { return &work_stack_; }
+
+ // Creates a TestWork with index i and adds it to work_stack().
+ void CreateWork(int i) {
+ work_stack_.AddWork(this, i);
+ }
+
+ private:
+ ::std::vector<int> created_works_, invoked_works_;
+ WorkStack<TestWork, 20> work_stack_;
+};
+
+typedef WorkStackTest WorkStackDeathTest;
+
+TEST_F(WorkStackTest, Basic) {
+ EXPECT_FALSE(work_stack()->HasWork());
+ EXPECT_EQ(0u, created_works()->size());
+ EXPECT_EQ(0u, invoked_works()->size());
+
+ CreateWork(971);
+ EXPECT_TRUE(work_stack()->HasWork());
+ EXPECT_EQ(1u, created_works()->size());
+ EXPECT_EQ(0u, invoked_works()->size());
+ EXPECT_EQ(971, created_works()->at(0));
+
+ work_stack()->CompleteWork();
+ EXPECT_FALSE(work_stack()->HasWork());
+ EXPECT_EQ(1u, created_works()->size());
+ EXPECT_EQ(1u, invoked_works()->size());
+ EXPECT_EQ(971, invoked_works()->at(0));
+}
+
+TEST_F(WorkStackTest, DropWork) {
+ CreateWork(971);
+ CreateWork(254);
+ EXPECT_EQ(2u, created_works()->size());
+
+ work_stack()->DropWork();
+ EXPECT_FALSE(work_stack()->HasWork());
+ work_stack()->CompleteWork();
+ EXPECT_EQ(0u, invoked_works()->size());
+}
+
+// Tests that the works get run in the correct order.
+TEST_F(WorkStackTest, InvocationOrder) {
+ CreateWork(971);
+ CreateWork(254);
+ CreateWork(1678);
+
+ work_stack()->CompleteWork();
+ EXPECT_EQ((::std::vector<int>{971, 254, 1678}), *created_works());
+ EXPECT_EQ((::std::vector<int>{1678, 254, 971}), *invoked_works());
+}
+
+// Tests that it handles adding too many works intelligently.
+TEST_F(WorkStackDeathTest, TooManyWorks) {
+ logging::Init();
+ EXPECT_DEATH(
+ {
+ logging::AddImplementation(new util::DeathTestLogImplementation());
+ for (int i = 0; i < 1000; ++i) {
+ CreateWork(i);
+ }
+ },
+ ".*too many works.*");
+}
+
+} // namespace testing
+} // namespace transaction
+} // namespace aos
diff --git a/aos/common/util/BUILD b/aos/common/util/BUILD
index 646f3bf..bd70463 100644
--- a/aos/common/util/BUILD
+++ b/aos/common/util/BUILD
@@ -172,3 +172,10 @@
'//aos/testing:googletest',
],
)
+
+cc_library(
+ name = 'compiler_memory_barrier',
+ hdrs = [
+ 'compiler_memory_barrier.h',
+ ],
+)
diff --git a/aos/common/util/compiler_memory_barrier.h b/aos/common/util/compiler_memory_barrier.h
new file mode 100644
index 0000000..5126941
--- /dev/null
+++ b/aos/common/util/compiler_memory_barrier.h
@@ -0,0 +1,11 @@
+#ifndef AOS_COMMON_UTIL_COMPILER_MEMORY_BARRIER_H_
+#define AOS_COMMON_UTIL_COMPILER_MEMORY_BARRIER_H_
+
+// Prevents the compiler from reordering memory operations around this.
+// Using this function makes it clearer what you're doing and easier to be
+// portable.
+static inline void aos_compiler_memory_barrier(void) {
+ __asm__ __volatile__("" ::: "memory");
+}
+
+#endif // AOS_COMMON_UTIL_COMPILER_MEMORY_BARRIER_H_
diff --git a/aos/linux_code/ipc_lib/BUILD b/aos/linux_code/ipc_lib/BUILD
index 47b9e8e..576774e 100644
--- a/aos/linux_code/ipc_lib/BUILD
+++ b/aos/linux_code/ipc_lib/BUILD
@@ -125,12 +125,13 @@
cc_library(
name = 'condition',
+ visibility = ['//aos/common:__pkg__'],
srcs = [
'condition.cc',
],
deps = [
':mutex',
- '//aos/common:condition',
+ '//aos/common:real_condition',
':aos_sync',
'//aos/common/logging:logging_interface',
],
@@ -151,12 +152,13 @@
cc_library(
name = 'event',
+ visibility = ['//aos/common:__pkg__'],
srcs = [
'event.cc',
],
deps = [
':aos_sync',
'//aos/common/logging:logging_interface',
- '//aos/common:event',
+ '//aos/common:real_event',
],
)
diff --git a/aos/linux_code/ipc_lib/event.cc b/aos/linux_code/ipc_lib/event.cc
index e95b45e..ae32d39 100644
--- a/aos/linux_code/ipc_lib/event.cc
+++ b/aos/linux_code/ipc_lib/event.cc
@@ -20,6 +20,18 @@
PLOG(FATAL, "futex_wait(%p) failed", &impl_);
}
+bool Event::WaitTimeout(const ::aos::time::Time &timeout) {
+ const auto timeout_timespec = timeout.ToTimespec();
+ int ret;
+ do {
+ ret = futex_wait_timeout(&impl_, &timeout_timespec);
+ } while (ret == 1);
+ if (ret == 0) return true;
+ if (ret == 2) return false;
+ CHECK_EQ(-1, ret);
+ PLOG(FATAL, "futex_wait(%p) failed", &impl_);
+}
+
// We're not going to expose the number woken because that's not easily portable
// to condition variable-based implementations.
void Event::Set() {
diff --git a/aos/linux_code/queue-tmpl.h b/aos/linux_code/queue-tmpl.h
index 25eb8d9..1362515 100644
--- a/aos/linux_code/queue-tmpl.h
+++ b/aos/linux_code/queue-tmpl.h
@@ -102,6 +102,7 @@
T *Queue<T>::MakeRawMessage() {
T *ret = static_cast<T *>(queue_->GetMessage());
assert(ret != NULL);
+ ret->Zero();
return ret;
}
@@ -109,7 +110,6 @@
aos::MessageBuilder<T> Queue<T>::MakeWithBuilder() {
Init();
T *const ret = MakeRawMessage();
- ret->Zero();
return aos::MessageBuilder<T>(queue_, ret);
}
diff --git a/tools/bazel.rc b/tools/bazel.rc
index 1b77288..5ea0fcf 100644
--- a/tools/bazel.rc
+++ b/tools/bazel.rc
@@ -9,6 +9,8 @@
# TODO(Brian): Fix Bazel so this actually does something again...
build --hdrs_check=strict
+build --crosstool_top=//tools/cpp:toolchain
+
# Show paths to a few more than just 1 target.
build --show_result 15
# Dump the output of the failing test to stdout.