Repository navigation
Lurch strafe: lock-free hot path + strict key-state tracking - #36
Merged
Merged
Conversation
Performance - Replace g_activeSpamKeys vector + g_activeKeysMutex with a single std::atomic<uint32_t> g_lurchState (WASD bitmask in low 4 bits + 24-bit epoch). Hook thread and spam thread no longer take a mutex to publish/consume the active key set. - Drop statusMutex_ from KbdHookBackend; per-counter atomics so the LL-hook callback never blocks. Mirror the change in InterceptionBackend. - Cache per-VK scan code + flag template in SpamLogic so SendKeyInputBatch no longer calls MapVirtualKeyW on every spam cycle. Switch batch buffer to stack std::array<INPUT,4>. - KeybindManager previous-state tracker: std::map<int,bool> + std::mutex -> std::array<std::atomic<bool>,256>. Removes allocation + per-keystroke mutex. Strict key-state tracking - Edge dedup in HandleFeatureKeyEvent: ignore events that don't change physicalKeyDown (drops OS autorepeat storm). - Configurable per-VK debounce window (debounce_us, default 500) rejects bounce / driver echo. - Acquire/release ordering on shared atomics (physicalKeyDown, g_isSpamActive, g_lurchState) so observers can't see partial state on weakly-ordered architectures. - Spam thread re-validates the lurch epoch and g_isSpamActive immediately before SendInput, closing the snapshot->inject race that emitted ghost DOWN events for just-released keys. - Cleanup path uses the same CAS protocol as the publisher, so cleanup and publish interleave safely.
Contributor
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Targets two related concerns on the lurch-strafe (Spam) pipeline that you raised: (1) performance of the input → spam-thread hot path, and (2) strict key-state tracking to eliminate ghost inputs and instability.
Performance — lock-free hot path
g_activeSpamKeys(std::vector<int>),g_activeKeysMutex, andg_spamKeysEpochcollapse into onestd::atomic<uint32_t> g_lurchState(low 4 bits = WASD bitmask, high 24 bits = epoch). Hook thread and spam thread no longer take a mutex to publish/consume the active key set. The publisher uses a CAS loop that only bumps the epoch when the key bits actually change.KbdHookBackend::statusMutex_is gone — the LL-hook callback path is now mutex-free, eliminating a real risk of Windows silently unregistering the hook under contention (it enforces a 300 ms timeout by default). Same change applied toInterceptionBackend.SpamLogic.SendKeyInputBatchno longer callsMapVirtualKeyWper cycle; the per-VK scan code andKEYEVENTF_EXTENDEDKEYtemplate are cached on first use.INPUTbuffer.SendKeyInputBatchusesstd::array<INPUT, 4>(WASD bound) instead of a heap vector, removing per-cycle allocator churn.std::map<int,bool>+std::mutextostd::array<std::atomic<bool>, 256>. Same observable semantics, no allocation, no global lock per keystroke.Strict key-state tracking — ghost-input & instability
HandleFeatureKeyEventnow short-circuits any event that does not changephysicalKeyDown. This drops the Windows autorepeat storm (1 DOWN per ~30 ms while a key is held), which previously drove the full feature chain (axis mutex, publish, CV wake) on every repeat. Behaviour is preserved — every downstream consumer already deduped autorepeats internally.Config::DebounceUs(debounce_usinconfig.cfg, default 500 µs). Opposite-edge transitions arriving within the window of the previous edge are dropped as bounce / driver echo. The default sits below human dexterity (~10 ms) and above typical mechanical-switch debounce (~50 µs). Set to0to disable.physicalKeyDown,g_isSpamActive, andg_lurchStateso observers on the spam thread / GUI thread can never see a partial state on weakly-ordered architectures (latent on x86, real on ARM).SendInput. Spam thread re-checks the lurch epoch andg_isSpamActiveimmediately before injecting. Closes the snapshot→inject race that previously emitted ghost DOWN events for just-released keys.CleanupSpamStateuses the same CAS protocol as the publisher; the two operations interleave safely instead of fighting over the vector.Scope intentionally NOT in this PR
physicalKeyDownagainstGetAsyncKeyStateonWM_ACTIVATEAPP/ idle tick).Logger::Log).timeBeginPeriod(1)+ busy-spin inSpamLogic.These are behaviour-changing or larger refactors; happy to follow up with separate PRs once you confirm direction.
Review & Testing Checklist for Human
debounce_us = 0inconfig.cfgand retry — that isolates whether debounce is the culprit.Notes
g_activeSpamKeyswas internal-only. The atomic-bitmask layout is documented inline inGlobals.h.KeyState's deleted copy/move ops are a small correctness improvement: the struct was only ever used as elements ofg_KeyInfo[256], so disallowing copy/move is closer to intent and removes the maintenance burden of keeping the manual copy/move ops in sync with new fields likelastEdgeNs.Link to Devin session: https://app.devin.ai/sessions/aefc79c6e4004edfac6d5aef0487ec4b
Requested by: @fL0P1337