Python Queues memory leaks when called inside thread - python

I have python TCP client and need to send media(.mpg) file in a loop to a 'C' TCP server.
I have following code, where in separate thread I am reading the 10K blocks of file and sending it and doing it all over again in loop, I think it is because of my implementation of thread module, or tcp send. I am using Queues to print the logs on my GUI ( Tkinter ) but after some times it goes out of memory..
UPDATE 1 - Added more code as requested
Thread class "Sendmpgthread" used to create thread to send data
.
.
def __init__ ( self, otherparams,MainGUI):
.
.
self.MainGUI = MainGUI
self.lock = threading.Lock()
Thread.__init__(self)
#This is the one causing leak, this is called inside loop
def pushlog(self,msg):
self.MainGUI.queuelog.put(msg)
def send(self, mysocket, block):
size = len(block)
pos = 0;
while size > 0:
try:
curpos = mysocket.send(block[pos:])
except socket.timeout, msg:
if self.over:
self.pushlog(Exit Send)
return False
except socket.error, msg:
print 'Exception'
return False
pos = pos + curpos
size = size - curpos
return True
def run(self):
media_file = None
mysocket = None
try:
mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mysocket.connect((self.ip, string.atoi(self.port)))
media_file = open(self.file, 'rb')
while not self.over:
chunk = media_file.read(10000)
if not chunk: # EOF Reset it
print 'resetting stream'
media_file.seek(0, 0)
continue
if not self.send(mysocket, chunk): # If some error or thread is killed
break;
#disabling this solves the issue
self.pushlog('print how much data sent')
except socket.error, msg:
print 'print exception'
except Exception, msg:
print 'print exception'
try:
if media_file is not None:
media_file.close()
media_file = None
if mysocket is not None:
mysocket.close()
mysocket = None
finally:
print 'some cleaning'
def kill(self):
self.over = True
I figured out that it is because of wrong implementation of Queue as commenting that piece resolves the issue
UPDATE 2 - MainGUI class which is called from above Thread class
class MainGUI(Frame):
def __init__(self, other args):
#some code
.
.
#from the above thread class used to send data
self.send_mpg_status = Sendmpgthread(params)
self.send_mpg_status.start()
self.after(100, self.updatelog)
self.queuelog = Queue.Queue()
def updatelog(self):
try:
msg = self.queuelog.get_nowait()
while msg is not None:
self.printlog(msg)
msg = self.queuelog.get_nowait()
except Queue.Empty:
pass
if self.send_mpg_status: # only continue when sending
self.after(100, self.updatelog)
def printlog(self,msg):
#print in GUI

Since printlog is adding to a tkinter text control, the memory occupied by that control will grow with each message (it has to store all the log messages in order to display them).
Unless storing all the logs is critical, a common solution is to limit the maximum number of log lines displayed.
A naive implementation is to eliminate extra lines from the begining after the control reaches a maximum number of messages. Add a function to get the number of lines in the control and then, in printlog something similar to:
while getnumlines(self.edit) > self.maxloglines:
self.edit.delete('1.0', '1.end')
(above code not tested)
update: some general guidelines
Keep in mind that what might look like a memory leak does not always mean that a function is wrong, or that the memory is no longer accessible. Many times there is missing cleanup code for a container that is accumulating elements.
A basic general approach for this kind of problems:
form an opinion on what part of the code might be causing the problem
check it by commenting that code out (or keep commenting code until you find a candidate)
look for containers in the responsible code, add code to print their size
decide what elements can be safely removed from that container, and when to do it
test the result

I can't see anything obviously wrong with your code snippet.
To reduce memory usage a bit under Python 2.7, I'd use buffer(block, pos) instead of block[pos:]. Also I'd use mysocket.sendall(block) instead of your send method.
If the ideas above don't solve your problem, then the bug is most probably elsewhere in your code. Could you please post the shortest possible version of the full Python script which still grows out-of-memory (http://sscce.org/)? That increases your change of getting useful help.

