Skip to content

Commit ca26a49

Browse files
authored
Merge pull request #1109 from makermelissa-piclaw/fix-libgpiod-cleanup
Ensure libgpiod resources are cleaned up on exit
2 parents 68068ab + 5b7b669 commit ca26a49

8 files changed

Lines changed: 123 additions & 15 deletions

File tree

src/adafruit_blinka/microcontroller/amlogic/a311d/pulseio/PulseIn.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88
import atexit
99
import random
10+
import signal
1011
import struct
1112
import sysv_ipc
1213

@@ -31,6 +32,18 @@ def final():
3132
atexit.register(final)
3233

3334

35+
def _signal_handler(signum, frame): # pylint: disable=unused-argument
36+
"""Handle SIGTERM/SIGINT to ensure cleanup runs"""
37+
final()
38+
raise SystemExit(1)
39+
40+
41+
try:
42+
signal.signal(signal.SIGTERM, _signal_handler)
43+
except (OSError, ValueError):
44+
pass # Not all environments allow signal handling
45+
46+
3447
# pylint: disable=c-extension-no-member
3548
class PulseIn:
3649
"""PulseIn Class to read PWM signals"""
@@ -108,14 +121,26 @@ def _wait_receive_msg(self, timeout=0, type=2):
108121

109122
# pylint: enable=redefined-builtin
110123

124+
def __del__(self):
125+
self.deinit()
126+
111127
def deinit(self):
112128
"""Deinitialises the PulseIn and releases any hardware and software
113129
resources for reuse."""
114130
# Clean up after ourselves
115-
self._process.terminate()
116-
procs.remove(self._process)
117-
self._mq.remove()
118-
queues.remove(self._mq)
131+
if self._process is not None:
132+
self._process.terminate()
133+
if self._process in procs:
134+
procs.remove(self._process)
135+
self._process = None
136+
if self._mq is not None:
137+
try:
138+
self._mq.remove()
139+
except sysv_ipc.ExistentialError:
140+
pass # Already removed
141+
if self._mq in queues:
142+
queues.remove(self._mq)
143+
self._mq = None
119144

120145
def __enter__(self):
121146
"""No-op used by Context Managers."""

src/adafruit_blinka/microcontroller/amlogic/meson_g12_common/pulseio/PulseIn.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88
import atexit
99
import random
10+
import signal
1011
import sysv_ipc
1112

1213
DEBUG = False
@@ -30,6 +31,18 @@ def final():
3031
atexit.register(final)
3132

3233

34+
def _signal_handler(signum, frame): # pylint: disable=unused-argument
35+
"""Handle SIGTERM/SIGINT to ensure cleanup runs"""
36+
final()
37+
raise SystemExit(1)
38+
39+
40+
try:
41+
signal.signal(signal.SIGTERM, _signal_handler)
42+
except (OSError, ValueError):
43+
pass # Not all environments allow signal handling
44+
45+
3346
# pylint: disable=c-extension-no-member
3447
class PulseIn:
3548
"""PulseIn Class to read PWM signals"""
@@ -110,14 +123,26 @@ def _wait_receive_msg(self, timeout=0, type=2):
110123

111124
# pylint: enable=redefined-builtin
112125

126+
def __del__(self):
127+
self.deinit()
128+
113129
def deinit(self):
114130
"""Deinitialises the PulseIn and releases any hardware and software
115131
resources for reuse."""
116132
# Clean up after ourselves
117-
self._process.terminate()
118-
procs.remove(self._process)
119-
self._mq.remove()
120-
queues.remove(self._mq)
133+
if self._process is not None:
134+
self._process.terminate()
135+
if self._process in procs:
136+
procs.remove(self._process)
137+
self._process = None
138+
if self._mq is not None:
139+
try:
140+
self._mq.remove()
141+
except sysv_ipc.ExistentialError:
142+
pass # Already removed
143+
if self._mq in queues:
144+
queues.remove(self._mq)
145+
self._mq = None
121146

122147
def __enter__(self):
123148
"""No-op used by Context Managers."""

src/adafruit_blinka/microcontroller/bcm283x/pulseio/PulseIn.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88
import atexit
99
import random
10+
import signal
1011
import struct
1112
import sysv_ipc
1213

@@ -31,6 +32,18 @@ def final():
3132
atexit.register(final)
3233

3334

