1 //===-- Linux implementation of the call_once function --------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Futex.h"
10 
11 #include "include/sys/syscall.h" // For syscall numbers.
12 #include "include/threads.h"     // For call_once related type definition.
13 #include "src/__support/CPP/atomic.h"
14 #include "src/__support/OSUtil/syscall.h" // For syscall functions.
15 #include "src/__support/common.h"
16 #include "src/threads/call_once.h"
17 #include "src/threads/linux/Futex.h"
18 
19 #include <limits.h>
20 #include <linux/futex.h>
21 
22 namespace __llvm_libc {
23 
24 static constexpr FutexWordType START = 0x11;
25 static constexpr FutexWordType WAITING = 0x22;
26 static constexpr FutexWordType FINISH = 0x33;
27 static constexpr once_flag ONCE_FLAG_INIT_VAL = ONCE_FLAG_INIT;
28 
29 LLVM_LIBC_FUNCTION(void, call_once,
30                    (once_flag * flag, __call_once_func_t func)) {
31   auto *futex_word = reinterpret_cast<cpp::Atomic<FutexWordType> *>(flag);
32   static_assert(sizeof(*futex_word) == sizeof(once_flag));
33 
34   FutexWordType not_called = ONCE_FLAG_INIT_VAL.__word;
35 
36   // The C standard wording says:
37   //
38   //     The completion of the function func synchronizes with all
39   //     previous or subsequent calls to call_once with the same
40   //     flag variable.
41   //
42   // What this means is that, the call_once call can return only after
43   // the called function |func| returns. So, we use futexes to synchronize
44   // calls with the same flag value.
45   if (futex_word->compare_exchange_strong(not_called, START)) {
46     func();
47     auto status = futex_word->exchange(FINISH);
48     if (status == WAITING) {
49       __llvm_libc::syscall(SYS_futex, &futex_word->val, FUTEX_WAKE_PRIVATE,
50                            INT_MAX, // Wake all waiters.
51                            0, 0, 0);
52     }
53     return;
54   }
55 
56   FutexWordType status = START;
57   if (futex_word->compare_exchange_strong(status, WAITING) ||
58       status == WAITING) {
59     __llvm_libc::syscall(SYS_futex, &futex_word->val, FUTEX_WAIT_PRIVATE,
60                          WAITING, // Block only if status is still |WAITING|.
61                          0, 0, 0);
62   }
63 }
64 
65 } // namespace __llvm_libc
66