Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bcm283x pulseio _mq.receive stuck after SIGINT(CTRL-C) #89

Open
iwanicki92 opened this issue May 12, 2023 · 2 comments
Open

bcm283x pulseio _mq.receive stuck after SIGINT(CTRL-C) #89

iwanicki92 opened this issue May 12, 2023 · 2 comments

Comments

@iwanicki92
Copy link

iwanicki92 commented May 12, 2023

I'm not sure if it's DHT or pulseio fault but sometimes when using CTRL-C(with custom signal handler) program gets stuck on self._mq.receive(block=True, type=type) line in bcm283x/pulseio/PulseIn.py file, ps prints: libgpiod_pulsei <defunct> and sometimes before getting stuck it prints line of numbers e.g.
73, 51, 75, 50, 75, 53, 26, 53, 26, 51, 73, 52, 73, 53, 26, 53, 26, 53, 26, 51, 27, 52, 26, 53, 26, 53, 26, 53, 26, 51, 28, 51, 73, 52, 73, 53, 73, 52, 73, 51, 74, 52, 73, 53, 73, 54, 26, 51, 74, 52, 73, 51, 75, 53, 26, 52, 27, 51, 73, 53, 26, 51, 75, 53.

Python version: Python 3.9.2
OS: Raspbian GNU/Linux 11 (bullseye)
Kernel: Linux 5.15.61-v7+
Architecture: armv7l
Hardware: Raspberry Pi 3 Model B V1.2
DHT22: ASAIR AM2302 SNE1222402511-J

Thread stacktrace:

<RepeatTimer(Thread-1, started 1985381440)>
  File "/usr/lib/python3.9/threading.py", line 912, in _bootstrap
    self._bootstrap_inner()
  File "/usr/lib/python3.9/threading.py", line 954, in _bootstrap_inner
    self.run()
  File "/home/iwans/repozytorium_github/sensors-and-screen-controller/dht_bug.py", line 16, in run
    self.function(*self.args, **self.kwargs)
  File "/home/iwans/repozytorium_github/sensors-and-screen-controller/dht_bug.py", line 27, in print_data
    humidity = dht.humidity
  File "/home/iwans/repozytorium_github/.venv/lib/python3.9/site-packages/adafruit_dht.py", line 284, in humidity
    self.measure()
  File "/home/iwans/repozytorium_github/.venv/lib/python3.9/site-packages/adafruit_dht.py", line 219, in measure
    pulses = self._get_pulses_pulseio()
  File "/home/iwans/repozytorium_github/.venv/lib/python3.9/site-packages/adafruit_dht.py", line 148, in _get_pulses_pulseio
    while self.pulse_in:
  File "/home/iwans/repozytorium_github/.venv/lib/python3.9/site-packages/adafruit_blinka/microcontroller/bcm283x/pulseio/PulseIn.py", line 168, in __len__
    message = self._wait_receive_msg()
  File "/home/iwans/repozytorium_github/.venv/lib/python3.9/site-packages/adafruit_blinka/microcontroller/bcm283x/pulseio/PulseIn.py", line 104, in _wait_receive_msg
    message = self._mq.receive(block=True, type=type)

Installed adafruit packages:

Adafruit-Blinka==8.19.0
adafruit-circuitpython-busdevice==5.2.4
adafruit-circuitpython-dht==4.0.0
adafruit-circuitpython-requests==1.13.2
adafruit-circuitpython-typing==1.9.2
Adafruit-PlatformDetect==3.46.0
Adafruit-PureIO==1.1.10
pyftdi==0.54.0
pyserial==3.5
pyusb==1.2.1
rpi-ws281x==4.3.4
RPi.GPIO==0.7.1
sysv-ipc==1.1.0
typing-extensions==4.5.0

Minimal code to replicate problem(stacktrace is from different code run on another thread so I can detect when it hangs and print stacktrace).
Run code, press CTRL-C and after a while(couple seconds) program should hang.

import signal
import time
import board
from adafruit_dht import DHT22

if __name__ == "__main__":
    dht = DHT22(board.D4)
    def sigint_handler(_1, _2):
        pass
    signal.signal(signal.SIGINT, sigint_handler)

    while True:
        try:
            print(f"{dht.humidity=}, {dht.temperature=}")
        except:
            pass
        time.sleep(0.2)
@VinhNgT
Copy link

VinhNgT commented Jan 15, 2025

Try checking if the PulseIO subprocess is alive before calling print(f"{dht.humidity=}, {dht.temperature=}") to avoid deadlock:

while True:
    try:
        if dht.pulse_in._process.poll() == 0:
            print("CircuitPython PulseIn subprocess was closed!")
            break

        print(f"{dht.humidity=}, {dht.temperature=}")
    except:
        pass
    time.sleep(0.2)

This happens because signal.SIGINT does not close your application, it only requests it to close. So if the PulseIO subprocess closes before your program, and it also so happens that your program is in the middle or before print(f"{dht.humidity=}, {dht.temperature=}"), it will never finish because it's waiting for responses from PulseIO subprocess (which will never arrive because the subprocess is closed)

@iwanicki92
Copy link
Author

Your code works (I could also use stop variable set in signal handler) but if I used e.g.

print(f"{dht.humidity=}")
time.sleep(2)
print(f"{dht.temperature=}")

then I'd need to check if I have to stop before every call to dht (and possibly others).
Also I'm not sure if signal could come when we are already somewhere in dht, if it can then this check wouldn't be very reliable.

I guess I could just catch KeyboardInterrupt

#!/usr/bin/env python3

import signal
import sys
import time
import board
from adafruit_dht import DHT22

if __name__ == "__main__":
    dht = DHT22(board.D4)

    while True:
        try:
            print(f"{dht.humidity=}", end=", ")
            sys.stdout.flush()
            time.sleep(2)
            print(f"{dht.temperature=}")
        except KeyboardInterrupt as e:
            break
        time.sleep(0.2)

    print("Cleanup")

Or maybe add signal handler and raise custom interrupt exception.

My original use case was a little more complicated where main thread started multiple threading.Timer to handle different sensors and then waited for stop event (raised in signal handler).
Unfortunately there is possibility that some sensor threads are running during handling of SIGINT which could lead to sensor thread getting stuck (in PulseIO) and then main thread getting stuck on threading.join() when cleaning up.
I'm not sure how safe/error prone would it be to forcefully stop running sensor threads or raising exception in them.

I stopped working on this project over a year ago so it's not a priority but I guess at least a note in documentation or readme that using PulseIO library (and any other that uses it) is not safe after receiving SIGINT (because libgpiod_pulsei process is killed even if signal is handled in Python).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants