{"record":{"id":"ce05cae303f4d046","repo":"microsoft/FASTER","slug":"getting-handle-in-disposed-device","errorCode":null,"errorMessage":"Getting handle in disposed device","messagePattern":"Getting handle in disposed device","errorType":"exception","errorClass":"FasterException","httpStatus":null,"severity":"error","filePath":"cs/src/core/Device/AsyncPool.cs","lineNumber":49,"sourceCode":"        public AsyncPool(int size, Func<T> creator)\n        {\n            this.size = size;\n            this.creator = creator;\n            this.handleAvailable = new SemaphoreSlim(0);\n            this.itemQueue = new ConcurrentQueue<T>();\n        }\n\n        /// <summary>\n        /// Get item synchronously\n        /// </summary>\n        /// <param name=\"token\"></param>\n        /// <returns></returns>\n        public T Get(CancellationToken token = default)\n        {\n            for (; ; )\n            {\n                if (disposed)\n                    throw new FasterException(\"Getting handle in disposed device\");\n\n                if (GetOrAdd(itemQueue, out T item))\n                    return item;\n\n                handleAvailable.Wait(token);\n            }\n        }\n\n        /// <summary>\n        /// Get item asynchronously\n        /// </summary>\n        /// <param name=\"token\"></param>\n        /// <returns></returns>\n        public async ValueTask<T> GetAsync(CancellationToken token = default)\n        {\n            for (; ; )\n            {\n                if (disposed)","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/microsoft/FASTER/blob/321d872eabda6a0345c8bd76419f89723ed864ae/cs/src/core/Device/AsyncPool.cs#L31-L67","documentation":"AsyncPool is a lightweight handle pool used internally by devices. Once the pool (backing device) has been disposed, Get can no longer hand out handles, so it throws to prevent using resources backed by a torn-down device.","triggerScenarios":"Calling pool.Get() (directly or indirectly through device read/write paths) after Dispose() was called on the device or the pool itself.","commonSituations":"Shutting down FASTER / disposing a device while background flush or checkpoint operations are still issuing I/O; use-after-dispose races between an application thread and a cleanup path.","solutions":["Ensure all I/O (reads, writes, checkpoints, log iteration) completes before disposing the device/store.","Keep the FASTER instance alive as long as any session or background operation can use the device.","Guard shutdown with synchronization so Dispose happens after all worker threads finish.","Catch FasterException at the I/O call site during shutdown to handle in-flight calls gracefully."],"exampleFix":"// before\nfht.Dispose();\ndevice.Dispose();\nbackgroundWriter.Join();\n// after\nbackgroundWriter.Join();\nfht.Dispose();\ndevice.Dispose();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try\n{\n    var item = pool.Get(token);\n}\ncatch (FasterException) when (disposed) // or ObjectDisposedException around device usage\n{\n    throw new OperationCanceledException(\"Device disposed during shutdown\");\n}","preventionTips":["Join/await all worker and background I/O tasks before disposing the device.","Centralize disposal ordering: sessions -> FASTER instance -> device.","Use CancellationToken to unwind workers before Dispose."],"tags":["csharp","device","disposed-resource","faster"],"backgroundTag":"invalid-state-transition","analyzedSha":"321d872eabda6a0345c8bd76419f89723ed864ae","analyzedAt":"2026-09-15T22:18:00.693Z","contentChangedAt":"2026-09-15T22:18:00.693Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}