xref: /original-bsd/usr.sbin/amd/amd/clock.c (revision 9a765c18)
1 /*
2  * $Id: clock.c,v 5.2 90/06/23 22:19:21 jsp Rel $
3  *
4  * Copyright (c) 1989 Jan-Simon Pendry
5  * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
6  * Copyright (c) 1989 The Regents of the University of California.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Jan-Simon Pendry at Imperial College, London.
11  *
12  * %sccs.include.redist.c%
13  *
14  *	@(#)clock.c	5.1 (Berkeley) 06/29/90
15  */
16 
17 /*
18  * Callouts.
19  *
20  * Modelled on kernel object of the same name.
21  * See usual references.
22  *
23  * Use of a heap-based mechanism was rejected:
24  * 1.  more complext implementation needed.
25  * 2.  not obvious that a list is too slow for amd.
26  */
27 
28 #include "am.h"
29 
30 typedef struct callout callout;
31 struct callout {
32 	callout	*c_next;		/* List of callouts */
33 	void	(*c_fn)();		/* Function to call */
34 	voidp	c_closure;		/* Closure to pass to call */
35 	time_t	c_time;			/* Time of call */
36 	int	c_id;			/* Unique identifier */
37 };
38 
39 static callout callouts;		/* List of pending callouts */
40 static callout *free_callouts;		/* Cache of free callouts */
41 static int nfree_callouts;		/* Number on free list */
42 static int callout_id;			/* Next free callout identifier */
43 time_t next_softclock;			/* Time of next call to softclock() */
44 
45 /*
46  * Number of callout slots we keep on the free list
47  */
48 #define	CALLOUT_FREE_SLOP	10
49 
50 /*
51  * Assumption: valid id's are non-zero.
52  */
53 #define	CID_ALLOC()	(++callout_id)
54 #define	CID_UNDEF	(0)
55 
56 static callout *alloc_callout()
57 {
58 	callout *cp = free_callouts;
59 	if (cp) {
60 		--nfree_callouts;
61 		free_callouts = free_callouts->c_next;
62 		return cp;
63 	}
64 	return ALLOC(callout);
65 }
66 
67 static void free_callout(cp)
68 callout *cp;
69 {
70 	if (nfree_callouts > CALLOUT_FREE_SLOP) {
71 		free((voidp) cp);
72 	} else {
73 		cp->c_next = free_callouts;
74 		free_callouts = cp;
75 		nfree_callouts++;
76 	}
77 }
78 
79 /*
80  * Schedule a callout.
81  *
82  * (*fn)(closure) will be called at clocktime() + secs
83  */
84 int timeout(secs, fn, closure)
85 unsigned int secs;
86 void (*fn)();
87 voidp closure;
88 {
89 	callout *cp, *cp2;
90 	time_t t = clocktime() + secs;
91 
92 	/*
93 	 * Allocate and fill in a new callout structure
94 	 */
95 	callout *cpnew = alloc_callout();
96 	cpnew->c_closure = closure;
97 	cpnew->c_fn = fn;
98 	cpnew->c_time = t;
99 	cpnew->c_id = CID_ALLOC();
100 
101 	if (t < next_softclock)
102 		next_softclock = t;
103 
104 	/*
105 	 * Find the correct place in the list
106 	 */
107 	for (cp = &callouts; cp2 = cp->c_next; cp = cp2)
108 		if (cp2->c_time >= t)
109 			break;
110 
111 	/*
112 	 * And link it in
113 	 */
114 	cp->c_next = cpnew;
115 	cpnew->c_next = cp2;
116 
117 	/*
118 	 * Return callout identifier
119 	 */
120 	return cpnew->c_id;
121 }
122 
123 /*
124  * De-schedule a callout
125  */
126 void untimeout(id)
127 int id;
128 {
129 	callout *cp, *cp2;
130 	for (cp = &callouts; cp2 = cp->c_next; cp = cp2) {
131 		if (cp2->c_id == id) {
132 			cp->c_next = cp2->c_next;
133 			free_callout(cp2);
134 			break;
135 		}
136 	}
137 }
138 
139 /*
140  * Clock handler
141  */
142 int softclock()
143 {
144 	time_t now;
145 	callout *cp;
146 
147 	do {
148 		if (task_notify_todo)
149 			task_notify();
150 
151 		now = clocktime();
152 
153 		/*
154 		 * While there are more callouts waiting...
155 		 */
156 		while ((cp = callouts.c_next) && cp->c_time <= now) {
157 			/*
158 			 * Extract first from list, save fn & closure and
159 			 * unlink callout from list and free.
160 			 * Finally call function.
161 			 *
162 			 * The free is done first because
163 			 * it is quite common that the
164 			 * function will call timeout()
165 			 * and try to allocate a callout
166 			 */
167 			void (*fn)() = cp->c_fn;
168 			voidp closure = cp->c_closure;
169 
170 			callouts.c_next = cp->c_next;
171 			free_callout(cp);
172 #ifdef DEBUG
173 			/*dlog("Calling %#x(%#x)", fn, closure);*/
174 #endif /* DEBUG */
175 			(*fn)(closure);
176 		}
177 
178 	} while (task_notify_todo);
179 
180 	/*
181 	 * Return number of seconds to next event,
182 	 * or 0 if there is no event.
183 	 */
184 	if (cp = callouts.c_next)
185 		return cp->c_time - now;
186 	return 0;
187 }
188