xref: /netbsd/external/bsd/am-utils/dist/amd/clock.c (revision 6550d01e)
1 /*	$NetBSD: clock.c,v 1.1.1.2 2009/03/20 20:26:49 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1997-2009 Erez Zadok
5  * Copyright (c) 1989 Jan-Simon Pendry
6  * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
7  * Copyright (c) 1989 The Regents of the University of California.
8  * All rights reserved.
9  *
10  * This code is derived from software contributed to Berkeley by
11  * Jan-Simon Pendry at Imperial College, London.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgment:
23  *      This product includes software developed by the University of
24  *      California, Berkeley and its contributors.
25  * 4. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  *
41  *
42  * File: am-utils/amd/clock.c
43  *
44  */
45 
46 /*
47  * Callouts.
48  *
49  * Modeled on kernel object of the same name.
50  * See usual references.
51  *
52  * Use of a heap-based mechanism was rejected:
53  * 1.  more complex implementation needed.
54  * 2.  not obvious that a list is too slow for Amd.
55  */
56 
57 #ifdef HAVE_CONFIG_H
58 # include <config.h>
59 #endif /* HAVE_CONFIG_H */
60 #include <am_defs.h>
61 #include <amd.h>
62 
63 void reschedule_timeouts(time_t now, time_t then);
64 
65 typedef struct callout callout;
66 struct callout {
67   callout *c_next;		/* List of callouts */
68   callout_fun *c_fn;		/* Function to call */
69   opaque_t c_arg;		/* Argument to pass to call */
70   time_t c_time;		/* Time of call */
71   int c_id;			/* Unique identifier */
72 };
73 
74 static callout callouts;	/* List of pending callouts */
75 static callout *free_callouts;	/* Cache of free callouts */
76 static int nfree_callouts;	/* Number on free list */
77 static int callout_id;		/* Next free callout identifier */
78 
79 time_t next_softclock;		/* Time of next call to softclock() */
80 
81 
82 /*
83  * Number of callout slots we keep on the free list
84  */
85 #define	CALLOUT_FREE_SLOP	10
86 
87 /*
88  * Global assumption: valid id's are non-zero.
89  */
90 #define	CID_ALLOC()		(++callout_id)
91 #define	CID_UNDEF		(0)
92 
93 
94 static callout *
95 alloc_callout(void)
96 {
97   callout *cp = free_callouts;
98 
99   if (cp) {
100     --nfree_callouts;
101     free_callouts = free_callouts->c_next;
102     return cp;
103   }
104   return ALLOC(struct callout);
105 }
106 
107 
108 static void
109 free_callout(callout *cp)
110 {
111   if (nfree_callouts > CALLOUT_FREE_SLOP) {
112     XFREE(cp);
113   } else {
114     cp->c_next = free_callouts;
115     free_callouts = cp;
116     nfree_callouts++;
117   }
118 }
119 
120 
121 /*
122  * Schedule a callout.
123  *
124  * (*fn)(fn_arg) will be called at clocktime(NULL) + secs
125  */
126 int
127 timeout(u_int secs, callout_fun *fn, opaque_t fn_arg)
128 {
129   callout *cp, *cp2;
130   time_t t = clocktime(NULL) + secs;
131 
132   /*
133    * Allocate and fill in a new callout structure
134    */
135   callout *cpnew = alloc_callout();
136   cpnew->c_arg = fn_arg;
137   cpnew->c_fn = fn;
138   cpnew->c_time = t;
139   cpnew->c_id = CID_ALLOC();
140 
141   if (t < next_softclock)
142     next_softclock = t;
143 
144   /*
145    * Find the correct place in the list
146    */
147   for (cp = &callouts; (cp2 = cp->c_next); cp = cp2)
148     if (cp2->c_time >= t)
149       break;
150 
151   /*
152    * And link it in
153    */
154   cp->c_next = cpnew;
155   cpnew->c_next = cp2;
156 
157   /*
158    * Return callout identifier
159    */
160   return cpnew->c_id;
161 }
162 
163 
164 /*
165  * De-schedule a callout
166  */
167 void
168 untimeout(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  */
184 void
185 reschedule_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       dlog("rescheduling job %d back %ld seconds",
193 	   cp->c_id, (long) (cp->c_time - now));
194       next_softclock = cp->c_time = now;
195     }
196   }
197 }
198 
199 
200 /*
201  * Clock handler
202  */
203 int
204 softclock(void)
205 {
206   time_t now;
207   callout *cp;
208 
209   do {
210     if (task_notify_todo)
211       do_task_notify();
212 
213     now = clocktime(NULL);
214 
215     /*
216      * While there are more callouts waiting...
217      */
218     while ((cp = callouts.c_next) && cp->c_time <= now) {
219       /*
220        * Extract first from list, save fn & fn_arg and
221        * unlink callout from list and free.
222        * Finally call function.
223        *
224        * The free is done first because
225        * it is quite common that the
226        * function will call timeout()
227        * and try to allocate a callout
228        */
229       callout_fun *fn = cp->c_fn;
230       opaque_t fn_arg = cp->c_arg;
231 
232       callouts.c_next = cp->c_next;
233       free_callout(cp);
234       (*fn) (fn_arg);
235     }
236 
237   } while (task_notify_todo);
238 
239   /*
240    * Return number of seconds to next event,
241    * or 0 if there is no event.
242    */
243   if ((cp = callouts.c_next))
244     return cp->c_time - now;
245   return 0;
246 }
247