Skip to content

[libc][arc4random 3/4] implement global and local random state #152617

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions libc/src/__support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,30 @@ add_header_library(
libc.src.__support.macros.attributes
)

add_header_library(
aba_ptr
HDRS
aba_ptr.h
DEPENDS
libc.hdr.types.size_t
libc.src.__support.common
libc.src.__support.threads.sleep
)

add_header_library(
mpmc_stack
HDRS
mpmc_stack.h
DEPENDS
libc.src.__support.aba_ptr
libc.src.__support.common
libc.src.__support.CPP.atomic
libc.src.__support.CPP.new
libc.src.__support.CPP.optional
libc.src.__support.CPP.type_traits
)


add_subdirectory(FPUtil)
add_subdirectory(OSUtil)
add_subdirectory(StringUtil)
Expand Down
83 changes: 83 additions & 0 deletions libc/src/__support/aba_ptr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//===-- Transactional Ptr for ABA prevention --------------------*- 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___SUPPORT_TAGGED_POINTER_H
#define LLVM_LIBC_SRC___SUPPORT_TAGGED_POINTER_H

#include "hdr/types/size_t.h"
#include "src/__support/common.h"
#include "src/__support/threads/sleep.h"

#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16
#define LIBC_ABA_PTR_IS_ATOMIC true
#else
#define LIBC_ABA_PTR_IS_ATOMIC false
#endif

namespace LIBC_NAMESPACE_DECL {

template <class T, bool IsAtomic> struct AbaPtrImpl {
union Impl {
struct alignas(2 * alignof(void *)) Atomic {
T *ptr;
size_t tag;
} atomic;
struct Mutex {
T *ptr;
bool locked;
} mutex;
} impl;

LIBC_INLINE constexpr AbaPtrImpl(T *ptr)
: impl(IsAtomic ? Impl{.atomic{ptr, 0}} : Impl{.mutex{ptr, false}}) {}

/// User must guarantee that operation is redoable.
template <class Op> LIBC_INLINE void transaction(Op &&op) {
if constexpr (IsAtomic) {
for (;;) {
typename Impl::Atomic snapshot, next;
__atomic_load(&impl.atomic, &snapshot, __ATOMIC_RELAXED);
next.ptr = op(snapshot.ptr);
// Wrapping add for unsigned integers.
next.tag = snapshot.tag + 1;
if (__atomic_compare_exchange(&impl.atomic, &snapshot, &next, true,
__ATOMIC_ACQ_REL, __ATOMIC_RELAXED))
return;
}
} else {
// Acquire the lock.
while (__atomic_exchange_n(&impl.mutex.locked, true, __ATOMIC_ACQUIRE))
while (__atomic_load_n(&impl.mutex.locked, __ATOMIC_RELAXED))
LIBC_NAMESPACE::sleep_briefly();

impl.mutex.ptr = op(impl.mutex.ptr);
// Release the lock.
__atomic_store_n(&impl.mutex.locked, false, __ATOMIC_RELEASE);
}
}

LIBC_INLINE T *get() const {
if constexpr (IsAtomic) {
// Weak micro-architectures typically regards simultaneous partial word
// loading and full word loading as a race condition. While there are
// implementations that uses racy read anyway, we still load the whole
// word to avoid any complications.
typename Impl::Atomic snapshot;
__atomic_load(&impl.atomic, &snapshot, __ATOMIC_RELAXED);
return snapshot.ptr;
} else {
return impl.mutex.ptr;
}
}
};

template <class T> using AbaPtr = AbaPtrImpl<T, LIBC_ABA_PTR_IS_ATOMIC>;
} // namespace LIBC_NAMESPACE_DECL

#undef LIBC_ABA_PTR_IS_ATOMIC
#endif
107 changes: 107 additions & 0 deletions libc/src/__support/mpmc_stack.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//===-- Simple Lock-free MPMC Stack -----------------------------*- 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___SUPPORT_MPMC_STACK_H
#define LLVM_LIBC_SRC___SUPPORT_MPMC_STACK_H

#include "src/__support/CPP/atomic.h"
#include "src/__support/CPP/new.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/aba_ptr.h"

namespace LIBC_NAMESPACE_DECL {
template <class T> class MPMCStack {
struct Node {
cpp::Atomic<size_t> visitor;
Node *next;
T value;

LIBC_INLINE Node(T val) : visitor(0), next(nullptr), value(val) {}
};
AbaPtr<Node> head;

public:
static_assert(cpp::is_copy_constructible<T>::value,
"T must be copy constructible");
LIBC_INLINE constexpr MPMCStack() : head(nullptr) {}
LIBC_INLINE bool push(T value) {
AllocChecker ac;
Node *new_node = new (ac) Node(value);
if (!ac)
return false;
head.transaction([new_node](Node *old_head) {
new_node->next = old_head;
return new_node;
});
return true;
}
LIBC_INLINE bool push_all(T values[], size_t count) {
struct Guard {
size_t count;
Node **allocated;
LIBC_INLINE Guard(Node *allocated[]) : count(0), allocated(allocated) {}
LIBC_INLINE ~Guard() {
for (size_t i = 0; i < count; ++i)
delete allocated[i];
}
LIBC_INLINE void add(Node *node) { allocated[count++] = node; }
LIBC_INLINE void clear() { count = 0; }
};
// Variable sized array is a GNU extension.
__extension__ Node *allocated[count];
{
Guard guard(allocated);
for (size_t i = 0; i < count; ++i) {
AllocChecker ac;
Node *new_node = new (ac) Node(values[i]);
if (!ac)
return false;
guard.add(new_node);
if (i != 0)
new_node->next = allocated[i - 1];
}
guard.clear();
}
head.transaction([&allocated, count](Node *old_head) {
allocated[0]->next = old_head;
return allocated[count - 1];
});
return true;
}
LIBC_INLINE cpp::optional<T> pop() {
cpp::optional<T> res = cpp::nullopt;
Node *node = nullptr;
head.transaction([&](Node *current_head) -> Node * {
if (!current_head) {
res = cpp::nullopt;
return nullptr;
}
node = current_head;
node->visitor.fetch_add(1);
res = cpp::optional<T>{node->value};
Node *next = node->next;
node->visitor.fetch_sub(1);
return next;
});
// On a successful transaction, a node is popped by us. So we must delete
// it. When we are at here, no one else can acquire
// new reference to the node, but we still need to wait until other threads
// inside the transaction who may potentially be holding a reference to the
// node.
if (res) {
// Spin until the node is no longer in use.
while (node->visitor.load() != 0)
LIBC_NAMESPACE::sleep_briefly();
delete node;
}
return res;
}
};
} // namespace LIBC_NAMESPACE_DECL

#endif
20 changes: 20 additions & 0 deletions libc/src/stdlib/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,23 @@ add_entrypoint_object(
libc.src.signal.raise
libc.src.stdlib._Exit
)

add_header_library(
vdso_rng
HDRS
vdso_rng.h
DEPENDS
libc.src.__support.threads.thread # For __cxa_thread_atexit_impl
libc.src.__support.CPP.algorithm
libc.src.__support.CPP.bit
libc.src.__support.CPP.mutex
libc.src.__support.CPP.optional
libc.src.__support.OSUtil.linux.vdso
libc.src.__support.OSUtil.osutil
libc.src.__support.macros.config
libc.src.__support.mpmc_stack
libc.src.__support.threads.callonce
libc.src.__support.threads.linux.raw_mutex
libc.src.sys.auxv.getauxval # TODO: remove public entrypoint dependency
libc.include.sys_syscall
)
Loading
Loading