Skip to content

Commit

Permalink
cpp runtime: Remove pthread dependency.
Browse files Browse the repository at this point in the history
ANTLR doesn't use threads, and it used not to depend on pthread library
either.

It changed recently in 2022: antlr#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 <sonzogniarthur@gmail.com>
  • Loading branch information
ArthurSonzogni committed May 16, 2023
1 parent 2c07aa8 commit e6ef806
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 26 deletions.
14 changes: 0 additions & 14 deletions runtime/Cpp/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
37 changes: 25 additions & 12 deletions runtime/Cpp/runtime/src/internal/Synchronization.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@

#include "antlr4-common.h"

#include <atomic>
#include <functional>
#include <mutex>
#include <shared_mutex>
#include <utility>

#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
Expand Down Expand Up @@ -135,20 +136,32 @@ namespace antlr4::internal {
template <typename Callable, typename... Args>
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<bool> _returned{false};
};

template <typename Callable, typename... Args>
void call_once(OnceFlag &onceFlag, Callable &&callable, Args&&... args) {
#if ANTLR4CPP_USING_ABSEIL
absl::call_once(onceFlag._impl, std::forward<Callable>(callable), std::forward<Args>(args)...);
#else
std::call_once(onceFlag._impl, std::forward<Callable>(callable), std::forward<Args>(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<std::mutex> 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>(callable), std::forward<Args>(args)...);

onceFlag._returned.store(true, std::memory_order_release);
}

} // namespace antlr4::internal

0 comments on commit e6ef806

Please sign in to comment.