clock.c revision 119679
1279264Sdelphij/* 296593Smarkm * Copyright (c) 1997-2003 Erez Zadok 396593Smarkm * Copyright (c) 1989 Jan-Simon Pendry 4142429Snectar * Copyright (c) 1989 Imperial College of Science, Technology & Medicine 596593Smarkm * Copyright (c) 1989 The Regents of the University of California. 696593Smarkm * All rights reserved. 796593Smarkm * 896593Smarkm * This code is derived from software contributed to Berkeley by 996593Smarkm * Jan-Simon Pendry at Imperial College, London. 1096593Smarkm * 1196593Smarkm * Redistribution and use in source and binary forms, with or without 1296593Smarkm * modification, are permitted provided that the following conditions 1396593Smarkm * are met: 1496593Smarkm * 1. Redistributions of source code must retain the above copyright 1596593Smarkm * notice, this list of conditions and the following disclaimer. 1696593Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1796593Smarkm * notice, this list of conditions and the following disclaimer in the 1896593Smarkm * documentation and/or other materials provided with the distribution. 1996593Smarkm * 3. All advertising materials mentioning features or use of this software 20215698Ssimon * must display the following acknowledgment: 21215698Ssimon * This product includes software developed by the University of 22215698Ssimon * California, Berkeley and its contributors. 23215698Ssimon * 4. Neither the name of the University nor the names of its contributors 24215698Ssimon * may be used to endorse or promote products derived from this software 2596593Smarkm * without specific prior written permission. 2696593Smarkm * 2796593Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2896593Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2996593Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3096593Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3196593Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3296593Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3396593Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3496593Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3596593Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3696593Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3796593Smarkm * SUCH DAMAGE. 3896593Smarkm * 3996593Smarkm * %W% (Berkeley) %G% 4096593Smarkm * 41279264Sdelphij * $Id: clock.c,v 1.4.2.3 2002/12/27 22:44:33 ezk Exp $ 42279264Sdelphij * 4396593Smarkm */ 4496593Smarkm 45215698Ssimon/* 46215698Ssimon * Callouts. 47215698Ssimon * 48215698Ssimon * Modeled on kernel object of the same name. 49142429Snectar * See usual references. 50215698Ssimon * 51142429Snectar * Use of a heap-based mechanism was rejected: 52142429Snectar * 1. more complex implementation needed. 53279264Sdelphij * 2. not obvious that a list is too slow for Amd. 54279264Sdelphij */ 55279264Sdelphij 5696593Smarkm#ifdef HAVE_CONFIG_H 57279264Sdelphij# include <config.h> 58279264Sdelphij#endif /* HAVE_CONFIG_H */ 59279264Sdelphij#include <am_defs.h> 60279264Sdelphij#include <amd.h> 61279264Sdelphij 62279264Sdelphijint timeout(u_int secs, void (*fn) (voidp), voidp closure); 63215698Ssimonvoid reschedule_timeouts(time_t now, time_t then); 64279264Sdelphij 65279264Sdelphijtypedef struct callout callout; 66279264Sdelphijstruct callout { 67279264Sdelphij callout *c_next; /* List of callouts */ 68279264Sdelphij void (*c_fn) (voidp); /* Function to call */ 69215698Ssimon voidp c_closure; /* Closure to pass to call */ 70279264Sdelphij time_t c_time; /* Time of call */ 7196593Smarkm int c_id; /* Unique identifier */ 7296593Smarkm}; 7396593Smarkm 7496593Smarkmstatic callout callouts; /* List of pending callouts */ 7596593Smarkmstatic callout *free_callouts; /* Cache of free callouts */ 7696593Smarkmstatic int nfree_callouts; /* Number on free list */ 7796593Smarkmstatic int callout_id; /* Next free callout identifier */ 7896593Smarkm 7996593Smarkmtime_t next_softclock; /* Time of next call to softclock() */ 8096593Smarkm 8196593Smarkm 8296593Smarkm/* 8396593Smarkm * Number of callout slots we keep on the free list 8496593Smarkm */ 8596593Smarkm#define CALLOUT_FREE_SLOP 10 8696593Smarkm 8796593Smarkm/* 8896593Smarkm * Global assumption: valid id's are non-zero. 8996593Smarkm */ 9096593Smarkm#define CID_ALLOC(struct ) (++callout_id) 9196593Smarkm#define CID_UNDEF (0) 9296593Smarkm 9396593Smarkm 9496593Smarkmstatic callout * 9596593Smarkmalloc_callout(void) 9696593Smarkm{ 9796593Smarkm callout *cp = free_callouts; 9896593Smarkm 9996593Smarkm if (cp) { 10096593Smarkm --nfree_callouts; 10196593Smarkm free_callouts = free_callouts->c_next; 10296593Smarkm return cp; 10396593Smarkm } 10496593Smarkm return ALLOC(struct callout); 10596593Smarkm} 10696593Smarkm 10796593Smarkm 10896593Smarkmstatic void 10996593Smarkmfree_callout(callout *cp) 11096593Smarkm{ 11196593Smarkm if (nfree_callouts > CALLOUT_FREE_SLOP) { 11296593Smarkm XFREE(cp); 11396593Smarkm } else { 11496593Smarkm cp->c_next = free_callouts; 11596593Smarkm free_callouts = cp; 11696593Smarkm nfree_callouts++; 11796593Smarkm } 11896593Smarkm} 11996593Smarkm 12096593Smarkm 12196593Smarkm/* 12296593Smarkm * Schedule a callout. 12396593Smarkm * 12496593Smarkm * (*fn)(closure) will be called at clocktime() + secs 12596593Smarkm */ 12696593Smarkmint 12796593Smarkmtimeout(u_int secs, void (*fn) (voidp), voidp closure) 12896593Smarkm{ 12996593Smarkm callout *cp, *cp2; 13096593Smarkm time_t t = clocktime() + secs; 13196593Smarkm 13296593Smarkm /* 133142429Snectar * Allocate and fill in a new callout structure 13496593Smarkm */ 135100946Snectar callout *cpnew = alloc_callout(); 136279264Sdelphij cpnew->c_closure = closure; 137215698Ssimon cpnew->c_fn = fn; 138215698Ssimon cpnew->c_time = t; 139215698Ssimon cpnew->c_id = CID_ALLOC(struct ); 140215698Ssimon 14196593Smarkm if (t < next_softclock) 14296593Smarkm next_softclock = t; 14396593Smarkm 14496593Smarkm /* 14596593Smarkm * Find the correct place in the list 14696593Smarkm */ 147215698Ssimon for (cp = &callouts; (cp2 = cp->c_next); cp = cp2) 14896593Smarkm if (cp2->c_time >= t) 149215698Ssimon break; 15096593Smarkm 15196593Smarkm /* 15296593Smarkm * And link it in 15396593Smarkm */ 15496593Smarkm cp->c_next = cpnew; 15596593Smarkm cpnew->c_next = cp2; 15696593Smarkm 15796593Smarkm /* 15896593Smarkm * Return callout identifier 15996593Smarkm */ 160142429Snectar return cpnew->c_id; 16196593Smarkm} 16296593Smarkm 163142429Snectar 16496593Smarkm/* 16596593Smarkm * De-schedule a callout 16696593Smarkm */ 167void 168untimeout(int id) 169{ 170 callout *cp, *cp2; 171 for (cp = &callouts; (cp2 = cp->c_next); cp = cp2) { 172 if (cp2->c_id == id) { 173 cp->c_next = cp2->c_next; 174 free_callout(cp2); 175 break; 176 } 177 } 178} 179 180 181/* 182 * Reschedule after clock changed 183 */ 184void 185reschedule_timeouts(time_t now, time_t then) 186{ 187 callout *cp; 188 189 for (cp = callouts.c_next; cp; cp = cp->c_next) { 190 if (cp->c_time >= now && cp->c_time <= then) { 191 plog(XLOG_WARNING, "job %d rescheduled to run immediately", cp->c_id); 192#ifdef DEBUG 193 dlog("rescheduling job %d back %ld seconds", 194 cp->c_id, (long) (cp->c_time - now)); 195#endif /* DEBUG */ 196 next_softclock = cp->c_time = now; 197 } 198 } 199} 200 201 202/* 203 * Clock handler 204 */ 205int 206softclock(void) 207{ 208 time_t now; 209 callout *cp; 210 211 do { 212 if (task_notify_todo) 213 do_task_notify(); 214 215 now = clocktime(); 216 217 /* 218 * While there are more callouts waiting... 219 */ 220 while ((cp = callouts.c_next) && cp->c_time <= now) { 221 /* 222 * Extract first from list, save fn & closure and 223 * unlink callout from list and free. 224 * Finally call function. 225 * 226 * The free is done first because 227 * it is quite common that the 228 * function will call timeout() 229 * and try to allocate a callout 230 */ 231 void (*fn) (voidp) = cp->c_fn; 232 voidp closure = cp->c_closure; 233 234 callouts.c_next = cp->c_next; 235 free_callout(cp); 236 (*fn) (closure); 237 } 238 239 } while (task_notify_todo); 240 241 /* 242 * Return number of seconds to next event, 243 * or 0 if there is no event. 244 */ 245 if ((cp = callouts.c_next)) 246 return cp->c_time - now; 247 return 0; 248} 249