35+
def _signal_handler(signum, frame): # pylint: disable=unused-argument
36+
"""Handle SIGTERM/SIGINT to ensure cleanup runs"""
37+
final()
38+
raise SystemExit(1)
39+
40+
41+
try:
42+
signal.signal(signal.SIGTERM, _signal_handler)
43+
except (OSError, ValueError):
44+
pass # Not all environments allow signal handling
45+
46+
3447
# pylint: disable=c-extension-no-member
3548
class PulseIn:
3649
"""PulseIn Class to read PWM signals"""
@@ -112,14 +125,26 @@ def _wait_receive_msg(self, timeout=0, type=2):
112125

113126
# pylint: enable=redefined-builtin
114127

128+
def __del__(self):
129+
self.deinit()
130+
115131
def deinit(self):
116132
"""Deinitialises the PulseIn and releases any hardware and software
117133
resources for reuse."""
118134
# Clean up after ourselves
119-
self._process.terminate()
120-
procs.remove(self._process)
121-
self._mq.remove()
122-
queues.remove(self._mq)
135+
if self._process is not None:
136+
self._process.terminate()
137+
if self._process in procs:
138+
procs.remove(self._process)
139+
self._process = None
140+
if self._mq is not None:
141+
try:
142+
self._mq.remove()
143+
except sysv_ipc.ExistentialError:
144+
pass # Already removed
145+
if self._mq in queues:
146+
queues.remove(self._mq)
147+
self._mq = None
123148

124149
def __enter__(self):
125150
"""No-op used by Context Managers."""

src/adafruit_blinka/microcontroller/generic_linux/libgpiod/libgpiod_chip_1_x.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ def __init__(self, chip_id: str):
2626
else:
2727
self.num_lines = self.num_lines
2828

29+
def __del__(self):
30+
if self._chip:
31+
self._chip.close()
32+
self._chip = None
33+
2934
def __repr__(self):
3035
return self.id
3136

src/adafruit_blinka/microcontroller/generic_linux/libgpiod/libgpiod_chip_2_x.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ def __init__(self, chip_id: str):
2222
info = self._chip.get_info()
2323
self.num_lines = info.num_lines
2424

25+
def __del__(self):
26+
if self._chip:
27+
self._chip.close()
28+
self._chip = None
29+
2530
def __repr__(self):
2631
return self.id
2732

src/adafruit_blinka/microcontroller/generic_linux/libgpiod/libgpiod_pin_1_x.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ def __init__(self, pin_id):
3838
self._chip = gpiod.chip("gpiochip0", gpiod.chip.OPEN_BY_NAME)
3939
self._line = None
4040

41+
def __del__(self):
42+
self.deinit()
43+
4144
def __repr__(self):
4245
return str(self.id)
4346

@@ -115,6 +118,15 @@ def init(self, mode=IN, pull=None):
115118
else:
116119
raise RuntimeError("Invalid mode for pin: %s" % self.id)
117120

121+
def deinit(self):
122+
"""Release the pin and associated resources."""
123+
if self._line:
124+
self._line.release()
125+
self._line = None
126+
if self._chip:
127+
self._chip.close()
128+
self._chip = None
129+
118130
def value(self, val=None):
119131
"""Set or return the Pin Value"""
120132
if val is None:

src/adafruit_blinka/microcontroller/generic_linux/libgpiod/libgpiod_pin_2_x.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ def __init__(self, pin_id):
3737
self._line_request = None
3838

3939
def __del__(self):
40-
if self._line_request:
41-
self._line_request.release()
40+
self.deinit()
4241

4342
def __repr__(self):
4443
return str(self.id)
@@ -89,6 +88,15 @@ def init(self, mode=IN, pull=None):
8988
else:
9089
raise RuntimeError("Invalid mode for pin: %s" % self.id)
9190

91+
def deinit(self):
92+
"""Release the pin and associated resources."""
93+
if self._line_request:
94+
self._line_request.release()
95+
self._line_request = None
96+
if self._chip:
97+
self._chip.close()
98+
self._chip = None
99+
92100
def value(self, val=None):
93101
"""Set or return the Pin Value"""
94102
if val is None:

src/digitalio.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ def switch_to_input(self, pull=None):
7979

8080
def deinit(self):
8181
"""Deinitialize the Digital Pin"""
82-
del self._pin
82+
if self._pin is not None:
83+
if hasattr(self._pin, "deinit"):
84+
self._pin.deinit()
85+
self._pin = None
8386

8487
@property
8588
def direction(self):

0 commit comments

Comments
 (0)