Serenity Operating System
at master 88 lines 3.2 kB view raw
1/* 2 * Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Assertions.h> 8#include <AK/Atomic.h> 9#include <AK/Types.h> 10#include <pthread.h> 11#include <serenity.h> 12 13enum State : i32 { 14 INITIAL = PTHREAD_ONCE_INIT, 15 DONE, 16 PERFORMING_NO_WAITERS, 17 PERFORMING_WITH_WAITERS, 18}; 19 20// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_once.html 21int pthread_once(pthread_once_t* self, void (*callback)(void)) 22{ 23 auto& state = reinterpret_cast<Atomic<State>&>(*self); 24 25 // See what the current state is, and at the same time grab the lock if we 26 // got here first. We need acquire ordering here because if we see 27 // State::DONE, everything we do after that should "happen after" everything 28 // the other thread has done before writing the State::DONE. 29 State state2 = State::INITIAL; 30 bool have_exchanged = state.compare_exchange_strong( 31 state2, State::PERFORMING_NO_WAITERS, AK::memory_order_acquire); 32 33 if (have_exchanged) { 34 // We observed State::INITIAL and we've changed it to 35 // State::PERFORMING_NO_WAITERS, so it's us who should perform the 36 // operation. 37 callback(); 38 // Now, record that we're done. 39 state2 = state.exchange(State::DONE, AK::memory_order_release); 40 switch (state2) { 41 case State::INITIAL: 42 case State::DONE: 43 VERIFY_NOT_REACHED(); 44 case State::PERFORMING_NO_WAITERS: 45 // The fast path: there's no contention, so we don't have to wake 46 // anyone. 47 break; 48 case State::PERFORMING_WITH_WAITERS: 49 futex_wake(self, INT_MAX, false); 50 break; 51 } 52 53 return 0; 54 } 55 56 // We did not get there first. Let's see if we have to wait. 57 // state2 contains the observed state. 58 while (true) { 59 switch (state2) { 60 case State::INITIAL: 61 VERIFY_NOT_REACHED(); 62 case State::DONE: 63 // Awesome, nothing to do then. 64 return 0; 65 case State::PERFORMING_NO_WAITERS: 66 // We're going to wait for it, but we have to record that we're 67 // waiting and the other thread should wake us up. We need acquire 68 // ordering here for the same reason as above. 69 have_exchanged = state.compare_exchange_strong( 70 state2, State::PERFORMING_WITH_WAITERS, AK::memory_order_acquire); 71 if (!have_exchanged) { 72 // Something has changed already, reevaluate without waiting. 73 continue; 74 } 75 state2 = State::PERFORMING_WITH_WAITERS; 76 [[fallthrough]]; 77 case State::PERFORMING_WITH_WAITERS: 78 // Let's wait for it. 79 futex_wait(self, state2, nullptr, 0, false); 80 // We have been woken up, but that might have been due to a signal 81 // or something, so we have to reevaluate. We need acquire ordering 82 // here for the same reason as above. Hopefully we'll just see 83 // State::DONE this time, but who knows. 84 state2 = state.load(AK::memory_order_acquire); 85 continue; 86 } 87 } 88}