Out of memory errors are indicative of data being generated but not consumed or released. Looking through your code I would guess these two areas:
Messages are being pushed onto a Queue.Queue() instance in the pushlog method. Are they being consumed?
The MainGui printlog method may be writing text somewhere. eg. Is it continually writing to some kind of GUI widget without any pruning of messages?
From the code you've posted, here's what I would try:
Put a print statement in updatelog. If this is not being continually called for some reason such as a failed after() call, then the queuelog will continue to grow without bound.
If updatelog is continually being called, then turn your focus to printlog. Comment the contents of this function to see if out of memory errors still occur. If they don't, then something in printlog may be holding on to the logged data, you'll need to dig deeper to find out what.
Apart from this, the code could be cleaned up a bit. self.queuelog is not created until after the thread is started which gives rise to a race condition where the thread may try to write into the queue before it has been created. Creation of queuelog should be moved to somewhere before the thread is started.
updatelog could also be refactored to remove redundancy:
def updatelog(self):
try:
while True:
msg = self.queuelog.get_nowait()
self.printlog(msg)
except Queue.Empty:
pass
And I assume the the kill function is called from the GUI thread. To avoid thread race conditions, the self.over should be a thread safe variable such as a threading.Event object.
def __init__(...):
self.over = threading.Event()
def kill(self):
self.over.set()

There is no data piling up in your TCP sending loop.
Memory error is probably caused by logging queue, as you have not posted complete code try using following class for logging:
from threading import Thread, Event, Lock
from time import sleep, time as now
class LogRecord(object):
__slots__ = ["txt", "params"]
def __init__(self, txt, params):
self.txt, self.params = txt, params
class AsyncLog(Thread):
DEBUGGING_EMULATE_SLOW_IO = True
def __init__(self, queue_max_size=15, queue_min_size=5):
Thread.__init__(self)
self.queue_max_size, self.queue_min_size = queue_max_size, queue_min_size
self._queuelock = Lock()
self._queue = [] # protected by _queuelock
self._discarded_count = 0 # protected by _queuelock
self._pushed_event = Event()
self.setDaemon(True)
self.start()
def log(self, message, **params):
with self._queuelock:
self._queue.append(LogRecord(message, params))
if len(self._queue) > self.queue_max_size:
# empty the queue:
self._discarded_count += len(self._queue) - self.queue_min_size
del self._queue[self.queue_min_size:] # empty the queue instead of creating new list (= [])
self._pushed_event.set()
def run(self):
while 1: # no reason for exit condition here
logs, discarded_count = None, 0
with self._queuelock:
if len(self._queue) > 0:
# select buffered messages for printing, releasing lock ASAP
logs = self._queue[:]
del self._queue[:]
self._pushed_event.clear()
discarded_count = self._discarded_count
self._discarded_count = 0
if not logs:
self._pushed_event.wait()
self._pushed_event.clear()
continue
else:
# print logs
if discarded_count:
print ".. {0} log records missing ..".format(discarded_count)
for log_record in logs:
self.write_line(log_record)
if self.DEBUGGING_EMULATE_SLOW_IO:
sleep(0.5)
def write_line(self, log_record):
print log_record.txt, " ".join(["{0}={1}".format(name, value) for name, value in log_record.params.items()])
if __name__ == "__main__":
class MainGUI:
def __init__(self):
self._async_log = AsyncLog()
self.log = self._async_log.log # stored as bound method
def do_this_test(self):
print "I am about to log 100 times per sec, while text output frequency is 2Hz (twice per second)"
def log_100_records_in_one_second(itteration_index):
for i in xrange(100):
self.log("something happened", timestamp=now(), session=3.1415, itteration=itteration_index)
sleep(0.01)
for iter_index in range(3):
log_100_records_in_one_second(iter_index)
test = MainGUI()
test.do_this_test()
I have noticed that you do not sleep() anywhere in the sending loop, this means data is read as fast as it can and is sent as fast as it can. Note that this is not desirable behavior when playing media files - container time-stamps are there to dictate data-rate.

Related

pyaudio stream.read() doesn't return data until stream stops

