SVGHMI: prevent browser and watchdog commands to become zombie once finished.
--- a/svghmi/svghmi.py Wed Oct 12 11:59:47 2022 +0200
+++ b/svghmi/svghmi.py Tue Oct 18 11:09:40 2022 +0200
@@ -636,19 +636,29 @@
svghmi_cmds[thing] = (
"Popen(" +
repr(shlex.split(given_command.format(**svghmi_options))) +
- ")") if given_command else "pass # no command given"
+ ")") if given_command else "None # no command given"
runtimefile_path = os.path.join(buildpath, "runtime_%s_svghmi_.py" % location_str)
runtimefile = open(runtimefile_path, 'w')
runtimefile.write("""
-# TODO : multiple watchdog (one for each svghmi instance)
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# generated by beremiz/svghmi/svghmi.py
+
+browser_proc = None
+
def svghmi_{location}_watchdog_trigger():
- {svghmi_cmds[Watchdog]}
+ global browser_proc
+ restart_proc = {svghmi_cmds[Watchdog]}
+ waitpid_timeout(restart_proc, "SVGHMI watchdog triggered command")
+ waitpid_timeout(browser_proc, "SVGHMI browser process")
+ browser_proc = None
max_svghmi_sessions = {maxConnections_total}
def _runtime_{location}_svghmi_start():
- global svghmi_watchdog, svghmi_servers
+ global svghmi_watchdog, svghmi_servers, browser_proc
srv = svghmi_servers.get("{interface}:{port}", None)
if srv is not None:
@@ -673,7 +683,7 @@
path_list.append("{path}")
- {svghmi_cmds[Start]}
+ browser_proc = {svghmi_cmds[Start]}
if {enable_watchdog}:
if svghmi_watchdog is None:
@@ -686,7 +696,7 @@
def _runtime_{location}_svghmi_stop():
- global svghmi_watchdog, svghmi_servers
+ global svghmi_watchdog, svghmi_servers, browser_proc
if svghmi_watchdog is not None:
svghmi_watchdog.cancel()
@@ -702,7 +712,10 @@
svghmi_listener.stopListening()
svghmi_servers.pop("{interface}:{port}")
- {svghmi_cmds[Stop]}
+ stop_proc = {svghmi_cmds[Stop]}
+ waitpid_timeout(stop_proc, "SVGHMI stop command")
+ waitpid_timeout(browser_proc, "SVGHMI browser process")
+ browser_proc = None
""".format(location=location_str,
xhtml=target_fname,
--- a/svghmi/svghmi_server.py Wed Oct 12 11:59:47 2022 +0200
+++ b/svghmi/svghmi_server.py Tue Oct 18 11:09:40 2022 +0200
@@ -8,6 +8,7 @@
from __future__ import absolute_import
import errno
from threading import RLock, Timer
+import os, time
try:
from runtime.spawn_subprocess import Popen
@@ -23,6 +24,9 @@
from autobahn.websocket.protocol import WebSocketProtocol
from autobahn.twisted.resource import WebSocketResource
+from runtime.loglevels import LogLevelsDict
+from runtime import GetPLCObjectSingleton
+
max_svghmi_sessions = None
svghmi_watchdog = None
@@ -299,3 +303,21 @@
render_HEAD = render_GET
+def waitpid_timeout(proc, helpstr="", timeout = 3):
+ if proc is None:
+ return
+ def waitpid_timeout_loop(pid=proc.pid, timeout = timeout):
+ try:
+ while os.waitpid(pid,os.WNOHANG) == (0,0):
+ time.sleep(1)
+ timeout = timeout - 1
+ if not timeout:
+ GetPLCObjectSingleton().LogMessage(
+ LogLevelsDict["WARNING"],
+ "Timeout waiting for {} PID: {}".format(helpstr, str(pid)))
+ break
+ except OSError:
+ # workaround exception "OSError: [Errno 10] No child processes"
+ pass
+ Thread(target=waitpid_timeout_loop, name="Zombie hunter").start()
+