I have a CherryPy server running on a BeagleBone Black. Server generates a simple webpage and does local SPI reads / writes (hardware interface). The application is going to be used on a local network with 1-2 clients at a time. I need to prevent a CherryPy class function being called twice, two or more instances before it completes. Thoughts?Answer1:
As saaj commented, a simple threading.Lock() will prevent the handler from being run at the same time by another client. I might also add, <a href="http://blog.schmichael.com/2007/09/20/session-locking-and-performance-in-cherrypy/" rel="nofollow">using cherrypy.session.acquire_lock()</a> will prevent the same client from the running two handlers simultaneously.
Refreshing article on Python locks and stuff: <a href="http://effbot.org/zone/thread-synchronization.htm" rel="nofollow">http://effbot.org/zone/thread-synchronization.htm</a>
Although I would make saaj's solution much simpler by using a "with" statement in Python, to hide all those fancy lock acquisitions/releases and try/except block.
lock = threading.Lock() @cherrypy.expose def index(self): with lock: # do stuff in the handler. # this code will only be run by one client at a time return '<html></html>'Answer2:
It is general synchronization question, though <em>CherryPy</em> side has a subtlety. <em>CherryPy</em> is a threaded-server so it is sufficient to have an application level lock, e.g.
The subtlety is that you can't see the <em>run-or-fail</em> behaviour from within a single browser because of pipelining, <a href="http://en.wikipedia.org/wiki/HTTP_persistent_connection" rel="nofollow">Keep-Alive</a> or caching. Which one it is is hard to guess as the behaviour varies in Chromium and Firefox. As far as I can see <em>CherryPy</em> will try to serialize processing of request coming from single <em>TCP</em> connection, which effectively results in subsequent requests waiting for active request in a queue. With some trial-and-error I've found that adding cache-prevention token leads to the desired behaviour (even though Chromium still sends
Connection: keep-alive for XHR where Firefox does not).
The cause of request serialisation coming from one browser to the same URL doesn't lie in server-side. It's an implementation detail of a browser cache (<a href="https://stackoverflow.com/a/30595857/2072035" rel="nofollow">details</a>). Though, the solution of adding random query string parameter,
nc, is correct.
<a href='#' data-wait='0'>Run or fail</a>
<a href='#' data-wait='1'>Run or wait</a></body> </html> ''' def calculate(self): time.sleep(8) return 'Long task result' @cherrypy.expose def runOrWait(self, **kwargs): self.lock.acquire() try: return self.calculate() finally: self.lock.release() @cherrypy.expose def runOrFail(self, **kwargs): locked = self.lock.acquire(False) if not locked: raise cherrypy.HTTPError(503, 'Task is already running') else: try: return self.calculate() finally: self.lock.release() if __name__ == '__main__': cherrypy.quickstart(App(), '/', config)