All checks were successful
Tests / Clang total: 1533, passed: 1533
Clang |Total|New|Outstanding|Fixed|Trend
|:-:|:-:|:-:|:-:|:-:
|0|0|0|0|:clap:
Tests / Debug total: 1531, passed: 1531
Tests / SIMD fallback total: 1533, passed: 1533
Tests / Release [gcc] total: 1533, passed: 1533
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend
|:-:|:-:|:-:|:-:|:-:
|0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 1144, passed: 1144
Tests / Coverage total: 1151, passed: 1151
Code Coverage #### Project Overview
No changes detected, that affect the code coverage.
* Line Coverage: 98.81% (1739/1760)
* Branch Coverage: 64.01% (1526/2384)
* Complexity Density: 0.00
* Lines of Code: 1760
#### Quality Gates Summary
Output truncated.
weaselab/conflict-set/pipeline/head This commit looks good
162 lines
4.8 KiB
C++
162 lines
4.8 KiB
C++
#include "ConflictSet.h"
|
|
#include "Internal.h"
|
|
#include <functional>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
|
|
// This implementation isn't correct for range queries :). It's just intended as
|
|
// a reference for performance comparison with point queries.
|
|
|
|
// struct is from "https://www.cppstories.com/2021/heterogeneous-access-cpp20/"
|
|
struct string_hash {
|
|
using is_transparent = void;
|
|
[[nodiscard]] size_t operator()(std::string_view txt) const {
|
|
return std::hash<std::string_view>{}(txt);
|
|
}
|
|
[[nodiscard]] size_t operator()(const std::string &txt) const {
|
|
return std::hash<std::string>{}(txt);
|
|
}
|
|
};
|
|
|
|
struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|
Impl(int64_t oldestVersion) : oldestVersion(oldestVersion) {}
|
|
void check(const ConflictSet::ReadRange *reads, ConflictSet::Result *results,
|
|
int count) const {
|
|
for (int i = 0; i < count; ++i) {
|
|
auto key =
|
|
std::string_view((const char *)reads[i].begin.p, reads[i].begin.len);
|
|
auto version = reads[i].readVersion;
|
|
if (version < oldestVersion) {
|
|
results[i] = TooOld;
|
|
continue;
|
|
}
|
|
auto iter = map.find(key);
|
|
results[i] =
|
|
iter == map.end() || iter->second <= version ? Commit : Conflict;
|
|
}
|
|
}
|
|
|
|
void addWrites(const ConflictSet::WriteRange *writes, int count,
|
|
int64_t writeVersion) {
|
|
for (int i = 0; i < count; ++i) {
|
|
auto &max = map[std::string((const char *)writes[i].begin.p,
|
|
writes[i].begin.len)];
|
|
assert(writeVersion >= max);
|
|
max = writeVersion;
|
|
keyUpdates += 2;
|
|
}
|
|
}
|
|
|
|
void setOldestVersion(int64_t oldestVersion) {
|
|
if (oldestVersion <= this->oldestVersion) {
|
|
return;
|
|
}
|
|
this->oldestVersion = oldestVersion;
|
|
if (keyUpdates < 100) {
|
|
return;
|
|
}
|
|
auto iter = map.find(removalKey);
|
|
while (keyUpdates > 0) {
|
|
if (iter == map.end()) {
|
|
iter = map.begin();
|
|
}
|
|
for (; iter != map.end(); --keyUpdates) {
|
|
if (iter->second <= oldestVersion) {
|
|
iter = map.erase(iter);
|
|
} else {
|
|
++iter;
|
|
}
|
|
}
|
|
}
|
|
if (iter == map.end()) {
|
|
removalKey.clear();
|
|
} else {
|
|
removalKey = iter->first;
|
|
}
|
|
}
|
|
|
|
private:
|
|
int64_t keyUpdates = 0;
|
|
int64_t oldestVersion;
|
|
std::unordered_map<std::string, int64_t, string_hash, std::equal_to<>> map;
|
|
std::string removalKey;
|
|
};
|
|
|
|
void ConflictSet::check(const ReadRange *reads, Result *results,
|
|
int count) const {
|
|
return impl->check(reads, results, count);
|
|
}
|
|
|
|
void ConflictSet::addWrites(const WriteRange *writes, int count,
|
|
int64_t writeVersion) {
|
|
return impl->addWrites(writes, count, writeVersion);
|
|
}
|
|
|
|
void ConflictSet::setOldestVersion(int64_t oldestVersion) {
|
|
return impl->setOldestVersion(oldestVersion);
|
|
}
|
|
|
|
int64_t ConflictSet::getBytes() const { return -1; }
|
|
|
|
void ConflictSet::getMetricsV1(MetricsV1 **metrics, int *count) const {
|
|
*metrics = nullptr;
|
|
*count = 0;
|
|
}
|
|
|
|
double ConflictSet::MetricsV1::getValue() const { return 0; }
|
|
|
|
ConflictSet::ConflictSet(int64_t oldestVersion)
|
|
: impl(new(safe_malloc(sizeof(Impl))) Impl{oldestVersion}) {}
|
|
|
|
ConflictSet::~ConflictSet() {
|
|
if (impl) {
|
|
impl->~Impl();
|
|
safe_free(impl, sizeof(Impl));
|
|
}
|
|
}
|
|
|
|
ConflictSet::ConflictSet(ConflictSet &&other) noexcept
|
|
: impl(std::exchange(other.impl, nullptr)) {}
|
|
|
|
ConflictSet &ConflictSet::operator=(ConflictSet &&other) noexcept {
|
|
impl = std::exchange(other.impl, nullptr);
|
|
return *this;
|
|
}
|
|
|
|
using ConflictSet_Result = ConflictSet::Result;
|
|
using ConflictSet_Key = ConflictSet::Key;
|
|
using ConflictSet_ReadRange = ConflictSet::ReadRange;
|
|
using ConflictSet_WriteRange = ConflictSet::WriteRange;
|
|
|
|
extern "C" {
|
|
__attribute__((__visibility__("default"))) void
|
|
ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads,
|
|
ConflictSet_Result *results, int count) {
|
|
((ConflictSet::Impl *)cs)->check(reads, results, count);
|
|
}
|
|
__attribute__((__visibility__("default"))) void
|
|
ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count,
|
|
int64_t writeVersion) {
|
|
((ConflictSet::Impl *)cs)->addWrites(writes, count, writeVersion);
|
|
}
|
|
__attribute__((__visibility__("default"))) void
|
|
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
|
|
((ConflictSet::Impl *)cs)->setOldestVersion(oldestVersion);
|
|
}
|
|
__attribute__((__visibility__("default"))) void *
|
|
ConflictSet_create(int64_t oldestVersion) {
|
|
return new (safe_malloc(sizeof(ConflictSet::Impl)))
|
|
ConflictSet::Impl{oldestVersion};
|
|
}
|
|
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
|
using Impl = ConflictSet::Impl;
|
|
((Impl *)cs)->~Impl();
|
|
safe_free(cs, sizeof(Impl));
|
|
}
|
|
__attribute__((__visibility__("default"))) int64_t
|
|
ConflictSet_getBytes(void *cs) {
|
|
using Impl = ConflictSet::Impl;
|
|
return -1;
|
|
}
|
|
}
|