Skip to content

Commit 79fdf80

Browse files
add tests
1 parent 80b6c24 commit 79fdf80

File tree

5 files changed

+124
-8
lines changed

5 files changed

+124
-8
lines changed

libc/src/__support/CMakeLists.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,30 @@ add_header_library(
398398
libc.src.__support.macros.attributes
399399
)
400400

401+
add_header_library(
402+
aba_ptr
403+
HDRS
404+
aba_ptr.h
405+
DEPENDS
406+
libc.hdr.types.size_t
407+
libc.src.__support.common
408+
libc.src.__support.threads.sleep
409+
)
410+
411+
add_header_library(
412+
mpmc_stack
413+
HDRS
414+
mpmc_stack.h
415+
DEPENDS
416+
libc.src.__support.aba_ptr
417+
libc.src.__support.common
418+
libc.src.__support.CPP.atomic
419+
libc.src.__support.CPP.new
420+
libc.src.__support.CPP.optional
421+
libc.src.__support.CPP.type_traits
422+
)
423+
424+
401425
add_subdirectory(FPUtil)
402426
add_subdirectory(OSUtil)
403427
add_subdirectory(StringUtil)

libc/src/__support/aba_ptr.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLVM_LIBC_SRC___SUPPORT_TAGGED_POINTER_H
1010
#define LLVM_LIBC_SRC___SUPPORT_TAGGED_POINTER_H
1111

12+
#include "hdr/types/size_t.h"
1213
#include "src/__support/common.h"
1314
#include "src/__support/threads/sleep.h"
1415

