10baf0e8cSSiva Chandra Reddy //===-- Linux implementation of the call_once function --------------------===//
20baf0e8cSSiva Chandra Reddy //
30baf0e8cSSiva Chandra Reddy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40baf0e8cSSiva Chandra Reddy // See https://llvm.org/LICENSE.txt for license information.
50baf0e8cSSiva Chandra Reddy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60baf0e8cSSiva Chandra Reddy //
70baf0e8cSSiva Chandra Reddy //===----------------------------------------------------------------------===//
80baf0e8cSSiva Chandra Reddy 
9*75747c73SSiva Chandra Reddy #include "Futex.h"
10*75747c73SSiva Chandra Reddy 
110baf0e8cSSiva Chandra Reddy #include "include/sys/syscall.h" // For syscall numbers.
120baf0e8cSSiva Chandra Reddy #include "include/threads.h"     // For call_once related type definition.
13*75747c73SSiva Chandra Reddy #include "src/__support/CPP/atomic.h"
143cc2161cSSiva Chandra Reddy #include "src/__support/OSUtil/syscall.h" // For syscall functions.
150baf0e8cSSiva Chandra Reddy #include "src/__support/common.h"
16*75747c73SSiva Chandra Reddy #include "src/threads/call_once.h"
17004c7b1dSSiva Chandra Reddy #include "src/threads/linux/Futex.h"
180baf0e8cSSiva Chandra Reddy 
190baf0e8cSSiva Chandra Reddy #include <limits.h>
200baf0e8cSSiva Chandra Reddy #include <linux/futex.h>
210baf0e8cSSiva Chandra Reddy 
220baf0e8cSSiva Chandra Reddy namespace __llvm_libc {
230baf0e8cSSiva Chandra Reddy 
24*75747c73SSiva Chandra Reddy static constexpr FutexWordType START = 0x11;
25*75747c73SSiva Chandra Reddy static constexpr FutexWordType WAITING = 0x22;
26*75747c73SSiva Chandra Reddy static constexpr FutexWordType FINISH = 0x33;
27*75747c73SSiva Chandra Reddy static constexpr once_flag ONCE_FLAG_INIT_VAL = ONCE_FLAG_INIT;
280baf0e8cSSiva Chandra Reddy 
29a0b65a7bSMichael Jones LLVM_LIBC_FUNCTION(void, call_once,
30a0b65a7bSMichael Jones                    (once_flag * flag, __call_once_func_t func)) {
31*75747c73SSiva Chandra Reddy   auto *futex_word = reinterpret_cast<cpp::Atomic<FutexWordType> *>(flag);
32*75747c73SSiva Chandra Reddy   static_assert(sizeof(*futex_word) == sizeof(once_flag));
33*75747c73SSiva Chandra Reddy 
34*75747c73SSiva Chandra Reddy   FutexWordType not_called = ONCE_FLAG_INIT_VAL.__word;
350baf0e8cSSiva Chandra Reddy 
360baf0e8cSSiva Chandra Reddy   // The C standard wording says:
370baf0e8cSSiva Chandra Reddy   //
380baf0e8cSSiva Chandra Reddy   //     The completion of the function func synchronizes with all
390baf0e8cSSiva Chandra Reddy   //     previous or subsequent calls to call_once with the same
400baf0e8cSSiva Chandra Reddy   //     flag variable.
410baf0e8cSSiva Chandra Reddy   //
420baf0e8cSSiva Chandra Reddy   // What this means is that, the call_once call can return only after
430baf0e8cSSiva Chandra Reddy   // the called function |func| returns. So, we use futexes to synchronize
440baf0e8cSSiva Chandra Reddy   // calls with the same flag value.
45*75747c73SSiva Chandra Reddy   if (futex_word->compare_exchange_strong(not_called, START)) {
460baf0e8cSSiva Chandra Reddy     func();
47*75747c73SSiva Chandra Reddy     auto status = futex_word->exchange(FINISH);
480baf0e8cSSiva Chandra Reddy     if (status == WAITING) {
49*75747c73SSiva Chandra Reddy       __llvm_libc::syscall(SYS_futex, &futex_word->val, FUTEX_WAKE_PRIVATE,
500baf0e8cSSiva Chandra Reddy                            INT_MAX, // Wake all waiters.
510baf0e8cSSiva Chandra Reddy                            0, 0, 0);
520baf0e8cSSiva Chandra Reddy     }
530baf0e8cSSiva Chandra Reddy     return;
540baf0e8cSSiva Chandra Reddy   }
550baf0e8cSSiva Chandra Reddy 
56*75747c73SSiva Chandra Reddy   FutexWordType status = START;
57*75747c73SSiva Chandra Reddy   if (futex_word->compare_exchange_strong(status, WAITING) ||
580baf0e8cSSiva Chandra Reddy       status == WAITING) {
59*75747c73SSiva Chandra Reddy     __llvm_libc::syscall(SYS_futex, &futex_word->val, FUTEX_WAIT_PRIVATE,
600baf0e8cSSiva Chandra Reddy                          WAITING, // Block only if status is still |WAITING|.
610baf0e8cSSiva Chandra Reddy                          0, 0, 0);
620baf0e8cSSiva Chandra Reddy   }
630baf0e8cSSiva Chandra Reddy }
640baf0e8cSSiva Chandra Reddy 
650baf0e8cSSiva Chandra Reddy } // namespace __llvm_libc
66