Make ScopeTracker thread safe via TLS (thread local storage)
[libi2ncommon] / src / tracefunc.cpp
index ab1ec5e..368559c 100644 (file)
@@ -21,9 +21,9 @@ on this file might be covered by the GNU General Public License.
  * @brief implementation of tracing functionality.
  *
  * @copyright © Copyright 2008 by Intra2net AG
+ * 
+ * Every thread gets its' own ScopeTracker stack via TLS (thread local storage)
  *
- * @note This module is not thread safe!
- * @todo make this module thread safe (only useful when we can use threads...)
  */
 
 #include "tracefunc.hpp"
@@ -31,7 +31,7 @@ on this file might be covered by the GNU General Public License.
 #include <list>
 #include <vector>
 #include <sstream>
-
+#include <boost/thread/tss.hpp>
 
 namespace I2n
 {
@@ -40,51 +40,62 @@ namespace Tracer
 
 using Logger::GlobalLogger;
 
+class PerThreadContainer
+{
+public:
+    typedef std::list< ScopeTracker* > ScopeTrackerList;
+
+    /// List of currently active scope trackers
+    ScopeTrackerList scope_tracker_list;
+
+    /// Precomputed indent levels
+    std::vector< std::string > indents;
+};
 
 namespace
 {
 
-typedef std::list< ScopeTracker* > ScopeTrackerList;
-
-ScopeTrackerList scope_tracker_list;
+/// Internal TLS (thread local storage) data container
+boost::thread_specific_ptr<PerThreadContainer> thread_container;
 
+/**
+ * Helper function to retrieve TLS pointer.
+ * Initializes the pointer if called for the first time.
+ * @return Pointer to the per thread container
+ */
+static PerThreadContainer *TLS_get_container()
+{
+    // First call? Construct options container
+    if (thread_container.get() == NULL)
+        thread_container.reset(new PerThreadContainer());
 
-std::vector< std::string > indents;
+    PerThreadContainer *per_thread = thread_container.get();
 
+    return per_thread;
+}
 
 /**
  * @brief ensures indent level string to exist up to the desired level.
  * @param level the desired indent level.
  */
-void ensure_indent_level(int level)
+static void ensure_indent_level(PerThreadContainer *per_thread, int level)
 {
-   while (indents.size() <= level)
+    // First initialization?
+    if (per_thread->indents.empty())
+    {
+         per_thread->indents.reserve(10);
+         if (level < 10)
+             level = 10;
+
+         per_thread->indents.push_back("");
+    }
+
+   while (per_thread->indents.size() <= level)
    {
-      indents.push_back( indents.back() + "  " );
+      per_thread->indents.push_back( per_thread->indents.back() + "  " );
    }
 } // eo ensure_indent_level(int)
 
-
-/**
- * @brief module initializer.
- */
-class __Initializer
-{
-   public:
-      __Initializer()
-      {
-         // pre initialize indent strings:
-         indents.clear();
-         indents.reserve(10);
-         indents.push_back("");
-         for(int i=10; i-->0;)
-         {
-            indents.push_back( indents.back() + "  " );
-         }
-      }
-} __initializer;
-
-
 } // eo namespace <anonymous>
 
 
@@ -100,13 +111,14 @@ class __Initializer
  */
 ScopeTracker::ScopeTracker(
     const SourceLocation& loc)
-: Location(loc)
-, Depth(0)
-, FuncDepth(0)
+    : Location(loc)
+    , Depth(0)
+    , FuncDepth(0)
+    , PerThread(TLS_get_container())
 {
-   if (!scope_tracker_list.empty())
+    if (!PerThread->scope_tracker_list.empty())
    {
-      ScopeTracker* last_tracker= scope_tracker_list.back();
+      ScopeTracker* last_tracker= PerThread->scope_tracker_list.back();
       if (Location)
       {
          if (last_tracker->Location
@@ -118,8 +130,8 @@ ScopeTracker::ScopeTracker(
       }
       Depth= last_tracker->Depth + 1;
    }
-   ensure_indent_level(Depth);
-   scope_tracker_list.push_back(this);
+   ensure_indent_level(PerThread, Depth);
+   PerThread->scope_tracker_list.push_back(this);
    {
       std::ostringstream ostr;
       if (Location.FunctionName.empty())
@@ -139,7 +151,7 @@ ScopeTracker::ScopeTracker(
    // spit a message
    if (Logger::has_log_level(Logger::LogLevel::Debug))
    {
-      GlobalLogger.debug() << indents[Depth] << "ENTER " << Tag;
+      GlobalLogger.debug() << PerThread->indents[Depth] << "ENTER " << Tag;
    }
 } // eo ScopeTrcaker::ScopeTracker(const SourceLocation&)
 
@@ -154,15 +166,15 @@ ScopeTracker::~ScopeTracker()
    // spit a message
    if (Logger::has_log_level(Logger::LogLevel::Debug))
    {
-      GlobalLogger.debug() << indents[Depth] << "LEAVE " << Tag;
+      GlobalLogger.debug() << PerThread->indents[Depth] << "LEAVE " << Tag;
    }
-   if (scope_tracker_list.empty())
+   if (PerThread->scope_tracker_list.empty())
    {
       return;
    }
-   if (scope_tracker_list.back() == this)
+   if (PerThread->scope_tracker_list.back() == this)
    {
-      scope_tracker_list.pop_back();
+      PerThread->scope_tracker_list.pop_back();
    }
    else
    {