1
0
Fork 1
mirror of https://github.com/TeamOctolings/Octobot.git synced 2025-01-31 09:09:00 +03:00

Force enumeration of global collections before using them (#106)

This PR closes #105 

This PR fixes exceptions caused by changing a collection's contents
while it is being enumerated. This can often happen with Guild- and
MemberDatas. By using `ToArray()` on these global collections and using
it in the `foreach` loop, we create a new copy of the collection,
preventing any modification to it.

While this does introduce a lot of memory allocations, there is no
fixing that. Usually, the fix to these exceptions would be to convert
the `foreach` to a reverse-`for`. However, because indices cannot be
used on these collections, that is not possible.

Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This commit is contained in:
Octol1ttle 2023-09-12 18:28:46 +05:00 committed by GitHub
parent 81595e58d3
commit 438ecfb41b
Signed by: GitHub
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 8 additions and 6 deletions

View file

@ -85,7 +85,7 @@ public class RemindCommandGroup : CommandGroup
} }
var builder = new StringBuilder(); var builder = new StringBuilder();
for (var i = 0; i < data.Reminders.Count; i++) for (var i = data.Reminders.Count - 1; i >= 0; i--)
{ {
var reminder = data.Reminders[i]; var reminder = data.Reminders[i];
builder.AppendLine( builder.AppendLine(
@ -168,8 +168,7 @@ public class RemindCommandGroup : CommandGroup
[RequireContext(ChannelContext.Guild)] [RequireContext(ChannelContext.Guild)]
[UsedImplicitly] [UsedImplicitly]
public async Task<Result> ExecuteDeleteReminderAsync( public async Task<Result> ExecuteDeleteReminderAsync(
[Description("Index of reminder to delete")] [Description("Index of reminder to delete")] [MinValue(0)]
[MinValue(0)]
int index) int index)
{ {
if (!_context.TryGetContextIDs(out var guildId, out _, out var userId)) if (!_context.TryGetContextIDs(out var guildId, out _, out var userId))

View file

@ -46,7 +46,8 @@ public sealed class GuildDataService : IHostedService
{ {
_logger.LogInformation("Saving guild data..."); _logger.LogInformation("Saving guild data...");
var tasks = new List<Task>(); var tasks = new List<Task>();
foreach (var data in _datas.Values) var datas = _datas.Values.ToArray();
foreach (var data in datas)
{ {
await using var settingsStream = File.Create(data.SettingsPath); await using var settingsStream = File.Create(data.SettingsPath);
tasks.Add(JsonSerializer.SerializeAsync(settingsStream, data.Settings, cancellationToken: ct)); tasks.Add(JsonSerializer.SerializeAsync(settingsStream, data.Settings, cancellationToken: ct));
@ -54,7 +55,8 @@ public sealed class GuildDataService : IHostedService
await using var eventsStream = File.Create(data.ScheduledEventsPath); await using var eventsStream = File.Create(data.ScheduledEventsPath);
tasks.Add(JsonSerializer.SerializeAsync(eventsStream, data.ScheduledEvents, cancellationToken: ct)); tasks.Add(JsonSerializer.SerializeAsync(eventsStream, data.ScheduledEvents, cancellationToken: ct));
foreach (var memberData in data.MemberData.Values) var memberDatas = data.MemberData.Values.ToArray();
foreach (var memberData in memberDatas)
{ {
await using var memberDataStream = File.Create($"{data.MemberDataPath}/{memberData.Id}.json"); await using var memberDataStream = File.Create($"{data.MemberDataPath}/{memberData.Id}.json");
tasks.Add(JsonSerializer.SerializeAsync(memberDataStream, memberData, cancellationToken: ct)); tasks.Add(JsonSerializer.SerializeAsync(memberDataStream, memberData, cancellationToken: ct));

View file

@ -63,7 +63,8 @@ public sealed partial class MemberUpdateService : BackgroundService
var guildData = await _guildData.GetData(guildId, ct); var guildData = await _guildData.GetData(guildId, ct);
var defaultRole = GuildSettings.DefaultRole.Get(guildData.Settings); var defaultRole = GuildSettings.DefaultRole.Get(guildData.Settings);
var failedResults = new List<Result>(); var failedResults = new List<Result>();
foreach (var data in guildData.MemberData.Values) var memberDatas = guildData.MemberData.Values.ToArray();
foreach (var data in memberDatas)
{ {
var tickResult = await TickMemberDataAsync(guildId, guildData, defaultRole, data, ct); var tickResult = await TickMemberDataAsync(guildId, guildData, defaultRole, data, ct);
failedResults.AddIfFailed(tickResult); failedResults.AddIfFailed(tickResult);