@@ -24,7 +25,7 @@ template <class T, bool IsAtomic> struct AbaPtrImpl {
2425
union Impl {
2526
struct alignas(2 * alignof(void *)) Atomic {
2627
T *ptr;
27-
__SIZE_TYPE__ tag;
28+
size_t tag;
2829
} atomic;
2930
struct Mutex {
3031
T *ptr;
@@ -59,6 +60,20 @@ template <class T, bool IsAtomic> struct AbaPtrImpl {
5960
__atomic_store_n(&impl.mutex.locked, false, __ATOMIC_RELEASE);
6061
}
6162
}
63+
64+
LIBC_INLINE T *get() const {
65+
if constexpr (IsAtomic) {
66+
// Weak micro-architectures typically reguards simultaneous partial word
67+
// loading and full word loading as a race condition. While there are
68+
// implementations that uses racy read anyway, we still load the whole
69+
// word to avoid any complications.
70+
typename Impl::Atomic snapshot;
71+
__atomic_load(&impl.atomic, &snapshot, __ATOMIC_RELAXED);
72+
return snapshot.ptr;
73+
} else {
74+
return impl.mutex.ptr;
75+
}
76+
}
6277
};
6378

6479
template <class T> using AbaPtr = AbaPtrImpl<T, LIBC_ABA_PTR_IS_ATOMIC>;

libc/src/__support/mpmc_stack.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@
1212
#include "src/__support/CPP/atomic.h"
1313
#include "src/__support/CPP/new.h"
1414
#include "src/__support/CPP/optional.h"
15+
#include "src/__support/CPP/type_traits.h"
1516
#include "src/__support/aba_ptr.h"
1617

1718
namespace LIBC_NAMESPACE_DECL {
1819
template <class T> class MPMCStack {
1920
struct Node {
2021
cpp::Atomic<size_t> visitor;
21-
AbaPtr<Node> next;
22+
Node *next;
2223
T value;
2324

2425
LIBC_INLINE Node(T val) : visitor(0), next(nullptr), value(val) {}
@@ -31,7 +32,7 @@ template <class T> class MPMCStack {
3132
LIBC_INLINE MPMCStack() : head(nullptr) {}
3233
LIBC_INLINE bool push(T value) {
3334
AllocChecker ac;
34-
Node *new_node = new Node(value, ac);
35+
Node *new_node = new (ac) Node(value);
3536
if (!ac)
3637
return false;
3738
head.transaction([new_node](Node *old_head) {
@@ -41,17 +42,17 @@ template <class T> class MPMCStack {
4142
return true;
4243
}
4344
LIBC_INLINE cpp::optional<T> pop() {
44-
cpp::optional<T> res;
45-
Node *node;
46-
head.transaction([&](Node *current_head) {
45+
cpp::optional<T> res = cpp::nullopt;
46+
Node *node = nullptr;
47+
head.transaction([&](Node *current_head) -> Node * {
4748
if (!current_head) {
4849
res = cpp::nullopt;
4950
return nullptr;
5051
}
5152
node = current_head;
5253
node->visitor.fetch_add(1);
53-
res = node->value;
54-
auto next = node->next;
54+
res = cpp::optional<T>{node->value};
55+
Node *next = node->next;
5556
node->visitor.fetch_sub(1);
5657
return next;
5758
});

libc/test/integration/src/__support/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,18 @@ add_subdirectory(threads)
22
if(LIBC_TARGET_OS_IS_GPU)
33
add_subdirectory(GPU)
44
endif()
5+
6+
add_libc_integration_test_suite(libc-support-integration-tests)
7+
8+
add_integration_test(
9+
mpmc_stack_test
10+
SUITE
11+
libc-support-integration-tests
12+
SRCS
13+
mpmc_stack_test.cpp
14+
DEPENDS
15+
libc.src.__support.mpmc_stack
16+
libc.src.__support.threads.thread
17+
libc.src.pthread.pthread_create
18+
libc.src.pthread.pthread_join
19+
)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#include "src/__support/CPP/atomic.h"
2+
#include "src/__support/mpmc_stack.h"
3+
#include "src/pthread/pthread_create.h"
4+
#include "src/pthread/pthread_join.h"
5+
#include "test/IntegrationTest/test.h"
6+
7+
using namespace LIBC_NAMESPACE;
8+
9+
void smoke_test() {
10+
MPMCStack<int> stack;
11+
for (int i = 0; i <= 100; ++i)
12+
if (!stack.push(i))
13+
__builtin_trap();
14+
for (int i = 100; i >= 0; --i)
15+
if (*stack.pop() != i)
16+
__builtin_trap();
17+
if (stack.pop())
18+
__builtin_trap(); // Should be empty now.
19+
}
20+
21+
void multithread_test() {
22+
constexpr static size_t NUM_THREADS = 5;
23+
constexpr static size_t NUM_PUSHES = 100;
24+
struct State {
25+
MPMCStack<size_t> stack;
26+
cpp::Atomic<size_t> counter = 0;
27+
cpp::Atomic<bool> flags[NUM_PUSHES];
28+
} state;
29+
pthread_t threads[NUM_THREADS];
30+
for (size_t i = 0; i < NUM_THREADS; ++i) {
31+
LIBC_NAMESPACE::pthread_create(
32+
&threads[i], nullptr,
33+
[](void *arg) -> void * {
34+
State *state = static_cast<State *>(arg);
35+
for (;;) {
36+
size_t current = state->counter.fetch_add(1);
37+
if (current >= NUM_PUSHES)
38+
break;
39+
if (!state->stack.push(current))
40+
__builtin_trap();
41+
}
42+
while (auto res = state->stack.pop())
43+
state->flags[res.value()].store(true);
44+
return nullptr;
45+
},
46+
&state);
47+
}
48+
for (pthread_t thread : threads)
49+
LIBC_NAMESPACE::pthread_join(thread, nullptr);
50+
while (cpp::optional<size_t> res = state.stack.pop())
51+
state.flags[res.value()].store(true);
52+
for (size_t i = 0; i < NUM_PUSHES; ++i)
53+
if (!state.flags[i].load())
54+
__builtin_trap();
55+
}
56+
57+
TEST_MAIN() {
58+
smoke_test();
59+
multithread_test();
60+
return 0;
61+
}

0 commit comments

Comments
 (0)