@@ -212,7 +212,9 @@ namespace Aws
212212 auto available = std::make_shared<std::atomic<bool >>(true );
213213 if (publish->qos == AWS_MQTT5_QOS_AT_LEAST_ONCE)
214214 {
215- /* Eagerly acquire the puback control before invoking the user callback */
215+ /* Eagerly acquire the puback control before invoking the user callback.
216+ * An opaque wrapper (PubackControlHandle) is provided to the user callback via
217+ * acquirePubackControl(). */
216218 pubackControlId = aws_mqtt5_client_acquire_puback (client_core->m_client , publish);
217219
218220 if (pubackControlId != 0 )
@@ -226,22 +228,30 @@ namespace Aws
226228 {
227229 return nullptr ;
228230 }
231+ /* Mark as consumed so a second call returns nullptr */
229232 *available = false ;
230233 return handle;
231234 };
232235 }
233236 }
234237
235- bool pubackTaken = client_core->onPublishReceived (eventData);
238+ client_core->onPublishReceived (eventData);
236239
237- /* Explicitly invalidate the lambda now that the callback scope has ended.
238- * Any copy of the lambda saved by the user will now return nullptr
239- * immediately. */
240- *available = false ;
240+ /* Detect whether the user called acquirePubackControl() during the callback:
241+ * - If they called it and got a handle, *available was set to false inside
242+ * the lambda, so it is already false here.
243+ * - If they did NOT call it, *available is still true here.
244+ *
245+ * We use exchange(false) to atomically read the current value and set it to
246+ * false in one step. If it returns true, the user did NOT take control and
247+ * we must auto-invoke the PUBACK. If it returns false, the user already
248+ * consumed the handle and is responsible for calling InvokePuback() later.
249+ *
250+ * This also serves as the post-callback invalidation: after this point any
251+ * saved copy of the lambda will return nullptr immediately. */
252+ bool userDidNotTakeControl = available->exchange (false );
241253
242- /* If the user did not take manual control of the PUBACK (returned false),
243- * auto-invoke it now. Also auto-invoke on error to avoid losing the PUBACK. */
244- if (pubackControlId != 0 && !pubackTaken)
254+ if (pubackControlId != 0 && userDidNotTakeControl)
245255 {
246256 aws_mqtt5_client_invoke_puback (client_core->m_client , pubackControlId, nullptr );
247257 }
0 commit comments