eclipse-paho / eclipse-paho/paho.mqtt.java
The client is not thread safe when set qos0
- Dominant language
- Java
- Stars
- 2.3k
- Forks
- 919
- PR merge metrics
- No merged PRs in 30d
Description
Please fill out the form below before submitting, thank you!
- [ yes] Bug exists Release Version 1.2.0 ( Master Branch)
- [ yes] Bug exists in MQTTv3 Client on Snapshot Version 1.2.1-SNAPSHOT (Develop Branch)
- [ not know ] Bug exists in MQTTv5 Client on Snapshot Version 1.2.1-SNAPSHOT (Develop Branch)
I found a bug that the mqttclient is not thread safe sometimes.
When we config qos0,and send messages with multiple threads at the same time,only one thread and work fine, and the others will wait at method waitForCompletion.
Here's why:
When sending a message, mqtt puts the message in the send queue. At the same time the key is set to 0.
The sending thread gets the message from the queue and sends it. After sending successfully, wake up aClient.
This is not a problem in a single thread, but in multithreading, in the ClientState.send method.
Multiple threads will put the message in the queue (pendingMessages) tokenStore.saveToken(token, message); This is a problem, the message has the same key and is 0, causing the message to be overwritten.
Mqttclient has a background thread that fetches messages from the queue and checks the tokens. At this time, if 4 threads send a message, there are 4 messages in the queue, but there is only one token. So only one message was sent successfully. See CommsSender.run()
Then only one thread of the initial aClient will be woken up and continue to send. Other concurrent threads will always be in the wait state.
this is the test code
`
private static AtomicInteger sendFinishCount = new AtomicInteger(0);
private static AtomicInteger sendCount = new AtomicInteger(0);
public static void main(String... args) throws InterruptedException, MqttException {
MqttServiceTest mqttServiceTest = new MqttServiceTest();
mqttServiceTest.initClient();
new Thread(new Task(mqttServiceTest)).start();
new Thread(new Task(mqttServiceTest)).start();
new Thread(new Task(mqttServiceTest)).start();
new Thread(new Task(mqttServiceTest)).start();
Thread.sleep(5000);
System.out.println(sendCount.toString());
System.out.println(sendFinishCount.toString());
Thread.sleep(5000);
System.out.println(sendCount.toString());
System.out.println(sendFinishCount.toString());
Thread.sleep(5000);
System.out.println(sendCount.toString());
System.out.println(sendFinishCount.toString());
Thread.sleep(5000);
System.out.println(sendCount.toString());
System.out.println(sendFinishCount.toString());
}
static class Task implements Runnable {
private MqttServiceTest mqttServiceTest;
public Task(MqttServiceTest mqttServiceTest) {
this.mqttServiceTest = mqttServiceTest;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
MqttMsg mqttMsg = new MqttMsg();
mqttMsg.setType("a");
mqttMsg.setPath("/test");
mqttMsg.setData("a");
sendCount.incrementAndGet();
try {
System.out.println(Thread.currentThread().getName());
mqttServiceTest.sendMsg(mqttMsg);
} catch (Exception e) {
System.out.println("发送失败,sendFinishCount=" + sendFinishCount.toString());
e.printStackTrace();
}
}
}
}
`
Contributor guide
Assessment
This issue has not been assessed yet.