[Zope-Checkins] CVS: Zope/lib/python/Lifetime - __init__.py:1.1.2.1 timeslicemodule.c:1.1.2.1

Chris McDonough chrism at zopemafia.com
Mon Dec 22 21:32:29 EST 2003


Update of /cvs-repository/Zope/lib/python/Lifetime
In directory cvs.zope.org:/tmp/cvs-serv31665/lib/python/Lifetime

Added Files:
      Tag: chrism-scheduling-branch
	__init__.py timeslicemodule.c 
Log Message:
This branch includes low-level scheduling services for Zope.

See http://www.plope.com/Members/chrism/scheduling_service for more information.


=== Added File Zope/lib/python/Lifetime/__init__.py ===
import sys, asyncore, time, operator, math
import timeslice

_shutdown_phase = 0
_shutdown_timeout = 30 # seconds per phase
_load_avg_delay = 2
_last_slice = timeslice.timeslice(_load_avg_delay)
_load_list = []
_load_list_max_len = 450 # 15 minutes worth of load data at full-tilt
_timeout = 30

# The shutdown phase counts up from 0 to 4.
#
# 0  Not yet terminating. running in main loop
#
# 1  Loss of service is imminent. Prepare any front-end proxies for this
#    happening by stopping any ICP servers, so that they can choose to send
#    requests to other Zope servers in the cluster.
#
# 2  Stop accepting any new requests.
#
# 3  Wait for all old requests to have been processed
#
# 4  Already terminated
#
# It is up to individual socket handlers to implement these actions, by
# providing the 'clean_shutdown_control' method. This is called intermittantly
# during shutdown with two parameters; the current phase number, and the amount
# of time that it has currently been in that phase. This method should return
# true if it does not yet want shutdown to proceed to the next phase.
#
# We also maintain a rough system load indicator based on the frequency
# at which we return from the asyncore.poll function and provide an API
# that returns a load average value based on the load indicator.

def shutdown(exit_code,fast = 0):
    global _shutdown_phase
    global _shutdown_timeout
    if _shutdown_phase == 0:
        # Thread safety? proably no need to care
        import ZServer
        ZServer.exit_code = exit_code
        _shutdown_phase = 1
    if fast:
        # Someone wants us to shutdown fast. This is hooked into SIGTERM - so
        # possibly the system is going down and we can expect a SIGKILL within
        # a few seconds.  Limit each shutdown phase to one second. This is fast
        # enough, but still clean.
        _shutdown_timeout = 1.0

def loop():
    # Run the main loop until someone calls shutdown()
    lifetime_loop()
    # Gradually close sockets in the right order, while running a select
    # loop to allow remaining requests to trickle away.
    graceful_shutdown_loop()

def lifetime_loop():
    # The main loop. Stay in here until we need to shutdown
    map = asyncore.socket_map
    ts = timeslice.timeslice
    timeout = _timeout
    while map and _shutdown_phase == 0:
        asyncore.poll(timeout, map)
        # perform load indicator maintenance at most every _load_avg_delay secs
        slice = ts(_load_avg_delay)
        if slice != _last_slice:
            # this is a function in order to make testing possible
            maintain_load_indicator(slice, timeout)

def graceful_shutdown_loop():
    # The shutdown loop. Allow various services to shutdown gradually.
    global _shutdown_phase
    timestamp = time.time()
    timeout = 1.0
    map = asyncore.socket_map
    while map and _shutdown_phase < 4:
        time_in_this_phase = time.time()-timestamp 
        veto = 0
        for fd,obj in map.items():
            try:
                fn = getattr(obj,'clean_shutdown_control')
            except AttributeError:
                pass
            else:
                try:
                    veto = veto or fn(_shutdown_phase,time_in_this_phase)
                except:
                    obj.handle_error()
        if veto and time_in_this_phase<_shutdown_timeout:
            # Any open socket handler can veto moving on to the next shutdown
            # phase.  (but not forever)
            asyncore.poll(timeout, map)
        else:
            # No vetos? That is one step closer to shutting down
            _shutdown_phase += 1
            timestamp = time.time()
    
def maintain_load_indicator(slice, timeout):
    global _last_slice
    # We only update the load list if we know we've not slept for equal or
    # longer than the timeout period (sleeping for longer implies that we
    # haven't actually done any work in the current timeslice)
    if not (slice - _last_slice >= timeout):
        if len(_load_list) > _load_list_max_len:
            _load_list.pop(0)
        _load_list.append(slice)
    _last_slice = slice

def load_avg(over_seconds=60, now=None):
    # being able to accept "now" just makes testing easier, it's not part of
    # the API
    if not _load_list:
        return 0.0
    if now is None: 
        now = int(time.time())
    since = now - over_seconds
    since_slice = timeslice.timeslice(_load_avg_delay, since)
    max_slice = max(_load_list)
    if since_slice >= max_slice:
        # Zope's not done any work at all over the period for which we want to
        # obtain the load average.
        return 0.0
    now_slice = timeslice.timeslice(_load_avg_delay, now)
    t = i = 0
    r = xrange(since_slice+_load_avg_delay, now_slice+_load_avg_delay,
               _load_avg_delay)
    for slice in r:
        if slice in _load_list:
            t = t + 1
        i = i + 1
    if i is 0:
        return 0.0
    load = float(t) / i
    if load < 0:
        load = 0.0
    if load > 1:
        load = 1.0
    return load



=== Added File Zope/lib/python/Lifetime/timeslicemodule.c ===
/*
**********************************************************************
* Copyright (c) 2001, 2002 Zope Corporation and Contributors.
* All Rights Reserved.
* This software is subject to the provisions of the Zope Public License,
* Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
* WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
* FOR A PARTICULAR PURPOSE.
**********************************************************************
*/

#include "Python.h"

PyDoc_STRVAR(timeslice__doc__, "timeslice module\n");

static PyObject *
timeslice(PyObject *self, PyObject *args)
{ 
  int period, low, high, x;
  int when;

  if (PyTuple_Size(args) == 1) {
    when = (int)time(NULL);
  }

  if (!PyArg_ParseTuple(args, "i|i:timeslice", &period, &when))
    return NULL;

  if (period <= 0) {
    PyErr_SetString(PyExc_ValueError, "period must be greater than 0");
    return NULL;
  }
  
  low = when - period + 1;
  high = when + 1;
  
  for (x=low; x <= high; x++) {
    if ( (x % period) == 0 ) {
      break;
    }
    
  }
  return Py_BuildValue("i", x);
}

static PyMethodDef methods[] = {
  {"timeslice", timeslice, METH_VARARGS,
    "timeslice(period, [when]) ->\n\
Return an integer representing a the lowest epoch time value\n\
within 'slice' of time based on a divisor of 'period'. 'period' denotes \n\
the number of seconds in a timeslice.  If 'when' is not supplied, it is\n\
 assumed to be the current integer epoch time."},
  {NULL, NULL},
};

PyMODINIT_FUNC
inittimeslice(void)
{
  Py_InitModule3("timeslice", methods, timeslice__doc__);
}





More information about the Zope-Checkins mailing list