Commit 446be901 authored by Andrey Kamakin's avatar Andrey Kamakin

Added lock free map code and simple bench.

Ref T8628
parent 82fc4014
LockFreeMap @ ca6a97b1
Subproject commit ca6a97b179822b6be0107d2302585487a6145d4b
/*------------------------------------------------------------------------
Junction: Concurrent data structures in C++
Copyright (c) 2016 Jeff Preshing
Distributed under the Simplified BSD License.
Original location: https://github.com/preshing/junction
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the LICENSE file for more information.
------------------------------------------------------------------------*/
#ifndef ATOMIC_H
#define ATOMIC_H
#include <atomic>
inline void signalFenceConsume()
{
std::atomic_signal_fence(std::memory_order_acquire);
}
inline void signalFenceAcquire()
{
std::atomic_signal_fence(std::memory_order_acquire);
}
inline void signalFenceRelease()
{
std::atomic_signal_fence(std::memory_order_release);
}
inline void signalFenceSeqCst()
{
std::atomic_signal_fence(std::memory_order_seq_cst);
}
inline void threadFenceConsume()
{
std::atomic_thread_fence(std::memory_order_acquire);
}
inline void threadFenceAcquire()
{
std::atomic_thread_fence(std::memory_order_acquire);
}
inline void threadFenceRelease()
{
std::atomic_thread_fence(std::memory_order_release);
}
inline void threadFenceSeqCst()
{
std::atomic_thread_fence(std::memory_order_seq_cst);
}
enum MemoryOrder {
Relaxed = std::memory_order_relaxed,
Consume = std::memory_order_consume,
Acquire = std::memory_order_acquire,
Release = std::memory_order_release,
ConsumeRelease = std::memory_order_acq_rel,
AcquireRelease = std::memory_order_acq_rel,
};
template <typename T>
class Atomic : protected std::atomic<T>
{
private:
T operator=(T value);
public:
Atomic()
{
}
Atomic(T value) : std::atomic<T>(value)
{
}
Atomic(const Atomic& other) : std::atomic<T>(other.loadNonatomic())
{
}
T loadNonatomic() const
{
return std::atomic<T>::load(std::memory_order_relaxed);
}
T load(MemoryOrder memoryOrder) const
{
return std::atomic<T>::load((std::memory_order) memoryOrder);
}
void storeNonatomic(T value)
{
return std::atomic<T>::store(value, std::memory_order_relaxed);
}
void store(T value, MemoryOrder memoryOrder)
{
return std::atomic<T>::store(value, (std::memory_order) memoryOrder);
}
T compareExchange(T expected, T desired, MemoryOrder memoryOrder)
{
std::atomic<T>::compare_exchange_strong(expected, desired, (std::memory_order) memoryOrder);
return expected; // modified by reference by compare_exchange_strong
}
bool compareExchangeStrong(T& expected, T desired, MemoryOrder memoryOrder)
{
return std::atomic<T>::compare_exchange_strong(expected, desired, (std::memory_order) memoryOrder);
}
bool compareExchangeWeak(T& expected, T desired, MemoryOrder success, MemoryOrder failure)
{
return std::atomic<T>::compare_exchange_weak(expected, desired, (std::memory_order) success, (std::memory_order) failure);
}
T exchange(T desired, MemoryOrder memoryOrder)
{
return std::atomic<T>::exchange(desired, (std::memory_order) memoryOrder);
}
T fetchAdd(T operand, MemoryOrder memoryOrder)
{
return std::atomic<T>::fetch_add(operand, (std::memory_order) memoryOrder);
}
T fetchSub(T operand, MemoryOrder memoryOrder)
{
return std::atomic<T>::fetch_sub(operand, (std::memory_order) memoryOrder);
}
T fetchAnd(T operand, MemoryOrder memoryOrder)
{
return std::atomic<T>::fetch_and(operand, (std::memory_order) memoryOrder);
}
T fetchOr(T operand, MemoryOrder memoryOrder)
{
return std::atomic<T>::fetch_or(operand, (std::memory_order) memoryOrder);
}
};
#endif // ATOMIC_H
This diff is collapsed.
This diff is collapsed.
/*------------------------------------------------------------------------
Junction: Concurrent data structures in C++
Copyright (c) 2016 Jeff Preshing
Distributed under the Simplified BSD License.
Original location: https://github.com/preshing/junction
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the LICENSE file for more information.
------------------------------------------------------------------------*/
#ifndef MAPTRAITS_H
#define MAPTRAITS_H
#include "Util.h"
template <class T>
struct DefaultKeyTraits {
typedef T Key;
typedef typename BestFit<T>::Unsigned Hash;
static const Key NullKey = Key(0);
static const Hash NullHash = Hash(0);
static Hash hash(T key)
{
return avalanche(Hash(key));
}
static Key dehash(Hash hash)
{
return (T) deavalanche(hash);
}
};
template <class T>
struct DefaultValueTraits {
typedef T Value;
typedef typename BestFit<T>::Unsigned IntType;
static const IntType NullValue = 0;
static const IntType Redirect = 1;
};
#endif // MAPTRAITS_H
/*------------------------------------------------------------------------
Junction: Concurrent data structures in C++
Copyright (c) 2016 Jeff Preshing
Distributed under the Simplified BSD License.
Original location: https://github.com/preshing/junction
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the LICENSE file for more information.
------------------------------------------------------------------------*/
#ifndef QSBR_H
#define QSBR_H
#include <QVector>
#include <QMutex>
#include <QMutexLocker>
#define CALL_MEMBER(obj, pmf) ((obj).*(pmf))
class QSBR
{
private:
struct Action {
void (*func)(void*);
quint64 param[4]; // Size limit found experimentally. Verified by assert below.
Action() = default;
Action(void (*f)(void*), void* p, quint64 paramSize) : func(f)
{
Q_ASSERT(paramSize <= sizeof(param)); // Verify size limit.
memcpy(&param, p, paramSize);
}
void operator()()
{
func(&param);
}
};
struct Status {
qint16 inUse : 1;
qint16 wasIdle : 1;
qint16 nextFree : 14;
Status() : inUse(1), wasIdle(0), nextFree(0)
{
}
};
QMutex m_mutex;
QVector<Status> m_status;
qint64 m_freeIndex;
qint64 m_numContexts;
qint64 m_remaining;
QVector<Action> m_deferredActions;
QVector<Action> m_pendingActions;
void onAllQuiescentStatesPassed(QVector<Action>& actions)
{
// m_mutex must be held
actions.swap(m_pendingActions);
m_pendingActions.swap(m_deferredActions);
m_remaining = m_numContexts;
for (quint64 i = 0; i < m_status.size(); i++) {
m_status[i].wasIdle = 0;
}
}
QSBR() : m_freeIndex(-1), m_numContexts(0), m_remaining(0)
{
}
public:
typedef qint16 Context;
static QSBR &instance()
{
static QSBR m_instance;
return m_instance;
}
Context createContext()
{
QMutexLocker guard(&m_mutex);
m_numContexts++;
m_remaining++;
Q_ASSERT(m_numContexts < (1 << 14));
qint64 context = m_freeIndex;
if (context >= 0) {
Q_ASSERT(context < (qint64) m_status.size());
Q_ASSERT(!m_status[context].inUse);
m_freeIndex = m_status[context].nextFree;
m_status[context] = Status();
} else {
context = m_status.size();
m_status.append(Status());
}
return context;
}
void destroyContext(QSBR::Context context)
{
QVector<Action> actions;
{
QMutexLocker guard(&m_mutex);
Q_ASSERT(context < m_status.size());
if (m_status[context].inUse && !m_status[context].wasIdle) {
Q_ASSERT(m_remaining > 0);
--m_remaining;
}
m_status[context].inUse = 0;
m_status[context].nextFree = m_freeIndex;
m_freeIndex = context;
m_numContexts--;
if (m_remaining == 0) {
onAllQuiescentStatesPassed(actions);
}
}
for (quint64 i = 0; i < actions.size(); i++) {
actions[i]();
}
}
template <class T>
void enqueue(void (T::*pmf)(), T* target)
{
struct Closure {
void (T::*pmf)();
T* target;
static void thunk(void* param)
{
Closure* self = (Closure*) param;
CALL_MEMBER(*self->target, self->pmf)();
}
};
Closure closure = {pmf, target};
QMutexLocker guard(&m_mutex);
m_deferredActions.append(Action(Closure::thunk, &closure, sizeof(closure)));
}
void update(QSBR::Context context)
{
QVector<Action> actions;
{
QMutexLocker guard(&m_mutex);
Q_ASSERT(context < m_status.size());
Status& status = m_status[context];
Q_ASSERT(status.inUse);
if (status.wasIdle) {
return;
}
status.wasIdle = 1;
Q_ASSERT(m_remaining > 0);
if (--m_remaining > 0) {
return;
}
onAllQuiescentStatesPassed(actions);
}
for (quint64 i = 0; i < actions.size(); i++) {
actions[i]();
}
}
void flush()
{
// This is like saying that all contexts are quiescent,
// so we can issue all actions at once.
// No lock is taken.
for (quint64 i = 0; i < m_pendingActions.size(); i++) {
m_pendingActions[i]();
}
m_pendingActions.clear();
for (quint64 i = 0; i < m_deferredActions.size(); i++) {
m_deferredActions[i]();
}
m_deferredActions.clear();
m_remaining = m_numContexts;
}
};
#endif // QSBR_H
/*------------------------------------------------------------------------
Junction: Concurrent data structures in C++
Copyright (c) 2016 Jeff Preshing
Distributed under the Simplified BSD License.
Original location: https://github.com/preshing/junction
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the LICENSE file for more information.
------------------------------------------------------------------------*/
#ifndef SIMPLEJOBCOORDINATOR_H
#define SIMPLEJOBCOORDINATOR_H
#include <QMutex>
#include <QWaitCondition>
#include <QMutexLocker>
#include "Atomic.h"
class SimpleJobCoordinator
{
public:
struct Job {
virtual ~Job()
{
}
virtual void run() = 0;
};
private:
Atomic<quint64> m_job;
QMutex mutex;
QWaitCondition condVar;
public:
SimpleJobCoordinator() : m_job(quint64(NULL))
{
}
Job* loadConsume() const
{
return (Job*) m_job.load(Consume);
}
void storeRelease(Job* job)
{
{
QMutexLocker guard(&mutex);
m_job.store(quint64(job), Release);
}
condVar.wakeAll();
}
void participate()
{
quint64 prevJob = quint64(NULL);
for (;;) {
quint64 job = m_job.load(Consume);
if (job == prevJob) {
QMutexLocker guard(&mutex);
for (;;) {
job = m_job.loadNonatomic(); // No concurrent writes inside lock
if (job != prevJob) {
break;
}
condVar.wait(&mutex);
}
}
if (job == 1) {
return;
}
reinterpret_cast<Job*>(job)->run();
prevJob = job;
}
}
void runOne(Job* job)
{
Q_ASSERT(job != (Job*) m_job.load(Relaxed));
storeRelease(job);
job->run();
}
void end()
{
{
QMutexLocker guard(&mutex);
m_job.store(1, Release);
}
condVar.wakeAll();
}
};
#endif // SIMPLEJOBCOORDINATOR_H
/*------------------------------------------------------------------------
Junction: Concurrent data structures in C++
Copyright (c) 2016 Jeff Preshing
Distributed under the Simplified BSD License.
Original location: https://github.com/preshing/junction
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the LICENSE file for more information.
------------------------------------------------------------------------*/
#ifndef UTIL_H
#define UTIL_H
#include <QtCore>
template <typename T>
struct BestFit;
template <>
struct BestFit<qint32> {
typedef quint32 Unsigned;
typedef qint32 Signed;
};
template <>
struct BestFit<quint32> {
typedef quint32 Unsigned;
typedef qint32 Signed;
};
template <>
struct BestFit<qint64> {
typedef quint64 Unsigned;
typedef qint64 Signed;
};
template <>
struct BestFit<quint64> {
typedef quint64 Unsigned;
typedef qint64 Signed;
};
template <class T>
struct BestFit<T*> {
typedef quint64 Unsigned;
typedef qint64 Signed;
};
inline quint32 roundUpPowerOf2(quint32 v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
inline qint32 roundUpPowerOf2(qint32 v)
{
return (qint32) roundUpPowerOf2((quint32) v);
}
inline quint64 roundUpPowerOf2(quint64 v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v |= v >> 32;
v++;
return v;
}
inline qint64 roundUpPowerOf2(qint64 v)
{
return (qint64) roundUpPowerOf2((quint64) v);
}
inline bool isPowerOf2(quint64 v)
{
return (v & (v - 1)) == 0;
}
// from code.google.com/p/smhasher/wiki/MurmurHash3
inline quint32 avalanche(quint32 h)
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
inline quint32 deavalanche(quint32 h)
{
h ^= h >> 16;
h *= 0x7ed1b41d;
h ^= (h ^ (h >> 13)) >> 13;
h *= 0xa5cb9243;
h ^= h >> 16;
return h;
}
// from code.google.com/p/smhasher/wiki/MurmurHash3
inline quint64 avalanche(quint64 h)
{
h ^= h >> 33;
h *= 0xff51afd7ed558ccd;
h ^= h >> 33;
h *= 0xc4ceb9fe1a85ec53;
h ^= h >> 33;
return h;
}
inline quint64 deavalanche(quint64 h)
{
h ^= h >> 33;
h *= 0x9cb4b2f8129337db;
h ^= h >> 33;
h *= 0x4f74430c22a54005;
h ^= h >> 33;
return h;
}
#endif // UTIL_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment