diff --git a/ArchiSteamFarm/BotDatabase.cs b/ArchiSteamFarm/BotDatabase.cs index ebfbd29e7..42c782859 100644 --- a/ArchiSteamFarm/BotDatabase.cs +++ b/ArchiSteamFarm/BotDatabase.cs @@ -20,7 +20,6 @@ // limitations under the License. using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -30,7 +29,13 @@ using Newtonsoft.Json; namespace ArchiSteamFarm { internal sealed class BotDatabase : IDisposable { - internal bool HasGamesToRedeemInBackground => GamesToRedeemInBackground.Count > 0; + internal bool HasGamesToRedeemInBackground { + get { + lock (GamesToRedeemInBackground) { + return GamesToRedeemInBackground.Count > 0; + } + } + } [JsonProperty(Required = Required.DisallowNull)] private readonly ConcurrentHashSet BlacklistedFromTradesSteamIDs = new ConcurrentHashSet(); @@ -38,7 +43,7 @@ namespace ArchiSteamFarm { private readonly SemaphoreSlim FileSemaphore = new SemaphoreSlim(1, 1); [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentDictionary GamesToRedeemInBackground = new ConcurrentDictionary(); + private readonly SortedDictionary GamesToRedeemInBackground = new SortedDictionary(); [JsonProperty(Required = Required.DisallowNull)] private readonly ConcurrentHashSet IdlingBlacklistedAppIDs = new ConcurrentHashSet(); @@ -87,8 +92,14 @@ namespace ArchiSteamFarm { return; } - // We use Count() and not Any() because we must ensure full loop pass - if (games.Count(game => GamesToRedeemInBackground.TryAdd(game.Key, game.Value)) > 0) { + bool save; + + lock (GamesToRedeemInBackground) { + // We use Count() and not Any() because we must ensure full loop pass + save = games.Count(game => GamesToRedeemInBackground.TryAdd(game.Key, game.Value)) > 0; + } + + if (save) { await Save().ConfigureAwait(false); } } @@ -99,7 +110,13 @@ namespace ArchiSteamFarm { return; } - if (GamesToRedeemInBackground.TryAdd(key, game)) { + bool save; + + lock (GamesToRedeemInBackground) { + save = GamesToRedeemInBackground.TryAdd(key, game); + } + + if (save) { await Save().ConfigureAwait(false); } } @@ -138,7 +155,13 @@ namespace ArchiSteamFarm { } internal IReadOnlyCollection GetBlacklistedFromTradesSteamIDs() => BlacklistedFromTradesSteamIDs; - internal KeyValuePair GetGameToRedeemInBackground() => GamesToRedeemInBackground.FirstOrDefault(); + + internal KeyValuePair GetGameToRedeemInBackground() { + lock (GamesToRedeemInBackground) { + return GamesToRedeemInBackground.FirstOrDefault(); + } + } + internal IReadOnlyCollection GetIdlingBlacklistedAppIDs() => IdlingBlacklistedAppIDs; internal IReadOnlyCollection GetIdlingPriorityAppIDs() => IdlingPriorityAppIDs; @@ -235,7 +258,13 @@ namespace ArchiSteamFarm { return; } - if (GamesToRedeemInBackground.Remove(key, out _)) { + bool save; + + lock (GamesToRedeemInBackground) { + save = GamesToRedeemInBackground.Remove(key, out _); + } + + if (save) { await Save().ConfigureAwait(false); } } diff --git a/ArchiSteamFarm/ConcurrentEnumerator.cs b/ArchiSteamFarm/ConcurrentEnumerator.cs deleted file mode 100644 index 30056ebff..000000000 --- a/ArchiSteamFarm/ConcurrentEnumerator.cs +++ /dev/null @@ -1,51 +0,0 @@ -// _ _ _ ____ _ _____ -// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___ -// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \ -// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | -// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| -// -// Copyright 2015-2018 Łukasz "JustArchi" Domeradzki -// Contact: JustArchi@JustArchi.net -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Threading; - -namespace ArchiSteamFarm { - internal sealed class ConcurrentEnumerator : IEnumerator { - public T Current => Enumerator.Current; - - private readonly IEnumerator Enumerator; - private readonly SemaphoreSlim SemaphoreSlim; - - object IEnumerator.Current => Current; - - internal ConcurrentEnumerator(IReadOnlyCollection collection, SemaphoreSlim semaphoreSlim) { - if ((collection == null) || (semaphoreSlim == null)) { - throw new ArgumentNullException(nameof(collection) + " || " + nameof(semaphoreSlim)); - } - - SemaphoreSlim = semaphoreSlim; - SemaphoreSlim.Wait(); - - Enumerator = collection.GetEnumerator(); - } - - public void Dispose() => SemaphoreSlim.Release(); - public bool MoveNext() => Enumerator.MoveNext(); - public void Reset() => Enumerator.Reset(); - } -} \ No newline at end of file diff --git a/ArchiSteamFarm/ConcurrentSortedHashSet.cs b/ArchiSteamFarm/ConcurrentSortedHashSet.cs index 364e790b8..0b7c13777 100644 --- a/ArchiSteamFarm/ConcurrentSortedHashSet.cs +++ b/ArchiSteamFarm/ConcurrentSortedHashSet.cs @@ -95,7 +95,7 @@ namespace ArchiSteamFarm { } } - public IEnumerator GetEnumerator() => new ConcurrentEnumerator(BackingCollection, CollectionSemaphore); + public IEnumerator GetEnumerator() => new ConcurrentEnumerator(BackingCollection, CollectionSemaphore); public void IntersectWith(IEnumerable other) { CollectionSemaphore.Wait(); @@ -213,5 +213,29 @@ namespace ArchiSteamFarm { CollectionSemaphore.Release(); } } + + private sealed class ConcurrentEnumerator : IEnumerator { + public T Current => Enumerator.Current; + + private readonly IEnumerator Enumerator; + private readonly SemaphoreSlim SemaphoreSlim; + + object IEnumerator.Current => Current; + + internal ConcurrentEnumerator(IReadOnlyCollection collection, SemaphoreSlim semaphoreSlim) { + if ((collection == null) || (semaphoreSlim == null)) { + throw new ArgumentNullException(nameof(collection) + " || " + nameof(semaphoreSlim)); + } + + SemaphoreSlim = semaphoreSlim; + semaphoreSlim.Wait(); + + Enumerator = collection.GetEnumerator(); + } + + public void Dispose() => SemaphoreSlim.Release(); + public bool MoveNext() => Enumerator.MoveNext(); + public void Reset() => Enumerator.Reset(); + } } } \ No newline at end of file