Browse Source

Allow logging from outside the main thread. These log messages are collected and handled at the end of frame.

Lasse Öörni 11 years ago
parent
commit
461a61319f
2 changed files with 81 additions and 0 deletions
  1. 46 0
      Source/Engine/IO/Log.cpp
  2. 35 0
      Source/Engine/IO/Log.h

+ 46 - 0
Source/Engine/IO/Log.cpp

@@ -22,11 +22,13 @@
 
 #include "Precompiled.h"
 #include "Context.h"
+#include "CoreEvents.h"
 #include "File.h"
 #include "IOEvents.h"
 #include "Log.h"
 #include "Mutex.h"
 #include "ProcessUtils.h"
+#include "Thread.h"
 #include "Timer.h"
 
 #include <cstdio>
@@ -66,6 +68,8 @@ Log::Log(Context* context) :
     quiet_(false)
 {
     logInstance = this;
+    
+    SubscribeToEvent(E_ENDFRAME, HANDLER(Log, HandleEndFrame));
 }
 
 Log::~Log()
@@ -129,6 +133,18 @@ void Log::Write(int level, const String& message)
 {
     assert(level >= LOG_DEBUG && level < LOG_NONE);
 
+    // If not in the main thread, store message for later processing
+    if (!Thread::IsMainThread())
+    {
+        if (logInstance)
+        {
+            MutexLock lock(logInstance->logMutex_);
+            logInstance->threadMessages_.Push(StoredLogMessage(message, level, false));
+        }
+        
+        return;
+    }
+
     // Do not log if message level excluded or if currently sending a log event
     if (!logInstance || logInstance->level_ > level || logInstance->inWrite_)
         return;
@@ -176,6 +192,18 @@ void Log::Write(int level, const String& message)
 
 void Log::WriteRaw(const String& message, bool error)
 {
+    // If not in the main thread, store message for later processing
+    if (!Thread::IsMainThread())
+    {
+        if (logInstance)
+        {
+            MutexLock lock(logInstance->logMutex_);
+            logInstance->threadMessages_.Push(StoredLogMessage(message, LOG_RAW, error));
+        }
+        
+        return;
+    }
+    
     // Prevent recursion during log event
     if (!logInstance || logInstance->inWrite_)
         return;
@@ -221,4 +249,22 @@ void Log::WriteRaw(const String& message, bool error)
     logInstance->inWrite_ = false;
 }
 
+void Log::HandleEndFrame(StringHash eventType, VariantMap& eventData)
+{
+    MutexLock lock(logMutex_);
+    
+    // Process messages accumulated from other threads (if any)
+    while (!threadMessages_.Empty())
+    {
+        const StoredLogMessage& stored = threadMessages_.Front();
+        
+        if (stored.level_ != LOG_RAW)
+            Write(stored.level_, stored.message_);
+        else
+            WriteRaw(stored.message_, stored.error_);
+        
+        threadMessages_.PopFront();
+    }
+}
+
 }

+ 35 - 0
Source/Engine/IO/Log.h

@@ -22,12 +22,16 @@
 
 #pragma once
 
+#include "List.h"
+#include "Mutex.h"
 #include "Object.h"
 #include "StringUtils.h"
 
 namespace Urho3D
 {
 
+/// Fictional message level to indicate a stored raw message.
+static const int LOG_RAW = -1;
 /// Debug message level. By default only shown in debug mode.
 static const int LOG_DEBUG = 0;
 /// Informative message level.
@@ -41,6 +45,30 @@ static const int LOG_NONE = 4;
 
 class File;
 
+/// Stored log message from another thread.
+struct StoredLogMessage
+{
+    /// Construct undefined.
+    StoredLogMessage()
+    {
+    }
+    
+    /// Construct with parameters.
+    StoredLogMessage(const String& message, int level, bool error) :
+        message_(message),
+        level_(level),
+        error_(error)
+    {
+    }
+    
+    /// Message text.
+    String message_;
+    /// Message level. -1 for raw messages.
+    int level_;
+    /// Error flag for raw messages.
+    bool error_;
+};
+
 /// Logging subsystem.
 class URHO3D_API Log : public Object
 {
@@ -78,6 +106,13 @@ public:
     static void WriteRaw(const String& message, bool error = false);
 
 private:
+    /// Handle end of frame. Process the threaded log messages.
+    void HandleEndFrame(StringHash eventType, VariantMap& eventData);
+    
+    /// Mutex for threaded operation.
+    Mutex logMutex_;
+    /// Log messages from other threads.
+    List<StoredLogMessage> threadMessages_;
     /// Log file.
     SharedPtr<File> logFile_;
     /// Last log message.