Skip to content

[WIP] Systematic testing: Test the full KV + History + Consensus stack, with deterministic and randomly-explored interleavings - #8238

Draft
Eddy Ashton (eddyashton) wants to merge 12 commits into
mainfrom
agents/real-stack-concurrency-fuzz-testing-main
Draft

[WIP] Systematic testing: Test the full KV + History + Consensus stack, with deterministic and randomly-explored interleavings#8238
Eddy Ashton (eddyashton) wants to merge 12 commits into
mainfrom
agents/real-stack-concurrency-fuzz-testing-main

Conversation

@eddyashton

Copy link
Copy Markdown
Member

This is extremely sketchy, just opening it here for early discussion.

Robot brief explanation

src/commit_concurrency/: a new test suite for KV/Raft/History concurrency

Problem: no existing suite drives a real ccf::kv::Store, a real aft::Aft, and a real MerkleTxHistory together under concurrency. Every existing suite stubs at least one of the three, so bugs in how they interact under real locking/threading go unfound.

What's added, two complementary test binaries:

  • commit_concurrency_test — real OS threads, real timing. A Checkpoint primitive (interleaving.h) lets scenarios pin specific rendezvous points; a randomized fuzzer runs writers, elections, and readers concurrently and checks cross-component invariants (history vs. store vs. raft all agreeing on committed state).

  • commit_concurrency_model_test — a deterministic scheduler that wholesale-swaps every ccf::pal::Mutex in the binary for a scheduler-aware type (via -include, so no call site opts in individually), and treats every lock acquisition/release (not just contended ones) as a branch point in the search. It can exhaust a scenario's interleavings or sample them randomly with a reproducible seed, and every failing schedule prints a full, human-readable decision trace showing real, semantic lock labels from production code itself (e.g. writer (serialise concurrent Store::commit() calls), elector (roll version and history back to tx_id)), not just an opaque mutex handoff.

Status: two known-bad scenarios are currently red on purpose (tagged NOTE_REJECTED_COMMIT_STALL at the specific broken assertions, with one canonical explanation of the underlying bug), reproducing a real gap in how a commit in flight interacts with a concurrent election: a transaction rejected for a stale view can leave a local write applied to the Store that never reaches consensus, and subsequent ordinary commits can keep failing to replicate until a further election restores agreement. Random sampling shows this is reachable via a wide swath of the interleaving space, not one freak timing (though note: uniform sampling over the abstract schedule space is not representative of real-world timing likelihood — it says something about the shape of the bug, not its production frequency). Both binaries build clean under TSAN and pass the existing kv_test/raft_test/history_test suites unchanged.

Known limitation flagged in-repo (TODO in deterministic_scheduler.h): branching on every single lock/unlock makes the search space for any real-stack scenario enormous (millions to 10^26+ schedules), forcing reliance on random sampling rather than exhaustive search. The next planned step is a per-scenario filter — using the labels already threaded through — to let a test choose a small, targeted set of "interesting" decision points (e.g. specifically the unlock of version_lock in Store::commit()) while everything else fast-passes through unbranched. Intended workflow: random search over the full space to find violations, promote each violation to a pinned deterministic regression test, then fuzz narrowly around those known points for cheap ongoing coverage.

Note for reviewers: this PR is for discussion/sharing, not merge-ready — scripts/ci-checks.sh will fail on the intentional TODO: comment (repo convention normally bans TODO/FIXME outright) and on the two deliberately-red test scenarios described above.

Robot verbose explanation

Motivation

We currently have no test suite that drives a real ccf::kv::Store, a real aft::Aft, and a real ccf::MerkleTxHistory together under concurrency. Every existing suite stubs at least one of the three:

  • kv_test — real Store, but consensus is always a stub (StubConsensus/PrimaryStubConsensus/etc.), and TxHistory is absent or hand-rolled.
  • raft_test — real Aft, but the store is LoggingStubStore, with no real KV semantics or conflict detection.
  • history_test — real Store and MerkleTxHistory, but consensus is a hand-rolled, single-threaded fake.

This gap matters: a prior investigation this session found and fixed a real bug in Store::commit()'s interaction with Store::rollback() under a concurrent view change, and confirming it required building bespoke pause/rendezvous machinery by hand rather than reusing anything. This PR is the generalized version of that machinery, built as its own suite rather than living inside kv_test.

