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