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)20int 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