diff --git a/ConflictSet.cpp b/ConflictSet.cpp index 6f9e210..804a88e 100644 --- a/ConflictSet.cpp +++ b/ConflictSet.cpp @@ -152,9 +152,6 @@ template struct ArenaAlloc { [[nodiscard]] T *allocate(size_t n) { if (n > 0xfffffffffffffffful / sizeof(T)) { // NOLINT - fprintf(stderr, "Requested bad alloc! sizeof(T): %zu, n: %zu\n", - sizeof(T), n); // NOLINT - fflush(stderr); abort(); } @@ -259,13 +256,10 @@ private: uint64_t inc{}; }; -// TODO provide a way to seed this -thread_local inline Random gRandom{0, 0}; - -template void shuffle(Container &x) { +template void shuffle(Random &rand, Container &x) { using std::swap; for (int i = x.size() - 1; i > 0; --i) { - int j = gRandom.bounded(i + 1); + int j = rand.bounded(i + 1); if (i != j) { swap(x[i], x[j]); } @@ -433,13 +427,6 @@ uint32_t Arbitrary::bounded(uint32_t s) { } } -void initFuzz(const uint8_t *data, size_t size) { - gArbitrary = Arbitrary{{data, size}}; - uint64_t state = gArbitrary.next(); - uint64_t seq = gArbitrary.next(); - gRandom = Random{state, seq}; -} - // ==================== END ARBITRARY IMPL ==================== #define SHOW_PRIORITY 0 @@ -501,7 +488,8 @@ struct Node { }; // Note: `rangeVersion` is left uninitialized. -Node *createNode(const Key &key, Node *parent, int64_t pointVersion) { +Node *createNode(const Key &key, Node *parent, int64_t pointVersion, + Random &rand) { assert(key.len <= std::numeric_limits::max()); Node *result = (Node *)malloc(sizeof(Node) + key.len); result->maxVersion = pointVersion; @@ -509,7 +497,7 @@ Node *createNode(const Key &key, Node *parent, int64_t pointVersion) { result->child[0] = nullptr; result->child[1] = nullptr; result->parent = parent; - result->priority = gRandom.next(); + result->priority = rand.next(); #if SHOW_PRIORITY result->priority &= 0xff; #endif @@ -902,10 +890,13 @@ bool checkCorrectness(Node *node, ReferenceImpl &refImpl) { } // namespace struct __attribute__((__visibility__("hidden"))) ConflictSet::Impl { + Random rand; Node *root; int64_t oldestVersion; - explicit Impl(int64_t oldestVersion) noexcept - : root(createNode({nullptr, 0}, nullptr, oldestVersion)), + + explicit Impl(int64_t oldestVersion, uint64_t seed) noexcept + : rand{seed & 0xfffffffful, seed >> 32}, + root(createNode({nullptr, 0}, nullptr, oldestVersion, rand)), oldestVersion(oldestVersion) { root->rangeVersion = oldestVersion; } @@ -954,11 +945,13 @@ struct __attribute__((__visibility__("hidden"))) ConflictSet::Impl { Node *parent; const Key *key; int64_t writeVersion; + Random *rand; StepwiseInsert() {} - StepwiseInsert(Node **root, const Key &key, int64_t writeVersion) - : current(root), parent(nullptr), key(&key), - writeVersion(writeVersion) {} + StepwiseInsert(Node **root, const Key &key, int64_t writeVersion, + Random *rand) + : current(root), parent(nullptr), key(&key), writeVersion(writeVersion), + rand(rand) {} bool step() { #if DEBUG fprintf(stderr, "Step insert of %.*s. At node: %.*s\n", key->len, key->p, @@ -966,7 +959,7 @@ struct __attribute__((__visibility__("hidden"))) ConflictSet::Impl { (*current) ? (const char *)((*current) + 1) : "nullptr"); #endif if (*current == nullptr) { - auto *newNode = createNode(*key, parent, writeVersion); + auto *newNode = createNode(*key, parent, writeVersion, *rand); *current = newNode; // We could interleave the iteration in ::next, but we'd need a careful // analysis for correctness and it's unlikely to be worthwhile. @@ -1000,7 +993,7 @@ struct __attribute__((__visibility__("hidden"))) ConflictSet::Impl { assert(writes[i].end.len == 0); stepwiseInserts[i] = - StepwiseInsert{&root, writes[i].begin, writes[i].writeVersion}; + StepwiseInsert{&root, writes[i].begin, writes[i].writeVersion, &rand}; } // TODO Descend until queries for front and back diverge @@ -1008,7 +1001,7 @@ struct __attribute__((__visibility__("hidden"))) ConflictSet::Impl { // Mitigate potential n^2 behavior of insertion by shuffling the insertion // order. Not sure how this interacts with interleaved insertion but it's // probably fine. - shuffle(stepwiseInserts); + shuffle(rand, stepwiseInserts); runInterleaved(stepwiseInserts); @@ -1091,8 +1084,8 @@ void ConflictSet::setOldestVersion(int64_t oldestVersion) { return impl->setOldestVersion(oldestVersion); } -ConflictSet::ConflictSet(int64_t oldestVersion) - : impl(new(malloc(sizeof(Impl))) Impl{oldestVersion}) {} +ConflictSet::ConflictSet(int64_t oldestVersion, uint64_t seed) + : impl(new(malloc(sizeof(Impl))) Impl{oldestVersion, seed}) {} ConflictSet::~ConflictSet() { if (impl) { @@ -1130,9 +1123,9 @@ ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) { ((ConflictSet::Impl *)cs)->setOldestVersion(oldestVersion); } __attribute__((__visibility__("default"))) void * -ConflictSet_create(int64_t oldestVersion) { +ConflictSet_create(int64_t oldestVersion, uint64_t seed) { return new (malloc(sizeof(ConflictSet::Impl))) - ConflictSet::Impl{oldestVersion}; + ConflictSet::Impl{oldestVersion, seed}; } __attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) { using Impl = ConflictSet::Impl; @@ -1217,7 +1210,7 @@ struct ReferenceImpl { #ifdef ENABLE_TESTS int main(void) { int64_t writeVersion = 0; - ConflictSet::Impl cs{writeVersion}; + ConflictSet::Impl cs{writeVersion, 0}; ReferenceImpl refImpl{writeVersion}; Arena arena; constexpr int kNumKeys = 10; @@ -1239,10 +1232,13 @@ int main(void) { #ifdef ENABLE_FUZZ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { // TODO call setOldestVersion, and check range writes/reads - initFuzz(data, size); + gArbitrary = Arbitrary{{data, size}}; + uint64_t state = gArbitrary.next(); + uint64_t seq = gArbitrary.next(); + auto rand = Random{state, seq}; int64_t writeVersion = 0; - ConflictSet::Impl cs{writeVersion}; + ConflictSet::Impl cs{writeVersion, rand.next()}; ReferenceImpl refImpl{writeVersion}; while (gArbitrary.hasEntropy()) { @@ -1254,7 +1250,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { std::set, ArenaAlloc> keys{ ArenaAlloc(&arena)}; while (int(keys.size()) < numWrites) { - keys.insert(gRandom.bounded(100)); + keys.insert(gArbitrary.hasEntropy() ? gArbitrary.bounded(100) + : rand.bounded(100)); } auto iter = keys.begin(); for (int i = 0; i < numWrites; ++i) { @@ -1276,7 +1273,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { std::set, ArenaAlloc> keys{ ArenaAlloc(&arena)}; while (int(keys.size()) < numReads) { - keys.insert(gRandom.bounded(100)); + keys.insert(gArbitrary.hasEntropy() ? gArbitrary.bounded(100) + : rand.bounded(100)); } auto iter = keys.begin(); for (int i = 0; i < numReads; ++i) { diff --git a/ConflictSet.h b/ConflictSet.h index ce32846..9da73b4 100644 --- a/ConflictSet.h +++ b/ConflictSet.h @@ -54,7 +54,7 @@ struct __attribute__((__visibility__("default"))) ConflictSet { /// Reads where readVersion < oldestVersion will result in `TooOld`. There are /// no writes initially. - explicit ConflictSet(int64_t oldestVersion); + explicit ConflictSet(int64_t oldestVersion, uint64_t seed); ~ConflictSet(); #if __cplusplus > 199711L @@ -126,7 +126,7 @@ void ConflictSet_addWrites(ConflictSet *cs, void ConflictSet_setOldestVersion(ConflictSet *cs, int64_t oldestVersion); /// Reads where readVersion < oldestVersion will result in `TooOld`. There are /// no writes initially. -ConflictSet *ConflictSet_create(int64_t oldestVersion); +ConflictSet *ConflictSet_create(int64_t oldestVersion, uint64_t seed); void ConflictSet_destroy(ConflictSet *cs); #endif diff --git a/conflict_set_c_api_test.c b/conflict_set_c_api_test.c index 09ec096..2b898e4 100644 --- a/conflict_set_c_api_test.c +++ b/conflict_set_c_api_test.c @@ -3,7 +3,7 @@ #include int main(void) { - ConflictSet *cs = ConflictSet_create(0); + ConflictSet *cs = ConflictSet_create(0, 0); ConflictSet_WriteRange w; ConflictSet_Result result; ConflictSet_ReadRange r; diff --git a/conflict_set_cxx_api_test.cpp b/conflict_set_cxx_api_test.cpp index b057dbf..6ad314b 100644 --- a/conflict_set_cxx_api_test.cpp +++ b/conflict_set_cxx_api_test.cpp @@ -3,7 +3,7 @@ #include int main(void) { - ConflictSet cs(0); + ConflictSet cs(0, 0); ConflictSet::WriteRange w; w.begin.p = (const uint8_t *)"0000"; w.begin.len = 4;