1 //===-- Linux implementation of the callonce function ---------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "futex_word.h"
11 #include "src/__support/CPP/atomic.h"
12 #include "src/__support/OSUtil/syscall.h" // For syscall functions.
13 #include "src/__support/threads/callonce.h"
16 #include <linux/futex.h>
17 #include <sys/syscall.h> // For syscall numbers.
19 namespace LIBC_NAMESPACE
{
21 static constexpr FutexWordType NOT_CALLED
= 0x0;
22 static constexpr FutexWordType START
= 0x11;
23 static constexpr FutexWordType WAITING
= 0x22;
24 static constexpr FutexWordType FINISH
= 0x33;
26 int callonce(CallOnceFlag
*flag
, CallOnceCallback
*func
) {
27 auto *futex_word
= reinterpret_cast<cpp::Atomic
<FutexWordType
> *>(flag
);
29 FutexWordType not_called
= NOT_CALLED
;
31 // The call_once call can return only after the called function |func|
32 // returns. So, we use futexes to synchronize calls with the same flag value.
33 if (futex_word
->compare_exchange_strong(not_called
, START
)) {
35 auto status
= futex_word
->exchange(FINISH
);
36 if (status
== WAITING
) {
37 LIBC_NAMESPACE::syscall_impl
<long>(FUTEX_SYSCALL_ID
, &futex_word
->val
,
39 INT_MAX
, // Wake all waiters.
45 FutexWordType status
= START
;
46 if (futex_word
->compare_exchange_strong(status
, WAITING
) ||
48 LIBC_NAMESPACE::syscall_impl
<long>(
49 FUTEX_SYSCALL_ID
, &futex_word
->val
, FUTEX_WAIT_PRIVATE
,
50 WAITING
, // Block only if status is still |WAITING|.
57 } // namespace LIBC_NAMESPACE