This is an automated email from the ASF dual-hosted git repository.

swebb2066 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git


The following commit(s) were added to refs/heads/master by this push:
     new b5fca83f Restore LoggingEvent throughput performance (#746)
b5fca83f is described below

commit b5fca83fdb063d24cf2b32e750a59c2356a9f057
Author: Stephen Webb <[email protected]>
AuthorDate: Sat Aug 29 11:10:58 2026 +1000

    Restore LoggingEvent throughput performance (#746)
---
 src/main/cpp/loggingevent.cpp            | 43 ++++++++++++++++++++++++++------
 src/test/cpp/asyncappenderracestress.cpp |  2 +-
 2 files changed, 37 insertions(+), 8 deletions(-)

diff --git a/src/main/cpp/loggingevent.cpp b/src/main/cpp/loggingevent.cpp
index 9bce733f..ed110564 100644
--- a/src/main/cpp/loggingevent.cpp
+++ b/src/main/cpp/loggingevent.cpp
@@ -33,6 +33,10 @@
 #include <log4cxx/helpers/messagebuffer.h>
 #include <log4cxx/helpers/date.h>
 #include <log4cxx/helpers/optional.h>
+#include <log4cxx/helpers/transcoder.h>
+#include <atomic>
+#include <memory>
+#include <thread>
 
 using namespace LOG4CXX_NS;
 using namespace LOG4CXX_NS::spi;
@@ -43,6 +47,7 @@ struct LoggingEvent::LoggingEventPrivate
        LoggingEventPrivate(const ThreadSpecificData::NamePairPtr p = 
ThreadSpecificData::getNames()) :
                timeStamp(0),
                pNames(p)
+               , renderState(RenderingRequired)
        {
        }
 
@@ -59,7 +64,8 @@ struct LoggingEvent::LoggingEventPrivate
                timeStamp(Date::currentTime()),
                locationInfo(locationInfo1),
                chronoTimeStamp(std::chrono::microseconds(timeStamp)),
-               pNames(p)
+               pNames(p),
+               renderState(RenderingCompleted)
        {
        }
 
@@ -77,6 +83,7 @@ struct LoggingEvent::LoggingEventPrivate
                , chronoTimeStamp(std::chrono::microseconds(timeStamp))
                , pNames(p)
                , messageAppender(std::move(messageAppenderArg))
+               , renderState(RenderingRequired)
        {
        }
 
@@ -91,7 +98,8 @@ struct LoggingEvent::LoggingEventPrivate
                timeStamp(Date::currentTime()),
                locationInfo(locationInfo1),
                chronoTimeStamp(std::chrono::microseconds(timeStamp)),
-               pNames(p)
+               pNames(p),
+               renderState(RenderingCompleted)
        {
        }
 
@@ -148,7 +156,7 @@ struct LoggingEvent::LoggingEventPrivate
         */
        helpers::AsyncBuffer messageAppender;
 
-       /** Ensures the deferred message is rendered exactly once.
+       /** Ensures an async message is rendered exactly once.
         *
         *  A LoggingEvent may be shared between threads, e.g. when it is
         *  queued to an AsyncAppender dispatcher while the logging thread
@@ -157,21 +165,42 @@ struct LoggingEvent::LoggingEventPrivate
         *  the closure vector while the other iterates it, and both
         *  move-assign \c message - a concurrent free/assign of the same
         *  heap buffer.
+        * Note: use of std::call_once was found to degrade throughput by up to 
120%
         */
-       std::once_flag renderOnce;
+       std::atomic<uint8_t> renderState;
+       static const uint8_t RenderingRequired = 0;
+       static const uint8_t RenderingActive = 1;
+       static const uint8_t RenderingCompleted = 2;
 
        void renderMessage()
        {
-               std::call_once(this->renderOnce, [this]()
+               if (renderState.load(std::memory_order_acquire) == 
RenderingCompleted)
+                       return;
+               uint8_t unrenderedState{ RenderingRequired };
+               if (renderState.compare_exchange_strong(unrenderedState, 
RenderingActive, std::memory_order_acquire))
                {
-                       if (!this->messageAppender.empty())
+                       if (this->messageAppender.empty())
+                               ;
+                       else try
                        {
                                helpers::LogCharMessageBuffer buf;
                                this->messageAppender.renderMessage(buf);
                                this->message = buf.extract_str(buf);
                                this->messageAppender.clear();
+
+                       }
+                       catch (const std::exception& e)
+                       {
+                               helpers::Transcoder::decode(e.what(), 
this->message);
+                       }
+                       catch (...)
+                       {
+                               this->message = LOG4CXX_STR("mesage rendering 
failed");
                        }
-               });
+                       renderState.store(RenderingCompleted, 
std::memory_order_release);
+               }
+               else while (renderState.load(std::memory_order_acquire) != 
RenderingCompleted)
+                       std::this_thread::yield();
        }
 };
 
diff --git a/src/test/cpp/asyncappenderracestress.cpp 
b/src/test/cpp/asyncappenderracestress.cpp
index f9e37716..aacf3a91 100644
--- a/src/test/cpp/asyncappenderracestress.cpp
+++ b/src/test/cpp/asyncappenderracestress.cpp
@@ -127,7 +127,7 @@ public:
        // Expected behavior:
        // - Without single-shot rendering, ThreadSanitizer reports data races 
in
        //   renderMessage (and the assertions below can observe torn messages).
-       // - With renderMessage guarded by std::call_once, ThreadSanitizer is 
clean
+       // - With renderMessage guarded by std::atomic, ThreadSanitizer is clean
        //   and both threads observe identical messages.
        void raceDeferredMessageRendering()
        {

Reply via email to