Implements protection on remote messages queues
This extends the freelist protection to the remote message queues. They effectively perform doubly linked list entries for the message queue with the enqueue operation first linking in the previous pointer, and then then atomically setting the next. This ensures that the visible states always satisfy the invariant that the forward and backward pointers are correct for any visisble object. There is a key_global that is used for all remote deallocations. The remote cache uses the same protection to build the temporary lists before forwarding to the next allocator. The mpscq is integrated into the remoteallocator as it is no longer a reusable datastructure, but a special purpose implementation.
This commit is contained in:
committed by
Matthew Parkinson
parent
c58b0a5f4d
commit
b501da69db
@@ -1,91 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "bits.h"
|
||||
#include "helpers.h"
|
||||
|
||||
#include <utility>
|
||||
namespace snmalloc
|
||||
{
|
||||
template<
|
||||
class T,
|
||||
template<typename> typename Ptr = Pointer,
|
||||
template<typename> typename AtomicPtr = AtomicPointer>
|
||||
class MPSCQ
|
||||
{
|
||||
private:
|
||||
static_assert(
|
||||
std::is_same<decltype(T::next), AtomicPtr<T>>::value,
|
||||
"T->next must be an AtomicPtr<T>");
|
||||
|
||||
AtomicPtr<T> back{nullptr};
|
||||
Ptr<T> front{nullptr};
|
||||
|
||||
public:
|
||||
constexpr MPSCQ() = default;
|
||||
|
||||
void invariant()
|
||||
{
|
||||
SNMALLOC_ASSERT(back != nullptr);
|
||||
SNMALLOC_ASSERT(front != nullptr);
|
||||
}
|
||||
|
||||
void init(Ptr<T> stub)
|
||||
{
|
||||
stub->next.store(nullptr, std::memory_order_relaxed);
|
||||
front = stub;
|
||||
back.store(stub, std::memory_order_relaxed);
|
||||
invariant();
|
||||
}
|
||||
|
||||
Ptr<T> destroy()
|
||||
{
|
||||
Ptr<T> fnt = front;
|
||||
back.store(nullptr, std::memory_order_relaxed);
|
||||
front = nullptr;
|
||||
return fnt;
|
||||
}
|
||||
|
||||
inline bool is_empty()
|
||||
{
|
||||
Ptr<T> bk = back.load(std::memory_order_relaxed);
|
||||
|
||||
return bk == front;
|
||||
}
|
||||
|
||||
void enqueue(Ptr<T> first, Ptr<T> last)
|
||||
{
|
||||
// Pushes a list of messages to the queue. Each message from first to
|
||||
// last should be linked together through their next pointers.
|
||||
invariant();
|
||||
last->next.store(nullptr, std::memory_order_relaxed);
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
Ptr<T> prev = back.exchange(last, std::memory_order_relaxed);
|
||||
prev->next.store(first, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
Ptr<T> peek()
|
||||
{
|
||||
return front;
|
||||
}
|
||||
|
||||
std::pair<Ptr<T>, bool> dequeue()
|
||||
{
|
||||
// Returns the front message, or null if not possible to return a message.
|
||||
invariant();
|
||||
Ptr<T> first = front;
|
||||
Ptr<T> next = first->next.load(std::memory_order_relaxed);
|
||||
|
||||
Aal::prefetch(&(next->next));
|
||||
if (next != nullptr)
|
||||
{
|
||||
front = next;
|
||||
SNMALLOC_ASSERT(front != nullptr);
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
invariant();
|
||||
return {first, true};
|
||||
}
|
||||
|
||||
return {nullptr, false};
|
||||
}
|
||||
};
|
||||
} // namespace snmalloc
|
||||
Reference in New Issue
Block a user