using System; using System.Collections.Concurrent; using System.Threading.Tasks; using MQTTnet.Packets; namespace MQTTnet.Client { public class MqttPacketDispatcher { private readonly ConcurrentDictionary, TaskCompletionSource> _awaiters = new ConcurrentDictionary, TaskCompletionSource>(); public void Dispatch(Exception exception) { foreach (var awaiter in _awaiters) { awaiter.Value.SetException(exception); } } public void Dispatch(MqttBasePacket packet) { if (packet == null) throw new ArgumentNullException(nameof(packet)); ushort identifier = 0; if (packet is IMqttPacketWithIdentifier packetWithIdentifier && packetWithIdentifier.PacketIdentifier.HasValue) { identifier = packetWithIdentifier.PacketIdentifier.Value; } var type = packet.GetType(); var key = new Tuple(identifier, type); if (_awaiters.TryRemove(key, out var tcs)) { tcs.TrySetResult(packet); return; } throw new InvalidOperationException($"Packet of type '{type.Name}' not handled or dispatched."); } public void Reset() { _awaiters.Clear(); } public TaskCompletionSource AddPacketAwaiter(ushort? identifier) where TResponsePacket : MqttBasePacket { var tcs = new TaskCompletionSource(); if (!identifier.HasValue) { identifier = 0; } var key = new Tuple(identifier ?? 0, typeof(TResponsePacket)); if (!_awaiters.TryAdd(key, tcs)) { throw new InvalidOperationException($"The packet dispatcher already has an awaiter for packet of type '{key.Item2.Name}' with identifier {key.Item1}."); } return tcs; } public void RemovePacketAwaiter(ushort? identifier) where TResponsePacket : MqttBasePacket { if (!identifier.HasValue) { identifier = 0; } var key = new Tuple(identifier ?? 0, typeof(TResponsePacket)); _awaiters.TryRemove(key, out var _); } } }