I'll be very detailed so there is no lack of information about what is happening
I have a class named Stream, that has the main function of reading data output from any selected audio device connected to the PC and return the data to another class (this part is not implemented in the code yet, but it's just for context). Here is the Stream class:
Stream.py
import pyaudio
import struct
from threading import Thread
from LEDController.BaseClass import BaseClass
from LEDController.Device import Device
class Stream(BaseClass):
def __init__(self, device: Device):
super().__init__(__name__)
self.__p = pyaudio.PyAudio()
self.stream = None
# Stream Settings
self.defaultFrames = 1024 * 8
self.format = pyaudio.paInt16
self.channels = device.channels
self.rate = device.defaultSampleRate
self.input = True
self.frames_per_buffer = self.defaultFrames
self.input_device_index = device.index
self.as_loopback = True
self.__open()
self.stop()
def __open(self):
try:
self.logger.info('Trying to Open Stream')
# Open Stream
self.stream = self.__p.open(
format=self.format,
channels=self.channels,
rate=self.rate,
input=self.input,
frames_per_buffer=self.defaultFrames,
input_device_index=self.input_device_index,
as_loopback=self.as_loopback,
# wasapi_fill_silence=True
)
except Exception as e:
self.logger.error(e)
else:
self.logger.info('Stream Opened')
def start(self):
try:
self.stream.start_stream()
self.read_thread = Thread(target=self.read)
self.read_thread.start()
self.logger.info("Stream Started")
return True
except Exception as e:
return self.logger.error(e)
def stop(self):
try:
self.stream.stop_stream()
self.logger.info("Stream Stopped")
return False
except Exception as e:
return self.logger.error(e)
def is_stopped(self):
return self.stream.is_stopped()
def read(self):
try:
while not self.is_stopped():
# Process Data
data = self.stream.read(self.defaultFrames) # This line doesn't return anything
data_int = struct.unpack(str(2 * self.defaultFrames) + 'h', data)
print(data_int)
# TODO return data
except Exception as e:
self.logger.error(e)
What it's doing:
At first, it receives a Device Object (code is not that important. However, I can edit and add it if needed).
The Device object has a few pieces of information about the current selected device (such as device default sample rate, channels, etc, as you can see in Stream.__init__(). So the Stream class uses those pieces of information to open a PyAudio Stream with PyAudio.open(...) and right after that, it stops the stream until the user (there is a GUI) opens it.
The Problem
The Stream.read() method it's called by a Thread, so it keeps running, processing, and returning the data in real-time. However when I call Stream.start() and it starts the read method, the same keeps stuck in the following line:
data = self.stream.read(self.defaultFrames)
I've added a print statement in this method to see the data flowing. However, this statement only gets called after stopping the stream, and no error is shown.
Output
2020-06-03 09:15:21,718 — LEDController.Stream.Stream - 57 — INFO — Stream Started
2020-06-03 09:15:24,371 — LEDController.Stream.Stream - 65 — INFO — Stream Stopped
(output of the print statement, it's quite long so I'll not put it here)
As you can see, no data is printed while the stream is open.
Looking at this output and after a few tests, I've figured out that self.stream.read(...) it's not returning any data, while the stream is opened and only returns when the stream stops.
After that conclusion, I've started looking for solutions and doing more tests, but I couldn't find anything, and only for the recorded, this problem was not happening before (it started like yesterday)
and I don't remember changing anything in my code.
Things that I've also tried, but haven't worked
Removing the Thread and calling Stream.read() normally
Reinstalling PyAudio
If you need any additional information, just tell me in the comments. I've tried to be as specific as possible and sorry if I didn't make my self clear enough. Just let me know and I'll update the question and fix the problem. Thanks in advance.

Python DBus remove signal match (remove signal receiver)

Community!
I have a little Problem with the Python-DBus-API. I create a signal-receiver and it does its job. However if I try to remove the signal, it won't be removed and the signalhandler (sigHandler) is happily called every time the signal matches.
class A(threading.Thread)
bus = None
mainloop = None
systemBusMainLoop = None
signalReceiver = None
def __init__(self,dbusMainLoop):
log("Hello.")
super(A, self).__init__()
gobject.threads_init()
self.mainloop = gobject.MainLoop()
self.systemBusMainLoop = dbusMainLoop
self.bus = dbus.SystemBus(mainloop=dbusMainLoop)
self.signalReceiver = self.bus.add_signal_receiver(self.sigHandler,
bus_name="org.bluez",
dbus_interface="org.freedesktop.DBus.Properties",
signal_name="PropertiesChanged",
path_keyword="path")
def run(self):
self.mainloop.run()
log("Running.")
def end(self):
log("Shutting down...")
self.bus.remove_signal_receiver(self.sigHandler,
self.signalReceiver,
dbus_interface="org.freedesktop.DBus.Properties")
#self.signalReceiver.remove() #tried this also
if (self.mainloop):
self.mainloop.quit()
del self.signalReceiver
log("Bye.")
def sigHandler(self, interface, changed, invalidated, path)
print interface
print changed
print invalidated
print path
Called:
dbusA = A(dbusMainLoop=dbus.mainloop.glib.DBusGMainLoop())
dbusA.run()
#doing something unrelated
dbusA.end() #remove the Signal
del dbusA
Do i miss something? Why ist my sigHandler not removed (or why is my match not removed).
Thanks in advance!
The line you commented out (self.signalReceiver.remove()) works.
add_signal_receiver() returns a SignalMatch instance on which you can call remove() method to unregister your callback.
The problem in your code snippet is that dbusA.run() is a blocking call because it calls self.mainloop.run() which itself is a blocking call.
This said, program execution will never reach dbusA.end(), thus signal unregistering and quitting the run loop will not happen. You can however call this method from a different thread, or from any event handler of your dbus connection.

Using python multiprocessing pipes

I am trying to write a class that will calculate checksums using multiple processes, thereby taking advantage of multiple cores. I have a quite simple class for this, and it works great when executing a simple case. But whenever I create two or more instances of the class, the worker never exits. It seems like it never get the message that the pipe has been closed by the parent.
All the code can be found below. I first calculate the md5 and sha1 checksums separately, which works, and then I try to perform the calculation in parallel, and then the program locks up when it is time to close the pipe.
What is going on here? Why aren't the pipes working as I expect? I guess I could do a workaround by sending a "Stop" message on the queue and make the child quit that way, but I'd really like to know why this isn't working as it is.
import multiprocessing
import hashlib
class ChecksumPipe(multiprocessing.Process):
def __init__(self, csname):
multiprocessing.Process.__init__(self, name = csname)
self.summer = eval("hashlib.%s()" % csname)
self.child_conn, self.parent_conn = multiprocessing.Pipe(duplex = False)
self.result_queue = multiprocessing.Queue(1)
self.daemon = True
self.start()
self.child_conn.close() # This is the parent. Close the unused end.
def run(self):
self.parent_conn.close() # This is the child. Close unused end.
while True:
try:
print "Waiting for more data...", self
block = self.child_conn.recv_bytes()
print "Got some data...", self
except EOFError:
print "Finished work", self
break
self.summer.update(block)
self.result_queue.put(self.summer.hexdigest())
self.result_queue.close()
self.child_conn.close()
def update(self, block):
self.parent_conn.send_bytes(block)
def hexdigest(self):
self.parent_conn.close()
return self.result_queue.get()
def main():
# Calculating the first checksum works
md5 = ChecksumPipe("md5")
md5.update("hello")
print "md5 is", md5.hexdigest()
# Calculating the second checksum works
sha1 = ChecksumPipe("sha1")
sha1.update("hello")
print "sha1 is", sha1.hexdigest()
# Calculating both checksums in parallel causes a lockup!
md5, sha1 = ChecksumPipe("md5"), ChecksumPipe("sha1")
md5.update("hello")
sha1.update("hello")
print "md5 and sha1 is", md5.hexdigest(), sha1.hexdigest() # Lockup here!
main()
PS. This problem has been solved Here is a working version of the above code if anyone is interested:
import multiprocessing
import hashlib
class ChecksumPipe(multiprocessing.Process):
all_open_parent_conns = []
def __init__(self, csname):
multiprocessing.Process.__init__(self, name = csname)
self.summer = eval("hashlib.%s()" % csname)
self.child_conn, self.parent_conn = multiprocessing.Pipe(duplex = False)
ChecksumPipe.all_open_parent_conns.append(self.parent_conn)
self.result_queue = multiprocessing.Queue(1)
self.daemon = True
self.start()
self.child_conn.close() # This is the parent. Close the unused end.
def run(self):
for conn in ChecksumPipe.all_open_parent_conns:
conn.close() # This is the child. Close unused ends.
while True:
try:
print "Waiting for more data...", self
block = self.child_conn.recv_bytes()
print "Got some data...", self
except EOFError:
print "Finished work", self
break
self.summer.update(block)
self.result_queue.put(self.summer.hexdigest())
self.result_queue.close()
self.child_conn.close()
def update(self, block):
self.parent_conn.send_bytes(block)
def hexdigest(self):
self.parent_conn.close()
return self.result_queue.get()
def main():
# Calculating the first checksum works
md5 = ChecksumPipe("md5")
md5.update("hello")
print "md5 is", md5.hexdigest()
# Calculating the second checksum works
sha1 = ChecksumPipe("sha1")
sha1.update("hello")
print "sha1 is", sha1.hexdigest()
# Calculating both checksums also works fine now
md5, sha1 = ChecksumPipe("md5"), ChecksumPipe("sha1")
md5.update("hello")
sha1.update("hello")
print "md5 and sha1 is", md5.hexdigest(), sha1.hexdigest()
main()
Yep, that is surprising behaviour indeed.
However, if you look at the output of lsof for the two parallel child processes it is easy to notice that the second child process has more file descriptors open.
What happens is that when two parallel child processes get started the second child inherits the pipes of the parent, so that when the parent calls self.parent_conn.close() the second child still has that pipe file descriptor open, so that the pipe file description doesn't get closed in the kernel (the reference count is more than 0), with the effect being that self.child_conn.recv_bytes() in the first parallel child process never read()s EOF and EOFError gets never thrown.
You may need to send an explicit shutdown message, rather then just closing file descriptors because there seem to be little control over what file descriptors get shared between which processes (there is no close-on-fork file descriptor flag).

Block main thread until python background thread finishes side-task

I have a threaded python application with a long-running mainloop in the background thread. This background mainloop is actually a call to pyglet.app.run(), which drives a GUI window and also can be configured to call other code periodically. I need a do_stuff(duration) function to be called at will from the main thread to trigger an animation in the GUI, wait for the animation to stop, and then return. The actual animation must be done in the background thread because the GUI library can't handle being driven by separate threads.
I believe I need to do something like this:
import threading
class StuffDoer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.max_n_times = 0
self.total_n_times = 0
self.paused_ev = threading.Event()
def run(self):
# this part is outside of my control
while True:
self._do_stuff()
# do other stuff
def _do_stuff(self):
# this part is under my control
if self.paused_ev.is_set():
if self.max_n_times > self.total_n_times:
self.paused_ev.clear()
else:
if self.total_n_times >= self.max_n_times:
self.paused_ev.set()
if not self.paused_ev.is_set():
# do stuff that must execute in the background thread
self.total_n_times += 1
sd = StuffDoer()
sd.start()
def do_stuff(n_times):
sd.max_n_times += n_times
sd.paused_ev.wait_for_clear() # wait_for_clear() does not exist
sd.paused_ev.wait()
assert (sd.total_n_times == sd.max_n_times)
EDIT: use max_n_times instead of stop_time to clarify why Thread.join(duration) won't do the trick.
From the documentation for threading.Event:
wait([timeout])
Block until the internal flag is true.
If the internal flag is true on entry,
return immediately. Otherwise, block
until another thread calls set() to
set the flag to true, or until the
optional timeout occurs.
I've found I can get the behavior I'm looking for if I have a pair of events, paused_ev and not_paused_ev, and use not_paused_ev.wait(). I could almost just use Thread.join(duration), except it needs to only return precisely when the background thread actually registers that the time is up. Is there some other synchronization object or other strategy I should be using instead?
I'd also be open to arguments that I'm approaching this whole thing the wrong way, provided they're good arguments.
Hoping I get some revision or additional info from my comment, but I'm kind of wondering if you're not overworking things by subclassing Thread. You can do things like this:
class MyWorker(object):
def __init__(self):
t = Thread(target = self._do_work, name "Worker Owned Thread")
t.daemon = True
t.start()
def _do_work(self):
While True:
# Something going on here, forever if necessary. This thread
# will go away if the other non-daemon threads terminate, possibly
# raising an exception depending this function's body.
I find this makes more sense when the method you want to run is something that is more appropriately a member function of some other class than it would be to as the run method on the thread. Additionally, this saves you from having to encapsulate a bunch of business logic inside of a Thread. All IMO, of course.
It appears that your GUI animation thread is using a spin-lock in its while True loop. This can be prevented using thread-safe queues. Based on my reading of your question, this approach would be functionally equivalent and efficient.
I'm omitting some details of your code above which would not change. I'm also assuming here that the run() method which you do not control uses the self.stop_time value to do its work; otherwise there is no need for a threadsafe queue.
from Queue import Queue
from threading import Event
class StuffDoer:
def __init__(self, inq, ready):
self.inq = inq
self.ready = ready
def _do_stuff(self):
self.ready.set()
self.stop_time = self.inq.get()
GUIqueue = Queue()
control = Event()
sd = StuffDoer(GUIqueue, control)
def do_stuff(duration):
control.clear()
GUIqueue.put(time.time() + duration)
control.wait()
I ended up using a Queue similar to what #wberry suggested, and making use of Queue.task_done and Queue.wait:
import Queue
import threading
class StuffDoer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.setDaemon(True)
self.max_n_times = 0
self.total_n_times = 0
self.do_queue = Queue.Queue()
def run(self):
# this part is outside of my control
while True:
self._do_stuff()
# do other stuff
def _do_stuff(self):
# this part is under my control
if self.total_n_times >= self.max_n_times:
try:
self.max_n_times += self.do_queue.get(block=False)
except Queue.Empty, e:
pass
if self.max_n_times > self.total_n_times:
# do stuff that must execute in the background thread
self.total_n_times += 1
if self.total_n_times >= self.max_n_times:
self.do_queue.task_done()
sd = StuffDoer()
sd.start()
def do_stuff(n_times):
sd.do_queue.put(n_times)
sd.do_queue.join()
assert (sd.total_n_times == sd.max_n_times)
I made solution based on #g.d.d.c advice for this question. There is my code:
threads = []
# initializing aux thread(s) in the main thread ...
t = threading.Thread(target=ThreadF, args=(...))
#t.setDaemon(True) # I'm not sure does it really needed
t.start()
threads.append(t.ident)
# Block main thread
while filter(lambda thread: thread.ident in threads, threading.enumerate()):
time.sleep(10)
Also, you can use Thread.join to block the main thread - it is better way.

Tools for implementing a watchdog timer in python

I'm writing some code for testing multithreaded programs (student homework--likely buggy), and want to be able to detect when they deadlock. When running properly, the programs regularly produce output to stdout, so that makes it fairly straightforward: if no output for X seconds, kill it and report deadlock. Here's the function prototype:
def run_with_watchdog(command, timeout):
"""Run shell command, watching for output. If the program doesn't
produce any output for <timeout> seconds, kill it and return 1.
If the program ends successfully, return 0."""
I can write it myself, but it's a bit tricky to get right, so I would prefer to use existing code if possible. Anyone written something similar?
Ok, see solution below. The subprocess module might also be relevant if you're doing something similar.
You can use expect (tcl) or pexpect (python) to do this.
import pexpect
c=pexpect.spawn('your_command')
c.expect("expected_output_regular_expression", timeout=10)
Here's a very slightly tested, but seemingly working, solution:
import sys
import time
import pexpect
# From http://pypi.python.org/pypi/pexpect/
DEADLOCK = 1
def run_with_watchdog(shell_command, timeout):
"""Run <shell_command>, watching for output, and echoing it to stdout.
If the program doesn't produce any output for <timeout> seconds,
kill it and return 1. If the program ends successfully, return 0.
Note: Assumes timeout is >> 1 second. """
child = pexpect.spawn('/bin/bash', ["-c", shell_command])
child.logfile_read = sys.stdout
while True:
try:
child.read_nonblocking(1000, timeout)
except pexpect.TIMEOUT:
# Child seems deadlocked. Kill it, return 1.
child.close(True)
return DEADLOCK
except pexpect.EOF:
# Reached EOF, means child finished properly.
return 0
# Don't spin continuously.
time.sleep(1)
if __name__ == "__main__":
print "Running with timer..."
ret = run_with_watchdog("./test-program < trace3.txt", 10)
if ret == DEADLOCK:
print "DEADLOCK!"
else:
print "Finished normally"
Another solution:
class Watchdog:
def __init__(self, timeout, userHandler=None): # timeout in seconds
self.timeout = timeout
if userHandler != None:
self.timer = Timer(self.timeout, userHandler)
else:
self.timer = Timer(self.timeout, self.handler)
def reset(self):
self.timer.cancel()
self.timer = Timer(self.timeout, self.handler)
def stop(self):
self.timer.cancel()
def handler(self):
raise self;
Usage if you want to make sure function finishes in less than x seconds:
watchdog = Watchdog(x)
try
... do something that might hang ...
except Watchdog:
... handle watchdog error ...
watchdog.stop()
Usage if you regularly execute something and want to make sure it is executed at least every y seconds:
def myHandler():
print "Watchdog expired"
watchdog = Watchdog(y, myHandler)
def doSomethingRegularly():
...
watchdog.reset()

Categories

Resources