aboutsummaryrefslogtreecommitdiff
path: root/libc/src/threads/linux
diff options
context:
space:
mode:
authorSiva Chandra Reddy <sivachandra@google.com>2020-05-12 16:01:28 -0700
committerSiva Chandra Reddy <sivachandra@google.com>2020-05-28 23:45:09 -0700
commit0baf0e8cfc1845ef92d397c1ae43793bf9e6aaad (patch)
tree270c0cb7d9f84429262c4e41b7340701342b309e /libc/src/threads/linux
parent17ed6dcb0c96ac6a6fd5021b326213dbd5fef250 (diff)
downloadllvm-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.txt13
-rw-r--r--libc/src/threads/linux/call_once.cpp58
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, &not_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