{"record":{"id":"d2fa094c204b2ebf","repo":"microsoft/FASTER","slug":"invalid-release-by-non-owner-thread","errorCode":null,"errorMessage":"Invalid release by non-owner thread","messagePattern":"Invalid release by non-owner thread","errorType":"exception","errorClass":"FasterException","httpStatus":null,"severity":"error","filePath":"cs/src/core/Allocator/AtomicOwner.cs","lineNumber":80,"sourceCode":"\n        /// <summary>\n        /// Release queue ownership\n        /// true: successful release\n        /// false: failed release\n        /// </summary>\n        /// <returns></returns>\n        public bool Release()\n        {\n            while (true)\n            {\n                var older = this;\n                var newer = older;\n\n                if (newer.count > 0)\n                    return false;\n\n                if (newer.owner == 0)\n                    throw new FasterException(\"Invalid release by non-owner thread\");\n                newer.owner = 0;\n\n                if (Interlocked.CompareExchange(ref this.atomic, newer.atomic, older.atomic) == older.atomic)\n                {\n                    return true;\n                }\n            }\n        }\n    }\n}\n","sourceCodeStart":62,"sourceCodeEnd":91,"githubUrl":"https://github.com/microsoft/FASTER/blob/321d872eabda6a0345c8bd76419f89723ed864ae/cs/src/core/Allocator/AtomicOwner.cs#L62-L91","documentation":"AtomicOwner tracks which epoch/owner thread holds a page buffer during flush/drain. Release is only legal by the owning thread; if the owner field is 0 (nobody owns it) when SuspendDrain calls Release, the internal ownership invariant is broken and it throws. This is an internal concurrency-invariant violation, usually caused by epoch/flush machinery misuse rather than user code.","triggerScenarios":"SuspendDrain -> Release invoked when the page ownership was already cleared (owner == 0) - typically from an epoch mis-drain, double-release, or a background flush completing out of order relative to thread suspension.","commonSituations":"Custom epoch/pooled thread management around the store; calling APIs that suspend drain from multiple threads concurrently; races exposed when tuning checkpoint/maintenance logic.","solutions":["Ensure all store operations (including suspend/maintenance APIs) run under FASTER's epoch management: use its session/thread-pool conventions and call SuspendDrain from a single coordinated thread.","Avoid concurrent SuspendDrain/Resume calls; serialize maintenance operations.","Upgrade FASTER to the latest patch - some owner/epoch races were fixed in later versions."],"exampleFix":"// before\n// two maintenance threads calling suspend concurrently\nThreadPool.QueueUserWorkItem(_ => store.Log.SuspendDrain());\nThreadPool.QueueUserWorkItem(_ => store.Log.SuspendDrain());\n\n// after\n// serialize maintenance on one thread/lock\nlock (maintLock) { store.Log.SuspendDrain(); ... store.Log.ResumeDrain(); }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { log.SuspendDrain(); }\ncatch (FasterException ex) when (ex.Message.Contains(\"non-owner thread\")) {\n    // serialize maintenance: ensure only one thread performs suspend/drain\n}","preventionTips":["Run SuspendDrain/Resume and maintenance from a single dedicated thread.","Use FASTER sessions per thread and its epoch system rather than custom thread pools.","Keep FASTER updated for concurrency race fixes."],"tags":["csharp","concurrency","faster","internal-invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"321d872eabda6a0345c8bd76419f89723ed864ae","analyzedAt":"2026-09-15T22:18:00.693Z","contentChangedAt":"2026-09-15T22:18:00.693Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}