From e6ef8063ecf1d2af2f9610f7caa2214405c6cdfd Mon Sep 17 00:00:00 2001 From: ArthurSonzogni Date: Sun, 22 Jan 2023 14:43:45 +0100 Subject: [PATCH] cpp runtime: Remove pthread dependency. ANTLR doesn't use threads, and it used not to depend on pthread library either. It changed recently in 2022: https://github.com/antlr/antlr4/issues/3708 The patch linked against pthread, because the GNU libstdc++ used to depend on it in their implementation of `std::call_once`. By the way, the libstdc++ stopped it in 2020: https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=93e79ed391b9c636f087e6eb7e70f14963cd10ad So this is not more needed. I would like to stop depending on pthread. I am using ANTLR in C++ WebAssembly for the website: https://arthursonzogni.com/Diagon/ It doesn't compile with emscripten anymore, because by default pthread is not enabled. It could be enabled, but it would force me to deploy cross-origin-isolation: https://web.dev/cross-origin-isolation-guide/ Solutions: 1. Stop linking against pthread, because the libstdc++ stopped depending on it for std::call_once. 2. Implement std::call_once ourself using std::atomic_flag 3. Implement std::call_once ourself using a boolean flag, assuming we don't need to support threads. I chose to do (2) in this patch. Signed-off-by: ArthurSonzogni --- runtime/Cpp/runtime/CMakeLists.txt | 14 ------- .../runtime/src/internal/Synchronization.h | 37 +++++++++++++------ 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/runtime/Cpp/runtime/CMakeLists.txt b/runtime/Cpp/runtime/CMakeLists.txt index 86fdab97f3e..20f0fc7f4df 100644 --- a/runtime/Cpp/runtime/CMakeLists.txt +++ b/runtime/Cpp/runtime/CMakeLists.txt @@ -39,20 +39,6 @@ if (ANTLR_BUILD_STATIC) add_library(antlr4_static STATIC ${libantlrcpp_SRC}) endif() -if (CMAKE_HOST_UNIX) - # Make sure to link against threads (pthreads) library in order to be able to - # make use of std::call_once in the code without producing runtime errors - # (see also https://github.com/antlr/antlr4/issues/3708 and/or https://stackoverflow.com/q/51584960). - find_package(Threads REQUIRED) - - if (TARGET antlr4_shared) - target_link_libraries(antlr4_shared Threads::Threads) - endif() - if (TARGET antlr4_static) - target_link_libraries(antlr4_static Threads::Threads) - endif() -endif() - IF(TRACE_ATN) ADD_DEFINITIONS(-DTRACE_ATN_SIM=1) ENDIF(TRACE_ATN) diff --git a/runtime/Cpp/runtime/src/internal/Synchronization.h b/runtime/Cpp/runtime/src/internal/Synchronization.h index 4f969a8ab64..d0612424ef1 100644 --- a/runtime/Cpp/runtime/src/internal/Synchronization.h +++ b/runtime/Cpp/runtime/src/internal/Synchronization.h @@ -27,12 +27,13 @@ #include "antlr4-common.h" +#include +#include #include #include #include #if ANTLR4CPP_USING_ABSEIL -#include "absl/base/call_once.h" #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" #define ANTLR4CPP_NO_THREAD_SAFTEY_ANALYSIS ABSL_NO_THREAD_SAFETY_ANALYSIS @@ -135,20 +136,32 @@ namespace antlr4::internal { template friend void call_once(OnceFlag &onceFlag, Callable &&callable, Args&&... args); -#if ANTLR4CPP_USING_ABSEIL - absl::once_flag _impl; -#else - std::once_flag _impl; -#endif + std::mutex _mutex; + std::atomic _returned{false}; }; template - void call_once(OnceFlag &onceFlag, Callable &&callable, Args&&... args) { -#if ANTLR4CPP_USING_ABSEIL - absl::call_once(onceFlag._impl, std::forward(callable), std::forward(args)...); -#else - std::call_once(onceFlag._impl, std::forward(callable), std::forward(args)...); -#endif + void call_once(OnceFlag &onceFlag, Callable &&callable, Args &&...args) { + // Fast path: If the callable has already been called and returned, we can + // just return immediately without using a lock. + if (onceFlag._returned.load(std::memory_order_acquire)) { + return; + } + + // Slow path: One thread is executing the callable, while the other threads + // are waiting for it to finish. + const std::lock_guard lock(onceFlag._mutex); + + // When two threads call call_once at the same time, the second thread + // already waited for the callable to finish. It must not call the callable + // again. + if (onceFlag._returned.load(std::memory_order_acquire)) { + return; + } + + std::invoke(std::forward(callable), std::forward(args)...); + + onceFlag._returned.store(true, std::memory_order_release); } } // namespace antlr4::internal