eclipse-paho / eclipse-paho/paho.mqtt.python

Client.loop_stop contains a race-condition

Open
#872 1 comment 1 reaction 0 assignees View on GitHub
Status: Available
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.