reduce memory allocations to zero
authorHans-Andreas Engel <engel@node.ch>
Fri, 8 Apr 2016 08:51:40 +0000 (10:51 +0200)
committerSergiu Deitsch <sergiu.deitsch@gmail.com>
Wed, 5 Jul 2017 22:02:38 +0000 (00:02 +0200)
CMakeLists.txt
src/config.h.cmake.in
src/glog/logging.h.in
src/logging.cc
src/logging_unittest.cc
src/logging_unittest.err

index a32cb00..89e8f81 100644 (file)
@@ -28,6 +28,7 @@ set (CPACK_PACKAGE_VERSION ${GLOG_VERSION})
 
 option (WITH_GFLAGS "Use gflags" ON)
 option (WITH_THREADS "Enable multithreading support" ON)
+option (WITH_TLS "Enable Thread Local Storage (TLS) support" ON)
 
 list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
 
@@ -183,6 +184,31 @@ using namespace Outer::Inner;;
 int main() { return i; }
 " HAVE_NAMESPACES)
 
+check_cxx_source_compiles ("
+__declspec(thread) int tls;
+int main() { }
+" HAVE_MSVC_TLS)
+
+check_cxx_source_compiles ("
+thread_local int tls;
+int main() { }
+" HAVE_CXX11_TLS)
+
+check_cxx_source_compiles ("
+__attribute__((thread)) int tls;
+int main() { }
+" HAVE_CYGWIN_TLS)
+
+if (WITH_TLS)
+  if (HAVE_CYGWIN_TLS)
+    set (GLOG_THREAD_LOCAL_STORAGE "__attribute__((thread))")
+  elseif (HAVE_MSVC_TLS)
+    set (GLOG_THREAD_LOCAL_STORAGE "__declspec(thread)")
+  elseif (HAVE_CXX11_TLS)
+    set (GLOG_THREAD_LOCAL_STORAGE thread_local)
+  endif (HAVE_CYGWIN_TLS)
+endif (WITH_TLS)
+
 set (_PC_FIELDS
   "gregs[REG_PC]"
   "gregs[REG_EIP]"
index 6635df1..a946451 100644 (file)
 /* location of source code */
 #cmakedefine TEST_SRC_DIR ${TEST_SRC_DIR}
 
+/* Define to necessary thread-local storage attribute. */
+#cmakedefine GLOG_THREAD_LOCAL_STORAGE ${GLOG_THREAD_LOCAL_STORAGE}
+
 /* Version number of package */
 #cmakedefine VERSION
 
index b1de2c9..04cce1d 100644 (file)
@@ -1107,6 +1107,12 @@ class GOOGLE_GLOG_DLL_DECL LogStreamBuf : public std::streambuf {
   LogStreamBuf(char *buf, int len) {
     setp(buf, buf + len - 2);
   }
+
+  // Resets the buffer. Useful if we reuse it by means of TLS.
+  void reset() {
+     setp(pbase(), epptr());
+  }
+
   // This effectively ignores overflow.
   virtual int_type overflow(int_type ch) {
     return ch;
@@ -1169,6 +1175,7 @@ public:
     size_t pcount() const { return streambuf_.pcount(); }
     char* pbase() const { return streambuf_.pbase(); }
     char* str() const { return pbase(); }
+    void reset() { streambuf_.reset(); }
 
   private:
     LogStream(const LogStream&);
index ec9eef1..42290f4 100644 (file)
@@ -336,6 +336,7 @@ const size_t LogMessage::kMaxLogMessageLen = 30000;
 
 struct LogMessage::LogMessageData  {
   LogMessageData();
+  void reset();
 
   int preserved_errno_;      // preserved errno
   // Buffer space; contains complete message text.
@@ -1141,10 +1142,22 @@ static bool fatal_msg_exclusive = true;
 static LogMessage::LogMessageData fatal_msg_data_exclusive;
 static LogMessage::LogMessageData fatal_msg_data_shared;
 
+#ifdef GLOG_THREAD_LOCAL_STORAGE
+// Static thread-local log data space to use, because typically at most one
+// LogMessageData object exists (in this case glog makes zero heap memory
+// allocations).
+static GLOG_THREAD_LOCAL_STORAGE bool thread_data_available = true;
+static GLOG_THREAD_LOCAL_STORAGE LogMessage::LogMessageData thread_msg_data;
+#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
+
 LogMessage::LogMessageData::LogMessageData()
   : stream_(message_text_, LogMessage::kMaxLogMessageLen, 0) {
 }
 
+void LogMessage::LogMessageData::reset() {
+    stream_.reset();
+}
+
 LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
                        int ctr, void (LogMessage::*send_method)())
     : allocated_(NULL) {
@@ -1197,8 +1210,22 @@ void LogMessage::Init(const char* file,
                       void (LogMessage::*send_method)()) {
   allocated_ = NULL;
   if (severity != GLOG_FATAL || !exit_on_dfatal) {
+#ifdef GLOG_THREAD_LOCAL_STORAGE
+    // No need for locking, because this is thread local.
+    if (thread_data_available) {
+      thread_data_available = false;
+      data_ = &thread_msg_data;
+      // Make sure to clear log data since it may have been used and filled with
+      // data. We do not want to append the new message to the previous one.
+      data_->reset();
+    } else {
+      allocated_ = new LogMessageData();
+      data_ = allocated_;
+    }
+#else // !defined(GLOG_THREAD_LOCAL_STORAGE)
     allocated_ = new LogMessageData();
     data_ = allocated_;
+#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
     data_->first_fatal_ = false;
   } else {
     MutexLock l(&fatal_msg_lock);
@@ -1267,6 +1294,10 @@ void LogMessage::Init(const char* file,
 
 LogMessage::~LogMessage() {
   Flush();
+#ifdef GLOG_THREAD_LOCAL_STORAGE
+  if (data_ == &thread_msg_data)
+    thread_data_available = true;
+#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
   delete allocated_;
 }
 
index b886222..76eb75b 100644 (file)
@@ -272,12 +272,14 @@ void TestLogging(bool check_counts) {
   LOG(ERROR) << string("foo") << ' '<< j << ' ' << setw(10) << j << " "
              << setw(1) << hex << j;
 
+  LOG(ERROR) << (&LOG(ERROR) && 0) << " nested LOG";
+
   LogMessage("foo", LogMessage::kNoLogPrefix, GLOG_INFO).stream() << "no prefix";
 
   if (check_counts) {
     CHECK_EQ(base_num_infos   + 14, LogMessage::num_messages(GLOG_INFO));
     CHECK_EQ(base_num_warning + 3,  LogMessage::num_messages(GLOG_WARNING));
-    CHECK_EQ(base_num_errors  + 15, LogMessage::num_messages(GLOG_ERROR));
+    CHECK_EQ(base_num_errors  + 17, LogMessage::num_messages(GLOG_ERROR));
   }
 }
 
index 4f80bf5..e08836f 100644 (file)
@@ -74,6 +74,8 @@ WDATE TIME__ THREADID logging_unittest.cc:LINE] log_if this
 IDATE TIME__ THREADID logging_unittest.cc:LINE] array
 IDATE TIME__ THREADID logging_unittest.cc:LINE] const array
 EDATE TIME__ THREADID logging_unittest.cc:LINE] foo 1000 0000001000 3e8
+EDATE TIME__ THREADID logging_unittest.cc:LINE] 0 nested LOG
+EDATE TIME__ THREADID logging_unittest.cc:LINE]
 no prefix
 IDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: foo bar 10 3.400000
 WDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: array