@@ -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
0 commit comments