This is additive only. No existing stub classes or test files are touched, consolidated, or removed — they remain legitimate, narrowly-scoped unit tests for their own layers.

What's in src/commit_concurrency/

Two complementary test binaries, both wired into CMakeLists.txt via add_unit_test(...) with DETECT_DEADLOCKS, both under a new concurrency CTest label:

commit_concurrency_test — real OS threads, real timing.

  • interleaving.h provides a Checkpoint primitive: any thread can pause at a named point and a controller thread decides when to release it, generalizing the ad hoc pattern from the original bug investigation into one reusable, named primitive.
  • threaded/deterministic.cpp ports the two hand-pinned scenarios from that investigation across as a harness sanity check, plus additional scenarios covering election-churn variations your review specifically asked about (stepping down to backup vs. reaching candidate again mid-commit).
  • threaded/fuzzer.cpp layers a randomized multi-actor fuzzer on top: N writer threads, an election-churn actor, and a reader thread continuously polling store->current_txid() against history's reported state — mirroring the actual production access pattern in frontend.h that made the original bug externally observable. Seeded (std::mt19937), logged on failure for reproducibility.

commit_concurrency_model_test — a deterministic, single-process scheduler for exhaustive/random exploration of interleavings, rather than relying on real OS thread timing to hit a specific ordering.

  • The core trick: ccf::pal::Mutex itself is wholesale-swapped for a scheduler-aware type across the whole binary via -include (interleaving_lock_override.h), so every real lock in store.h/raft.h/history.h participates without any call site opting in. ccf::tasks' own sources are recompiled into this target (not linked from the precompiled ccf_tasks.a), because it keeps a process-wide singleton job board that needs the same scheduler-aware locking to stay consistent across explored schedules.
  • deterministic_scheduler.h treats every lock acquisition and release as a decision point (not just contended ones — see "Known limitations" below), plus explicit yield_point()s for branching at points with no lock at all.
  • ccf::pal::unique_lock<LockType> (new, in include/ccf/pal/locking.h) is a labeled drop-in replacement for std::unique_lock/std::lock_guard, applied at all 62 real lock call sites across the three production headers. A failing schedule's trace shows real, human-readable reasons a lock was held (e.g. writer (serialise concurrent Store::commit() calls), elector (roll version and history back to tx_id)), not just an opaque sequence of mutex handoffs — this was deliberately built to make failures diagnosable by a human, not just a pass/fail signal.
  • explore_all_interleavings() exhausts a scenario via depth-first search with replay; explore_random_interleavings() samples a fixed, seeded number of schedules when the space is too large; estimate_schedule_count() gives a cheap random-walk estimate of a scenario's true size before committing to either.
  • model_checked/rejected_commit_stall.cpp reproduces the same bug as the threaded suite, but via this exhaustive/random mechanism instead of hand-pinned timing.

Four production headers (kv/store.h, consensus/aft/raft.h, consensus/aft/impl/state.h, node/history.h) gained a CCF_STATIC_LIBRARY_BUILD-guarded #error, enforcing they can never be compiled into ccf_kv/ccf_tasks/ccfcrypto — this is what makes it safe for commit_concurrency_model_test to recompile them under a different lock type without an ODR violation, and it's enforced at compile time, not just by convention.

Current status: two scenarios are deliberately red

Per explicit direction this session: tests should assert the correct expected behavior and fail honestly when that behavior isn't yet implemented, rather than being skipped or asserting the current (buggy) behavior. Both deterministic.cpp and rejected_commit_stall.cpp have a NOTE_REJECTED_COMMIT_STALL-tagged test/assertions documenting a real gap: a transaction rejected by Store::commit() for a stale view can leave a local write applied to the Store with no corresponding entry ever reaching consensus — and, worse, every ordinary transaction committed afterwards can keep succeeding locally without reaching consensus either, until a further election restores agreement.

Randomly sampling the model-checked version of this scenario (500 samples each, two variants) shows 733 of 1000 sampled schedules that reach the risky state go on to violate the invariant. That's evidence the bug is reachable through a broad part of the interleaving space, not one narrow, contrived ordering — though I want to flag explicitly that this is not a claim about real-world frequency: the scheduler samples uniformly over abstract decision points, which has no relationship to real timing (a lock held for nanoseconds vs. an election taking milliseconds of network round trips). Treat it as evidence about the shape of the bug, not its production likelihood.

