Skip to content

Commit 4ec7746

Browse files
authored
Introduce a NotDisconnected signal (#286)
1 parent 4443557 commit 4ec7746

File tree

5 files changed

+436
-392
lines changed

5 files changed

+436
-392
lines changed

Source/HiveMQtt/Client/Connection/ConnectionManager.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ internal ConnectState State
8787
// Event-like signal to indicate the connection reached Connected state
8888
private TaskCompletionSource<bool> connectedSignal = new(TaskCreationOptions.RunContinuationsAsynchronously);
8989

90+
// Event-like signal to indicate the connection state is not Disconnected (Connecting, Connected, or Disconnecting)
91+
// This is used by ConnectionWriterAsync which needs to be active during Connecting state
92+
private TaskCompletionSource<bool> notDisconnectedSignal = new(TaskCreationOptions.RunContinuationsAsynchronously);
93+
9094
/// <summary>
9195
/// Initializes a new instance of the <see cref="ConnectionManager"/> class.
9296
/// </summary>
@@ -99,6 +103,7 @@ public ConnectionManager(HiveMQClient client)
99103
this.OPubTransactionQueue = new BoundedDictionaryX<int, List<ControlPacket>>(65535);
100104
this.State = ConnectState.Disconnected;
101105
this.ResetConnectedSignal();
106+
this.ResetNotDisconnectedSignal();
102107

103108
// Connect the appropriate transport
104109
// Note: The actual transport is selected in ConnectAsync() based on WebSocketServer option.
@@ -115,6 +120,25 @@ public ConnectionManager(HiveMQClient client)
115120

116121
internal Task WaitUntilConnectedAsync(CancellationToken cancellationToken) => this.connectedSignal.Task.WaitAsync(cancellationToken);
117122

123+
/// <summary>
124+
/// Waits until the connection state is not Disconnected (i.e., Connecting, Connected, or Disconnecting).
125+
/// This is used by ConnectionWriterAsync which needs to be active during Connecting state to send CONNECT packets.
126+
/// Uses an event-driven signal instead of polling to avoid CPU waste during long disconnections.
127+
/// </summary>
128+
/// <param name="cancellationToken">The cancellation token.</param>
129+
/// <returns>A task that completes when state is not Disconnected.</returns>
130+
internal Task WaitUntilNotDisconnectedAsync(CancellationToken cancellationToken)
131+
{
132+
// Check state first - if already not disconnected, return immediately
133+
if (this.State != ConnectState.Disconnected)
134+
{
135+
return Task.CompletedTask;
136+
}
137+
138+
// Wait on the signal - this is event-driven and doesn't consume CPU while waiting
139+
return this.notDisconnectedSignal.Task.WaitAsync(cancellationToken);
140+
}
141+
118142
internal void SignalConnected()
119143
{
120144
if (!this.connectedSignal.Task.IsCompleted)
@@ -125,6 +149,23 @@ internal void SignalConnected()
125149

126150
internal void ResetConnectedSignal() => this.connectedSignal = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
127151

152+
/// <summary>
153+
/// Signals that the connection state is not Disconnected (i.e., Connecting, Connected, or Disconnecting).
154+
/// This is called when state transitions from Disconnected to any other state.
155+
/// </summary>
156+
internal void SignalNotDisconnected()
157+
{
158+
if (!this.notDisconnectedSignal.Task.IsCompleted)
159+
{
160+
this.notDisconnectedSignal.TrySetResult(true);
161+
}
162+
}
163+
164+
/// <summary>
165+
/// Resets the not-disconnected signal. Called when state transitions to Disconnected.
166+
/// </summary>
167+
internal void ResetNotDisconnectedSignal() => this.notDisconnectedSignal = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
168+
128169
internal async Task<bool> ConnectAsync()
129170
{
130171
// Connect the appropriate transport

Source/HiveMQtt/Client/Connection/ConnectionManagerHandlers.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ internal async Task<bool> HandleDisconnectionAsync(bool clean = true)
402402
// Set state to Disconnected AFTER tasks are cancelled and transport is closed
403403
// This ensures tasks see the correct state when they check during cancellation
404404
this.State = ConnectState.Disconnected;
405+
this.ResetNotDisconnectedSignal();
405406

406407
if (clean)
407408
{

0 commit comments

Comments
 (0)