{"record":{"id":"8106c1bda52c68a1","repo":"dotnet/reactive","slug":"refcountdisposable","errorCode":null,"errorMessage":"RefCountDisposable","messagePattern":"RefCountDisposable","errorType":"exception","errorClass":"ObjectDisposedException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Disposables/RefCountDisposable.cs","lineNumber":69,"sourceCode":"\n        /// <summary>\n        /// Returns a dependent disposable that when disposed decreases the refcount on the underlying disposable.\n        /// </summary>\n        /// <returns>A dependent disposable contributing to the reference count that manages the underlying disposable's lifetime.</returns>\n        /// <exception cref=\"ObjectDisposedException\">This instance has been disposed and is configured to throw in this case by <see cref=\"RefCountDisposable(IDisposable, bool)\"/>.</exception>\n        public IDisposable GetDisposable()\n        {\n            // the current state\n            var cnt = Volatile.Read(ref _count);\n\n            for (; ; )\n            {\n                // If bit 31 is set and the active count is zero, don't create an inner\n                if (cnt == int.MinValue)\n                {\n                    if (_throwWhenDisposed)\n                    {\n                        throw new ObjectDisposedException(\"RefCountDisposable\");\n                    }\n\n                    return Disposable.Empty;\n                }\n\n                // Should not overflow the bits 0..30\n                if ((cnt & 0x7FFFFFFF) == int.MaxValue)\n                {\n                    throw new OverflowException($\"RefCountDisposable can't handle more than {int.MaxValue} disposables\");\n                }\n\n                // Increment the active count by one, works because the increment\n                // won't affect bit 31\n                var u = Interlocked.CompareExchange(ref _count, cnt + 1, cnt);\n                if (u == cnt)\n                {\n                    return new InnerDisposable(this);\n                }","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Disposables/RefCountDisposable.cs#L51-L87","documentation":"GetDisposable throws ObjectDisposedException named 'RefCountDisposable' when the RefCountDisposable was constructed with throwWhenDisposed:true, is already disposed, and the active reference count is zero. Because bit 31 of the packed count marks disposal, the code detects 'disposed and no children' and refuses to hand out inner disposables.","triggerScenarios":"Calling GetDisposable() on a RefCountDisposable created with new RefCountDisposable(disposable, throwWhenDisposed:true) after Dispose() has been called and all outstanding inner disposables have been released.","commonSituations":"Code that keeps a long-lived RefCountDisposable, disposes it on shutdown, and then accidentally calls GetDisposable again from a race or a stale code path; also tests intentionally verifying the throw-on-disposed behavior.","solutions":["Check the IsDisposed property before calling GetDisposable and take an alternative path.","Construct with throwWhenDisposed:false if post-dispose GetDisposable calls should return Disposable.Empty instead of throwing.","Fix lifecycle ordering so GetDisposable is only called while the parent is alive (e.g. within a using block or while holding a lease)."],"exampleFix":"// before\nvar d = refCount.GetDisposable(); // may throw after dispose\n// after\nif (!refCount.IsDisposed)\n{\n    var d = refCount.GetDisposable();\n}","handlingStrategy":"try-catch","validationCode":"if (refCount.IsDisposed) { /* take alternate path */ } else { var d = refCount.GetDisposable(); }","typeGuard":"static bool CanGetDisposable(RefCountDisposable rc) => !rc.IsDisposed;","tryCatchPattern":"try { var d = refCount.GetDisposable(); }\ncatch (ObjectDisposedException) { /* treat as Disposable.Empty or re-acquire a new RefCountDisposable */ }","preventionTips":["Check IsDisposed before every GetDisposable call on shared instances.","Prefer constructing with throwWhenDisposed:false when post-dispose calls are expected and benign.","Scope GetDisposable usage inside using blocks tied to the parent's lifetime."],"tags":["object-disposed","lifecycle","disposables","system-reactive"],"backgroundTag":"invalid-state-transition","analyzedSha":"94b5d5ab912789f5abe9a72138a25bbd716fe59c","analyzedAt":"2026-09-15T02:26:24.759Z","contentChangedAt":"2026-09-15T02:26:24.759Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}