[libc] Add implementation of pthread_once.
The existing thrd_once function has been refactored so that the implementation can be shared between thrd_once and pthread_once functions. Reviewed By: michaelrj Differential Revision: https://reviews.llvm.org/D134716
This commit is contained in:
parent
18f954e715
commit
3367539010
|
@ -234,6 +234,7 @@ def ThreadsAPI : PublicAPI<"threads.h"> {
|
|||
|
||||
def PThreadAPI : PublicAPI<"pthread.h"> {
|
||||
let Types = [
|
||||
"__pthread_once_func_t",
|
||||
"__pthread_start_t",
|
||||
"__pthread_tss_dtor_t",
|
||||
"pthread_attr_t",
|
||||
|
@ -241,6 +242,7 @@ def PThreadAPI : PublicAPI<"pthread.h"> {
|
|||
"pthread_mutexattr_t",
|
||||
"pthread_t",
|
||||
"pthread_key_t",
|
||||
"pthread_once_t",
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -321,6 +321,7 @@ if(LLVM_LIBC_FULL_BUILD)
|
|||
libc.src.pthread.pthread_mutexattr_setpshared
|
||||
libc.src.pthread.pthread_mutexattr_setrobust
|
||||
libc.src.pthread.pthread_mutexattr_settype
|
||||
libc.src.pthread.pthread_once
|
||||
libc.src.pthread.pthread_setspecific
|
||||
|
||||
# stdio.h entrypoints
|
||||
|
|
|
@ -188,6 +188,8 @@ add_gen_header(
|
|||
.llvm-libc-types.pthread_key_t
|
||||
.llvm-libc-types.pthread_mutex_t
|
||||
.llvm-libc-types.pthread_mutexattr_t
|
||||
.llvm-libc-types.pthread_once_t
|
||||
.llvm-libc-types.__pthread_once_func_t
|
||||
.llvm-libc-types.pthread_t
|
||||
)
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ add_header(__bsearchcompare_t HDR __bsearchcompare_t.h)
|
|||
add_header(__call_once_func_t HDR __call_once_func_t.h)
|
||||
add_header(__futex_word HDR __futex_word.h)
|
||||
add_header(__mutex_type HDR __mutex_type.h DEPENDS .__futex_word)
|
||||
add_header(__pthread_once_func_t HDR __pthread_once_func_t.h)
|
||||
add_header(__pthread_start_t HDR __pthread_start_t.h)
|
||||
add_header(__pthread_tss_dtor_t HDR __pthread_tss_dtor_t.h)
|
||||
add_header(__qsortcompare_t HDR __qsortcompare_t.h)
|
||||
|
@ -38,6 +39,7 @@ add_header(pthread_key_t HDR pthread_key_t.h)
|
|||
add_header(pthread_mutex_t HDR pthread_mutex_t.h DEPENDS .__futex_word .__mutex_type)
|
||||
add_header(pthread_t HDR pthread_t.h DEPENDS .__thread_type)
|
||||
add_header(pthread_mutexattr_t HDR pthread_mutexattr_t.h)
|
||||
add_header(pthread_once_t HDR pthread_once_t.h DEPENDS .__futex_word)
|
||||
add_header(rlim_t HDR rlim_t.h)
|
||||
add_header(struct_rlimit HDR struct_rlimit.h DEPENDS .rlim_t)
|
||||
add_header(ssize_t HDR ssize_t.h)
|
||||
|
|
14
libc/include/llvm-libc-types/__pthread_once_func_t.h
Normal file
14
libc/include/llvm-libc-types/__pthread_once_func_t.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
//===-- Definition of __pthread_once_func_t type --------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __LLVM_LIBC_TYPES_PTHREAD_ONCE_FUNC_T_H__
|
||||
#define __LLVM_LIBC_TYPES_PTHREAD_ONCE_FUNC_T_H__
|
||||
|
||||
typedef void (*__pthread_once_func_t)(void);
|
||||
|
||||
#endif // __LLVM_LIBC_TYPES_PTHREAD_ONCE_FUNC_T_H__
|
20
libc/include/llvm-libc-types/pthread_once_t.h
Normal file
20
libc/include/llvm-libc-types/pthread_once_t.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
//===-- Definition of pthread_once_t type ---------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __LLVM_LIBC_TYPES_PTHREAD_ONCE_T_H__
|
||||
#define __LLVM_LIBC_TYPES_PTHREAD_ONCE_T_H__
|
||||
|
||||
#include <llvm-libc-types/__futex_word.h>
|
||||
|
||||
#ifdef __unix__
|
||||
typedef __futex_word pthread_once_t;
|
||||
#else
|
||||
#error "Once flag type not defined for the target platform."
|
||||
#endif
|
||||
|
||||
#endif // __LLVM_LIBC_TYPES_PTHREAD_ONCE_T_H__
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#define PTHREAD_STACK_MIN (1 << 14) // 16KB
|
||||
|
||||
#define PTHREAD_ONCE_INIT {0}
|
||||
|
||||
enum {
|
||||
PTHREAD_CREATE_JOINABLE = 0x0,
|
||||
PTHREAD_CREATE_DETACHED = 0x1,
|
||||
|
|
|
@ -14,6 +14,9 @@ def PThreadStartT : NamedType<"__pthread_start_t">;
|
|||
def PThreadTSSDtorT : NamedType<"__pthread_tss_dtor_t">;
|
||||
def PThreadKeyT : NamedType<"pthread_key_t">;
|
||||
def PThreadKeyTPtr : PtrType<PThreadKeyT>;
|
||||
def PThreadOnceT : NamedType<"pthread_once_t">;
|
||||
def PThreadOnceTPtr : PtrType<PThreadOnceT>;
|
||||
def PThreadOnceCallback : NamedType<"__pthread_once_func_t">;
|
||||
|
||||
def InoT : NamedType<"ino_t">;
|
||||
def UidT : NamedType<"uid_t">;
|
||||
|
@ -675,6 +678,8 @@ def POSIX : StandardSpec<"POSIX"> {
|
|||
PThreadKeyT,
|
||||
PThreadMutexAttrTType,
|
||||
PThreadMutexTType,
|
||||
PThreadOnceCallback,
|
||||
PThreadOnceT,
|
||||
PThreadStartT,
|
||||
PThreadTSSDtorT,
|
||||
PThreadTType,
|
||||
|
@ -861,6 +866,11 @@ def POSIX : StandardSpec<"POSIX"> {
|
|||
RetValSpec<VoidPtr>,
|
||||
[ArgSpec<PThreadKeyT>, ArgSpec<ConstVoidPtr>]
|
||||
>,
|
||||
FunctionSpec<
|
||||
"pthread_once",
|
||||
RetValSpec<IntType>,
|
||||
[ArgSpec<PThreadOnceTPtr>, ArgSpec<PThreadOnceCallback>]
|
||||
>,
|
||||
]
|
||||
>;
|
||||
|
||||
|
|
|
@ -43,3 +43,17 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.thread)
|
|||
libc.src.__support.CPP.optional
|
||||
)
|
||||
endif()
|
||||
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}/callonce.cpp)
|
||||
add_object_library(
|
||||
callonce
|
||||
SRCS
|
||||
${LIBC_TARGET_OS}/callonce.cpp
|
||||
HDRS
|
||||
callonce.h
|
||||
DEPENDS
|
||||
libc.include.sys_syscall
|
||||
libc.src.__support.CPP.atomic
|
||||
libc.src.__support.OSUtil.osutil
|
||||
)
|
||||
endif()
|
||||
|
|
16
libc/src/__support/threads/callonce.h
Normal file
16
libc/src/__support/threads/callonce.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
//===-- Types related to the callonce 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace __llvm_libc {
|
||||
|
||||
struct CallOnceFlag;
|
||||
using CallOnceCallback = void(void);
|
||||
|
||||
int callonce(CallOnceFlag *flag, CallOnceCallback *callback);
|
||||
|
||||
} // namespace __llvm_libc
|
55
libc/src/__support/threads/linux/callonce.cpp
Normal file
55
libc/src/__support/threads/linux/callonce.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
//===-- Linux implementation of the callonce 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 "futex_word.h"
|
||||
|
||||
#include "include/sys/syscall.h" // For syscall numbers.
|
||||
#include "src/__support/CPP/atomic.h"
|
||||
#include "src/__support/OSUtil/syscall.h" // For syscall functions.
|
||||
#include "src/__support/threads/callonce.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <linux/futex.h>
|
||||
|
||||
namespace __llvm_libc {
|
||||
|
||||
static constexpr FutexWordType NOT_CALLED = 0x0;
|
||||
static constexpr FutexWordType START = 0x11;
|
||||
static constexpr FutexWordType WAITING = 0x22;
|
||||
static constexpr FutexWordType FINISH = 0x33;
|
||||
|
||||
int callonce(CallOnceFlag *flag, CallOnceCallback *func) {
|
||||
auto *futex_word = reinterpret_cast<cpp::Atomic<FutexWordType> *>(flag);
|
||||
|
||||
FutexWordType not_called = NOT_CALLED;
|
||||
|
||||
// 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 (futex_word->compare_exchange_strong(not_called, START)) {
|
||||
func();
|
||||
auto status = futex_word->exchange(FINISH);
|
||||
if (status == WAITING) {
|
||||
__llvm_libc::syscall(SYS_futex, &futex_word->val, FUTEX_WAKE_PRIVATE,
|
||||
INT_MAX, // Wake all waiters.
|
||||
0, 0, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
FutexWordType status = START;
|
||||
if (futex_word->compare_exchange_strong(status, WAITING) ||
|
||||
status == WAITING) {
|
||||
__llvm_libc::syscall(SYS_futex, &futex_word->val, FUTEX_WAIT_PRIVATE,
|
||||
WAITING, // Block only if status is still |WAITING|.
|
||||
0, 0, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace __llvm_libc
|
|
@ -386,3 +386,14 @@ add_entrypoint_object(
|
|||
libc.include.pthread
|
||||
libc.src.__support.threads.thread
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
pthread_once
|
||||
SRCS
|
||||
pthread_once.cpp
|
||||
HDRS
|
||||
pthread_once.h
|
||||
DEPENDS
|
||||
libc.include.pthread
|
||||
libc.src.__support.threads.callonce
|
||||
)
|
||||
|
|
23
libc/src/pthread/pthread_once.cpp
Normal file
23
libc/src/pthread/pthread_once.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
//===-- Linux implementation of the pthread_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 "pthread_once.h"
|
||||
#include "src/__support/common.h"
|
||||
#include "src/__support/threads/callonce.h"
|
||||
|
||||
#include <pthread.h> // For pthread_once_t and __pthread_once_func_t definitions.
|
||||
|
||||
namespace __llvm_libc {
|
||||
|
||||
LLVM_LIBC_FUNCTION(int, pthread_once,
|
||||
(pthread_once_t * flag, __pthread_once_func_t func)) {
|
||||
return callonce(reinterpret_cast<CallOnceFlag *>(flag),
|
||||
reinterpret_cast<CallOnceCallback *>(func));
|
||||
}
|
||||
|
||||
} // namespace __llvm_libc
|
20
libc/src/pthread/pthread_once.h
Normal file
20
libc/src/pthread/pthread_once.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
//===-- Implementation header for pthread_once function ---------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIBC_SRC_THREADS_PTHREAD_ONCE_H
|
||||
#define LLVM_LIBC_SRC_THREADS_PTHREAD_ONCE_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
namespace __llvm_libc {
|
||||
|
||||
int pthread_once(pthread_once_t *flag, __pthread_once_func_t func);
|
||||
|
||||
} // namespace __llvm_libc
|
||||
|
||||
#endif // LLVM_LIBC_SRC_THREADS_PTHREAD_ONCE_H
|
|
@ -4,9 +4,13 @@ endif()
|
|||
|
||||
add_entrypoint_object(
|
||||
call_once
|
||||
ALIAS
|
||||
SRCS
|
||||
call_once.cpp
|
||||
HDRS
|
||||
call_once.h
|
||||
DEPENDS
|
||||
.${LIBC_TARGET_OS}.call_once
|
||||
libc.include.threads
|
||||
libc.src.__support.threads.callonce
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
|
|
23
libc/src/threads/call_once.cpp
Normal file
23
libc/src/threads/call_once.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
//===-- 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 "src/threads/call_once.h"
|
||||
#include "src/__support/common.h"
|
||||
#include "src/__support/threads/callonce.h"
|
||||
|
||||
#include <threads.h> // For once_flag and __call_once_func_t definitions.
|
||||
|
||||
namespace __llvm_libc {
|
||||
|
||||
LLVM_LIBC_FUNCTION(void, call_once,
|
||||
(once_flag * flag, __call_once_func_t func)) {
|
||||
callonce(reinterpret_cast<CallOnceFlag *>(flag),
|
||||
reinterpret_cast<CallOnceCallback *>(func));
|
||||
}
|
||||
|
||||
} // namespace __llvm_libc
|
|
@ -9,7 +9,7 @@
|
|||
#ifndef LLVM_LIBC_SRC_THREADS_CALL_ONCE_H
|
||||
#define LLVM_LIBC_SRC_THREADS_CALL_ONCE_H
|
||||
|
||||
#include "include/threads.h"
|
||||
#include <threads.h>
|
||||
|
||||
namespace __llvm_libc {
|
||||
|
||||
|
|
|
@ -1,17 +1,3 @@
|
|||
add_entrypoint_object(
|
||||
call_once
|
||||
SRCS
|
||||
call_once.cpp
|
||||
HDRS
|
||||
../call_once.h
|
||||
DEPENDS
|
||||
.threads_utils
|
||||
libc.include.sys_syscall
|
||||
libc.include.threads
|
||||
libc.src.__support.CPP.atomic
|
||||
libc.src.__support.OSUtil.osutil
|
||||
)
|
||||
|
||||
add_header_library(
|
||||
threads_utils
|
||||
HDRS
|
||||
|
|
|
@ -110,3 +110,23 @@ add_integration_test(
|
|||
libc.src.pthread.pthread_getspecific
|
||||
libc.src.pthread.pthread_setspecific
|
||||
)
|
||||
|
||||
add_integration_test(
|
||||
pthread_once_test
|
||||
SUITE
|
||||
libc-pthread-integration-tests
|
||||
SRCS
|
||||
pthread_once_test.cpp
|
||||
LOADER
|
||||
libc.loader.linux.crt1
|
||||
DEPENDS
|
||||
libc.include.pthread
|
||||
libc.src.pthread.pthread_once
|
||||
libc.src.pthread.pthread_mutex_destroy
|
||||
libc.src.pthread.pthread_mutex_init
|
||||
libc.src.pthread.pthread_mutex_lock
|
||||
libc.src.pthread.pthread_mutex_unlock
|
||||
libc.src.pthread.pthread_create
|
||||
libc.src.pthread.pthread_join
|
||||
libc.src.__support.CPP.atomic
|
||||
)
|
||||
|
|
115
libc/test/integration/src/pthread/pthread_once_test.cpp
Normal file
115
libc/test/integration/src/pthread/pthread_once_test.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
//===-- Tests for pthread_once --------------------------------------------===//
|
||||
//
|
||||
// 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 "src/__support/CPP/atomic.h"
|
||||
#include "src/pthread/pthread_create.h"
|
||||
#include "src/pthread/pthread_join.h"
|
||||
#include "src/pthread/pthread_mutex_destroy.h"
|
||||
#include "src/pthread/pthread_mutex_init.h"
|
||||
#include "src/pthread/pthread_mutex_lock.h"
|
||||
#include "src/pthread/pthread_mutex_unlock.h"
|
||||
#include "src/pthread/pthread_once.h"
|
||||
|
||||
#include "utils/IntegrationTest/test.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
static constexpr unsigned int NUM_THREADS = 5;
|
||||
static __llvm_libc::cpp::Atomic<unsigned int> thread_count;
|
||||
|
||||
static unsigned int call_count;
|
||||
static void pthread_once_func() { ++call_count; }
|
||||
|
||||
static void *func(void *) {
|
||||
static pthread_once_t flag = PTHREAD_ONCE_INIT;
|
||||
__llvm_libc::pthread_once(&flag, pthread_once_func);
|
||||
|
||||
thread_count.fetch_add(1);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void call_from_5_threads() {
|
||||
// Ensure the call count and thread count are 0 to begin with.
|
||||
call_count = 0;
|
||||
thread_count = 0;
|
||||
|
||||
pthread_t threads[NUM_THREADS];
|
||||
for (unsigned int i = 0; i < NUM_THREADS; ++i) {
|
||||
ASSERT_EQ(__llvm_libc::pthread_create(threads + i, nullptr, func, nullptr),
|
||||
0);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < NUM_THREADS; ++i) {
|
||||
void *retval;
|
||||
ASSERT_EQ(__llvm_libc::pthread_join(threads[i], &retval), 0);
|
||||
ASSERT_EQ(uintptr_t(retval), uintptr_t(0));
|
||||
}
|
||||
|
||||
EXPECT_EQ(thread_count.val, 5U);
|
||||
EXPECT_EQ(call_count, 1U);
|
||||
}
|
||||
|
||||
static pthread_mutex_t once_func_blocker;
|
||||
static void blocking_once_func() {
|
||||
__llvm_libc::pthread_mutex_lock(&once_func_blocker);
|
||||
__llvm_libc::pthread_mutex_unlock(&once_func_blocker);
|
||||
}
|
||||
|
||||
static __llvm_libc::cpp::Atomic<unsigned int> start_count;
|
||||
static __llvm_libc::cpp::Atomic<unsigned int> done_count;
|
||||
static void *once_func_caller(void *) {
|
||||
static pthread_once_t flag;
|
||||
start_count.fetch_add(1);
|
||||
__llvm_libc::pthread_once(&flag, blocking_once_func);
|
||||
done_count.fetch_add(1);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Test the synchronization aspect of the pthread_once function.
|
||||
// This is not a fool proof test, but something which might be
|
||||
// useful when we add a flakiness detection scheme to UnitTest.
|
||||
void test_synchronization() {
|
||||
start_count = 0;
|
||||
done_count = 0;
|
||||
|
||||
ASSERT_EQ(__llvm_libc::pthread_mutex_init(&once_func_blocker, nullptr), 0);
|
||||
// Lock the blocking mutex so that the once func blocks.
|
||||
ASSERT_EQ(__llvm_libc::pthread_mutex_lock(&once_func_blocker), 0);
|
||||
|
||||
pthread_t t1, t2;
|
||||
ASSERT_EQ(
|
||||
__llvm_libc::pthread_create(&t1, nullptr, once_func_caller, nullptr), 0);
|
||||
ASSERT_EQ(
|
||||
__llvm_libc::pthread_create(&t2, nullptr, once_func_caller, nullptr), 0);
|
||||
|
||||
while (start_count.load() != 2)
|
||||
; // Spin until both threads start.
|
||||
|
||||
// Since the once func is blocked, the threads should not be done yet.
|
||||
EXPECT_EQ(done_count.val, 0U);
|
||||
|
||||
// Unlock the blocking mutex so that the once func blocks.
|
||||
ASSERT_EQ(__llvm_libc::pthread_mutex_unlock(&once_func_blocker), 0);
|
||||
|
||||
void *retval;
|
||||
ASSERT_EQ(__llvm_libc::pthread_join(t1, &retval), uintptr_t(0));
|
||||
ASSERT_EQ(uintptr_t(retval), 0);
|
||||
ASSERT_EQ(__llvm_libc::pthread_join(t2, &retval), uintptr_t(0));
|
||||
ASSERT_EQ(uintptr_t(retval), 0);
|
||||
|
||||
ASSERT_EQ(done_count.val, 2U);
|
||||
|
||||
__llvm_libc::pthread_mutex_destroy(&once_func_blocker);
|
||||
}
|
||||
|
||||
TEST_MAIN() {
|
||||
call_from_5_threads();
|
||||
test_synchronization();
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user