29217

Limit parallel processes in CherryPy?

Question:

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. threading.Lock.

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).

If <em>run-or-fail</em> in single browser isn't important to you you can safely ignore the previous paragraph and <em>JavaScript</em> code in the following example.

<h2>Update</h2>

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.

#!/usr/bin/env python # -*- coding: utf-8 -*- import threading import time import cherrypy config = { 'global' : { 'server.socket_host' : '127.0.0.1', 'server.socket_port' : 8080, 'server.thread_pool' : 8 } } class App: lock = threading.Lock() @cherrypy.expose def index(self): return '''<!DOCTYPE html> <html> <head> <title>Lock demo</title> <script type='text/javascript' src='http://cdnjs.cloudflare.com/ajax/libs/qooxdoo/3.5.1/q.min.js'></script> <script type='text/javascript'> function runTask(wait) { var url = (wait ? '/runOrWait' : '/runOrFail') + '?nc=' + Date.now(); var xhr = q.io.xhr(url); xhr.on('loadend', function(xhr) { if(xhr.status == 200) { console.log('success', xhr.responseText) } else if(xhr.status == 503) { console.log('busy'); } }); xhr.send(); } q.ready(function() { q('p a').on('click', function(event) { event.preventDefault(); var wait = parseInt(q(event.getTarget()).getData('wait')); runTask(wait); }); }); </script> </head> <body>

<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)

Recommend

  • Exception handling in pipeline sequence
  • frame pointer omitting ? Any risk?
  • Redis is it possible to update multiple hset keys(not fields) using mset
  • Float versus Integer arithmetic performance on modern chips
  • Error : “Transport endpoint is already connected”
  • How can I get a connected client's IP address?
  • Hibernate returning the values in session, but not from the database
  • What are python generators? [duplicate]
  • Haskell Stack build specific executable
  • bash: “which adb” returns nothing, but “command -v adb” returns what i'd expect
  • How to replace one set of color 'shades' with another set
  • Proper way to add unescaped text from a field to a regex in postgres?
  • HALF_PTR Windows data type
  • android Navigation Bar hiding and persantage of usable screen overlap
  • Servlet stops working on Tomcat server after some hits or time
  • How can I restyle a word when rendering a pdf with pdf.js?
  • what makes a request a new request in asp.net C#
  • Find group of records that match multiple values
  • Ember.js model to be organised as a tree structure
  • System.InvalidCastException: Specified cast is not valid
  • Jackson Parser: ignore deserializing for type mismatch
  • OpenGL ES texture problem, 4 duplicate columns and horizontal lines (Android)
  • Read a local file using javascript
  • Asynchronous UI Testing in Xcode With Swift
  • Ajax Loaded meta Tags
  • Xamarin Forms - UWP Fonts
  • Sails.js/waterline: Executing waterline queries in toJSON function of a model?
  • Apache 2.4 and php-fpm does not trigger apache http basic auth for php pages
  • Arrow is showed instead of the material design version hamburger icon. Why doesn't syncState in
  • How can I use Kendo UI with Razor?
  • Redux, normalised entities and lodash merge
  • ActionScript 2 vs ActionScript 3 performance
  • Do create extension work in single-user mode in postgres?
  • How can I estimate amount of memory left with calling System.gc()?
  • Arrays break string types in Julia
  • What are the advantages and disadvantages of reading an entire file into a single String as opposed
  • Observable and ngFor in Angular 2
  • How to Embed XSL into XML
  • UserPrincipal.Current returns apppool on IIS
  • Conditional In-Line CSS for IE and Others?