]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-116738: Make syslog module thread-safe (#136760)
authorAlper <alperyoney@fb.com>
Mon, 21 Jul 2025 16:24:42 +0000 (09:24 -0700)
committerGitHub <noreply@github.com>
Mon, 21 Jul 2025 16:24:42 +0000 (09:24 -0700)
Make the setlogmask() function in the syslog module thread-safe. These changes are relevant for scenarios where the GIL is disabled or when using subinterpreters.

Lib/test/test_free_threading/test_syslog.py [new file with mode: 0644]
Misc/NEWS.d/next/Core_and_Builtins/2025-07-18-08-43-35.gh-issue-116738.i0HWtP.rst [new file with mode: 0644]
Modules/syslogmodule.c

diff --git a/Lib/test/test_free_threading/test_syslog.py b/Lib/test/test_free_threading/test_syslog.py
new file mode 100644 (file)
index 0000000..b374a98
--- /dev/null
@@ -0,0 +1,44 @@
+import unittest
+import threading
+
+from test.support import import_helper, threading_helper
+from test.support.threading_helper import run_concurrently
+
+syslog = import_helper.import_module("syslog")
+
+NTHREADS = 32
+
+# Similar to Lib/test/test_syslog.py, this test's purpose is to verify that
+# the code neither crashes nor leaks.
+
+
+@threading_helper.requires_working_threading()
+class TestSyslog(unittest.TestCase):
+    def test_racing_syslog(self):
+        def worker():
+            """
+            The syslog module provides the following functions:
+            openlog(), syslog(), closelog(), and setlogmask().
+            """
+            thread_id = threading.get_ident()
+            syslog.openlog(f"thread-id: {thread_id}")
+            try:
+                for _ in range(5):
+                    syslog.syslog("logline")
+                    syslog.setlogmask(syslog.LOG_MASK(syslog.LOG_INFO))
+                    syslog.syslog(syslog.LOG_INFO, "logline LOG_INFO")
+                    syslog.setlogmask(syslog.LOG_MASK(syslog.LOG_ERR))
+                    syslog.syslog(syslog.LOG_ERR, "logline LOG_ERR")
+                    syslog.setlogmask(syslog.LOG_UPTO(syslog.LOG_DEBUG))
+            finally:
+                syslog.closelog()
+
+        # Run the worker concurrently to exercise all these syslog functions
+        run_concurrently(
+            worker_func=worker,
+            nthreads=NTHREADS,
+        )
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-18-08-43-35.gh-issue-116738.i0HWtP.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-18-08-43-35.gh-issue-116738.i0HWtP.rst
new file mode 100644 (file)
index 0000000..77dca40
--- /dev/null
@@ -0,0 +1,2 @@
+Make functions in :mod:`syslog` thread-safe on the :term:`free threaded
+<free threading>` build.
index ab20fff1509dfeed8b0365008fd77060f0931b7d..5d7fd20c4e09992a6982c65140a1cb619093b96e 100644 (file)
@@ -298,7 +298,13 @@ syslog_setlogmask_impl(PyObject *module, long maskpri)
         return -1;
     }
 
-    return setlogmask(maskpri);
+    static PyMutex setlogmask_mutex = {0};
+    PyMutex_Lock(&setlogmask_mutex);
+    // Linux man page (3): setlogmask() is MT-Unsafe race:LogMask.
+    long previous_mask = setlogmask(maskpri);
+    PyMutex_Unlock(&setlogmask_mutex);
+
+    return previous_mask;
 }
 
 /*[clinic input]