Improve .NET 9.0+ locking performance (#3216)

This commit is contained in:
Mark Cilia Vincenti
2025-12-30 20:08:44 +01:00
committed by GitHub
parent fd6e3ad8df
commit 4e95dd7123
14 changed files with 31 additions and 16 deletions

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Threading;
namespace Discord.Commands namespace Discord.Commands
{ {
@@ -11,7 +12,7 @@ namespace Discord.Commands
private readonly ConcurrentDictionary<string, CommandMapNode> _nodes; private readonly ConcurrentDictionary<string, CommandMapNode> _nodes;
private readonly string _name; private readonly string _name;
private readonly object _lockObj = new object(); private readonly Lock _lockObj = new();
private ImmutableArray<CommandInfo> _commands; private ImmutableArray<CommandInfo> _commands;
public bool IsEmpty => _commands.Length == 0 && _nodes.Count == 0; public bool IsEmpty => _commands.Length == 0 && _nodes.Count == 0;

View File

@@ -19,5 +19,9 @@
<PackageReference Include="IDisposableAnalyzers" Version="4.0.8"> <PackageReference Include="IDisposableAnalyzers" Version="4.0.8">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))" Include="Backport.System.Threading.Lock" Version="3.1.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>analyzers</IncludeAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Model = Discord.API.Interaction; using Model = Discord.API.Interaction;
@@ -32,7 +33,7 @@ namespace Discord.Rest
/// </summary> /// </summary>
internal new RestCommandBaseData Data { get; private set; } internal new RestCommandBaseData Data { get; private set; }
private object _lock = new object(); private readonly Lock _lock = new();
internal RestCommandBase(DiscordRestClient client, Model model) internal RestCommandBase(DiscordRestClient client, Model model)
: base(client, model.Id) : base(client, model.Id)

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using DataModel = Discord.API.MessageComponentInteractionData; using DataModel = Discord.API.MessageComponentInteractionData;
using Model = Discord.API.Interaction; using Model = Discord.API.Interaction;
@@ -24,7 +25,7 @@ namespace Discord.Rest
/// <inheritdoc cref="IComponentInteraction.Message"/> /// <inheritdoc cref="IComponentInteraction.Message"/>
public RestUserMessage Message { get; private set; } public RestUserMessage Message { get; private set; }
private object _lock = new object(); private readonly Lock _lock = new();
internal RestMessageComponent(BaseDiscordClient client, Model model) internal RestMessageComponent(BaseDiscordClient client, Model model)
: base(client, model.Id) : base(client, model.Id)

View File

@@ -7,6 +7,7 @@ using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using DataModel = Discord.API.ModalInteractionData; using DataModel = Discord.API.ModalInteractionData;
@@ -41,7 +42,7 @@ namespace Discord.Rest
return entity; return entity;
} }
private object _lock = new object(); private readonly Lock _lock = new();
/// <summary> /// <summary>
/// Acknowledges this interaction with the <see cref="InteractionResponseType.DeferredUpdateMessage"/> if the modal was created /// Acknowledges this interaction with the <see cref="InteractionResponseType.DeferredUpdateMessage"/> if the modal was created

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using DataModel = Discord.API.AutocompleteInteractionData; using DataModel = Discord.API.AutocompleteInteractionData;
using Model = Discord.API.Interaction; using Model = Discord.API.Interaction;
@@ -19,7 +20,7 @@ namespace Discord.Rest
/// </summary> /// </summary>
public new RestAutocompleteInteractionData Data { get; } public new RestAutocompleteInteractionData Data { get; }
private object _lock = new object(); private readonly Lock _lock = new();
internal RestAutocompleteInteraction(DiscordRestClient client, Model model) internal RestAutocompleteInteraction(DiscordRestClient client, Model model)
: base(client, model.Id) : base(client, model.Id)

View File

