diff options
author | Siva Chandra Reddy <sivachandra@google.com> | 2020-05-12 16:01:28 -0700 |
---|---|---|
committer | Siva Chandra Reddy <sivachandra@google.com> | 2020-05-28 23:45:09 -0700 |
commit | 0baf0e8cfc1845ef92d397c1ae43793bf9e6aaad (patch) | |
tree | 270c0cb7d9f84429262c4e41b7340701342b309e /libc/src/threads/linux | |
parent | 17ed6dcb0c96ac6a6fd5021b326213dbd5fef250 (diff) | |
download | llvm-0baf0e8cfc1845ef92d397c1ae43793bf9e6aaad.zip llvm-0baf0e8cfc1845ef92d397c1ae43793bf9e6aaad.tar.gz llvm-0baf0e8cfc1845ef92d397c1ae43793bf9e6aaad.tar.bz2 |
[libc] Add implementation of call_once from threads.h.
Reviewers: abrachet, maskray
Differential Revision: https://reviews.llvm.org/D79828
Diffstat (limited to 'libc/src/threads/linux')
-rw-r--r-- | libc/src/threads/linux/CMakeLists.txt | 13 | ||||
-rw-r--r-- | libc/src/threads/linux/call_once.cpp | 58 |
2 files changed, 71 insertions, 0 deletions
diff --git a/libc/src/threads/linux/CMakeLists.txt b/libc/src/threads/linux/CMakeLists.txt index 63e956e..08a04c6 100644 --- a/libc/src/threads/linux/CMakeLists.txt +++ b/libc/src/threads/linux/CMakeLists.txt @@ -8,6 +8,19 @@ add_gen_header( ${LIBC_TARGET_MACHINE}/thread_start_args.h.in ) +add_entrypoint_object( + call_once + SRCS + call_once.cpp + HDRS + ../call_once.h + DEPENDS + .threads_utils + libc.config.linux.linux_syscall_h + libc.include.sys_syscall + libc.include.threads +) + add_header_library( threads_utils HDRS diff --git a/libc/src/threads/linux/call_once.cpp b/libc/src/threads/linux/call_once.cpp new file mode 100644 index 0000000..058f370 --- /dev/null +++ b/libc/src/threads/linux/call_once.cpp @@ -0,0 +1,58 @@ +//===-- Linux implementation of the call_once function --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "config/linux/syscall.h" // For syscall functions. +#include "include/sys/syscall.h" // For syscall numbers. +#include "include/threads.h" // For call_once related type definition. +#include "src/__support/common.h" +#include "src/threads/linux/thread_utils.h" + +#include <limits.h> +#include <linux/futex.h> +#include <stdatomic.h> + +namespace __llvm_libc { + +static constexpr unsigned START = 0x11; +static constexpr unsigned WAITING = 0x22; +static constexpr unsigned FINISH = 0x33; + +void LLVM_LIBC_ENTRYPOINT(call_once)(once_flag *flag, __call_once_func_t func) { + FutexData *futex_word = reinterpret_cast<FutexData *>(flag); + unsigned int not_called = ONCE_FLAG_INIT; + + // The C standard wording says: + // + // The completion of the function func synchronizes with all + // previous or subsequent calls to call_once with the same + // flag variable. + // + // What this means is that, the call_once call can return only after + // the called function |func| returns. So, we use futexes to synchronize + // calls with the same flag value. + if (::atomic_compare_exchange_strong(futex_word, ¬_called, START)) { + func(); + auto status = ::atomic_exchange(futex_word, FINISH); + if (status == WAITING) { + __llvm_libc::syscall(SYS_futex, futex_word, FUTEX_WAKE_PRIVATE, + INT_MAX, // Wake all waiters. + 0, 0, 0); + } + return; + } + + unsigned int status = START; + if (::atomic_compare_exchange_strong(futex_word, &status, WAITING) || + status == WAITING) { + __llvm_libc::syscall(SYS_futex, futex_word, FUTEX_WAIT_PRIVATE, + WAITING, // Block only if status is still |WAITING|. + 0, 0, 0); + } +} + +} // namespace __llvm_libc |