]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/log.cpp
Add support for thread-specific log targets.
[wxWidgets.git] / src / common / log.cpp
index 1b599cc8f8f38ea193f3f8514e3703ab93375bfb..293fcd1dc8a10a75d34121fb79f260af8a8d3142 100644 (file)
@@ -42,7 +42,9 @@
 #include "wx/msgout.h"
 #include "wx/textfile.h"
 #include "wx/thread.h"
+#include "wx/private/threadinfo.h"
 #include "wx/crt.h"
+#include "wx/vector.h"
 
 // other standard headers
 #ifndef __WXWINCE__
@@ -68,32 +70,47 @@ const char *wxLOG_COMPONENT = "";
 
 #if wxUSE_THREADS
 
-// define static functions providing access to the critical sections we use
-// instead of just using static critical section variables as log functions may
-// be used during static initialization and while this is certainly not
-// advisable it's still better to not crash (as we'd do if we used a yet
-// uninitialized critical section) if it happens
+wxTLS_TYPE(wxThreadSpecificInfo) wxThreadInfoVar;
 
-static inline wxCriticalSection& GetTraceMaskCS()
-{
-    static wxCriticalSection s_csTrace;
-
-    return s_csTrace;
-}
-
-static inline wxCriticalSection& GetPreviousLogCS()
+namespace
 {
-    static wxCriticalSection s_csPrev;
 
-    return s_csPrev;
-}
+// contains messages logged by the other threads and waiting to be shown until
+// Flush() is called in the main one
+typedef wxVector<wxLogRecord> wxLogRecords;
+wxLogRecords gs_bufferedLogRecords;
 
-static inline wxCriticalSection& GetLevelsCS()
-{
-    static wxCriticalSection s_csLevels;
+// this macro allows to define an object which will be initialized before any
+// other function in this file is called: this is necessary to allow log
+// functions to be used during static initialization (this is not advisable
+// anyhow but we should at least try to not crash) and to also ensure that they
+// are initialized by the time static initialization is done, i.e. before any
+// threads are created hopefully
+//
+// the net effect of all this is that you can use Get##name##CS() function to
+// access the critical function without worrying about it being not initialized
+//
+// see also WX_DEFINE_GLOBAL_CONV2() in src/common/strconv.cpp
+#define WX_DEFINE_LOG_CS(name)                                                \
+    inline wxCriticalSection& Get##name##CS()                                 \
+    {                                                                         \
+        static wxCriticalSection s_cs##name;                                  \
+        return s_cs##name;                                                    \
+    }                                                                         \
+                                                                              \
+    wxCriticalSection *gs_##name##CSPtr = &Get##name##CS()
+
+// this critical section is used for buffering the messages from threads other
+// than main, i.e. it protects all accesses to gs_bufferedLogRecords above
+WX_DEFINE_LOG_CS(BackgroundLog);
+
+// this one is used for protecting ms_aTraceMasks from concurrent access
+WX_DEFINE_LOG_CS(TraceMask);
+
+// and this one is used for gs_componentLevels 
+WX_DEFINE_LOG_CS(Levels);
 
-    return s_csLevels;
-}
+} // anonymous namespace
 
 #endif // wxUSE_THREADS
 
@@ -171,13 +188,6 @@ void wxSafeShowMessage(const wxString& title, const wxString& text)
 // ----------------------------------------------------------------------------
 
 unsigned wxLog::LogLastRepeatIfNeeded()