Validation

  • Normal build: kv_test, raft_test, history_test all pass unchanged (28k+/1M+/46 assertions respectively).
  • commit_concurrency_test and commit_concurrency_model_test both build and run cleanly, producing the two known-bad results deterministically across repeated runs.
  • Both new binaries build and run clean under -DTSAN=ON with zero data race warnings — meaningful given the whole point of this suite is real concurrent access to shared production state.
  • scripts/cpp-format-checks.sh, copyright-checks.sh, includes-checks.sh, and ascii-checks.sh all pass.

What's deliberately not merge-ready, and why this is a draft

  • scripts/todo-checks.sh will fail. There's an intentional TODO: comment in deterministic_scheduler.h (repo convention otherwise bans TODO/FIXME outright) describing the single most valuable next step, left in deliberately for this PR's discussion rather than filed elsewhere and forgotten.
  • Two test cases are red by design (see above) — this is the point, not an oversight, but it means ctest will report failures for this suite until the underlying store.h bug is fixed.

Known limitation and the planned next step

Branching on every lock/unlock (rather than only contended ones) makes the search space for any real-stack scenario enormous — estimate_schedule_count() reports roughly 16.7 million to 4.4 trillion schedules for the single-writer scenario alone, and up to ~10²⁶ for the two-writer one. That forces reliance on random sampling rather than exhaustive search for anything beyond toy scenarios, which is a real loss of rigor compared to what exhaustive search would give.

The planned fix (see the TODO in deterministic_scheduler.h): treat every lock/unlock/yield_point() as only a candidate decision point, and let a per-scenario predicate — matched against the semantic labels already being reported, so no further production code changes are needed — decide which of them actually branch the search versus fast-passing through unchanged. This lets a test dial the search space down to exactly the handful of points it cares about (e.g. specifically the unlock of version_lock in Store::commit()), rather than an all-or-nothing choice between "every lock branches" (intractable) and "only explicit yield_point()s branch" (may silently miss semantic-lock-ordering bugs). The intended workflow once this exists: random search over the full, unfiltered space to find violations, promote each violation into a deterministic regression test pinned to its exact decision sequence, then fuzz narrowly around those known points for cheap, ongoing, targeted coverage.

Explicitly out of scope for this PR

Consolidating or replacing the existing stub zoo (StubConsensus family, LoggingStubStore, DummyConsensus/CompactingConsensus/RollbackConsensus) is a separate, higher-risk decision, not bundled here.

Adds a new test suite (real_stack_concurrency_test) that drives a real
ccf::kv::Store, a real aft::Aft<LedgerStubProxy>, and a real
ccf::MerkleTxHistory together, under genuine OS-thread concurrency and
genuine raft view changes - something no existing suite does (kv_test,
raft_test, and history_test each stub out at least one of these three).

- src/kv/test/interleaving.h: a reusable Checkpoint pause/release
  primitive and a random_delay helper, for pinning or fuzzing thread
  interleavings without bespoke per-test machinery.
- src/consensus/aft/test/real_stack/fixture.h: RealStackFixture, a
  harness wiring up the real Store + Aft + MerkleTxHistory, with
  helpers to drive genuine leadership changes and signature commits.
- smoke.cpp: non-concurrent sanity checks for the harness itself.
- deterministic.cpp: pinned scenarios covering leadership loss/regain
  around an in-flight commit, including NOTE_REJECTED_COMMIT_STALL,
  which documents a real bug where a rejected commit can permanently
  stall replication until a further election.
- fuzzer.cpp: a randomised multi-actor fuzzer (writers, election
  churn, and a continuous reader) checking the same invariants
  continuously, plus a slower soak variant using real crypto.

Several tests exercise NOTE_IS_PRIMARY_RACE, a pre-existing data race
in aft::Aft::is_primary(), and are expected to fail occasionally (or
abort the process under ThreadSanitizer) until that is fixed.

Registered via add_unit_test with DETECT_DEADLOCKS, under a new
"concurrency" CTest label.
…ize into src/commit_concurrency

- Add a deterministic, cooperative scheduler (commit_concurrency_model_test)
  that exhaustively (or randomly, for larger scenarios) explores thread
  interleavings of the real Store + Aft + MerkleTxHistory stack, by
  wholesale-swapping ccf::pal::Mutex for a scheduler-aware type across the
  whole test binary via -include, rather than opting individual call sites
  in.
