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