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 ee8e777a Synchronize AsyncAppender getter access with buffer mutex 
(#674)
ee8e777a is described below

commit ee8e777a3d3b01448bb70c97d148916c9123b3b2
Author: jmestwa-coder <[email protected]>
AuthorDate: Sat May 16 10:54:23 2026 +0530

    Synchronize AsyncAppender getter access with buffer mutex (#674)
---
 src/main/cpp/asyncappender.cpp           |   2 +
 src/test/cpp/CMakeLists.txt              |   1 +
 src/test/cpp/asyncappenderracestress.cpp | 116 +++++++++++++++++++++++++++++++
 3 files changed, 119 insertions(+)

diff --git a/src/main/cpp/asyncappender.cpp b/src/main/cpp/asyncappender.cpp
index 206ba53a..ef12808d 100644
--- a/src/main/cpp/asyncappender.cpp
+++ b/src/main/cpp/asyncappender.cpp
@@ -531,6 +531,7 @@ void AsyncAppender::setBufferSize(int size)
 
 int AsyncAppender::getBufferSize() const
 {
+       std::lock_guard<std::mutex> lock(priv->bufferMutex);
        return priv->bufferSize;
 }
 
@@ -543,6 +544,7 @@ void AsyncAppender::setBlocking(bool value)
 
 bool AsyncAppender::getBlocking() const
 {
+       std::lock_guard<std::mutex> lock(priv->bufferMutex);
        return priv->blocking;
 }
 
diff --git a/src/test/cpp/CMakeLists.txt b/src/test/cpp/CMakeLists.txt
index 5e18a1d4..f20a84f8 100644
--- a/src/test/cpp/CMakeLists.txt
+++ b/src/test/cpp/CMakeLists.txt
@@ -47,6 +47,7 @@ find_program(GZIP_APP gzip REQUIRED)
 set(ALL_LOG4CXX_TESTS
     autoconfiguretestcase
     asyncappendertestcase
+    asyncappenderracestress
     consoleappendertestcase
     decodingtest
     encodingtest
diff --git a/src/test/cpp/asyncappenderracestress.cpp 
b/src/test/cpp/asyncappenderracestress.cpp
new file mode 100644
index 00000000..ef0e9eba
--- /dev/null
+++ b/src/test/cpp/asyncappenderracestress.cpp
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logunit.h"
+
+#include <log4cxx/asyncappender.h>
+
+#include <atomic>
+#include <memory>
+#include <thread>
+
+using namespace log4cxx;
+
+LOGUNIT_CLASS(AsyncAppenderRaceStressTestCase)
+{
+       LOGUNIT_TEST_SUITE(AsyncAppenderRaceStressTestCase);
+       LOGUNIT_TEST(raceGetSetBufferSize);
+       LOGUNIT_TEST(raceGetSetBlocking);
+       LOGUNIT_TEST_SUITE_END();
+
+public:
+       // This test is intentionally simple:
+       // - it creates concurrent overlap between a setter (writes under 
bufferMutex) and a getter (read),
+       // - it relies on ThreadSanitizer to prove the data race in the 
unsynchronized implementation,
+       // - it is bounded, deterministic, and does not depend on timing sleeps 
or logging side effects.
+       //
+       // Expected behavior:
+       // - With unsynchronized getters, ThreadSanitizer reports data races 
involving getBufferSize/getBlocking.
+       // - With getters protected by bufferMutex, ThreadSanitizer is clean.
+       void raceGetSetBufferSize()
+       {
+               auto async = std::make_shared<AsyncAppender>();
+               async->setName(LOG4CXX_STR("AsyncAppenderRaceStress"));
+
+               std::atomic<int> ready{ 0 };
+               std::atomic<bool> start{ false };
+
+               std::thread writer([&]()
+               {
+                       ready.fetch_add(1, std::memory_order_release);
+                       while (!start.load(std::memory_order_acquire)) {}
+                       // Toggle between 1 and 2 to avoid exercising allocator 
behavior; we only want the data race.
+                       for (int i = 0; i < 200000; ++i)
+                       {
+                               async->setBufferSize((i & 1) + 1);
+                       }
+               });
+
+               std::thread reader([&]()
+               {
+                       ready.fetch_add(1, std::memory_order_release);
+                       while (!start.load(std::memory_order_acquire)) {}
+                       for (int i = 0; i < 200000; ++i)
+                       {
+                               (void)async->getBufferSize();
+                       }
+               });
+
+               while (ready.load(std::memory_order_acquire) != 2) {}
+               start.store(true, std::memory_order_release);
+
+               writer.join();
+               reader.join();
+        }
+
+       void raceGetSetBlocking()
+       {
+               auto async = std::make_shared<AsyncAppender>();
+               async->setName(LOG4CXX_STR("AsyncAppenderRaceStressBlocking"));
+
+               std::atomic<int> ready{ 0 };
+               std::atomic<bool> start{ false };
+
+               std::thread writer([&]()
+               {
+                       ready.fetch_add(1, std::memory_order_release);
+                       while (!start.load(std::memory_order_acquire)) {}
+                       for (int i = 0; i < 200000; ++i)
+                       {
+                               async->setBlocking((i & 1) != 0);
+                       }
+               });
+
+               std::thread reader([&]()
+               {
+                       ready.fetch_add(1, std::memory_order_release);
+                       while (!start.load(std::memory_order_acquire)) {}
+                       for (int i = 0; i < 200000; ++i)
+                       {
+                               (void)async->getBlocking();
+                       }
+               });
+
+               while (ready.load(std::memory_order_acquire) != 2) {}
+               start.store(true, std::memory_order_release);
+
+               writer.join();
+               reader.join();
+       }
+};
+
+LOGUNIT_TEST_SUITE_REGISTRATION(AsyncAppenderRaceStressTestCase);

Reply via email to