Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Changeset 1231

Show
Ignore:
Timestamp:
08/09/06 18:50:46
Author:
fumanchu
Message:

New deadlock monitor which sets Response.timed_out to True if Response.time < now - config.get("deadlock_timeout"). The request thread periodically checkes Response.timed_out and raises TimeoutError? if it is True. Current checks are in HookMap?.run, Request.respond, Body.set, and Response.finalize, more can be added later if needed.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/cherrypy/__init__.py

    r1225 r1231  
    55import logging as _logging 
    66 
    7 from _cperror import HTTPError, HTTPRedirect, InternalRedirect, NotFound, WrongConfigValue 
     7from _cperror import HTTPError, HTTPRedirect, InternalRedirect, NotFound 
     8from _cperror import WrongConfigValue, TimeoutError 
    89import config 
    910 
  • trunk/cherrypy/_cpengine.py

    r1227 r1231  
    5656        self.seen_threads = {} 
    5757         
     58        self.servings = [] 
     59         
    5860        self.mtimes = {} 
    5961        self.reload_files = [] 
     
    7173        for func in self.on_start_engine_list: 
    7274            func() 
     75         
    7376        self.state = STARTED 
     77         
     78        freq = float(cherrypy.config.get('deadlock_poll_freq', 60)) 
     79        if freq > 0: 
     80            self.monitor_thread = threading.Timer(freq, self.monitor) 
     81            self.monitor_thread.start() 
     82         
    7483        if blocking: 
    7584            self.block() 
     
    157166                func() 
    158167             
     168            if self.monitor_thread: 
     169                self.monitor_thread.cancel() 
     170                self.monitor_thread = None 
     171             
    159172            self.state = STOPPED 
    160173            cherrypy.log("CherryPy shut down", "ENGINE") 
     
    183196        """ 
    184197        if self.state == STOPPED: 
    185             r = NotReadyRequest("The CherryPy engine has stopped.") 
     198            req = NotReadyRequest("The CherryPy engine has stopped.") 
    186199        elif self.state == STARTING: 
    187             r = NotReadyRequest("The CherryPy engine could not start.") 
     200            req = NotReadyRequest("The CherryPy engine could not start.") 
    188201        else: 
    189202            # Only run on_start_thread_list if the engine is running. 
     
    195208                for func in self.on_start_thread_list: 
    196209                    func(i) 
    197             r = self.request_class(local_host, remote_host, scheme) 
    198         cherrypy.serving.request = r 
    199         cherrypy.serving.response = self.response_class() 
    200         return r 
     210            req = self.request_class(local_host, remote_host, scheme) 
     211        cherrypy.serving.request = req 
     212        cherrypy.serving.response = resp = self.response_class() 
     213        self.servings.append((req, resp)) 
     214        return req 
     215     
     216    def monitor(self): 
     217        """Check timeout on all responses.""" 
     218        if self.state == STARTED: 
     219            for req, resp in self.servings: 
     220                resp.check_timeout() 
     221            freq = float(cherrypy.config.get('deadlock_poll_freq', 60)) 
     222            self.monitor_thread = threading.Timer(freq, self.monitor) 
     223            self.monitor_thread.start() 
    201224     
    202225    def start_with_callback(self, func, args=None, kwargs=None): 
  • trunk/cherrypy/_cperror.py

    r1229 r1231  
    201201            path = cherrypy.request.path 
    202202        self.args = (path,) 
    203         HTTPError.__init__(self, 404, "The path %s was not found." % repr(path)) 
     203        HTTPError.__init__(self, 404, "The path %r was not found." % path) 
     204 
     205 
     206class TimeoutError(Exception): 
     207    """Exception raised when Response.timed_out is detected.""" 
     208    pass 
    204209 
    205210 
  • trunk/cherrypy/_cprequest.py

    r1229 r1231  
    3131    def run(self, point): 
    3232        """Execute all registered callbacks for the given point.""" 
     33        if cherrypy.response.timed_out: 
     34            raise cherrypy.TimeoutError() 
     35         
    3336        failsafe = point in self.failsafe 
    3437        for callback in self.callbacks[point]: 
     
    108111            self.closed = True 
    109112            self.hooks.run('on_end_request') 
     113             
     114            s = (self, cherrypy.serving.response) 
     115            try: 
     116                cherrypy.engine.servings.remove(s) 
     117            except ValueError: 
     118                pass 
     119             
    110120            cherrypy.serving.__dict__.clear() 
    111121     
     
    180190        except (KeyboardInterrupt, SystemExit): 
    181191            raise 
     192        except cherrypy.TimeoutError: 
     193            raise 
    182194        except: 
    183195            if cherrypy.config.get("throw_errors", False): 
     
    199211        try: 
    200212            try: 
     213                if cherrypy.response.timed_out: 
     214                    raise cherrypy.TimeoutError() 
     215                 
    201216                self.hooks = HookMap(self.hookpoints) 
    202217                self.hooks.failsafe = ['on_start_resource', 'on_end_resource', 
     
    595610     
    596611    def __set__(self, obj, value): 
     612        if cherrypy.response.timed_out: 
     613            raise cherrypy.TimeoutError() 
    597614        # Convert the given value to an iterable object. 
    598615        if isinstance(value, types.FileType): 
     
    624641     
    625642    # Class attributes for dev-time introspection. 
    626     status = None 
    627     header_list = None 
     643    status = "" 
     644    header_list = [] 
    628645    headers = http.HeaderMap() 
    629646    simple_cookie = Cookie.SimpleCookie() 
    630647    body = Body() 
     648    time = None 
     649    timed_out = False 
    631650     
    632651    def __init__(self): 
    633652        self.status = None 
    634653        self.header_list = None 
    635         self.body = None 
     654        self._body = [] 
    636655        self.time = time.time() 
    637656         
     
    653672    def finalize(self): 
    654673        """Transform headers (and cookies) into cherrypy.response.header_list.""" 
     674        if self.timed_out: 
     675            raise cherrypy.TimeoutError() 
    655676         
    656677        try: 
     
    686707                name, value = line.split(": ", 1) 
    687708                h.append((name, value)) 
     709     
     710    def check_timeout(self): 
     711        """If now > self.time + deadlock_timeout, set self.timed_out. 
     712         
     713        This purposefully sets a flag, rather than raising an error, 
     714        so that a monitor thread can interrupt the Response thread. 
     715        """ 
     716        timeout = float(cherrypy.config.get('deadlock_timeout', 300)) 
     717        if time.time() > self.time + timeout: 
     718            self.timed_out = True 
     719 
  • trunk/cherrypy/test/test_states.py

    r1228 r1231  
    2323        return "app was restarted succesfully" 
    2424    restart.exposed = True 
     25     
     26    def block_explicit(self): 
     27        while True: 
     28            if cherrypy.response.timed_out: 
     29                cherrypy.response.timed_out = False 
     30                return "broken!" 
     31            time.sleep(0.1) 
     32    block_explicit.exposed = True 
     33     
     34    def block_implicit(self): 
     35        raise cherrypy.InternalRedirect("/block_implicit") 
     36    block_implicit.exposed = True 
     37    block_implicit._cp_config = {'recursive_redirect': True} 
    2538 
    2639cherrypy.tree.mount(Root()) 
     
    2841    'log_to_screen': False, 
    2942    'environment': 'production', 
     43    'deadlock_poll_freq': 1, 
     44    'deadlock_timeout': 2, 
    3045    }) 
    3146 
     
    183198            self.assertEqual(len(db_connection.threads), 0) 
    184199     
    185     def test_3_Autoreload(self): 
     200    def test_3_Deadlocks(self): 
     201        cherrypy.engine.start(blocking=False) 
     202        cherrypy.server.start() 
     203        try: 
     204            self.assertNotEqual(cherrypy.engine.monitor_thread, None) 
     205             
     206            # Request a "normal" page. 
     207            self.assertEqual(cherrypy.engine.servings, []) 
     208            self.getPage("/") 
     209            self.assertBody("Hello World") 
     210            # request.close is called async. 
     211            while cherrypy.engine.servings: 
     212                time.sleep(0.1) 
     213             
     214            # Request a page that explicitly checks itself for deadlock. 
     215            # The deadlock_timeout should be 2 secs. 
     216            self.getPage("/block_explicit") 
     217            self.assertBody("broken!") 
     218             
     219            # Request a page that implicitly breaks deadlock. 
     220            # If we deadlock, we want to touch as little code as possible, 
     221            # so we won't even call handle_error, just bail ASAP. 
     222            self.getPage("/block_implicit") 
     223            self.assertStatus(500) 
     224            self.assertInBody("raise cherrypy.TimeoutError()") 
     225        finally: 
     226            cherrypy.engine.stop() 
     227            cherrypy.server.stop() 
     228     
     229    def test_4_Autoreload(self): 
    186230        if self.server_class: 
    187231            demoscript = os.path.join(os.getcwd(), os.path.dirname(__file__), 
  • trunk/cherrypy/test/test_states_demo.py

    r1219 r1231  
    2828                            }) 
    2929    cherrypy.quickstart(Root()) 
    30      
     30                       

Hosted by WebFaction

Log in as guest/cpguest to create tickets