Serenity Operating System
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}