Merge pull request #547 from vbBerni/develop

Fix Managed Client should dispose inner mqtt client #527
This commit is contained in:
Christian
2019-03-22 20:31:24 +01:00
committed by GitHub
4 changed files with 140 additions and 24 deletions
@@ -27,9 +27,11 @@ namespace MQTTnet.Extensions.ManagedClient
private CancellationTokenSource _connectionCancellationToken; private CancellationTokenSource _connectionCancellationToken;
private CancellationTokenSource _publishingCancellationToken; private CancellationTokenSource _publishingCancellationToken;
private Task _maintainConnectionTask;
private ManagedMqttClientStorageManager _storageManager; private ManagedMqttClientStorageManager _storageManager;
private bool _disposed = false;
private bool _subscriptionsNotPushed; private bool _subscriptionsNotPushed;
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetChildLogger logger) public ManagedMqttClient(IMqttClient mqttClient, IMqttNetChildLogger logger)
@@ -73,6 +75,8 @@ namespace MQTTnet.Extensions.ManagedClient
public async Task StartAsync(IManagedMqttClientOptions options) public async Task StartAsync(IManagedMqttClientOptions options)
{ {
ThrowIfDisposed();
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));
if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options)); if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options));
@@ -81,7 +85,7 @@ namespace MQTTnet.Extensions.ManagedClient
throw new NotSupportedException("The managed client does not support existing sessions."); throw new NotSupportedException("The managed client does not support existing sessions.");
} }
if (_connectionCancellationToken != null) throw new InvalidOperationException("The managed client is already started."); if (!_maintainConnectionTask?.IsCompleted ?? false) throw new InvalidOperationException("The managed client is already started.");
Options = options; Options = options;
@@ -99,24 +103,32 @@ namespace MQTTnet.Extensions.ManagedClient
_connectionCancellationToken = new CancellationTokenSource(); _connectionCancellationToken = new CancellationTokenSource();
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(() => MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.Token); _maintainConnectionTask = Task.Run(() => MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.Token);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
_logger.Info("Started"); _logger.Info("Started");
} }
public Task StopAsync() public async Task StopAsync()
{ {
ThrowIfDisposed();
StopPublishing(); StopPublishing();
StopMaintainingConnection(); StopMaintainingConnection();
_messageQueue.Clear(); _messageQueue.Clear();
return Task.FromResult(0); if (_maintainConnectionTask != null)
{
await Task.WhenAny(_maintainConnectionTask);
_maintainConnectionTask = null;
}
} }
public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken) public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken)
{ {
ThrowIfDisposed();
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
await PublishAsync(new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage).Build()).ConfigureAwait(false); await PublishAsync(new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage).Build()).ConfigureAwait(false);
@@ -125,6 +137,8 @@ namespace MQTTnet.Extensions.ManagedClient
public async Task PublishAsync(ManagedMqttApplicationMessage applicationMessage) public async Task PublishAsync(ManagedMqttApplicationMessage applicationMessage)
{ {
ThrowIfDisposed();
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
ManagedMqttApplicationMessage removedMessage = null; ManagedMqttApplicationMessage removedMessage = null;
@@ -180,6 +194,8 @@ namespace MQTTnet.Extensions.ManagedClient
public Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters) public Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
{ {
ThrowIfDisposed();
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters)); if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
lock (_subscriptions) lock (_subscriptions)
@@ -196,6 +212,8 @@ namespace MQTTnet.Extensions.ManagedClient
public Task UnsubscribeAsync(IEnumerable<string> topics) public Task UnsubscribeAsync(IEnumerable<string> topics)
{ {
ThrowIfDisposed();
if (topics == null) throw new ArgumentNullException(nameof(topics)); if (topics == null) throw new ArgumentNullException(nameof(topics));
lock (_subscriptions) lock (_subscriptions)
@@ -215,8 +233,31 @@ namespace MQTTnet.Extensions.ManagedClient
public void Dispose() public void Dispose()
{ {
_connectionCancellationToken?.Dispose(); if (_disposed)
_publishingCancellationToken?.Dispose(); {
return;
}
_disposed = true;
StopPublishing();
StopMaintainingConnection();
if (_maintainConnectionTask != null)
{
Task.WaitAny(_maintainConnectionTask);
_maintainConnectionTask = null;
}
_mqttClient.Dispose();
}
private void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(ManagedMqttClient));
}
} }
private async Task MaintainConnectionAsync(CancellationToken cancellationToken) private async Task MaintainConnectionAsync(CancellationToken cancellationToken)
@@ -237,16 +278,19 @@ namespace MQTTnet.Extensions.ManagedClient
} }
finally finally
{ {
try if (!_disposed)
{ {
await _mqttClient.DisconnectAsync().ConfigureAwait(false); try
} {
catch (Exception exception) await _mqttClient.DisconnectAsync().ConfigureAwait(false);
{ }
_logger.Error(exception, "Error while disconnecting."); catch (Exception exception)
} {
_logger.Error(exception, "Error while disconnecting.");
}
_logger.Info("Stopped"); _logger.Info("Stopped");
}
} }
} }
@@ -377,7 +421,7 @@ namespace MQTTnet.Extensions.ManagedClient
private async Task SynchronizeSubscriptionsAsync() private async Task SynchronizeSubscriptionsAsync()
{ {
_logger.Info(nameof(ManagedMqttClient), "Synchronizing subscriptions"); _logger.Info("Synchronizing subscriptions");
List<TopicFilter> subscriptions; List<TopicFilter> subscriptions;
HashSet<string> unsubscriptions; HashSet<string> unsubscriptions;
@@ -1,7 +1,12 @@
using System.Threading.Tasks; using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
using MQTTnet.Extensions.ManagedClient; using MQTTnet.Extensions.ManagedClient;
using MQTTnet.Server; using MQTTnet.Server;
using MQTTnet.Tests.Mockups;
namespace MQTTnet.Tests namespace MQTTnet.Tests
{ {
@@ -40,5 +45,67 @@ namespace MQTTnet.Tests
await managedClient.StopAsync(); await managedClient.StopAsync();
} }
} }
[TestMethod]
public async Task ManagedClients_Will_Message_Send()
{
using (var testEnvironment = new TestEnvironment())
{
var receivedMessagesCount = 0;
var factory = new MqttFactory();
await testEnvironment.StartServerAsync();
var willMessage = new MqttApplicationMessageBuilder().WithTopic("My/last/will").WithAtMostOnceQoS().Build();
var clientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("localhost", testEnvironment.ServerPort)
.WithWillMessage(willMessage);
var dyingClient = testEnvironment.CreateClient();
var dyingManagedClient = new ManagedMqttClient(dyingClient, testEnvironment.ClientLogger.CreateChildLogger());
await dyingManagedClient.StartAsync(new ManagedMqttClientOptionsBuilder()
.WithClientOptions(clientOptions)
.Build());
var recievingClient = await testEnvironment.ConnectClientAsync();
await recievingClient.SubscribeAsync("My/last/will");
recievingClient.UseReceivedApplicationMessageHandler(context => Interlocked.Increment(ref receivedMessagesCount));
dyingManagedClient.Dispose();
await Task.Delay(1000);
Assert.AreEqual(1, receivedMessagesCount);
}
}
[TestMethod]
public async Task Start_Stop()
{
using (var testEnvironment = new TestEnvironment())
{
var factory = new MqttFactory();
var server = await testEnvironment.StartServerAsync();
var managedClient = new ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetLogger().CreateChildLogger());
var clientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("localhost", testEnvironment.ServerPort);
TaskCompletionSource<bool> connected = new TaskCompletionSource<bool>();
managedClient.Connected += (s, e) => { connected.SetResult(true); };
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder()
.WithClientOptions(clientOptions)
.Build());
await connected.Task;
await managedClient.StopAsync();
Assert.AreEqual(0, (await server.GetClientStatusAsync()).Count);
}
}
} }
} }
@@ -29,6 +29,10 @@ namespace MQTTnet.Tests.Mockups
public int ServerPort { get; set; } = 1888; public int ServerPort { get; set; } = 1888;
public IMqttNetLogger ServerLogger => _serverLogger;
public IMqttNetLogger ClientLogger => _clientLogger;
public TestEnvironment() public TestEnvironment()
{ {
_serverLogger.LogMessagePublished += (s, e) => _serverLogger.LogMessagePublished += (s, e) =>
@@ -56,7 +60,9 @@ namespace MQTTnet.Tests.Mockups
public IMqttClient CreateClient() public IMqttClient CreateClient()
{ {
return _mqttFactory.CreateMqttClient(_clientLogger); var client = _mqttFactory.CreateMqttClient(_clientLogger);
_clients.Add(client);
return client;
} }
public Task<IMqttServer> StartServerAsync() public Task<IMqttServer> StartServerAsync()
@@ -84,10 +90,9 @@ namespace MQTTnet.Tests.Mockups
public async Task<IMqttClient> ConnectClientAsync(MqttClientOptionsBuilder options) public async Task<IMqttClient> ConnectClientAsync(MqttClientOptionsBuilder options)
{ {
var client = _mqttFactory.CreateMqttClient(_clientLogger); var client = CreateClient();
await client.ConnectAsync(options.WithTcpServer("localhost", ServerPort).Build()); await client.ConnectAsync(options.WithTcpServer("localhost", ServerPort).Build());
_clients.Add(client);
return client; return client;
} }
+2 -2
View File
@@ -64,9 +64,9 @@ namespace MQTTnet.Tests
} }
finally finally
{ {
MqttNetGlobalLogger.LogMessagePublished -= globalLog;
await managedClient.StopAsync(); await managedClient.StopAsync();
MqttNetGlobalLogger.LogMessagePublished -= globalLog;
} }
Assert.IsFalse(invalidLogIdOccured); Assert.IsFalse(invalidLogIdOccured);