2015-11-22 05:56:43 +08:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "threading/sync/lockable.hpp"
|
|
|
|
#include "transactions/transaction.hpp"
|
|
|
|
|
|
|
|
#include "memory/lazy_gc.hpp"
|
2015-12-06 23:42:47 +08:00
|
|
|
#include "mvcc/serialization_error.hpp"
|
|
|
|
#include "database/locking/record_lock.hpp"
|
2015-11-22 05:56:43 +08:00
|
|
|
|
|
|
|
namespace mvcc
|
|
|
|
{
|
|
|
|
|
|
|
|
template <class T>
|
2015-12-08 05:43:54 +08:00
|
|
|
class VersionList : public LazyGC<VersionList<T>>
|
2015-11-22 05:56:43 +08:00
|
|
|
{
|
2015-12-08 01:45:44 +08:00
|
|
|
friend class Accessor;
|
|
|
|
|
2015-11-22 05:56:43 +08:00
|
|
|
public:
|
2015-12-08 05:43:54 +08:00
|
|
|
|
|
|
|
using uptr = std::unique_ptr<VersionList<T>>;
|
|
|
|
|
2015-11-22 05:56:43 +08:00
|
|
|
class Accessor
|
|
|
|
{
|
2015-12-06 23:42:47 +08:00
|
|
|
friend class VersionList<T>;
|
2015-11-22 05:56:43 +08:00
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
Accessor(tx::Transaction& transaction, VersionList<T>& record)
|
2015-12-08 01:45:44 +08:00
|
|
|
: transaction(transaction), record(record)
|
|
|
|
{
|
|
|
|
record.add_ref();
|
|
|
|
}
|
|
|
|
|
2015-12-08 05:43:54 +08:00
|
|
|
public:
|
2015-12-08 01:45:44 +08:00
|
|
|
~Accessor()
|
|
|
|
{
|
|
|
|
record.release_ref();
|
|
|
|
}
|
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
Accessor(const Accessor&) = default;
|
|
|
|
Accessor(Accessor&&) = default;
|
|
|
|
|
|
|
|
Accessor& operator=(const Accessor&) = delete;
|
|
|
|
Accessor& operator=(Accessor&&) = delete;
|
|
|
|
|
|
|
|
T* insert()
|
|
|
|
{
|
|
|
|
return record.insert(transaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
const T* find() const
|
|
|
|
{
|
|
|
|
return record.find(transaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
T* update()
|
|
|
|
{
|
|
|
|
return record.update(transaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool remove()
|
|
|
|
{
|
|
|
|
return record.remove(transaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
tx::Transaction& transaction;
|
|
|
|
VersionList<T>& record;
|
2015-11-22 05:56:43 +08:00
|
|
|
};
|
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
VersionList() = default;
|
|
|
|
|
|
|
|
VersionList(const VersionList&) = delete;
|
|
|
|
VersionList(VersionList&&) = delete;
|
|
|
|
|
|
|
|
Accessor access(tx::Transaction& transaction)
|
2015-11-23 04:35:40 +08:00
|
|
|
{
|
2015-12-06 23:42:47 +08:00
|
|
|
return Accessor(transaction, *this);
|
|
|
|
}
|
2015-11-23 04:35:40 +08:00
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
friend std::ostream& operator<<(std::ostream& stream,
|
|
|
|
const VersionList<T>& vlist)
|
|
|
|
{
|
|
|
|
stream << "VersionList" << std::endl;
|
2015-11-23 04:35:40 +08:00
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
auto record = vlist.head.load();
|
|
|
|
|
|
|
|
while(record != nullptr)
|
|
|
|
{
|
|
|
|
stream << "-- " << *record << std::endl;
|
|
|
|
record = record->next();
|
|
|
|
}
|
|
|
|
|
|
|
|
return stream;
|
2015-11-23 04:35:40 +08:00
|
|
|
}
|
2015-11-22 05:56:43 +08:00
|
|
|
|
2015-12-08 05:43:54 +08:00
|
|
|
auto gc_lock_acquire()
|
|
|
|
{
|
|
|
|
return std::unique_lock<RecordLock>(lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void vacuum()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-11-22 05:56:43 +08:00
|
|
|
private:
|
2015-12-08 01:45:44 +08:00
|
|
|
std::atomic<T*> head {nullptr};
|
2015-12-06 23:42:47 +08:00
|
|
|
RecordLock lock;
|
|
|
|
//static Recycler recycler;
|
2015-11-22 05:56:43 +08:00
|
|
|
|
2015-11-23 04:35:40 +08:00
|
|
|
T* find(const tx::Transaction& t)
|
|
|
|
{
|
|
|
|
auto r = head.load(std::memory_order_acquire);
|
|
|
|
|
|
|
|
// nullptr
|
|
|
|
// |
|
|
|
|
// [v1] ...
|
|
|
|
// |
|
|
|
|
// [v2] <------+
|
|
|
|
// | |
|
|
|
|
// [v3] <------+
|
|
|
|
// | | Jump backwards until you find a first visible
|
|
|
|
// [VerList] ----+ version, or you reach the end of the list
|
|
|
|
//
|
|
|
|
while(r != nullptr && !r->visible(t))
|
2015-12-06 23:42:47 +08:00
|
|
|
r = r->next(std::memory_order_acquire);
|
2015-11-23 04:35:40 +08:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
T* insert(tx::Transaction& t)
|
2015-11-23 04:35:40 +08:00
|
|
|
{
|
2015-12-06 23:42:47 +08:00
|
|
|
assert(head == nullptr);
|
2015-12-08 01:45:44 +08:00
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
// create a first version of the record
|
|
|
|
// TODO replace 'new' with something better
|
|
|
|
auto v1 = new T();
|
2015-11-23 04:35:40 +08:00
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
// mark the record as created by the transaction t
|
|
|
|
v1->mark_created(t);
|
2015-11-23 04:35:40 +08:00
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
head.store(v1, std::memory_order_release);
|
2015-11-23 04:35:40 +08:00
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
return v1;
|
2015-11-23 04:35:40 +08:00
|
|
|
}
|
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
T* update(tx::Transaction& t)
|
2015-11-23 04:35:40 +08:00
|
|
|
{
|
2015-12-06 23:42:47 +08:00
|
|
|
assert(head != nullptr);
|
|
|
|
auto record = lock_and_validate(t);
|
2015-11-23 04:35:40 +08:00
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
// check if we found any visible records
|
|
|
|
if(!record)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
auto updated = new T();
|
|
|
|
updated->data = record->data;
|
|
|
|
|
|
|
|
updated->mark_created(t);
|
|
|
|
record->mark_deleted(t);
|
2015-11-23 04:35:40 +08:00
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
updated->next(record, std::memory_order_release);
|
|
|
|
head.store(updated, std::memory_order_release);
|
|
|
|
|
|
|
|
return updated;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool remove(tx::Transaction& t)
|
|
|
|
{
|
|
|
|
assert(head != nullptr);
|
|
|
|
auto record = lock_and_validate(t);
|
|
|
|
|
|
|
|
if(!record)
|
2015-11-23 04:35:40 +08:00
|
|
|
return false;
|
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
return record->mark_deleted(t), true;
|
|
|
|
}
|
|
|
|
|
|
|
|
T* lock_and_validate(T* record, tx::Transaction& t)
|
|
|
|
{
|
|
|
|
assert(record != nullptr);
|
|
|
|
assert(record == find(t));
|
2015-11-23 04:35:40 +08:00
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
// take a lock on this node
|
|
|
|
t.take_lock(lock);
|
|
|
|
|
|
|
|
// if the record hasn't been deleted yet or the deleting transaction
|
|
|
|
// has aborted, it's ok to modify it
|
2015-12-08 01:45:44 +08:00
|
|
|
if(!record->tx.exp() || record->hints.load().exp.is_aborted())
|
2015-12-06 23:42:47 +08:00
|
|
|
return record;
|
|
|
|
|
|
|
|
// if it committed, then we have a serialization conflict
|
2015-12-08 01:45:44 +08:00
|
|
|
assert(record->hints.load().exp.is_committed());
|
2015-12-06 23:42:47 +08:00
|
|
|
throw SerializationError();
|
|
|
|
}
|
2015-11-23 04:35:40 +08:00
|
|
|
|
2015-12-06 23:42:47 +08:00
|
|
|
T* lock_and_validate(tx::Transaction& t)
|
|
|
|
{
|
|
|
|
// find the visible record version to delete
|
|
|
|
auto record = find(t);
|
|
|
|
return record == nullptr ? nullptr : lock_and_validate(record, t);
|
|
|
|
}
|
2015-11-22 05:56:43 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|
2015-12-06 23:42:47 +08:00
|
|
|
|
|
|
|
// these are convenient typedefs to use in other contexes to make the code a
|
|
|
|
// bit clearer
|
|
|
|
class Vertex;
|
|
|
|
class Edge;
|
|
|
|
|
|
|
|
using VertexRecord = mvcc::VersionList<Vertex>;
|
|
|
|
using EdgeRecord = mvcc::VersionList<Edge>;
|