- Added wxTempFile::Flush().
- Added support for wxLongLong and wxULongLong in wxVariant.
- Added wxVector::swap().
+- Many wxLog improvements:
+ * wxLogXXX() functions are now thread-safe.
+ * Log levels can now be set independently for different log components.
+ * wxLog::DoLogRecord() has access to the location of the log message
+ (file, line and function name) and id of the thread which generated it.
+ * SetThreadActiveTarget() allows to set up thread-specific log targets.
All (GUI):
// 17 modal dialogs one after another)
virtual void Flush();
- // flush the active target if any
- static void FlushActive()
- {
- if ( !ms_suspendCount )
- {
- wxLog *log = GetActiveTarget();
- if ( log )
- log->Flush();
- }
- }
+ // flush the active target if any and also output any pending messages from
+ // background threads
+ static void FlushActive();
- // only one sink is active at each moment
- // get current log target, will call wxApp::CreateLogTarget() to
- // create one if none exists
+ // only one sink is active at each moment get current log target, will call
+ // wxAppTraits::CreateLogTarget() to create one if none exists
static wxLog *GetActiveTarget();
- // change log target, pLogger may be NULL
- static wxLog *SetActiveTarget(wxLog *pLogger);
+ // change log target, logger may be NULL
+ static wxLog *SetActiveTarget(wxLog *logger);
+
+#if wxUSE_THREADS
+ // change log target for the current thread only, shouldn't be called from
+ // the main thread as it doesn't use thread-specific log target
+ static wxLog *SetThreadActiveTarget(wxLog *logger);
+#endif // wxUSE_THREADS
// suspend the message flushing of the main target until the next call
// to Resume() - this is mainly for internal use (to prevent wxYield()
unsigned LogLastRepeatIfNeeded();
private:
- // called from OnLog() if it's called from the main thread and from Flush()
+#if wxUSE_THREADS
+ // called from FlushActive() to really log any buffered messages logged
+ // from the other threads
+ void FlushThreadMessages();
+#endif // wxUSE_THREADS
+
+ // called from OnLog() if it's called from the main thread or if we have a
+ // (presumably MT-safe) thread-specific logger and by FlushThreadMessages()
// when it plays back the buffered messages logged from the other threads
- void OnLogInMainThread(wxLogLevel level,
- const wxString& msg,
- const wxLogRecordInfo& info);
+ void CallDoLogNow(wxLogLevel level,
+ const wxString& msg,
+ const wxLogRecordInfo& info);
// static variables
--- /dev/null
+///////////////////////////////////////////////////////////////////////////////
+// Name: wx/private/threadinfo.h
+// Purpose: declaration of wxThreadSpecificInfo: thread-specific information
+// Author: Vadim Zeitlin
+// Created: 2009-07-13
+// RCS-ID: $Id: wxhead.h,v 1.11 2009-06-29 10:23:04 zeitlin Exp $
+// Copyright: (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
+// Licence: wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _WX_PRIVATE_THREADINFO_H_
+#define _WX_PRIVATE_THREADINFO_H_
+
+#if wxUSE_THREADS
+
+#include "wx/tls.h"
+
+class WXDLLIMPEXP_FWD_BASE wxLog;
+
+// ----------------------------------------------------------------------------
+// wxThreadSpecificInfo: contains all thread-specific information used by wx
+// ----------------------------------------------------------------------------
+
+// currently the only thread-specific information we use is the active wxLog
+// target but more could be added in the future (e.g. current wxLocale would be
+// a likely candidate) and we will group all of them in this struct to avoid
+// consuming more TLS slots than necessary as there is only a limited number of
+// them
+
+// NB: this must be a POD to be stored in TLS
+struct wxThreadSpecificInfo
+{
+ wxLog *logger;
+};
+
+// currently this is defined in src/common/log.cpp
+extern wxTLS_TYPE(wxThreadSpecificInfo) wxThreadInfoVar;
+#define wxThreadInfo wxTLS_VALUE(wxThreadInfoVar)
+
+#endif // wxUSE_THREADS
+
+#endif // _WX_PRIVATE_THREADINFO_H_
+
If the buffer is already empty, nothing happens.
- It should only be called from the main application thread.
-
If you override this method in a derived class, call the base class
- version first, before doing anything else, to ensure that any buffered
- messages from the other threads are logged.
+ version first, before doing anything else.
*/
virtual void Flush();
/**
Flushes the current log target if any, does nothing if there is none.
- As Flush() itself, this method should only be called from the main
- application thread.
+ When this method is called from the main thread context, it also
+ flushes any previously buffered messages logged by the other threads.
+ When it is called from the other threads it simply calls Flush() on the
+ currently active log target, so it mostly makes sense to do this if a
+ thread has its own logger set with SetThreadActiveTarget().
*/
static void FlushActive();
/**
Returns the pointer to the active log target (may be @NULL).
+
+ Notice that if SetActiveTarget() hadn't been previously explicitly
+ called, this function will by default try to create a log target by
+ calling wxAppTraits::CreateLogTarget() which may be overridden in a
+ user-defined traits class to change the default behaviour. You may also
+ call DontCreateOnDemand() to disable this behaviour.
+
+ When this function is called from threads other than main one,
+ auto-creation doesn't happen. But if the thread has a thread-specific
+ log target previously set by SetThreadActiveTarget(), it is returned
+ instead of the global one. Otherwise, the global log target is
+ returned.
*/
static wxLog* GetActiveTarget();
To suppress logging use a new instance of wxLogNull not @NULL. If the
active log target is set to @NULL a new default log target will be
created when logging occurs.
+
+ @see SetThreadActiveTarget()
*/
static wxLog* SetActiveTarget(wxLog* logtarget);
*/
static void SetRepetitionCounting(bool repetCounting = true);
+ /**
+ Sets a thread-specific log target.
+
+ The log target passed to this function will be used for all messages
+ logged by the current thread using the usual wxLog functions. This
+ shouldn't be called from the main thread which never uses a thread-
+ specific log target but can be used for the other threads to handle
+ thread logging completely separately; instead of buffering thread log
+ messages in the main thread logger.
+
+ Notice that unlike for SetActiveTarget(), wxWidgets does not destroy
+ the thread-specific log targets when the thread terminates so doing
+ this is your responsibility.
+
+ This method is only available if @c wxUSE_THREADS is 1, i.e. wxWidgets
+ was compiled with threads support.
+
+ @param logger
+ The new thread-specific log target, possibly @NULL.
+ @return
+ The previous thread-specific log target, initially @NULL.
+
+ @since 2.9.1
+ */
+ static wxLog *SetThreadActiveTarget(wxLog *logger);
+
/**
Sets the timestamp format prepended by the default log targets to all
messages. The string may contain any normal characters as well as %
// Modified by:
// Created: 06/16/98
// RCS-ID: $Id$
-// Copyright: (c) 1998-2002 wxWidgets team
+// Copyright: (c) 1998-2009 wxWidgets team
// Licence: wxWindows license
/////////////////////////////////////////////////////////////////////////////
wxMessageDialog dialog(this,
_T("wxWidgets multithreaded application sample\n")
_T("(c) 1998 Julian Smart, Guilhem Lavaux\n")
- _T("(c) 1999 Vadim Zeitlin\n")
- _T("(c) 2000 Robert Roebling"),
+ _T("(c) 2000 Robert Roebling\n")
+ _T("(c) 1999,2009 Vadim Zeitlin"),
_T("About wxThread sample"),
wxOK | wxICON_INFORMATION);
wxThread::ExitCode MyGUIThread::Entry()
{
+ // this goes to the main window
+ wxLogMessage("GUI thread starting");
+
+ // use a thread-specific log target for this thread to show that its
+ // messages don't appear in the main window while it runs
+ wxLogBuffer logBuf;
+ wxLog::SetThreadActiveTarget(&logBuf);
+
for (int i=0; i<GUITHREAD_NUM_UPDATES && !TestDestroy(); i++)
{
// inform the GUI toolkit that we're going to use GUI functions
event.SetInt(i+1);
wxQueueEvent( m_dlg, event.Clone() );
+ if ( !((i + 1) % 10) )
+ {
+ // this message will go to the buffer
+ wxLogMessage("Step #%d.", i + 1);
+ }
+
// give the main thread the time to refresh before we lock the GUI mutex again
// FIXME: find a better way to do this!
wxMilliSleep(100);
}
+ // now remove the thread-specific thread target
+ wxLog::SetThreadActiveTarget(NULL);
+
+ // so that this goes to the main window again
+ wxLogMessage("GUI thread finished.");
+
return (ExitCode)0;
}
#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"
#if wxUSE_THREADS
+wxTLS_TYPE(wxThreadSpecificInfo) wxThreadInfoVar;
+
namespace
{
#endif
}
- wxLog *pLogger = GetActiveTarget();
- if ( !pLogger )
- return;
+ wxLog *logger;
#if wxUSE_THREADS
if ( !wxThread::IsMain() )
{
- wxCriticalSectionLocker lock(GetBackgroundLogCS());
+ 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));
+ gs_bufferedLogRecords.push_back(wxLogRecord(level, msg, info));
- // ensure that our Flush() will be called soon
- wxWakeUpIdle();
+ // 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;
+ return;
+ }
+ //else: we have a thread-specific logger, we can send messages to it
+ // directly
}
+ else
#endif // wxUSE_THREADS
+ {
+ logger = ms_pLogger;
+ if ( !logger )
+ return;
+ }
- pLogger->OnLogInMainThread(level, msg, info);
+ logger->CallDoLogNow(level, msg, info);
}
void
-wxLog::OnLogInMainThread(wxLogLevel level,
- const wxString& msg,
- const wxLogRecordInfo& info)
+wxLog::CallDoLogNow(wxLogLevel level,
+ const wxString& msg,
+ const wxLogRecordInfo& info)
{
if ( GetRepetitionCounting() )
{
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()
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;
#endif // wxUSE_DATETIME
}
-void wxLog::Flush()
-{
#if wxUSE_THREADS
- wxASSERT_MSG( wxThread::IsMain(),
- "should be called from the main thread only" );
+void wxLog::FlushThreadMessages()
+{
// check if we have queued messages from other threads
wxLogRecords bufferedLogRecords;
it != bufferedLogRecords.end();
++it )
{
- OnLogInMainThread(it->level, it->msg, it->info);
+ 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
// ----------------------------------------------------------------------------