eclipse-paho / eclipse-paho/paho.mqtt.python
Client.loop_stop contains a race-condition
- Dominant language
- Python
- Stars
- 2.4k
- Forks
- 742
- Avg merge
- 12d 48m
- Merged PRs (30d)
- 1
Description
# Bug Description
`Client.loop_stop` expects that `self._thread` stays valid, but `_thread_main` unsets `self._thread` after exiting, causing a race-condition:
```bash
$ python3 test.py
Traceback (most recent call last):
File "test.py", line 14, in
client.loop_stop()
File "src/paho/mqtt/client.py", line 2365, in loop_stop
self._thread.join()
^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'join'
```
# Reproduction
Trigger it by applying this patch to paho-mqtt, which simply adds a wait, so `_thread_main` always wins the race:
```patch
diff --git a/src/paho/mqtt/client.py b/src/paho/mqtt/client.py
index 4ccc869..dc01f5e 100644
--- a/src/paho/mqtt/client.py
+++ b/src/paho/mqtt/client.py
@@ -2360,6 +2360,7 @@ class Client:
return MQTTErrorCode.MQTT_ERR_INVAL
self._thread_terminate = True
+ time.sleep (2)
if threading.current_thread() != self._thread:
self._thread.join()
```
Then run this minimal MQTT client:
```python
import time
from paho.mqtt.client import Client
from paho.mqtt.enums import CallbackAPIVersion, MQTTProtocolVersion
client = Client(CallbackAPIVersion.VERSION2, 'testclient', protocol=MQTTProtocolVersion.MQTTv5)
client.loop_start()
client.connect(host='localhost')
time.sleep (2)
client.disconnect()
client.loop_stop()
```
# Environment
* Python version: Python 3.11.2
* Library version: Commit d45de3737879cfe7a6acc361631fa5cb1ef584bb
* Operating system (including version): Debian 12
* MQTT server (name, version, configuration, hosting details): mosquitto 2.0.15 without any configuration.
Contributor guide
Research direction
Start in src/paho/mqtt/client.py by reading Client.loop_stop alongside _thread_main and the handling of self._thread. Run the minimal MQTT client reproduction against mosquitto with the shown delay to observe the race. Done means loop_stop can complete after the worker exits without raising an AttributeError.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- networking
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100