Skip to content

Commit b7e9e07

Browse files
wip
1 parent 27ed1f9 commit b7e9e07

File tree

2 files changed

+145
-0
lines changed

2 files changed

+145
-0
lines changed

libc/src/__support/aba_ptr.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===-- Transactional Ptr for ABA prevention --------------------*- 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___SUPPORT_TAGGED_POINTER_H
10+
#define LLVM_LIBC_SRC___SUPPORT_TAGGED_POINTER_H
11+
12+
#include "src/__support/common.h"
13+
#include "src/__support/threads/sleep.h"
14+
15+
#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16
16+
#define LIBC_ABA_PTR_IS_ATOMIC true
17+
#else
18+
#define LIBC_ABA_PTR_IS_ATOMIC false
19+
#endif
20+
21+
namespace LIBC_NAMESPACE_DECL {
22+
23+
template <class T, bool IsAtomic> struct AbaPtrImpl {
24+
union Impl {
25+
struct alignas(2 * alignof(void *)) Atomic {
26+
T *ptr;
27+
__SIZE_TYPE__ tag;
28+
} atomic;
29+
struct Mutex {
30+
T *ptr;
31+
bool locked;
32+
} mutex;
33+
} impl;
34+
35+
LIBC_INLINE constexpr AbaPtrImpl(T *ptr)
36+
: impl(IsAtomic ? Impl{.atomic{ptr, 0}} : Impl{.mutex{ptr, false}}) {}
37+
38+
/// User must guarantee that operation is redoable.
39+
template <class Op> LIBC_INLINE void transaction(Op &&op) {
40+
if constexpr (IsAtomic) {
41+
for (;;) {
42+
typename Impl::Atomic snapshot, next;
43+
__atomic_load(&impl.atomic, &snapshot, __ATOMIC_RELAXED);
44+
next.ptr = op(snapshot.ptr);
45+
// Wrapping add for unsigned integers.
46+
next.tag = snapshot.tag + 1;
47+
if (__atomic_compare_exchange(&impl.atomic, &snapshot, &next, true,
48+
__ATOMIC_ACQ_REL, __ATOMIC_RELAXED)) {
49+
return;
50+
}
51+
}
52+
} else {
53+
// Acquire the lock.
54+
while (__atomic_exchange_n(&impl.mutex.locked, true, __ATOMIC_ACQUIRE)) {
55+
while (__atomic_load_n(&impl.mutex.locked, __ATOMIC_RELAXED)) {
56+
LIBC_NAMESPACE::sleep_briefly();
57+
}
58+
}
59+
impl.mutex.ptr = op(impl.mutex.ptr);
60+
// Release the lock.
61+
__atomic_store_n(&impl.mutex.locked, false, __ATOMIC_RELEASE);
62+
}
63+
}
64+
};
65+
66+
template <class T> using AbaPtr = AbaPtrImpl<T, LIBC_ABA_PTR_IS_ATOMIC>;
67+
} // namespace LIBC_NAMESPACE_DECL
68+
69+
#undef LIBC_ABA_PTR_IS_ATOMIC
70+
#endif

libc/src/__support/mpmc_stack.h

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//===-- Simple Lock-free MPMC Stack -----------------------------*- 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___SUPPORT_MPMC_STACK_H
10+
#define LLVM_LIBC_SRC___SUPPORT_MPMC_STACK_H
11+
12+
#include "src/__support/CPP/atomic.h"
13+
#include "src/__support/CPP/new.h"
14+
#include "src/__support/CPP/optional.h"
15+
#include "src/__support/aba_ptr.h"
16+
17+
namespace LIBC_NAMESPACE_DECL {
18+
template <class T> class MPMCStack {
19+
struct Node {
20+
cpp::Atomic<size_t> visitor;
21+
AbaPtr<Node> next;
22+
T value;
23+
24+
LIBC_INLINE Node(T val) : visitor(0), next(nullptr), value(val) {}
25+
};
26+
AbaPtr<Node> head;
27+
28+
public:
29+
static_assert(cpp::is_copy_constructible<T>::value,
30+
"T must be copy constructible");
31+
LIBC_INLINE MPMCStack() : head(nullptr) {}
32+
LIBC_INLINE bool push(T value) {
33+
AllocChecker ac;
34+
Node *new_node = new Node(value, ac);
35+
if (!ac) {
36+
return false;
37+
}
38+
head.transaction([new_node](Node *old_head) {
39+
new_node->next = old_head;
40+
return new_node;
41+
});
42+
return true;
43+
}
44+
LIBC_INLINE cpp::optional<T> pop() {
45+
cpp::optional<T> res;
46+
Node *node;
47+
head.transaction([&](Node *current_head) {
48+
if (!current_head) {
49+
res = cpp::nullopt;
50+
return nullptr;
51+
}
52+
node = current_head;
53+
node->visitor.fetch_add(1);
54+
res = node->value;
55+
auto next = node->next;
56+
node->visitor.fetch_sub(1);
57+
return next;
58+
});
59+
// On a successful transaction, a node is popped by us. So we must delete
60+
// it. When we are at here, no one else can acquire
61+
// new reference to the node, but we still need to wait until other threads
62+
// inside the transaction who may potentially be holding a reference to the
63+
// node.
64+
if (res) {
65+
// Spin until the node is no longer in use.
66+
while (node->visitor.load() != 0)
67+
LIBC_NAMESPACE::sleep_briefly();
68+
delete node;
69+
}
70+
return res;
71+
}
72+
};
73+
} // namespace LIBC_NAMESPACE_DECL
74+
75+
#endif

0 commit comments

Comments
 (0)