Skip to content

Commit abc040e

Browse files
author
Siva Chandra Reddy
committed
[libc] Add linux implementations of thrd_create and thrd_join functions.
Reviewers: abrachet, phosek Differential Revision: https://reviews.llvm.org/D75380
1 parent a0cd413 commit abc040e

File tree

16 files changed

+329
-0
lines changed

16 files changed

+329
-0
lines changed

libc/config/linux/api.td

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,15 @@ def SignalAPI : PublicAPI<"signal.h"> {
150150
];
151151
}
152152

153+
def ThreadStartT : TypeDecl<"thrd_start_t"> {
154+
let Decl = "typedef int (*thrd_start_t)(void *);";
155+
}
156+
153157
def ThreadsAPI : PublicAPI<"threads.h"> {
158+
let TypeDeclarations = [
159+
ThreadStartT,
160+
];
161+
154162
let Enumerations = [
155163
"mtx_plain",
156164
"mtx_recursive",
@@ -161,4 +169,9 @@ def ThreadsAPI : PublicAPI<"threads.h"> {
161169
"thrd_error",
162170
"thrd_nomem",
163171
];
172+
173+
let Functions = [
174+
"thrd_create",
175+
"thrd_join",
176+
];
164177
}

libc/config/linux/threads.h.in

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//===--------- Linux specific definitions of types from threads.h ---------===//
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+
%%begin()
10+
11+
typedef struct {
12+
unsigned char __clear_tid[4];
13+
int __tid;
14+
void *__stack;
15+
int __stack_size;
16+
int __retval;
17+
} thrd_t;

libc/include/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,12 @@ add_gen_header(
3939
threads_h
4040
DEF_FILE threads.h.def
4141
GEN_HDR threads.h
42+
PARAMS
43+
platform_threads=../config/${LIBC_TARGET_OS}/threads.h.in
4244
DEPENDS
4345
llvm_libc_common_h
46+
DATA_FILES
47+
../config/${LIBC_TARGET_OS}/threads.h.in
4448
)
4549

4650
add_gen_header(

libc/include/threads.h.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
#include <__llvm-libc-common.h>
1313

14+
%%include_file(${platform_threads})
15+
1416
%%public_api()
1517

1618
#endif // LLVM_LIBC_THREADS_H

libc/lib/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ add_entrypoint_library(
2222
# stdlib.h entrypoints
2323
_Exit
2424
abort
25+
26+
# threads.h entrypoints
27+
thrd_create
28+
thrd_join
2529
)
2630

2731
add_entrypoint_library(

libc/src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ add_subdirectory(stdlib)
55
add_subdirectory(string)
66
# TODO: Add this target conditional to the target OS.
77
add_subdirectory(sys)
8+
add_subdirectory(threads)
89

910
add_subdirectory(__support)

libc/src/threads/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
2+
add_subdirectory(${LIBC_TARGET_OS})
3+
endif()

libc/src/threads/linux/CMakeLists.txt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
add_header_library(
2+
threads_utils
3+
HDRS
4+
thread_utils.h
5+
)
6+
7+
add_entrypoint_object(
8+
thrd_create
9+
SRCS
10+
thrd_create.cpp
11+
HDRS
12+
../thrd_create.h
13+
DEPENDS
14+
errno_h
15+
linux_syscall_h
16+
mmap
17+
support_common_h
18+
sys_syscall_h
19+
threads_h
20+
threads_utils
21+
__errno_location
22+
)
23+
24+
add_entrypoint_object(
25+
thrd_join
26+
SRCS
27+
thrd_join.cpp
28+
HDRS
29+
../thrd_join.h
30+
DEPENDS
31+
linux_syscall_h
32+
munmap
33+
support_common_h
34+
sys_syscall_h
35+
threads_h
36+
threads_utils
37+
)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//===---------- Linux implementation of the thrd_create 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 "config/linux/syscall.h" // For syscall function.
10+
#include "include/errno.h" // For E* error values.
11+
#include "include/sys/mman.h" // For PROT_* and MAP_* definitions.
12+
#include "include/sys/syscall.h" // For syscall numbers.
13+
#include "include/threads.h" // For thrd_* type definitions.
14+
#include "src/__support/common.h"
15+
#include "src/errno/llvmlibc_errno.h"
16+
#include "src/sys/mman/mmap.h"
17+
#include "src/sys/mman/munmap.h"
18+
#include "src/threads/linux/thread_utils.h"
19+
20+
#include <linux/futex.h> // For futex operations.
21+
#include <linux/sched.h> // For CLONE_* flags.
22+
#include <stdint.h>
23+
24+
namespace __llvm_libc {
25+
26+
static void start_thread(thrd_t *thread, thrd_start_t func, void *arg) {
27+
__llvm_libc::syscall(SYS_exit, thread->__retval = func(arg));
28+
}
29+
30+
int LLVM_LIBC_ENTRYPOINT(thrd_create)(thrd_t *thread, thrd_start_t func,
31+
void *arg) {
32+
unsigned clone_flags =
33+
CLONE_VM // Share the memory space with the parent.
34+
| CLONE_FS // Share the file system with the parent.
35+
| CLONE_FILES // Share the files with the parent.
36+
| CLONE_SIGHAND // Share the signal handlers with the parent.
37+
| CLONE_THREAD // Same thread group as the parent.
38+
| CLONE_SYSVSEM // Share a single list of System V semaphore adjustment
39+
// values
40+
| CLONE_PARENT_SETTID // Set child thread ID in |ptid| of the parent.
41+
| CLONE_CHILD_CLEARTID; // Let the kernel clear the tid address and futex
42+
// wake the joining thread.
43+
// TODO: Add the CLONE_SETTLS flag and setup the TLS area correctly when
44+
// making the clone syscall.
45+
46+
void *stack = __llvm_libc::mmap(nullptr, ThreadParams::DefaultStackSize,
47+
PROT_READ | PROT_WRITE,
48+
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
49+
if (stack == MAP_FAILED)
50+
return llvmlibc_errno == ENOMEM ? thrd_nomem : thrd_error;
51+
52+
thread->__stack = stack;
53+
thread->__stack_size = ThreadParams::DefaultStackSize;
54+
thread->__retval = -1;
55+
FutexData *clear_tid_address =
56+
reinterpret_cast<FutexData *>(thread->__clear_tid);
57+
*clear_tid_address = ThreadParams::ClearTIDValue;
58+
59+
long clone_result = __llvm_libc::syscall(
60+
SYS_clone, clone_flags,
61+
reinterpret_cast<uintptr_t>(stack) + ThreadParams::DefaultStackSize - 1,
62+
&thread->__tid, clear_tid_address, 0);
63+
64+
if (clone_result == 0) {
65+
start_thread(thread, func, arg);
66+
} else if (clone_result < 0) {
67+
int error_val = -clone_result;
68+
return error_val == ENOMEM ? thrd_nomem : thrd_error;
69+
}
70+
71+
return thrd_success;
72+
}
73+
74+
} // namespace __llvm_libc

libc/src/threads/linux/thrd_join.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===----------- Linux implementation of the thrd_join 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 "config/linux/syscall.h" // For syscall function.
10+
#include "include/sys/syscall.h" // For syscall numbers.
11+
#include "include/threads.h" // For thrd_* type definitions.
12+
#include "src/__support/common.h"
13+
#include "src/sys/mman/munmap.h"
14+
#include "src/threads/linux/thread_utils.h"
15+
16+
#include <linux/futex.h> // For futex operations.
17+
#include <stdatomic.h> // For atomic_load.
18+
19+
namespace __llvm_libc {
20+
21+
int LLVM_LIBC_ENTRYPOINT(thrd_join)(thrd_t *thread, int *retval) {
22+
FutexData *clear_tid_address =
23+
reinterpret_cast<FutexData *>(thread->__clear_tid);
24+
25+
while (atomic_load(clear_tid_address) != 0) {
26+
// We cannot do a FUTEX_WAIT_PRIVATE here as the kernel does a
27+
// FUTEX_WAKE and not a FUTEX_WAKE_PRIVATE.
28+
__llvm_libc::syscall(SYS_futex, clear_tid_address, FUTEX_WAIT,
29+
ThreadParams::ClearTIDValue, nullptr);
30+
31+
// The kernel should set the value at the clear tid address to zero.
32+
// If not, it is a spurious wake and we should continue to wait on
33+
// the futex.
34+
}
35+
36+
*retval = thread->__retval;
37+
38+
if (__llvm_libc::munmap(thread->__stack, thread->__stack_size) == -1)
39+
return thrd_error;
40+
41+
return thrd_success;
42+
}
43+
44+
} // namespace __llvm_libc

libc/src/threads/linux/thread_utils.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===--- Linux specific definitions to support mutex operations --*- C++ -*===//
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+
#ifndef LLVM_LIBC_SRC_THREADS_LINUX_THREAD_UTILS_H
10+
#define LLVM_LIBC_SRC_THREADS_LINUX_THREAD_UTILS_H
11+
12+
#include <stdint.h>
13+
14+
using FutexData = _Atomic uint32_t;
15+
16+
struct ThreadParams {
17+
static constexpr uintptr_t DefaultStackSize = 1 << 15; // 32 KB
18+
static constexpr uint32_t ClearTIDValue = 0xABCD1234;
19+
};
20+
21+
#endif // LLVM_LIBC_SRC_THREADS_LINUX_THREAD_UTILS_H

libc/src/threads/thrd_create.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===------- Implementation header for thrd_create function ------ *-C++-* ===//
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+
#ifndef LLVM_LIBC_SRC_THREADS_LINUX_THRD_CREATE_H
10+
#define LLVM_LIBC_SRC_THREADS_LINUX_THRD_CREATE_H
11+
12+
#include "include/threads.h"
13+
14+
namespace __llvm_libc {
15+
16+
int thrd_create(thrd_t *thread, thrd_start_t func, void *arg);
17+
18+
} // namespace __llvm_libc
19+
20+
#endif // LLVM_LIBC_SRC_THREADS_LINUX_THRD_CREATE_H

libc/src/threads/thrd_join.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-------- Implementation header for thrd_join function ------- *-C++-* ===//
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+
#ifndef LLVM_LIBC_SRC_THREADS_LINUX_THRD_JOIN_H
10+
#define LLVM_LIBC_SRC_THREADS_LINUX_THRD_JOIN_H
11+
12+
#include "include/threads.h"
13+
14+
namespace __llvm_libc {
15+
16+
int thrd_join(thrd_t *thread, int *retval);
17+
18+
} // namespace __llvm_libc
19+
20+
#endif // LLVM_LIBC_SRC_THREADS_LINUX_THRD_JOIN_H

libc/test/src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ add_subdirectory(signal)
33
add_subdirectory(stdlib)
44
add_subdirectory(string)
55
add_subdirectory(sys)
6+
add_subdirectory(threads)

libc/test/src/threads/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
add_libc_testsuite(libc_threads_unittests)
2+
3+
add_libc_unittest(
4+
thrd_test
5+
SUITE
6+
libc_threads_unittests
7+
SRCS
8+
thrd_test.cpp
9+
DEPENDS
10+
__errno_location
11+
mmap
12+
munmap
13+
threads_h
14+
thrd_create
15+
thrd_join
16+
)

libc/test/src/threads/thrd_test.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//===---------------------- Unittests for thrd_t --------------------------===//
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 "include/threads.h"
10+
#include "src/threads/thrd_create.h"
11+
#include "src/threads/thrd_join.h"
12+
#include "utils/UnitTest/Test.h"
13+
14+
static constexpr int thread_count = 1000;
15+
static int counter = 0;
16+
static int thread_func(void *) {
17+
++counter;
18+
return 0;
19+
}
20+
21+
TEST(ThreadTest, CreateAndJoin) {
22+
for (counter = 0; counter <= thread_count;) {
23+
thrd_t thread;
24+
int old_counter_val = counter;
25+
ASSERT_EQ(__llvm_libc::thrd_create(&thread, thread_func, nullptr),
26+
(int)thrd_success);
27+
int retval = thread_count + 1; // Start with a retval we dont expect.
28+
ASSERT_EQ(__llvm_libc::thrd_join(&thread, &retval), (int)thrd_success);
29+
ASSERT_EQ(retval, 0);
30+
ASSERT_EQ(counter, old_counter_val + 1);
31+
}
32+
}
33+
34+
static int return_arg(void *arg) { return *reinterpret_cast<int *>(arg); }
35+
36+
TEST(ThreadTest, SpawnAndJoin) {
37+
thrd_t thread_list[thread_count];
38+
int args[thread_count];
39+
40+
for (int i = 0; i < thread_count; ++i) {
41+
args[i] = i;
42+
ASSERT_EQ(__llvm_libc::thrd_create(thread_list + i, return_arg, args + i),
43+
(int)thrd_success);
44+
}
45+
46+
for (int i = 0; i < thread_count; ++i) {
47+
int retval = thread_count + 1; // Start with a retval we dont expect.
48+
ASSERT_EQ(__llvm_libc::thrd_join(&thread_list[i], &retval),
49+
(int)thrd_success);
50+
ASSERT_EQ(retval, i);
51+
}
52+
}

0 commit comments

Comments
 (0)