- Add ccf::pal::unique_lock<LockType>, a labeled drop-in replacement for
  std::unique_lock/std::lock_guard against ccf::pal::Mutex, so a failing
  schedule's trace can show real, semantic reasons a lock was held/released,
  not just an opaque mutex handoff. Apply it at the real call sites in
  store.h, raft.h, and history.h, with a handful of explicit labels at
  high-value points (Store::commit()/rollback(), force_become_primary()).
- Fix a real bug in the scheduler harness found while wiring this up: the
  driver thread's reserved actor id could write one entry past the
  per-actor action-tracking vector once real locks started reporting labels
  unconditionally - fixed by reserving that slot.
- Move all commit-concurrency test suite files (previously split across
  src/kv/test and src/consensus/aft/test) into a single new top-level
  directory, src/commit_concurrency/.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… ones

- DeterministicScheduler::before_lock()/after_unlock() now call
  choose_next() unconditionally, so the scheduler considers every ready
  actor at every real lock acquisition and release, not only when a lock
  is actually contended - closing the gap where two critical sections
  separated by an uncontended lock boundary (rather than a hand-placed
  yield_point()) were never explored interleaved.
- The reserved driver "actor" (DriverRegistration) is a deliberate
  exception: it never contends with a real actor for any lock, so its own
  incidental lock use only updates ownership bookkeeping, never branches.
  This fixes a real, deterministic hang the above change first exposed:
  without this, the driver's own lock use while running real application
  code (e.g. fixture construction) could get "scheduled away" in favour of
  a real actor thread that had not started yet, with nothing left to ever
  hand control back.
- Converted the single-writer scenario in rejected_commit_stall.cpp from
  exhaustive search to random sampling, matching the two-writer scenario:
  estimate_schedule_count() now reports this scenario's interleaving space
  at roughly 16.7 million to 4.4 trillion schedules (up from an exact 3
  when only contended locks branched), making exhaustive search
  infeasible for any real-stack scenario.
- Updated deterministic_scheduler_test.cpp's toy expectations for the
  resulting increase in schedule count (6 -> 736) now that every lock/
  unlock branches.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CCF_GUARDED_BY/CCF_REQUIRES are in real use elsewhere in this codebase
(ccf::tasks, src/ds/, etc.), so unconditionally disabling thread-safety
analysis for every ccf::pal::unique_lock user was a real regression, not
a no-op - it happened to not bite yet only because none of store.h/
raft.h/impl/state.h/history.h currently use CCF_GUARDED_BY.

ccf::pal::unique_lock now carries its own CCF_SCOPED_CAPABILITY/
CCF_ACQUIRE/CCF_RELEASE/CCF_TRY_ACQUIRE annotations, mirroring
MutexGuard, giving real static verification for the ordinary case.
Clang's built-in std::unique_lock support additionally understands a
conditionally-taken lock (construct with std::defer_lock, only
sometimes .lock()/.try_lock() depending on runtime state) well enough to
verify it; that specific pattern isn't supported for a user-annotated
type, and needs an explicit opt-out. Rather than disabling analysis
project-wide, CCF_NO_THREAD_SAFETY_ANALYSIS is applied narrowly to just
the 3 real call sites that use this pattern (Store::commit_deserialised(),
and the two signature-emission paths in history.h), each with a comment
explaining why.

Also folds in the previous, already-staged Waypoint -> YieldPoint rename
and removal of the dead, uncalled set_action().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…date per-actor state

- Add ActorEventKind::Requested, reported by before_lock() as its own
  decision point before checking whether the lock is even free. Without
  this, whichever actor happened to be running when it reached an
  uncontended lock always won it unconditionally - no other actor ever
  got a chance to reach for the same lock first, since only one actor's
  code runs at a time. This was a real gap in interleaving coverage, not
  just a display gap.
