From 2f3b9c49a5fa4e867b77b40191c4221db17f0a16 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sun, 12 Jun 2022 20:16:06 +0200 Subject: LibPthread: Move the pthread and semaphore implementation to LibC This additionally adds some compatibility code to redirect linking attempts for LibPthread to LibC instead. --- Userland/Libraries/LibC/pthread_once.cpp | 88 ++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 Userland/Libraries/LibC/pthread_once.cpp (limited to 'Userland/Libraries/LibC/pthread_once.cpp') diff --git a/Userland/Libraries/LibC/pthread_once.cpp b/Userland/Libraries/LibC/pthread_once.cpp new file mode 100644 index 0000000000..ab435d4639 --- /dev/null +++ b/Userland/Libraries/LibC/pthread_once.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020, Sergey Bugaev + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +enum State : i32 { + INITIAL = PTHREAD_ONCE_INIT, + DONE, + PERFORMING_NO_WAITERS, + PERFORMING_WITH_WAITERS, +}; + +// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_once.html +int pthread_once(pthread_once_t* self, void (*callback)(void)) +{ + auto& state = reinterpret_cast&>(*self); + + // See what the current state is, and at the same time grab the lock if we + // got here first. We need acquire ordering here because if we see + // State::DONE, everything we do after that should "happen after" everything + // the other thread has done before writing the State::DONE. + State state2 = State::INITIAL; + bool have_exchanged = state.compare_exchange_strong( + state2, State::PERFORMING_NO_WAITERS, AK::memory_order_acquire); + + if (have_exchanged) { + // We observed State::INITIAL and we've changed it to + // State::PERFORMING_NO_WAITERS, so it's us who should perform the + // operation. + callback(); + // Now, record that we're done. + state2 = state.exchange(State::DONE, AK::memory_order_release); + switch (state2) { + case State::INITIAL: + case State::DONE: + VERIFY_NOT_REACHED(); + case State::PERFORMING_NO_WAITERS: + // The fast path: there's no contention, so we don't have to wake + // anyone. + break; + case State::PERFORMING_WITH_WAITERS: + futex_wake(self, INT_MAX); + break; + } + + return 0; + } + + // We did not get there first. Let's see if we have to wait. + // state2 contains the observed state. + while (true) { + switch (state2) { + case State::INITIAL: + VERIFY_NOT_REACHED(); + case State::DONE: + // Awesome, nothing to do then. + return 0; + case State::PERFORMING_NO_WAITERS: + // We're going to wait for it, but we have to record that we're + // waiting and the other thread should wake us up. We need acquire + // ordering here for the same reason as above. + have_exchanged = state.compare_exchange_strong( + state2, State::PERFORMING_WITH_WAITERS, AK::memory_order_acquire); + if (!have_exchanged) { + // Something has changed already, reevaluate without waiting. + continue; + } + state2 = State::PERFORMING_WITH_WAITERS; + [[fallthrough]]; + case State::PERFORMING_WITH_WAITERS: + // Let's wait for it. + futex_wait(self, state2, nullptr, 0); + // We have been woken up, but that might have been due to a signal + // or something, so we have to reevaluate. We need acquire ordering + // here for the same reason as above. Hopefully we'll just see + // State::DONE this time, but who knows. + state2 = state.load(AK::memory_order_acquire); + continue; + } + } +} -- cgit v1.2.3