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