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