- Rewrite describe() as a genuine one-event-per-line stream instead of
  listing every ready-but-not-chosen actor at each decision. Since only
  one actor's code ever runs at a time, the actor that triggers decision
  i is always exactly whoever was chosen at decision i-1 - so each line
  can name that actor and its event directly (e.g. "writer 0 acquires
  version_lock, elector resumes"), with "resumes" only shown when a
  different actor is chosen next. decision_path() keeps the full
  ready/chosen_index data other callers (e.g. backtracking) still need;
  only the human-facing rendering changed.
- Consolidate the three same-sized, separately-indexed std::vectors
  (finished, blocked_on_lock, current_event) into one
  std::vector<ActorState>, centralising the +1-for-the-driver sizing
  reasoning in one place instead of three.
- Update deterministic_scheduler_test.cpp's stale schedule-count comment
  and bound (736 -> 10968), now that Requested adds a third decision
  point per lock life-cycle.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…pstream

#8242 ("Reject stale-view writes before local commit") landed on main
during this session's rebase, independently fixing the exact bug the
NOTE_REJECTED_COMMIT_STALL-tagged tests here were built to catch - all
of them now pass reliably (deterministic and 500-sample model-checked
runs alike), so the old "expected to fail until fixed" framing and tag
were stale.

- Removed the NOTE_REJECTED_COMMIT_STALL tag and its defining comment;
  the DOCTEST_CHECKs it marked are ordinary passing assertions now.
- Reworded the two affected test cases' comments to note they are
  regression tests for #8242, and to explain what they add beyond
  kv_test.cpp's own direct, single-threaded test of the same rejection
  (driving it through a real election instead, and cross-checking
  TxHistory and raft's own replication index).
- Removed the now-defunct middle nullptr (version_resolver) argument
  from one CommittableTx::commit() call, matching #8242's own signature
  change and migration note.

Comments deliberately avoid narrating what #8242 changed or how - that's
what git history is for, and it rots fast; only "this is a regression
test for #8242" is kept, since the tests would otherwise look like
ordinary, low-value assertions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
"Model checking" implies formal/exhaustive verification, which is
misleading here: this codebase already has a genuinely exhaustive,
TLC-based model-checking concept (.github/workflows/ci-verification.yml),
and this suite's own real-stack scenarios only use
explore_random_interleavings() (sampling), not exhaustive search.

- src/commit_concurrency/model_checked/ -> scheduled/, alongside
  deterministic_scheduler.h, deterministic_scheduler_test.cpp, and
  interleaving_lock_override.h, which now live there too (nothing outside
  this directory uses any of them). This leaves src/commit_concurrency/
  with exactly two peer subdirectories - threaded/ (real OS threads,
  seeded but not exactly replayable) and scheduled/ (single-process,
  cooperative, byte-for-byte replayable) - and no loose top-level files.
- commit_concurrency_model_test -> commit_concurrency_scheduled_test
  (CMake target/binary), commit_concurrency_model ->
  commit_concurrency_scheduled (doctest suite tag).
- rejected_commit_stall.cpp -> rejected_commit.cpp: the old name was
  overly specific to one particular way a rejected commit could go wrong.
- src/commit_concurrency/interleaving.h -> threaded/checkpoint.h (+ its
  test file -> threaded/checkpoint_test.cpp, suite tag "interleaving" ->
  "checkpoint"): this is a distinct, complementary primitive - a manual
  pause/release rendezvous for real OS threads - not part of the
  scheduled/ stack, so "interleavings" was freed up for that instead.
- Comments reworded throughout to describe "systematically exploring the
  interleaving space" (exhaustively where the space is small enough,
  randomly sampling otherwise), rather than "model checking".

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…irectly

The #error-guarded macro required 4 general production headers (store.h,
raft.h, impl/state.h, history.h) to know about one specific test target's
build assumptions, to protect against a scenario that was never actually
happening (none of ccf_kv's/ccf_tasks' own sources include any of those
headers) - and provided no more protection than this replacement against
a genuinely new future library nobody thought to guard, since it only
ever covered libraries that explicitly opted in to the macro.

- Removed the #error block from all 4 headers entirely.
- ccf_kv's and ccf_tasks' source lists are now shared CMake variables
  (CCF_KV_SOURCES/CCF_TASKS_SOURCES), used by both their real library
  targets and commit_concurrency_scheduled_test, which recompiles them
  directly (as it already did for ccf_tasks) instead of linking the
  prebuilt library - avoiding an ODR violation by construction, not by
  assertion.
- Removed CCF_STATIC_LIBRARY_BUILD from ccf_kv, ccf_tasks, and ccfcrypto
  (in cmake/crypto.cmake) entirely.
- Added ccf_forbid_layout_sensitive_libraries(), defined directly next to
  its one call site rather than in cmake/common.cmake, as a safety net:
  fails CMake configure if ccf_kv/ccf_tasks are ever linked into this
  target directly instead of recompiled - verified this actually fires
  for both libraries, then reverted the deliberate breakage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… interception

