In my Python rule, I often use recursive timers in the form of (cut down class and rule definition):
class HueGroupMgr(object):
**** class other stuff ***
def getFromBridge(self):
*** do some stuff ***
self.poll_timer = Timer(POLL_INTERVAL, lambda: self.getFromBridge())
self.poll_timer.start()
def cleanUpTimers(self):
if self.poll_timer != None:
self.poll_timer.cancel()
hueGroupMgr = HueGroupMgr()
@rule("Hue Group Manager Rule", description="This is an rule for adding groups support for hue groups")
class HueGroupsRule(object):
def __init__(self):
self.triggers = hueGroupMgr.getAllTriggers()
def execute(self, module, inputs):
hueGroupMgr.execute(module, inputs)
def scriptUnloaded():
log.info("************* Unload HueGroupsRule ********* ")
hueGroupMgr.cleanUpTimers()
It seems that although I have a cleaning up timers called by scriptUnloaded sometimes the recursive timer stays active and survives a code reload (saving a new version of code). During developing and debugging this drives me sometimes crazy.
2 Options: Making this code race condition safe, or manually restart the NGRE.
I have some ideas about what could solve the race condition, what I like to ask is how is it possible to restart the NGRE.
I looked at bundle:list but did not find any candidate to restart wit bundle:restart.
is not called when a script error is detected at the loading of the rule and the error is before loading the decorated class.
e.g.
class HueGroupMgr(object):
**** class other stuff ***
def getFromBridge(self):
*** do some stuff ***
self.poll_timer = Timer(POLL_INTERVAL, lambda: self.getFromBridge())
self.poll_timer.start()
def cleanUpTimers(self):
if self.poll_timer != None:
self.poll_timer.cancel()
hueGroupMgr = HueGroupMgr()
# a bug here
a = unknownB
@rule("Hue Group Manager Rule", description="This is an rule for adding groups support for hue groups")
class HueGroupsRule(object):
def __init__(self):
self.triggers = hueGroupMgr.getAllTriggers()
def execute(self, module, inputs):
hueGroupMgr.execute(module, inputs)
def scriptUnloaded():
log.info("************* Unload HueGroupsRule ********* ")
hueGroupMgr.cleanUpTimers()
The solution for scriptUnloaded() was not called when a script error occurs is to move scriptUnloaded() topmost in the file before anything (private library modules or instance declaration) is imported or declared. That makes sure that the scriptUnloaded() is called even a error in code is introduced when reloading script update.
If you use threading.Timer, you could use something like this in a test script to clear all running timers, but this will wipe out the ones that are supposed to be there too! Something similar can probably be done for timers created with createTimer.
import threading
from org.python.core import FunctionThread
for thread in threading.enumerate():
if isinstance(thread, FunctionThread) and str(thread.state) == "TIMED_WAITING":
thread.stop()
Ran into some problems when trying to implement a foolproof timer class. It seems that the thread delivered by threading.enumerate() as such has a different type depending on how it is called.
I created an example showing that in one case it type FunctionThread and in others it is _Timer:
from threading import Timer
import threading
from core.log import logging, LOG_PREFIX
log = logging.getLogger(LOG_PREFIX + ".hello.log")
def tmr_fnc():
log.info(">TRIGGER<")
my_thread = Timer(5, lambda: tmr_fnc())
my_thread.setName("MyThread")
my_thread.start()
def show_my_threads():
for thread in threading.enumerate():
if thread.name == "MyThread":
log.info('*** FUNCTION *** thread.name: {} , type(thread): {} '.format(thread.name, type(thread)))
for thread in threading.enumerate():
if thread.name == "MyThread":
log.info('*** PLAIN *** thread.name: {} , type(thread): {} '.format(thread.name, type(thread)))
show_my_threads()