Added a message bus system

This commit is contained in:
Thorsten Sommer 2024-06-30 13:55:42 +02:00
parent 50a03e6c9d
commit 548d1eef26
Signed by: tsommer
GPG Key ID: 371BBA77A02C0108
6 changed files with 148 additions and 0 deletions

View File

@ -25,6 +25,7 @@ builder.Services.AddMudServices(config =>
}); });
builder.Services.AddMudMarkdownServices(); builder.Services.AddMudMarkdownServices();
builder.Services.AddSingleton(MessageBus.INSTANCE);
builder.Services.AddSingleton<Rust>(); builder.Services.AddSingleton<Rust>();
builder.Services.AddMudMarkdownClipboardService<MarkdownClipboardService>(); builder.Services.AddMudMarkdownClipboardService<MarkdownClipboardService>();
builder.Services.AddSingleton<SettingsManager>(); builder.Services.AddSingleton<SettingsManager>();

View File

@ -0,0 +1,13 @@
namespace AIStudio.Tools;
public enum Event
{
NONE,
// Common events:
STATE_HAS_CHANGED,
// Update events:
USER_SEARCH_FOR_UPDATE,
UPDATE_AVAILABLE,
}

View File

@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Components;
namespace AIStudio.Tools;
public interface IMessageBusReceiver
{
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data);
}

View File

@ -0,0 +1,44 @@
using Microsoft.AspNetCore.Components;
namespace AIStudio.Tools;
public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBusReceiver
{
[Inject]
protected MessageBus MessageBus { get; init; } = null!;
#region Overrides of ComponentBase
protected override void OnInitialized()
{
this.MessageBus.RegisterComponent(this);
base.OnInitialized();
}
#endregion
#region Implementation of IMessageBusReceiver
public abstract Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data);
#endregion
#region Implementation of IDisposable
public void Dispose()
{
this.MessageBus.Unregister(this);
}
#endregion
protected async Task SendMessage<T>(Event triggeredEvent, T? data = default)
{
await this.MessageBus.SendMessage(this, triggeredEvent, data);
}
protected void ApplyFilters(ComponentBase[] components, Event[] events)
{
this.MessageBus.ApplyFilters(this, components, events);
}
}

View File

@ -0,0 +1,66 @@
using System.Collections.Concurrent;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Tools;
public sealed class MessageBus
{
public static readonly MessageBus INSTANCE = new();
private readonly ConcurrentDictionary<IMessageBusReceiver, ComponentBase[]> componentFilters = new();
private readonly ConcurrentDictionary<IMessageBusReceiver, Event[]> componentEvents = new();
private readonly ConcurrentQueue<Message> messageQueue = new();
private readonly SemaphoreSlim sendingSemaphore = new(1, 1);
private MessageBus()
{
}
public void ApplyFilters(IMessageBusReceiver receiver, ComponentBase[] components, Event[] events)
{
this.componentFilters[receiver] = components;
this.componentEvents[receiver] = events;
}
public void RegisterComponent(IMessageBusReceiver receiver)
{
this.componentFilters.TryAdd(receiver, []);
this.componentEvents.TryAdd(receiver, []);
}
public void Unregister(IMessageBusReceiver receiver)
{
this.componentFilters.TryRemove(receiver, out _);
this.componentEvents.TryRemove(receiver, out _);
}
private record class Message(ComponentBase? SendingComponent, Event TriggeredEvent, object? Data);
public async Task SendMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data = default)
{
this.messageQueue.Enqueue(new Message(sendingComponent, triggeredEvent, data));
try
{
await this.sendingSemaphore.WaitAsync();
while (this.messageQueue.TryDequeue(out var message))
{
foreach (var (receiver, componentFilter) in this.componentFilters)
{
if (componentFilter.Length > 0 && sendingComponent is not null && !componentFilter.Contains(sendingComponent))
continue;
var eventFilter = this.componentEvents[receiver];
if (eventFilter.Length == 0 || eventFilter.Contains(triggeredEvent))
// We don't await the task here because we don't want to block the message bus:
_ = receiver.ProcessMessage(message.SendingComponent, message.TriggeredEvent, message.Data);
}
}
}
finally
{
this.sendingSemaphore.Release();
}
}
}

View File

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Components;
namespace AIStudio.Tools;
public static class MessageBusExtensions
{
public static async Task SendMessage<T>(this ComponentBase component, Event triggeredEvent, T? data = default)
{
await MessageBus.INSTANCE.SendMessage(component, triggeredEvent, data);
}
public static void ApplyFilters(this IMessageBusReceiver component, ComponentBase[] components, Event[] events)
{
MessageBus.INSTANCE.ApplyFilters(component, components, events);
}
}