{"record":{"id":"2184d727419bd954","repo":"stride3d/stride","slug":"trying-to-unlock-while-another-thread-owns-the-lock","errorCode":null,"errorMessage":"Trying to unlock while another thread owns the lock.","messagePattern":"Trying to unlock while another thread owns the lock\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/core/Stride.Core.Design/MicroThreadLock.cs","lineNumber":208,"sourceCode":"        internal override void Reenter()\n        {\n            Monitor.Enter(MicroThreadLock.syncLock);\n            base.Reenter();\n        }\n\n        internal void Take()\n        {\n            if (MicroThreadLock.currentSyncLockThread != 0)\n                throw new InvalidOperationException(\"Trying to lock while another thread owns the lock.\");\n\n            MicroThreadLock.currentSyncLockThread = Environment.CurrentManagedThreadId;\n            MicroThreadLock.currentSyncLock = this;\n        }\n\n        internal override void Release()\n        {\n            if (MicroThreadLock.currentSyncLockThread != Environment.CurrentManagedThreadId)\n                throw new InvalidOperationException(\"Trying to unlock while another thread owns the lock.\");\n\n            MicroThreadLock.currentSyncLockThread = 0;\n            MicroThreadLock.currentSyncLock = null;\n        }\n\n        public IDisposable Lock()\n        {\n            if (!locked)\n            {\n                // We register here because we are in the proper thread.\n                Take();\n                Monitor.Enter(MicroThreadLock.syncLock);\n            }\n            else\n            {\n                Reenter();\n            }\n            locked = true;","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/core/Stride.Core.Design/MicroThreadLock.cs#L190-L226","documentation":"SyncLock.Release verifies that the thread calling it is the same thread that currently owns the sync lock (currentSyncLockThread). If a different thread attempts to release, ownership bookkeeping would be corrupted, so it throws InvalidOperationException.","triggerScenarios":"Disposing the IDisposable returned by Lock() on a thread other than the acquiring thread — e.g. passing the IDisposable across threads, marshaling disposal to the UI thread, or using async void/continuations that resume on a different thread.","commonSituations":"await inside a Lock() scope with ConfigureAwait(false) or no sync context, so the using's Dispose runs on a ThreadPool thread; queueing Dispose to a dispatcher; leaking the IDisposable to another thread.","solutions":["Keep the entire Lock() using-scope on the acquiring thread: avoid awaits inside it or use the same synchronization context.","Do not pass the lock's IDisposable to other threads for disposal.","Use await ... with a captured context, or restructure so disposal happens synchronously on the owner thread.","Check Environment.CurrentManagedThreadId at acquisition and disposal when debugging ownership issues."],"exampleFix":"// before\nusing (syncLock.Lock())\n{\n    await Task.Run(Work); // continuation may release on another thread\n}\n// after\nusing (syncLock.Lock())\n{\n    Work(); // keep scope synchronous on owning thread\n}\nawait Task.Run(Work);","handlingStrategy":"try-catch","validationCode":"// Before disposing, confirm thread affinity:\nvar ownerThread = acquireThreadId; // recorded at Lock() time\nbool canRelease = ownerThread == Environment.CurrentManagedThreadId;","typeGuard":"bool CanRelease(int acquiringThreadId) => acquiringThreadId == Environment.CurrentManagedThreadId;","tryCatchPattern":"try { lockHandle.Dispose(); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"another thread owns the lock\")) { /* marshal disposal back to the owning thread */ }","preventionTips":["No awaits inside Lock() scopes, or keep the same sync context","Never pass the Lock() IDisposable across threads","Dispose in the same call stack that acquired"],"tags":["csharp","threading","locking","thread-affinity"],"backgroundTag":"invalid-state-transition","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}