[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