{"id":2564,"date":"2019-09-10T10:47:18","date_gmt":"2019-09-10T09:47:18","guid":{"rendered":"https:\/\/solidt.eu\/site\/?p=2564"},"modified":"2024-04-22T13:58:40","modified_gmt":"2024-04-22T12:58:40","slug":"c-locker","status":"publish","type":"post","link":"https:\/\/solidt.eu\/site\/c-locker\/","title":{"rendered":"C# locker"},"content":{"rendered":"\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using System;\nusing System.Collections.Concurrent;\nusing System.Threading;\n\npublic class Locker : IDisposable\n{\n    private static readonly ConcurrentDictionary&lt;string, SemaphoreSlim> keyLocks = new ConcurrentDictionary&lt;string, SemaphoreSlim>();\n    private readonly string _key;\n\n    public Locker(string key) : this(key, TimeSpan.FromMinutes(1))\n    {\n    }\n\n    public Locker(string key, TimeSpan timeout)\n    {\n        _key = key;\n        var keyLock = keyLocks.GetOrAdd(key, new SemaphoreSlim(1, 1));\n        if (!keyLock.Wait(timeout))\n            throw new Exception($\"Failed to get lock for key: {key}\");\n    }\n\n    public void Dispose()\n    {\n        if (keyLocks.TryGetValue(_key, out SemaphoreSlim keyLock))\n            keyLock.Release();\n    }\n}\n\/\/ usage:\n\nvar key = \"InvoiceCache\";\nusing (new Locker(key))\n{\n\tvar cacheValue = ObjectCache.GetCacheItem(key)?.Value;\n\tif (cacheValue != null)\n\t\treturn (T)cacheValue;\n\tT result = await generator().ConfigureAwait(false); \/\/ Configure await false ensures the function throws errors\n\tvar cacheItem = new CacheItem(key, result);\n\tvar policy = new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.Add(validTime) };\n\tObjectCache.Add(cacheItem, policy);\n\treturn result;\n}\n\n<\/pre>\n\n\n\n<p>With cleanup:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using System;\nusing System.Collections.Concurrent;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Domain.Helpers\n{\n        public class SemaphoreItem\n    {\n        public SemaphoreItem(string key)\n        {\n            Key = key;\n        }\n        public string Key { get; private set; }\n        public DateTime LastUsed = DateTime.Now;\n        public SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1);\n    }\n\n    public class Locker : IDisposable\n    {\n        private static readonly ConcurrentDictionary&lt;string, SemaphoreItem> locks = new ConcurrentDictionary&lt;string, SemaphoreItem>();\n        private static DateTime _lastCleanUp = DateTime.Now;\n        public static Locker GetLock(string key)\n        {\n            var semaphoreItem = locks.GetOrAdd(key, (string x) => new SemaphoreItem(key));\n            semaphoreItem.LastUsed = DateTime.Now;\n            var success = semaphoreItem.Semaphore.Wait(0);\n            return new Locker(semaphoreItem, success);\n        }\n        public static async Task&lt;Locker> WaitForLock(string key, TimeSpan timeout)\n        {\n            var semaphoreItem = locks.GetOrAdd(key, (string x) => new SemaphoreItem(key));\n            semaphoreItem.LastUsed = DateTime.Now;\n            var success = await semaphoreItem.Semaphore.WaitAsync(timeout);\n            return new Locker(semaphoreItem, success);\n        }\n\n        private readonly SemaphoreItem SemaphoreItem;\n        public bool HasLock { get; }\n\n        private Locker(SemaphoreItem semaphoreItem, bool hasLock)\n        {\n            SemaphoreItem = semaphoreItem;\n            HasLock = hasLock;\n        }\n        public void CleanUp()\n        {\n            if (_lastCleanUp.AddMinutes(10) > DateTime.Now)\n                return;\n\n            _lastCleanUp = DateTime.Now;\n            foreach (var item in locks)\n            {\n                if (item.Value.Semaphore.CurrentCount == 0)\n                {\n                    item.Value.LastUsed = DateTime.Now;\n                    continue; \/\/ semaphore still in use\n                }\n                if (item.Value.LastUsed.AddMinutes(10) > DateTime.Now)\n                    continue;\n                locks.TryRemove(item.Key, out SemaphoreItem _);\n                item.Value.Semaphore.Dispose();\n            }\n        }\n        #region IDisposable Support\n        private bool disposedValue = false;\n        protected virtual void Dispose(bool disposing)\n        {\n            if (disposedValue) return;\n            disposedValue = true;\n            if (!disposing) return;                \n            try\n            {\n                SemaphoreItem.Semaphore.Release();\n                SemaphoreItem.LastUsed = DateTime.Now;\n            }\n            catch (ObjectDisposedException) { } \/\/ ignore\n            CleanUp();\n        }\n        public void Dispose() => Dispose(true);\n        #endregion\n    }\n}\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>With cleanup:<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[6,4,1],"tags":[],"class_list":["post-2564","post","type-post","status-publish","format-standard","hentry","category-dotnet","category-programming","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/2564","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/comments?post=2564"}],"version-history":[{"count":11,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/2564\/revisions"}],"predecessor-version":[{"id":8439,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/2564\/revisions\/8439"}],"wp:attachment":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/media?parent=2564"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/categories?post=2564"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/tags?post=2564"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}