Search code examples
pythoncherrypy

Python limit class to single instance?


I have a cherryPy server which, among other things, access needs to access a "single instance resource". Single instance resource is serial communication to another device and an entire set of operations needs to be performed before it can be released for access again.

cherryPy by definition is multithreading, and I would like to keep it that way. How can I pipeline access to this single resource? The best I could come up with is adding a class attribute "busy" and checking its state before executing access to the single resource.

What confuses me is that the commClass.init print only prints once.

    # ========= IMPORTS =============
import cherrypy
import simplejson
import os
import time
import random

class commClass:
    busy = False
    instance = 0

    def __init__(self, uid):
        self.uid = uid
        commClass.instance += 1
        print("INIT:", self.uid, "INST:", commClass.instance)

    def singleResource(self, periods):
        for i in range (0, periods):
            print("ID:", self.uid, " WASTE TIME:", i)
            time.sleep(1)

PATH = os.path.abspath(os.path.dirname(__file__))
comm = commClass(random.randrange(0, 1000))

def wasteTime():
    global comm
    while comm.busy:
        print("busy...")
        time.sleep(0.5)

    comm.busy = True
    comm.singleResource(5)
    comm.busy = False


# ============== Main Server Object ======
class threadTest(object):

    def __init__(self):
        pass

    @cherrypy.expose
    @cherrypy.tools.json_out()
    def wasteTime(self):
        wasteTime()
        servResponse = dict()
        return simplejson.dumps(dict(servResponse))

conf = {'/':
    {
        'tools.staticdir.on': True,
        'tools.staticdir.dir': PATH,
        'tools.staticdir.index': 'cherryPyThreadTest.html',
    }
}


if __name__=='__main__':
    print("Server _ON_")
    #print(Labyrinth.ip)
    cherrypy.server.socket_host = '0.0.0.0'
    cherrypy.server.thread_pool = 30
    cherrypy.server.socket_port = 80
    cherrypy.quickstart(threadTest(), '/', conf)

The output is as expected:

INIT: 643 INST: 1
Server _ON_
ID: 643  WASTE TIME: 0
busy...
busy...
ID: 643  WASTE TIME: 1
busy...
busy...
ID: 643  WASTE TIME: 2
busy...
busy...
ID: 643  WASTE TIME: 3
busy...
busy...
ID: 643  WASTE TIME: 4
busy...
busy...
192.168.252.142 - - [18/Dec/2018:11:22:24] "POST /wasteTime HTTP/1.1" 200 4 "http://xxx.xxx.xxx.xxx/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
ID: 643  WASTE TIME: 0
ID: 643  WASTE TIME: 1
ID: 643  WASTE TIME: 2
ID: 643  WASTE TIME: 3
ID: 643  WASTE TIME: 4

Solution

  • What you want is a lock that prevents more than one thread from running that function at a time.

    import threading
    
    class Comm(object):
        def __init__(self, uid):
            self.lock = threading.Lock()
            self.uid = uid
        def singleResource(self, periods):
            with self.lock:
                for i in range (0, periods):
                    print("ID:", self.uid, " WASTE TIME:", i)
                    time.sleep(1)
    
    comm = Comm(0)
    
    def wasteTime():
        comm.singleResource(5)