Make AOS logging per thread, not global
We noticed that if 2 threads use AOS_LOG, they occasionally crash. The
stack trace looked like 2 messages were being constructed
simultaneously. Digging in, this is possible because there is 1 global
logger which gets passed (patchily) to the per-thread context.
We've got a bunch of different cases and (reasonable) ways to handle
them when ShmEventLoop comes into play.
Simple single threaded appliction:
* Outside Run() -> stderr
* Inside Run() -> /aos
Single event loop appliction with extra threads
* Outside Run() -> stderr
* Inside Run() -> /aos
* Other threads -> stderr
Multiple event loop appliction with extra threads
* Outside Run() -> stderr
* Inside Run() -> /aos
* Other threads -> stderr
Simulation:
* Inside Run -> /aos
* Outside Run -> stderr
This pretty quickly quickly looks like a thread local logging handler
and a fallback to stderr. We don't really need much more, and don't
want to worry about synchronization for much more, so lets just do the
simple thing for now and revise if there are better use cases.
Change-Id: I90d76516a55f0b01a8a0e27ad0434dac5921e261
diff --git a/aos/logging/implementations.h b/aos/logging/implementations.h
index 962982b..6395c37 100644
--- a/aos/logging/implementations.h
+++ b/aos/logging/implementations.h
@@ -35,8 +35,6 @@
// Contains all of the information about a given logging call.
struct LogMessage {
- enum class Type : uint8_t { kString };
-
int32_t seconds, nseconds;
// message_length is just the length of the actual data (which member depends
// on the type).
@@ -45,26 +43,9 @@
static_assert(sizeof(source) == 4, "that's how they get printed");
// Per task/thread.
uint16_t sequence;
- Type type;
log_level level;
char name[LOG_MESSAGE_NAME_LEN];
- union {
- char message[LOG_MESSAGE_LEN];
- struct {
- uint32_t type_id;
- size_t string_length;
- // The message string and then the serialized structure.
- char serialized[LOG_MESSAGE_LEN - sizeof(type) - sizeof(string_length)];
- } structure;
- struct {
- // The type ID of the element type.
- uint32_t type;
- int rows, cols;
- size_t string_length;
- // The message string and then the serialized matrix.
- char data[LOG_MESSAGE_LEN - sizeof(type) - sizeof(rows) - sizeof(cols)];
- } matrix;
- };
+ char message[LOG_MESSAGE_LEN];
};
static_assert(shm_ok<LogMessage>::value, "it's going in a queue");
@@ -118,20 +99,13 @@
FILE *const stream_;
};
+// Returns the current implementation.
std::shared_ptr<LogImplementation> GetImplementation();
// Sets the current implementation.
void SetImplementation(std::shared_ptr<LogImplementation> implementation);
-// Updates the log implementation, returning the current implementation.
-std::shared_ptr<LogImplementation> SwapImplementation(
- std::shared_ptr<LogImplementation> implementation);
-
-// Must be called at least once per process/load before anything else is
-// called. This function is safe to call multiple times from multiple
-// tasks/threads.
-void Init();
-
+// A logging implementation which just uses a callback.
class CallbackLogImplementation : public HandleMessageLogImplementation {
public:
CallbackLogImplementation(
@@ -144,27 +118,13 @@
::std::function<void(const LogMessage &)> callback_;
};
-// Resets all information in this task/thread to its initial state.
-// NOTE: This is not the opposite of Init(). The state that this deletes is
-// lazily created when needed. It is actually the opposite of Load().
-void Cleanup();
-
-void RegisterCallbackImplementation(
- const ::std::function<void(const LogMessage &)> &callback);
-
class ScopedLogRestorer {
public:
- ScopedLogRestorer() = default;
-
- ~ScopedLogRestorer() {
- if (prev_impl_) {
- SetImplementation(std::move(prev_impl_));
- }
- Cleanup();
- }
+ ScopedLogRestorer() : prev_impl_(GetImplementation()) {}
+ ~ScopedLogRestorer() { SetImplementation(std::move(prev_impl_)); }
void Swap(std::shared_ptr<LogImplementation> new_impl) {
- prev_impl_ = SwapImplementation(std::move(new_impl));
+ SetImplementation(std::move(new_impl));
}
private: