Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,25 @@
`mqttclient` example now relies on the automatic ping, keeping its
previous manual keep-alive loop under `WOLFMQTT_NO_TIME` (#501)

* API / Behavior Changes
- A v5 `CONNECT` now advertises `Receive Maximum` set to
`MQTT_MAX_RECV_QOS2` (16 by default) unless the application supplied its
own `MQTT_PROP_RECEIVE_MAX`. This bounds the QoS 1 and QoS 2 PUBLISH
packets a conforming server may have in flight toward the client
[MQTT-3.3.4], keeping inbound QoS 2 within the client's de-duplication
table so a retransmitted PUBLISH cannot be delivered twice
[MQTT-4.3.3-10]. Override `MQTT_MAX_RECV_QOS2` in `user_settings.h` to
trade memory for a larger window, or set the property explicitly to keep
full control.
- In `WOLFMQTT_MULTITHREAD` builds, a QoS 2 PUBREC rejection processed by
the reading thread now also completes the originating
`MqttClient_Publish_WriteOnly` pending response with
`MQTT_CODE_ERROR_PUBLISH_REJECTED`, so the publisher's next poll returns
that error instead of spinning on `MQTT_CODE_CONTINUE` until
`cmd_timeout_ms`. This is the only v5 rejection surfaced on the write-only
path; a QoS 1 PUBACK or QoS 2 PUBCOMP reason code >= 0x80 is still not
detected there. Supersedes the v2.1.0 note below.

### v2.1.0 (07/02/2026)
Release 2.1.0 has been developed according to wolfSSL's development and QA
process (see link below) and successfully passed the quality criteria.
Expand Down
35 changes: 33 additions & 2 deletions examples/multithread/multithread.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@
#define NUM_PUB_PER_TASK 2
#endif

/* How many cmd_timeout_ms intervals a write-only publish may spend waiting for
the reader thread to complete its acknowledgement before the timeout is
treated as terminal. Bounds the poll loop in publish_task against a broker
that never acknowledges. */
#ifndef PUBLISH_ACK_MAX_TIMEOUTS
#define PUBLISH_ACK_MAX_TIMEOUTS 3
#endif

/* Maximum size for network read/write callbacks. There is also a v5 define that
describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */
#ifndef MAX_BUFFER_SIZE
Expand Down Expand Up @@ -655,6 +663,7 @@ static void *publish_task(void *param)
MQTTCtx *mqttCtx = (MQTTCtx*)param;
MqttPublish publish[NUM_PUB_PER_TASK];
word32 startSec[NUM_PUB_PER_TASK];
int acktimeouts[NUM_PUB_PER_TASK];

/* Build publish */
for (i=0; i<NUM_PUB_PER_TASK; i++) {
Expand All @@ -670,21 +679,43 @@ static void *publish_task(void *param)

rc[i] = MQTT_CODE_CONTINUE;
startSec[i] = 0;
acktimeouts[i] = 0;
}

/* Send until != continue */
/* Send until complete. A write-only publish returns MQTT_CODE_CONTINUE
* while its acknowledgement is still outstanding; the reader thread
* completes that ack, so keep polling until it reports success rather than
* abandoning an in-flight publish. Cancelling one whose PUBLISH already
* reached the wire cannot reclaim its Receive Maximum unit until reconnect,
* since the server keeps counting it [MQTT-4.9]. */
for (i=0; i<NUM_PUB_PER_TASK; i++) {
while (rc[i] == MQTT_CODE_CONTINUE) {
rc[i] = MqttClient_Publish_WriteOnly(&mqttCtx->client, &publish[i],
NULL);
rc[i] = check_response(mqttCtx, rc[i], &startSec[i],
MQTT_PACKET_TYPE_PUBLISH, mqttCtx->cmd_timeout_ms);
#ifndef WOLFMQTT_TEST_CANCEL
Comment thread
aidangarske marked this conversation as resolved.
/* A benign poll timeout is not a failure for an in-flight write-only
* publish; keep waiting for the reader thread to finish its ack.
* Bounded, though: mqtt_check_timeout restarts its interval every
* time it reports a timeout, so converting every one back to
* MQTT_CODE_CONTINUE would poll forever against a broker that never
* acknowledges, and this thread would never join. After
* PUBLISH_ACK_MAX_TIMEOUTS intervals the timeout stays terminal. */
if (rc[i] == MQTT_CODE_ERROR_TIMEOUT &&
++acktimeouts[i] < PUBLISH_ACK_MAX_TIMEOUTS) {
rc[i] = MQTT_CODE_CONTINUE;
}
#endif
}
}

/* Report result */
for (i=0; i<NUM_PUB_PER_TASK; i++) {
if (rc[i] != MQTT_CODE_SUCCESS) {
/* Only cancel on a genuine terminal error, never a publish still in
* flight (MQTT_CODE_CONTINUE): cancelling one whose PUBLISH is already
* on the wire forfeits its reserved Receive Maximum unit [MQTT-4.9]. */
if (rc[i] != MQTT_CODE_SUCCESS && rc[i] != MQTT_CODE_CONTINUE) {
MqttClient_CancelMessage(&mqttCtx->client, (MqttObject*)&publish[i]);
}

Expand Down
Loading
Loading