Search code examples
getcherrypy

When one GET request starts working, I want set a variable which can immediately be read by other GET requests


It seems like the module level variable isn't actually "changed" until the request is completed. I need it to update immediately, so that other requests can be aware that the work is being processed.

Here is an example of the issue:

import cherrypy
import time


@cherrypy.expose
class CherryPySleeps(object):
    status = "not sleeping"

    @cherrypy.tools.accept(media='text/plain')
    def GET(self):
        if CherryPySleeps.status == "not sleeping":
            CherryPySleeps.status = "sleeping"
            time.sleep(10)
            CherryPySleeps.status = "not sleeping"
            return "I finished sleeping"
        else:
            return "I am sleeping, shhh"


if __name__ == '__main__':
    conf = {
        '/': {
            'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
            'tools.sessions.on': True,
            'tools.response_headers.on': True,
            'tools.response_headers.headers': [('Content-Type', 'text/plain')],
        }
    }
    cherrypy.quickstart(CherryPySleeps(), '/', conf)

When I open 2 browser tabs, request the page on the first, then wait a second, then request the page on the second, I get a 10 second pause on each tab, then "I finished sleeping".

What I want is for the second tab to very quickly respond, "I am sleeping, shhh"


Solution

  • If you want to guarantee some synchronicity among the requests (threads), you can use a lock for both read and write (or use the lock in the if condition). BTW I wasn't able to replicate the exact condition that you described (it worked for me), but at least the lock should guarantee that it will be a consistent result.

    Take a look at this example:

    import time
    import threading
    
    import cherrypy
    
    
    @cherrypy.expose
    class CherryPySleeps(object):
        def __init__(self):
            # share the same lock among read and write operations,
            # the read is not "strictly" required, but you can use
            # it to make sure there is no write happening at the
            # same time
            self._status_lock = threading.Lock()
            # the internal status variable
            self._status = "not sleeping"
    
        @property
        def status(self):
            with self._status_lock:
                return self._status
    
        @status.setter
        def status(self, new_status):
            with self._status_lock:
                self._status = new_status
    
        @cherrypy.tools.accept(media='text/plain')
        def GET(self):
            if self.status == "not sleeping":
                self.status = "sleeping"
                time.sleep(10)
                self.status = "not sleeping"
                return "I finished sleeping"
            else:
                return "I am sleeping, shhh"
    
    
    if __name__ == '__main__':
        conf = {
            '/': {
                'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
                'tools.sessions.on': True,
                'tools.response_headers.on': True,
                'tools.response_headers.headers': [('Content-Type', 'text/plain')],
            }
        }
        cherrypy.quickstart(CherryPySleeps(), '/', conf)
    

    There are other ways to do it, using the plugin system in CherryPy, but I think that for this example is not necessary.