clock.c revision 38494
1130803Smarcel/*
2130803Smarcel * Copyright (c) 1997-1998 Erez Zadok
3130803Smarcel * Copyright (c) 1989 Jan-Simon Pendry
4130803Smarcel * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
5130803Smarcel * Copyright (c) 1989 The Regents of the University of California.
6130803Smarcel * All rights reserved.
7130803Smarcel *
8130803Smarcel * This code is derived from software contributed to Berkeley by
9130803Smarcel * Jan-Simon Pendry at Imperial College, London.
10130803Smarcel *
11130803Smarcel * Redistribution and use in source and binary forms, with or without
12130803Smarcel * modification, are permitted provided that the following conditions
13130803Smarcel * are met:
14130803Smarcel * 1. Redistributions of source code must retain the above copyright
15130803Smarcel *    notice, this list of conditions and the following disclaimer.
16130803Smarcel * 2. Redistributions in binary form must reproduce the above copyright
17130803Smarcel *    notice, this list of conditions and the following disclaimer in the
18130803Smarcel *    documentation and/or other materials provided with the distribution.
19130803Smarcel * 3. All advertising materials mentioning features or use of this software
20130803Smarcel *    must display the following acknowledgement:
21130803Smarcel *      This product includes software developed by the University of
22130803Smarcel *      California, Berkeley and its contributors.
23130803Smarcel * 4. Neither the name of the University nor the names of its contributors
24130803Smarcel *    may be used to endorse or promote products derived from this software
25130803Smarcel *    without specific prior written permission.
26130803Smarcel *
27130803Smarcel * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28130803Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29130803Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30130803Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31130803Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32130803Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33130803Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34130803Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35130803Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36130803Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37130803Smarcel * SUCH DAMAGE.
38130803Smarcel *
39130803Smarcel *      %W% (Berkeley) %G%
40130803Smarcel *
41130803Smarcel * $Id: clock.c,v 5.2.2.1 1992/02/09 15:08:20 jsp beta $
42130803Smarcel *
43130803Smarcel */
44130803Smarcel
45130803Smarcel/*
46130803Smarcel * Callouts.
47130803Smarcel *
48130803Smarcel * Modelled on kernel object of the same name.
49130803Smarcel * See usual references.
50130803Smarcel *
51130803Smarcel * Use of a heap-based mechanism was rejected:
52130803Smarcel * 1.  more complex implementation needed.
53130803Smarcel * 2.  not obvious that a list is too slow for Amd.
54130803Smarcel */
55130803Smarcel
56130803Smarcel#ifdef HAVE_CONFIG_H
57130803Smarcel# include <config.h>
58130803Smarcel#endif /* HAVE_CONFIG_H */
59130803Smarcel#include <am_defs.h>
60130803Smarcel#include <amd.h>
61130803Smarcel
62130803Smarcelint timeout(u_int secs, void (*fn) (voidp), voidp closure);
63130803Smarcelvoid reschedule_timeouts(time_t now, time_t then);
64130803Smarcel
65130803Smarceltypedef struct callout callout;
66130803Smarcelstruct callout {
67130803Smarcel  callout *c_next;		/* List of callouts */
68130803Smarcel  void (*c_fn) (voidp);		/* Function to call */
69130803Smarcel  voidp c_closure;		/* Closure to pass to call */
70130803Smarcel  time_t c_time;		/* Time of call */
71130803Smarcel  int c_id;			/* Unique identifier */
72130803Smarcel};
73130803Smarcel
74130803Smarcelstatic callout callouts;	/* List of pending callouts */
75130803Smarcelstatic callout *free_callouts;	/* Cache of free callouts */
76130803Smarcelstatic int nfree_callouts;	/* Number on free list */
77130803Smarcelstatic int callout_id;		/* Next free callout identifier */
78130803Smarcel
79130803Smarceltime_t next_softclock;		/* Time of next call to softclock() */
80130803Smarcel
81130803Smarcel
82130803Smarcel/*
83130803Smarcel * Number of callout slots we keep on the free list
84130803Smarcel */
85130803Smarcel#define	CALLOUT_FREE_SLOP	10
86130803Smarcel
87130803Smarcel/*
88130803Smarcel * Global assumption: valid id's are non-zero.
89130803Smarcel */
90130803Smarcel#define	CID_ALLOC(struct )	(++callout_id)
91130803Smarcel#define	CID_UNDEF		(0)
92130803Smarcel
93130803Smarcel
94130803Smarcelstatic callout *
95130803Smarcelalloc_callout(void)
96130803Smarcel{
97130803Smarcel  callout *cp = free_callouts;
98130803Smarcel
99130803Smarcel  if (cp) {
100130803Smarcel    --nfree_callouts;
101130803Smarcel    free_callouts = free_callouts->c_next;
102130803Smarcel    return cp;
103130803Smarcel  }
104130803Smarcel  return ALLOC(struct callout);
105130803Smarcel}
106130803Smarcel
107130803Smarcel
108130803Smarcelstatic void
109130803Smarcelfree_callout(callout *cp)
110130803Smarcel{
111130803Smarcel  if (nfree_callouts > CALLOUT_FREE_SLOP) {
112130803Smarcel    XFREE(cp);
113130803Smarcel  } else {
114130803Smarcel    cp->c_next = free_callouts;
115130803Smarcel    free_callouts = cp;
116130803Smarcel    nfree_callouts++;
117130803Smarcel  }
118130803Smarcel}
119130803Smarcel
120130803Smarcel
121130803Smarcel/*
122130803Smarcel * Schedule a callout.
123130803Smarcel *
124130803Smarcel * (*fn)(closure) will be called at clocktime() + secs
125130803Smarcel */
126130803Smarcelint
127130803Smarceltimeout(u_int secs, void (*fn) (voidp), voidp closure)
128130803Smarcel{
129130803Smarcel  callout *cp, *cp2;
130130803Smarcel  time_t t = clocktime() + secs;
131130803Smarcel
132130803Smarcel  /*
133130803Smarcel   * Allocate and fill in a new callout structure
134130803Smarcel   */
135130803Smarcel  callout *cpnew = alloc_callout();
136130803Smarcel  cpnew->c_closure = closure;
137130803Smarcel  cpnew->c_fn = fn;
138130803Smarcel  cpnew->c_time = t;
139130803Smarcel  cpnew->c_id = CID_ALLOC(struct );
140130803Smarcel
141130803Smarcel  if (t < next_softclock)
142130803Smarcel    next_softclock = t;
143130803Smarcel
144130803Smarcel  /*
145130803Smarcel   * Find the correct place in the list
146130803Smarcel   */
147130803Smarcel  for (cp = &callouts; (cp2 = cp->c_next); cp = cp2)
148130803Smarcel    if (cp2->c_time >= t)
149130803Smarcel      break;
150130803Smarcel
151130803Smarcel  /*
152130803Smarcel   * And link it in
153130803Smarcel   */
154130803Smarcel  cp->c_next = cpnew;
155130803Smarcel  cpnew->c_next = cp2;
156130803Smarcel
157130803Smarcel  /*
158130803Smarcel   * Return callout identifier
159130803Smarcel   */
160130803Smarcel  return cpnew->c_id;
161130803Smarcel}
162130803Smarcel
163130803Smarcel
164130803Smarcel/*
165130803Smarcel * De-schedule a callout
166130803Smarcel */
167130803Smarcelvoid
168130803Smarceluntimeout(int id)
169130803Smarcel{
170130803Smarcel  callout *cp, *cp2;
171130803Smarcel  for (cp = &callouts; (cp2 = cp->c_next); cp = cp2) {
172130803Smarcel    if (cp2->c_id == id) {
173130803Smarcel      cp->c_next = cp2->c_next;
174130803Smarcel      free_callout(cp2);
175130803Smarcel      break;
176130803Smarcel    }
177130803Smarcel  }
178130803Smarcel}
179130803Smarcel
180130803Smarcel
181130803Smarcel/*
182130803Smarcel * Reschedule after clock changed
183130803Smarcel */
184130803Smarcelvoid
185130803Smarcelreschedule_timeouts(time_t now, time_t then)
186130803Smarcel{
187130803Smarcel  callout *cp;
188130803Smarcel
189130803Smarcel  for (cp = callouts.c_next; cp; cp = cp->c_next) {
190130803Smarcel    if (cp->c_time >= now && cp->c_time <= then) {
191130803Smarcel      plog(XLOG_WARNING, "job %d rescheduled to run immediately", cp->c_id);
192130803Smarcel#ifdef DEBUG
193130803Smarcel      dlog("rescheduling job %d back %d seconds", cp->c_id, cp->c_time - now);
194130803Smarcel#endif /* DEBUG */
195130803Smarcel      next_softclock = cp->c_time = now;
196130803Smarcel    }
197130803Smarcel  }
198130803Smarcel}
199130803Smarcel
200130803Smarcel
201130803Smarcel/*
202130803Smarcel * Clock handler
203130803Smarcel */
204130803Smarcelint
205130803Smarcelsoftclock(void)
206130803Smarcel{
207130803Smarcel  time_t now;
208130803Smarcel  callout *cp;
209130803Smarcel
210130803Smarcel  do {
211130803Smarcel    if (task_notify_todo)
212130803Smarcel      do_task_notify();
213130803Smarcel
214130803Smarcel    now = clocktime();
215130803Smarcel
216130803Smarcel    /*
217130803Smarcel     * While there are more callouts waiting...
218130803Smarcel     */
219130803Smarcel    while ((cp = callouts.c_next) && cp->c_time <= now) {
220130803Smarcel      /*
221130803Smarcel       * Extract first from list, save fn & closure and
222130803Smarcel       * unlink callout from list and free.
223130803Smarcel       * Finally call function.
224130803Smarcel       *
225130803Smarcel       * The free is done first because
226130803Smarcel       * it is quite common that the
227130803Smarcel       * function will call timeout()
228130803Smarcel       * and try to allocate a callout
229130803Smarcel       */
230130803Smarcel      void (*fn) (voidp) = cp->c_fn;
231130803Smarcel      voidp closure = cp->c_closure;
232130803Smarcel
233130803Smarcel      callouts.c_next = cp->c_next;
234130803Smarcel      free_callout(cp);
235130803Smarcel      (*fn) (closure);
236130803Smarcel    }
237130803Smarcel
238130803Smarcel  } while (task_notify_todo);
239130803Smarcel
240130803Smarcel  /*
241130803Smarcel   * Return number of seconds to next event,
242130803Smarcel   * or 0 if there is no event.
243130803Smarcel   */
244130803Smarcel  if ((cp = callouts.c_next))
245130803Smarcel    return cp->c_time - now;
246130803Smarcel  return 0;
247130803Smarcel}
248130803Smarcel