avoid unneeded overhead of ConcurrentQueue

This commit is contained in:
Jesse Plamondon-Willard 2022-06-22 01:53:37 -04:00
parent c91fbc82f8
commit be086cf005
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
3 changed files with 72 additions and 21 deletions

View File

@ -8,6 +8,9 @@
-->
## Upcoming release
* For players:
* Minor optimizations.
* For the web UI:
* Fixed the mod count in the log parser metadata.

View File

@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace StardewModdingAPI.Framework
{
/// <summary>A thread-safe command queue optimized for infrequent changes.</summary>
internal class CommandQueue
{
/********
** Fields
********/
/// <summary>The underlying list of queued commands to parse and execute.</summary>
private readonly List<string> RawCommandQueue = new();
/********
** Public methods
********/
/// <summary>Add a command to the queue.</summary>
/// <param name="command">The command to add.</param>
public void Add(string command)
{
lock (this.RawCommandQueue)
this.RawCommandQueue.Add(command);
}
/// <summary>Remove and return all queued commands, if any.</summary>
/// <param name="queued">The commands that were dequeued, in the order they were originally queued.</param>
/// <returns>Returns whether any values were dequeued.</returns>
[SuppressMessage("ReSharper", "InconsistentlySynchronizedField", Justification = "Deliberately check if it's empty before locking unnecessarily.")]
public bool TryDequeue([NotNullWhen(true)] out string[]? queued)
{
if (this.RawCommandQueue.Count is 0)
{
queued = null;
return false;
}
lock (this.RawCommandQueue)
{
queued = this.RawCommandQueue.ToArray();
this.RawCommandQueue.Clear();
return queued.Length > 0;
}
}
}
}

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
@ -147,8 +146,7 @@ namespace StardewModdingAPI.Framework
#endif
/// <summary>A list of queued commands to parse and execute.</summary>
/// <remarks>This property must be thread-safe, since it's accessed from a separate console input thread.</remarks>
private readonly ConcurrentQueue<string> RawCommandQueue = new();
private readonly CommandQueue RawCommandQueue = new();
/// <summary>A list of commands to execute on each screen.</summary>
private readonly PerScreen<List<QueuedCommand>> ScreenCommandQueue = new(() => new List<QueuedCommand>());
@ -437,7 +435,7 @@ namespace StardewModdingAPI.Framework
() => this.LogManager.RunConsoleInputLoop(
commandManager: this.CommandManager,
reloadTranslations: this.ReloadTranslations,
handleInput: input => this.RawCommandQueue.Enqueue(input),
handleInput: input => this.RawCommandQueue.Add(input),
continueWhile: () => this.IsGameRunning && !this.CancellationToken.IsCancellationRequested
)
).Start();
@ -525,7 +523,9 @@ namespace StardewModdingAPI.Framework
/*********
** Parse commands
*********/
while (this.RawCommandQueue.TryDequeue(out string? rawInput))
if (this.RawCommandQueue.TryDequeue(out string[]? rawCommands))
{
foreach (string rawInput in rawCommands)
{
// parse command
string? name;
@ -549,6 +549,7 @@ namespace StardewModdingAPI.Framework
// queue command for screen
this.ScreenCommandQueue.GetValueForScreen(screenId).Add(new(command, name, args));
}
}
/*********