aurum / python / aurum / repeatedcmd.py

from aurum.utils import readsystem
import time

# Threads do not run in vim on ARM linux. Have to use processes instead.
use_threads=False

if use_threads:
    from multiprocessing import Value
    from Queue import Queue
    from threading import Lock
    from threading import Thread as Process
else:
    from multiprocessing import Process, Queue, Value, Lock
    from signal import signal, SIGTERM, SIG_DFL

class ProcessHaltedError(Exception):
    pass

class RepeatedCmd(object):
    __slots__ = set(('interval', 'queue', 'qlock', 'vlock', 'func', 'process',
                     'value', 'args', 'kwargs', 'paused'))
    def __init__(self, interval, func, *args, **kwargs):
        self.interval = Value('f', float(interval))
        self.queue    = Queue()
        self.vlock    = Lock()
        self.qlock    = Lock()
        self.func     = func
        self.args     = args
        self.kwargs   = kwargs
        self.paused   = Value('b', 0)

        def procfunc(queue, qlock, vlock, interval, func, args, kwargs):
            # Override default signal vim handler with system default behavior 
            # (i.e. just terminate). Without this hack process.terminate() may 
            # leave the process alive. Bad, I do not know why it leaves it alive 
            # only in some limited set of cases.
            signal(SIGTERM, SIG_DFL)
            while interval.value > 0:
                starttime = time.clock()
                if not self.paused.value:
                    vlock.acquire()
                    value     = func(*args, **kwargs)
                    vlock.release()
                    qlock.acquire()
                    # If I only knew the size of func() output I would have used 
                    # Value()
                    while(not queue.empty()):
                        queue.get()
                    queue.put(value)
                    qlock.release()
                try:
                    time.sleep(interval.value-(time.clock()-starttime))
                except Exception:
                    # Work when interval-elapsed results in negative value.
                    pass

        self.process  = Process(target=procfunc,
                                args=(self.queue, self.qlock, self.vlock,
                                      self.interval,
                                      func, args, kwargs))
        self.process.start()
        self.value = None

    def setinterval(self, interval):
        if not self.alive():
            raise ProcessHaltedError('Child process is not alive')
        self.interval.value = float(interval)

    def getvalue(self):
        if not self.alive():
            raise ProcessHaltedError('Child process is not alive')
        self.qlock.acquire()
        if self.value is None or not self.queue.empty():
            while(not self.queue.empty()):
                self.value = self.queue.get()
        self.qlock.release()
        return self.value

    def getcurrentvalue(self):
        self.vlock.acquire()
        self.qlock.acquire()
        if self.value is None or not self.queue.empty():
            while(not self.queue.empty()):
                self.queue.get()
        self.qlock.release()
        self.value = self.func(*self.args, **self.kwargs)
        self.vlock.release()
        return self.value

    def alive(self):
        return self.process.is_alive()

    def stop(self):
        if not self.alive():
            raise ProcessHaltedError('Child process is not alive')
        # For some reason process.terminate() sometimes may not seem to work. 
        # This is a workaround. Seems it is no longer needed with 
        # self.process.join(), just keeping it here
        self.interval.value = -1.0
        self.process.terminate()
        # Anti-zombie code
        self.process.join()

    def pause(self):
        self.paused.value = 1

    def resume(self):
        self.paused.value = 0

    def __del__(self):
        if self.alive():
            return self.stop()

processes={}

thcount=0

def new(*args, **kwargs):
    global processes
    global thcount
    rcid=thcount
    thcount+=1
    processes[rcid]=RepeatedCmd(*args, **kwargs)
    return rcid

def get(rcid, now=False):
    global processes
    if now:
        return processes[rcid].getcurrentvalue()
    else:
        return processes[rcid].getvalue()

def remove(rcid):
    global processes
    process=processes.pop(rcid)
    process.stop()

def finish():
    global processes
    for process in processes.values():
        process.stop()
    processes={}

def pause(rcid):
    global processes
    processes[rcid].pause()

def resume(rcid):
    global processes
    processes[rcid].resume()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.