-{
-    wxCRIT_SECT_LOCKER(lock, GetPreviousLogCS());
-
-    return LogLastRepeatIfNeededUnlocked();
-}
-
-unsigned wxLog::LogLastRepeatIfNeededUnlocked()
 {
     const unsigned count = gs_prevLog.numRepeated;
 
@@ -252,14 +262,51 @@ wxLog::OnLog(wxLogLevel level,
 #endif
     }
 
-    wxLog *pLogger = GetActiveTarget();
-    if ( !pLogger )
-        return;
+    wxLog *logger;
 
-    if ( GetRepetitionCounting() )
+#if wxUSE_THREADS
+    if ( !wxThread::IsMain() )
+    {
+        logger = wxThreadInfo.logger;
+        if ( !logger )
+        {
+            if ( ms_pLogger )
+            {
+                // buffer the messages until they can be shown from the main
+                // thread
+                wxCriticalSectionLocker lock(GetBackgroundLogCS());
+
+                gs_bufferedLogRecords.push_back(wxLogRecord(level, msg, info));
+
+                // ensure that our Flush() will be called soon
+                wxWakeUpIdle();
+            }
+            //else: we don't have any logger at all, there is no need to log
+            //      anything
+
+            return;
+        }
+        //else: we have a thread-specific logger, we can send messages to it
+        //      directly
+    }
+    else
+#endif // wxUSE_THREADS
     {
-        wxCRIT_SECT_LOCKER(lock, GetPreviousLogCS());
+        logger = ms_pLogger;
+        if ( !logger )
+            return;
+    }
 
+    logger->CallDoLogNow(level, msg, info);
+}
+
+void
+wxLog::CallDoLogNow(wxLogLevel level,
+                    const wxString& msg,
+                    const wxLogRecordInfo& info)
+{
+    if ( GetRepetitionCounting() )
+    {
         if ( msg == gs_prevLog.msg )
         {
             gs_prevLog.numRepeated++;
@@ -269,7 +316,7 @@ wxLog::OnLog(wxLogLevel level,
             return;
         }
 
-        pLogger->LogLastRepeatIfNeededUnlocked();
+        LogLastRepeatIfNeeded();
 
         // reset repetition counter for a new message
         gs_prevLog.msg = msg;
@@ -297,7 +344,7 @@ wxLog::OnLog(wxLogLevel level,
     }
 #endif // wxUSE_LOG_TRACE
 
-    pLogger->DoLogRecord(level, prefix + msg + suffix, info);
+    DoLogRecord(level, prefix + msg + suffix, info);
 }
 
 void wxLog::DoLogRecord(wxLogLevel level,
@@ -402,6 +449,19 @@ void wxLog::DoLog(wxLogLevel WXUNUSED(level), const wchar_t *wzString, time_t t)
 
 wxLog *wxLog::GetActiveTarget()
 {
+#if wxUSE_THREADS
+    if ( !wxThread::IsMain() )
+    {
+        // check if we have a thread-specific log target
+        wxLog * const logger = wxThreadInfo.logger;
+
+        // the code below should be only executed for the main thread as
+        // CreateLogTarget() is not meant for auto-creating log targets for
+        // worker threads so skip it in any case
+        return logger ? logger : ms_pLogger;
+    }
+#endif // wxUSE_THREADS
+
     if ( ms_bAutoCreate && ms_pLogger == NULL ) {
         // prevent infinite recursion if someone calls wxLogXXX() from
         // wxApp::CreateLogTarget()
@@ -438,6 +498,22 @@ wxLog *wxLog::SetActiveTarget(wxLog *pLogger)
     return pOldLogger;
 }
 
+#if wxUSE_THREADS
+/* static */
+wxLog *wxLog::SetThreadActiveTarget(wxLog *logger)
+{
+    wxASSERT_MSG( !wxThread::IsMain(), "use SetActiveTarget() for main thread" );
+
+    wxLog * const oldLogger = wxThreadInfo.logger;
+    if ( oldLogger )
+        oldLogger->Flush();
+
+    wxThreadInfo.logger = logger;
+
+    return oldLogger;
+}
+#endif // wxUSE_THREADS
+
 void wxLog::DontCreateOnDemand()
 {
     ms_bAutoCreate = false;
@@ -555,17 +631,65 @@ void wxLog::TimeStamp(wxString *str)
 #endif // wxUSE_DATETIME
 }
 
+#if wxUSE_THREADS
+
+void wxLog::FlushThreadMessages()
+{
+    // check if we have queued messages from other threads
+    wxLogRecords bufferedLogRecords;
+
+    {
+        wxCriticalSectionLocker lock(GetBackgroundLogCS());
+        bufferedLogRecords.swap(gs_bufferedLogRecords);
+
+        // release the lock now to not keep it while we are logging the
+        // messages below, allowing background threads to run
+    }
+
+    if ( !bufferedLogRecords.empty() )
+    {
+        for ( wxLogRecords::const_iterator it = bufferedLogRecords.begin();
+              it != bufferedLogRecords.end();
+              ++it )
+        {
+            CallDoLogNow(it->level, it->msg, it->info);
+        }
+    }
+}
+
+#endif // wxUSE_THREADS
+
 void wxLog::Flush()
 {
     LogLastRepeatIfNeeded();
 }
 
+/* static */
+void wxLog::FlushActive()
+{
+    if ( ms_suspendCount )
+        return;
+
+    wxLog * const log = GetActiveTarget();
+    if ( log )
+    {
+#if wxUSE_THREADS
+        if ( wxThread::IsMain() )
+            log->FlushThreadMessages();
+#endif // wxUSE_THREADS
+
+        log->Flush();
+    }
+}
+
 // ----------------------------------------------------------------------------
 // wxLogBuffer implementation
 // ----------------------------------------------------------------------------
 
 void wxLogBuffer::Flush()
 {
+    wxLog::Flush();
+
     if ( !m_str.empty() )
     {
         wxMessageOutputBest out;