@@ -17,7 +17,7 @@ namespace Discord.Net.Queue
{ {
private const int MinimumSleepTimeMs = 750; private const int MinimumSleepTimeMs = 750;
private readonly object _lock; private readonly Lock _lock;
private readonly RequestQueue _queue; private readonly RequestQueue _queue;
private int _semaphore; private int _semaphore;
private DateTimeOffset? _resetTick; private DateTimeOffset? _resetTick;
@@ -32,7 +32,7 @@ namespace Discord.Net.Queue
_queue = queue; _queue = queue;
Id = id; Id = id;
_lock = new object(); _lock = new();
if (request.Options.IsClientBucket) if (request.Options.IsClientBucket)
WindowCount = ClientBucket.Get(request.Options.BucketId).WindowCount; WindowCount = ClientBucket.Get(request.Options.BucketId).WindowCount;

View File

@@ -21,7 +21,7 @@ namespace Discord.WebSocket
private ImmutableArray<StickerPack<SocketSticker>> _defaultStickers; private ImmutableArray<StickerPack<SocketSticker>> _defaultStickers;
private int _totalShards; private int _totalShards;
private SemaphoreSlim[] _identifySemaphores; private SemaphoreSlim[] _identifySemaphores;
private object _semaphoreResetLock; private readonly Lock _semaphoreResetLock;
private Task _semaphoreResetTask; private Task _semaphoreResetTask;
private bool _isDisposed; private bool _isDisposed;
@@ -80,7 +80,7 @@ namespace Discord.WebSocket
if (ids != null && config.TotalShards == null) if (ids != null && config.TotalShards == null)
throw new ArgumentException($"Custom ids are not supported when {nameof(config.TotalShards)} is not specified."); throw new ArgumentException($"Custom ids are not supported when {nameof(config.TotalShards)} is not specified.");
_semaphoreResetLock = new object(); _semaphoreResetLock = new();
_shardIdsToIndex = new Dictionary<int, int>(); _shardIdsToIndex = new Dictionary<int, int>();
config.DisplayInitialLog = false; config.DisplayInitialLog = false;
_baseConfig = config; _baseConfig = config;

View File

@@ -6,6 +6,7 @@ using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Model = Discord.API.Channel; using Model = Discord.API.Channel;
using ThreadMember = Discord.API.ThreadMember; using ThreadMember = Discord.API.ThreadMember;
@@ -110,8 +111,8 @@ namespace Discord.WebSocket
private bool _usersDownloaded; private bool _usersDownloaded;
private readonly object _downloadLock = new object(); private readonly Lock _downloadLock = new();
private readonly object _ownerLock = new object(); private readonly Lock _ownerLock = new();
private ulong _ownerId; private ulong _ownerId;

View File

@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using DataModel = Discord.API.MessageComponentInteractionData; using DataModel = Discord.API.MessageComponentInteractionData;
using Model = Discord.API.Interaction; using Model = Discord.API.Interaction;
@@ -24,7 +25,7 @@ namespace Discord.WebSocket
/// <inheritdoc cref="IComponentInteraction.Message"/> /// <inheritdoc cref="IComponentInteraction.Message"/>
public SocketUserMessage Message { get; private set; } public SocketUserMessage Message { get; private set; }
private object _lock = new object(); private readonly Lock _lock = new();
public override bool HasResponded { get; internal set; } = false; public override bool HasResponded { get; internal set; } = false;
internal SocketMessageComponent(DiscordSocketClient client, Model model, ISocketMessageChannel channel, SocketUser user) internal SocketMessageComponent(DiscordSocketClient client, Model model, ISocketMessageChannel channel, SocketUser user)

View File

@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using DataModel = Discord.API.ModalInteractionData; using DataModel = Discord.API.ModalInteractionData;
@@ -66,7 +67,7 @@ namespace Discord.WebSocket
/// <inheritdoc/> /// <inheritdoc/>
public override bool HasResponded { get; internal set; } public override bool HasResponded { get; internal set; }
private object _lock = new object(); private readonly Lock _lock = new();
/// <inheritdoc/> /// <inheritdoc/>
public override async Task RespondWithFilesAsync( public override async Task RespondWithFilesAsync(

View File

@@ -2,6 +2,7 @@ using Discord.Rest;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using DataModel = Discord.API.AutocompleteInteractionData; using DataModel = Discord.API.AutocompleteInteractionData;
using Model = Discord.API.Interaction; using Model = Discord.API.Interaction;
@@ -21,7 +22,7 @@ namespace Discord.WebSocket
/// <inheritdoc/> /// <inheritdoc/>
public override bool HasResponded { get; internal set; } public override bool HasResponded { get; internal set; }
private object _lock = new object(); private readonly Lock _lock = new();
internal SocketAutocompleteInteraction(DiscordSocketClient client, Model model, ISocketMessageChannel channel, SocketUser user) internal SocketAutocompleteInteraction(DiscordSocketClient client, Model model, ISocketMessageChannel channel, SocketUser user)
: base(client, model.Id, channel, user) : base(client, model.Id, channel, user)

View File

@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using DataModel = Discord.API.ApplicationCommandInteractionData; using DataModel = Discord.API.ApplicationCommandInteractionData;
using Model = Discord.API.Interaction; using Model = Discord.API.Interaction;
@@ -35,7 +36,7 @@ namespace Discord.WebSocket
/// <inheritdoc/> /// <inheritdoc/>
public override bool HasResponded { get; internal set; } public override bool HasResponded { get; internal set; }
private object _lock = new object(); private readonly Lock _lock = new();
internal SocketCommandBase(DiscordSocketClient client, Model model, ISocketMessageChannel channel, SocketUser user) internal SocketCommandBase(DiscordSocketClient client, Model model, ISocketMessageChannel channel, SocketUser user)
: base(client, model.Id, channel, user) : base(client, model.Id, channel, user)

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading;
using Model = Discord.API.User; using Model = Discord.API.User;
namespace Discord.WebSocket namespace Discord.WebSocket
@@ -31,7 +32,7 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
internal override SocketGlobalUser GlobalUser { get => this; set => throw new NotImplementedException(); } internal override SocketGlobalUser GlobalUser { get => this; set => throw new NotImplementedException(); }
private readonly object _lockObj = new object(); private readonly Lock _lockObj = new();
private ushort _references; private ushort _references;
private SocketGlobalUser(DiscordSocketClient discord, ulong id) private SocketGlobalUser(DiscordSocketClient discord, ulong id)