Skip to content

Commit

Permalink
Use recommended async dispose pattern
Browse files Browse the repository at this point in the history
Funny enough, non-breaking API changes since all of those classes are sealed.
  • Loading branch information
JustArchi authored and pull[bot] committed Nov 20, 2024
1 parent 241168f commit d361815
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 40 deletions.
17 changes: 12 additions & 5 deletions ArchiSteamFarm/Core/RemoteCommunication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

namespace ArchiSteamFarm.Core;

internal sealed class RemoteCommunication : IAsyncDisposable {
internal sealed class RemoteCommunication : IAsyncDisposable, IDisposable {
private const ushort MaxItemsForFairBots = ArchiWebHandler.MaxItemsInSingleInventoryRequest * WebBrowser.MaxTries; // Determines which fair bots we'll deprioritize when matching due to excessive number of inventory requests they need to make, which are likely to fail in the process or cause excessive delays
private const byte MaxMatchedBotsHard = 40; // Determines how many bots we can attempt to match in total, where match attempt is equal to analyzing bot's inventory
private const byte MaxMatchingRounds = 10; // Determines maximum amount of matching rounds we're going to consider before leaving the rest of work for the next batch
Expand All @@ -59,11 +59,7 @@ internal sealed class RemoteCommunication : IAsyncDisposable {

private readonly Bot Bot;
private readonly SemaphoreSlim MatchActivelySemaphore = new(1, 1);

#pragma warning disable CA2213 // False positive, .NET Framework can't understand DisposeAsync()
private readonly Timer? MatchActivelyTimer;
#pragma warning restore CA2213 // False positive, .NET Framework can't understand DisposeAsync()

private readonly SemaphoreSlim RequestsSemaphore = new(1, 1);

private DateTime LastAnnouncementCheck;
Expand All @@ -84,10 +80,21 @@ internal RemoteCommunication(Bot bot) {
}
}

public void Dispose() {
// Those are objects that are always being created if constructor doesn't throw exception
MatchActivelySemaphore.Dispose();
RequestsSemaphore.Dispose();

// Those are objects that might be null and the check should be in-place
MatchActivelyTimer?.Dispose();
}

public async ValueTask DisposeAsync() {
// Those are objects that are always being created if constructor doesn't throw exception
MatchActivelySemaphore.Dispose();
RequestsSemaphore.Dispose();

// Those are objects that might be null and the check should be in-place
if (MatchActivelyTimer != null) {
await MatchActivelyTimer.DisposeAsync().ConfigureAwait(false);
}
Expand Down
14 changes: 13 additions & 1 deletion ArchiSteamFarm/Helpers/CrossProcessFileBasedSemaphore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

namespace ArchiSteamFarm.Helpers;

internal sealed class CrossProcessFileBasedSemaphore : ICrossProcessSemaphore, IDisposable {
internal sealed class CrossProcessFileBasedSemaphore : IAsyncDisposable, ICrossProcessSemaphore, IDisposable {
private const ushort SpinLockDelay = 1000; // In milliseconds

private readonly string FilePath;
Expand All @@ -48,11 +48,23 @@ internal CrossProcessFileBasedSemaphore(string name) {
}

public void Dispose() {
// Those are objects that are always being created if constructor doesn't throw exception
LocalSemaphore.Dispose();

// Those are objects that might be null and the check should be in-place
FileLock?.Dispose();
}

public async ValueTask DisposeAsync() {
// Those are objects that are always being created if constructor doesn't throw exception
LocalSemaphore.Dispose();

// Those are objects that might be null and the check should be in-place
if (FileLock != null) {
await FileLock.DisposeAsync().ConfigureAwait(false);
}
}

void ICrossProcessSemaphore.Release() {
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
lock (LocalSemaphore) {
Expand Down
47 changes: 26 additions & 21 deletions ArchiSteamFarm/Steam/Bot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@

namespace ArchiSteamFarm.Steam;

public sealed class Bot : IAsyncDisposable {
public sealed class Bot : IAsyncDisposable, IDisposable {
internal const ushort CallbackSleep = 500; // In milliseconds
internal const byte MinCardsPerBadge = 5;

Expand Down Expand Up @@ -144,11 +144,7 @@ public sealed class Bot : IAsyncDisposable {
private readonly CallbackManager CallbackManager;
private readonly SemaphoreSlim CallbackSemaphore = new(1, 1);
private readonly SemaphoreSlim GamesRedeemerInBackgroundSemaphore = new(1, 1);

#pragma warning disable CA2213 // False positive, .NET Framework can't understand DisposeAsync()
private readonly Timer HeartBeatTimer;
#pragma warning restore CA2213 // False positive, .NET Framework can't understand DisposeAsync()

private readonly SemaphoreSlim InitializationSemaphore = new(1, 1);
private readonly SemaphoreSlim MessagingSemaphore = new(1, 1);
private readonly ConcurrentDictionary<UserNotificationsCallback.EUserNotification, uint> PastNotifications = new();
Expand Down Expand Up @@ -226,35 +222,20 @@ public sealed class Bot : IAsyncDisposable {
[JsonProperty]
private string? AvatarHash;

#pragma warning disable CA2213 // False positive, .NET Framework can't understand DisposeAsync()
private Timer? ConnectionFailureTimer;
#pragma warning restore CA2213 // False positive, .NET Framework can't understand DisposeAsync()

private bool FirstTradeSent;

#pragma warning disable CA2213 // False positive, .NET Framework can't understand DisposeAsync()
private Timer? GamesRedeemerInBackgroundTimer;
#pragma warning restore CA2213 // False positive, .NET Framework can't understand DisposeAsync()

private byte HeartBeatFailures;
private EResult LastLogOnResult;
private DateTime LastLogonSessionReplaced;
private bool LibraryLocked;
private byte LoginFailures;
private ulong MasterChatGroupID;

#pragma warning disable CA2213 // False positive, .NET Framework can't understand DisposeAsync()
private Timer? PlayingWasBlockedTimer;
#pragma warning restore CA2213 // False positive, .NET Framework can't understand DisposeAsync()

private bool ReconnectOnUserInitiated;
private RemoteCommunication? RemoteCommunication;
private bool SendCompleteTypesScheduled;

#pragma warning disable CA2213 // False positive, .NET Framework can't understand DisposeAsync()
private Timer? SendItemsTimer;
#pragma warning restore CA2213 // False positive, .NET Framework can't understand DisposeAsync()

private bool SteamParentalActive;
private SteamSaleEvent? SteamSaleEvent;
private string? TwoFactorCode;
Expand Down Expand Up @@ -345,15 +326,39 @@ private Bot(string botName, BotConfig botConfig, BotDatabase botDatabase) {
);
}

public async ValueTask DisposeAsync() {
public void Dispose() {
// Those are objects that are always being created if constructor doesn't throw exception
ArchiWebHandler.Dispose();
BotDatabase.Dispose();
CallbackSemaphore.Dispose();
GamesRedeemerInBackgroundSemaphore.Dispose();
InitializationSemaphore.Dispose();
MessagingSemaphore.Dispose();
SendCompleteTypesSemaphore.Dispose();
Trading.Dispose();

Actions.Dispose();
CardsFarmer.Dispose();
HeartBeatTimer.Dispose();

// Those are objects that might be null and the check should be in-place
ConnectionFailureTimer?.Dispose();
GamesRedeemerInBackgroundTimer?.Dispose();
PlayingWasBlockedTimer?.Dispose();
RemoteCommunication?.Dispose();
SendItemsTimer?.Dispose();
SteamSaleEvent?.Dispose();
}

public async ValueTask DisposeAsync() {
// Those are objects that are always being created if constructor doesn't throw exception
ArchiWebHandler.Dispose();
BotDatabase.Dispose();
CallbackSemaphore.Dispose();
GamesRedeemerInBackgroundSemaphore.Dispose();
InitializationSemaphore.Dispose();
MessagingSemaphore.Dispose();
SendCompleteTypesSemaphore.Dispose();
Trading.Dispose();

await Actions.DisposeAsync().ConfigureAwait(false);
Expand Down
15 changes: 11 additions & 4 deletions ArchiSteamFarm/Steam/Cards/CardsFarmer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

namespace ArchiSteamFarm.Steam.Cards;

public sealed class CardsFarmer : IAsyncDisposable {
public sealed class CardsFarmer : IAsyncDisposable, IDisposable {
internal const byte DaysForRefund = 14; // In how many days since payment we're allowed to refund
internal const byte HoursForRefund = 2; // Up to how many hours we're allowed to play for refund

Expand Down Expand Up @@ -120,10 +120,7 @@ public TimeSpan TimeRemaining {
private readonly SemaphoreSlim FarmingInitializationSemaphore = new(1, 1);
private readonly SemaphoreSlim FarmingResetSemaphore = new(0, 1);
private readonly ConcurrentList<Game> GamesToFarm = new();

#pragma warning disable CA2213 // False positive, .NET Framework can't understand DisposeAsync()
private readonly Timer? IdleFarmingTimer;
#pragma warning restore CA2213 // False positive, .NET Framework can't understand DisposeAsync()

private readonly ConcurrentDictionary<uint, DateTime> LocallyIgnoredAppIDs = new();

Expand Down Expand Up @@ -160,6 +157,16 @@ internal CardsFarmer(Bot bot) {
}
}

public void Dispose() {
// Those are objects that are always being created if constructor doesn't throw exception
EventSemaphore.Dispose();
FarmingInitializationSemaphore.Dispose();
FarmingResetSemaphore.Dispose();

// Those are objects that might be null and the check should be in-place
IdleFarmingTimer?.Dispose();
}

public async ValueTask DisposeAsync() {
// Those are objects that are always being created if constructor doesn't throw exception
EventSemaphore.Dispose();
Expand Down
7 changes: 3 additions & 4 deletions ArchiSteamFarm/Steam/Integration/SteamSaleEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,11 @@

namespace ArchiSteamFarm.Steam.Integration;

internal sealed class SteamSaleEvent : IAsyncDisposable {
internal sealed class SteamSaleEvent : IAsyncDisposable, IDisposable {
private const byte MaxSingleQueuesDaily = 3; // This is only a failsafe for infinite queue clearing (in case IsDiscoveryQueueAvailable() would fail us)

private readonly Bot Bot;

#pragma warning disable CA2213 // False positive, .NET Framework can't understand DisposeAsync()
private readonly Timer SaleEventTimer;
#pragma warning restore CA2213 // False positive, .NET Framework can't understand DisposeAsync()

internal SteamSaleEvent(Bot bot) {
Bot = bot ?? throw new ArgumentNullException(nameof(bot));
Expand All @@ -50,6 +47,8 @@ internal SteamSaleEvent(Bot bot) {
);
}

public void Dispose() => SaleEventTimer.Dispose();

public ValueTask DisposeAsync() => SaleEventTimer.DisposeAsync();

private async void ExploreDiscoveryQueue(object? state = null) {
Expand Down
13 changes: 9 additions & 4 deletions ArchiSteamFarm/Steam/Interaction/Actions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,27 @@

namespace ArchiSteamFarm.Steam.Interaction;

public sealed class Actions : IAsyncDisposable {
public sealed class Actions : IAsyncDisposable, IDisposable {
private static readonly SemaphoreSlim GiftCardsSemaphore = new(1, 1);

private readonly Bot Bot;
private readonly ConcurrentHashSet<ulong> HandledGifts = new();
private readonly SemaphoreSlim TradingSemaphore = new(1, 1);

#pragma warning disable CA2213 // False positive, .NET Framework can't understand DisposeAsync()
private Timer? CardsFarmerResumeTimer;
#pragma warning restore CA2213 // False positive, .NET Framework can't understand DisposeAsync()

private bool ProcessingGiftsScheduled;
private bool TradingScheduled;

internal Actions(Bot bot) => Bot = bot ?? throw new ArgumentNullException(nameof(bot));

public void Dispose() {
// Those are objects that are always being created if constructor doesn't throw exception
TradingSemaphore.Dispose();

// Those are objects that might be null and the check should be in-place
CardsFarmerResumeTimer?.Dispose();
}

public async ValueTask DisposeAsync() {
// Those are objects that are always being created if constructor doesn't throw exception
TradingSemaphore.Dispose();
Expand Down
7 changes: 6 additions & 1 deletion ArchiSteamFarm/Web/Responses/StreamResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

namespace ArchiSteamFarm.Web.Responses;

public sealed class StreamResponse : BasicResponse, IAsyncDisposable {
public sealed class StreamResponse : BasicResponse, IAsyncDisposable, IDisposable {
[PublicAPI]
public Stream? Content { get; }

Expand All @@ -43,6 +43,11 @@ internal StreamResponse(HttpResponseMessage httpResponseMessage) : base(httpResp
Length = httpResponseMessage.Content.Headers.ContentLength.GetValueOrDefault();
}

public void Dispose() {
Content?.Dispose();
ResponseMessage.Dispose();
}

public async ValueTask DisposeAsync() {
if (Content != null) {
await Content.DisposeAsync().ConfigureAwait(false);
Expand Down

0 comments on commit d361815

Please sign in to comment.