Skip to content

Commit 53cc5d7

Browse files
authored
Merge pull request #2629 from benderl/thread-handling
use own exception class in @exit_after
2 parents cc53d66 + c516fea commit 53cc5d7

2 files changed

Lines changed: 34 additions & 22 deletions

File tree

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,33 @@
1-
import _thread as thread
21
from threading import Timer
3-
import sys
2+
import threading
43

54

6-
def quit_function(fn_name):
7-
sys.stderr.flush() # Python 3 stderr is likely buffered.
8-
thread.interrupt_main() # raises KeyboardInterrupt
5+
class TimeoutException(Exception):
6+
pass
97

108

119
def exit_after(s):
12-
''' https://stackoverflow.com/questions/492519/timeout-on-a-function-call
13-
use as decorator to exit process if
14-
function takes longer than s seconds
10+
'''
11+
Nutze als Decorator, um einen Timeout für eine Funktion zu erzwingen.
12+
Wirft TimeoutException, wenn die Funktion länger als s Sekunden benötigt.
13+
Basiert auf https://stackoverflow.com/questions/492519/timeout-on-a-function-call.
1514
'''
1615
def outer(fn):
1716
def inner(*args, **kwargs):
18-
timer = Timer(s, quit_function, args=[fn.__name__])
17+
timer = Timer(
18+
s,
19+
lambda: thread_raise(TimeoutException(f"Timeout nach {s} Sekunden in {fn.__name__}")))
1920
timer.start()
2021
try:
21-
result = fn(*args, **kwargs)
22+
return fn(*args, **kwargs)
2223
finally:
2324
timer.cancel()
24-
return result
2525
return inner
2626
return outer
27+
28+
29+
def thread_raise(ex):
30+
# Raise Exception im aktuellen Thread (nur für Hauptthread zuverlässig)
31+
import ctypes
32+
tid = threading.get_ident()
33+
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), ctypes.py_object(ex))

packages/main.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
"""
44
# flake8: noqa: E402
55
import logging
6-
from helpermodules import logger
7-
from helpermodules.utils import run_command, thread_handler
86
import threading
97
import sys
8+
from helpermodules import logger
9+
from helpermodules.utils import run_command, thread_handler
10+
from helpermodules.utils._exit_after import TimeoutException
1011

1112
# als erstes logging initialisieren, damit auch ImportError geloggt werden
1213
logger.setup_logging()
@@ -45,9 +46,9 @@ def __init__(self):
4546
def handler10Sec(self):
4647
""" führt den Algorithmus durch.
4748
"""
48-
try:
49-
@exit_after(data.data.general_data.data.control_interval)
50-
def handler_with_control_interval():
49+
@exit_after(data.data.general_data.data.control_interval)
50+
def handler_with_control_interval():
51+
try:
5152
if (data.data.general_data.data.control_interval / 10) == self.interval_counter:
5253
data.data.copy_data()
5354
loadvars_.get_values()
@@ -69,6 +70,10 @@ def handler_with_control_interval():
6970
self.interval_counter = 1
7071
else:
7172
self.interval_counter = self.interval_counter + 1
73+
except Exception:
74+
log.exception("Fehler im Main-Modul 10s-Handler")
75+
76+
try:
7277
log.info("# ***Start*** ")
7378
log.debug(run_command.run_shell_command("top -b -n 1 | head -n 20"))
7479
log.debug(f'Drosselung: {run_command.run_shell_command("if which vcgencmd >/dev/null; then vcgencmd get_throttled; else echo not found; fi")}')
@@ -86,7 +91,7 @@ def handler_with_control_interval():
8691
logging.debug(line.strip())
8792
Pub().pub("openWB/set/system/time", timecheck.create_timestamp())
8893
handler_with_control_interval()
89-
except KeyboardInterrupt:
94+
except TimeoutException:
9095
log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc())
9196
except Exception:
9297
log.exception("Fehler im Main-Modul")
@@ -104,7 +109,7 @@ def handler5MinAlgorithm(self):
104109
data.data.general_data.grid_protection()
105110
data.data.optional_data.ocpp_transfer_meter_values()
106111
data.data.counter_all_data.validate_hierarchy()
107-
except KeyboardInterrupt:
112+
except TimeoutException:
108113
log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc())
109114
except Exception:
110115
log.exception("Fehler im Main-Modul")
@@ -139,7 +144,7 @@ def handler5Min(self):
139144
general_internal_chargepoint_handler.internal_chargepoint_handler.heartbeat = False
140145
with ChangedValuesContext(loadvars_.event_module_update_completed):
141146
sub.system_data["system"].update_ip_address()
142-
except KeyboardInterrupt:
147+
except TimeoutException:
143148
log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc())
144149
except Exception:
145150
log.exception("Fehler im Main-Modul")
@@ -151,7 +156,7 @@ def handler_midnight(self):
151156
thread_errors_path = Path(Path(__file__).resolve().parents[1]/"ramdisk"/"thread_errors.log")
152157
with thread_errors_path.open("w") as f:
153158
f.write("")
154-
except KeyboardInterrupt:
159+
except TimeoutException:
155160
log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc())
156161
except Exception:
157162
log.exception("Fehler im Main-Modul")
@@ -160,7 +165,7 @@ def handler_midnight(self):
160165
def handler_random_nightly(self):
161166
try:
162167
data.data.system_data["system"].thread_backup_and_send_to_cloud()
163-
except KeyboardInterrupt:
168+
except TimeoutException:
164169
log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc())
165170
except Exception:
166171
log.exception("Fehler im Main-Modul")
@@ -172,7 +177,7 @@ def handler_hour(self):
172177
for cp in data.data.cp_data.values():
173178
calculate_charge_cost(cp)
174179
data.data.optional_data.et_get_prices()
175-
except KeyboardInterrupt:
180+
except TimeoutException:
176181
log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc())
177182
except Exception:
178183
log.exception("Fehler im Main-Modul")

0 commit comments

Comments
 (0)