The previous design recompiled ccf_kv's/ccf_tasks' own sources directly
into commit_concurrency_scheduled_test (rather than linking the normal,
shared libraries) purely so that ccf::pal::Mutex could be swapped, at
compile time, for a distinct SchedulerMutex type. This worked, but meant
every real lock call site anywhere in the guarded headers had to be
recompiled under a different type just to be observed - more moving
parts than the goal (deterministically scheduling real lock/unlock
calls) actually requires.

- ccf::pal::Mutex is now a single, permanent type everywhere, always.
  Its lock()/try_lock()/unlock() stash their (optional) label in a
  thread-local hint immediately before making their real call - a small,
  generically useful piece of always-on introspection state, not itself
  aware of any test or scheduler.
- src/commit_concurrency/scheduled/pthread_mutex_wrap.cpp intercepts the
  real pthread_mutex_lock/unlock/trylock calls at link time (via
  -Wl,--wrap=..., only for commit_concurrency_scheduled_test), reads that
  hint to identify a genuine ccf::pal::Mutex call, and diverts it to
  DeterministicScheduler's before_lock()/after_unlock() instead of ever
  reaching the real mutex - with no need to track any mutex's address,
  and no risk of misattributing an unrelated lock (allocator, iostream,
  the scheduler's own bookkeeping mutex, etc.), verified empirically
  before relying on it.
- Removed entirely: SchedulerMutex, interleaving_lock_override.h, the
  -include compile flag, and the CCF_KV_SOURCES/CCF_TASKS_SOURCES
  recompilation machinery - commit_concurrency_scheduled_test now links
  ccf_kv/ccf_tasks completely normally, exactly like commit_concurrency_
  test does.
- Added a smoke test proving interception genuinely engages (checked it
  actually fails if the -Wl,--wrap=... flags are ever dropped - which
  separately also fails to link at all in that case).
- try_lock() under an active scheduler now aborts with a clear message
  rather than throwing: std::mutex::try_lock() is noexcept, so an
  exception escaping it would call std::terminate() anyway, with less
  control over the diagnostic than doing so explicitly.

Validated: exact exhaustive schedule count for the toy scenario in
deterministic_scheduler_test.cpp is unchanged (10968); full project
build succeeds; all test suites pass in both the normal and TSAN
configurations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…currency-fuzz-testing-main

Conflicts in include/ccf/pal/locking.h, src/kv/store.h,
src/consensus/aft/raft.h, and src/node/history.h, from upstream's
"Break the crypto, ds, and pal dependency cycle" (#8265): it moved
Mutex/MutexGuard/ConditionVariable from ccf::pal to ccf::ds (leaving
ccf::pal as a deprecated compatibility alias), and mechanically reverted
store.h/raft.h/history.h's few dozen lock call sites from our own
label-carrying ccf::pal::unique_lock back to plain std::lock_guard/
std::unique_lock (since our custom wrapper never existed upstream).

Resolved by taking upstream's version of all 4 files wholesale (their
functional changes preserved verbatim, including a genuine, unrelated fix
to Store::rollback()'s chunker/snapshot ordering), then moving our own
label-and-hook mechanism to its new home: ccf::ds::Mutex's own
lock()/try_lock()/unlock() now take an optional label (as ccf::pal::Mutex
used to), and ccf::ds::unique_lock<LockType> (as ccf::pal::unique_lock
used to) is the labeled replacement for std::unique_lock, both now living
in include/ccf/ds/locking.h. Only the 8 call sites that carried a
genuinely chosen label (4 in store.h, 2 in raft.h, 2 in history.h) were
converted back to ccf::ds::unique_lock; every other call site keeps
upstream's plain std::lock_guard/std::unique_lock exactly as-is, since a
label was never functionally required for the pthread_mutex_wrap.cpp
interception to work - only for scheduler describe() output to be more
readable at the handful of sites where that mattered enough to name.
Updated src/commit_concurrency/scheduled/ to reference ccf::ds instead of
ccf::pal throughout for the same reason.

Validated: full project build succeeds; all 5 concurrency-adjacent
suites (kv_test, raft_test, history_test, commit_concurrency_test,
commit_concurrency_scheduled_test) pass in both the normal and TSAN
configurations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant