• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- Linux implementation of the callonce 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 "src/__support/threads/callonce.h"
10 #include "src/__support/macros/optimization.h"
11 #include "src/__support/threads/linux/futex_utils.h"
12 
13 namespace LIBC_NAMESPACE {
14 
15 static constexpr FutexWordType NOT_CALLED = 0x0;
16 static constexpr FutexWordType START = 0x11;
17 static constexpr FutexWordType WAITING = 0x22;
18 static constexpr FutexWordType FINISH = 0x33;
19 
callonce(CallOnceFlag * flag,CallOnceCallback * func)20 int callonce(CallOnceFlag *flag, CallOnceCallback *func) {
21   auto *futex_word = reinterpret_cast<Futex *>(flag);
22 
23   FutexWordType not_called = NOT_CALLED;
24 
25   // Avoid cmpxchg operation if the function has already been called.
26   // The destination operand of cmpxchg may receive a write cycle without
27   // regard to the result of the comparison
28   if (LIBC_LIKELY(futex_word->load(cpp::MemoryOrder::RELAXED) == FINISH))
29     return 0;
30 
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)) {
34     func();
35     auto status = futex_word->exchange(FINISH);
36     if (status == WAITING)
37       futex_word->notify_all();
38     return 0;
39   }
40 
41   FutexWordType status = START;
42   if (futex_word->compare_exchange_strong(status, WAITING) ||
43       status == WAITING) {
44     futex_word->wait(WAITING);
45   }
46 
47   return 0;
48 }
49 
50 } // namespace LIBC_NAMESPACE
51