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