Annotation of fam/fam/Scheduler.c++, Revision 1.1.1.1
1.1 trev 1: // Copyright (C) 1999 Silicon Graphics, Inc. All Rights Reserved.
2: //
3: // This program is free software; you can redistribute it and/or modify it
4: // under the terms of version 2 of the GNU General Public License as
5: // published by the Free Software Foundation.
6: //
7: // This program is distributed in the hope that it would be useful, but
8: // WITHOUT ANY WARRANTY; without even the implied warranty of
9: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Further, any
10: // license provided herein, whether implied or otherwise, is limited to
11: // this program in accordance with the express provisions of the GNU
12: // General Public License. Patent licenses, if any, provided herein do not
13: // apply to combinations of this program with other product or programs, or
14: // any other product whatsoever. This program is distributed without any
15: // warranty that the program is delivered free of the rightful claim of any
16: // third person by way of infringement or the like. See the GNU General
17: // Public License for more details.
18: //
19: // You should have received a copy of the GNU General Public License along
20: // with this program; if not, write the Free Software Foundation, Inc., 59
21: // Temple Place - Suite 330, Boston MA 02111-1307, USA.
22:
23: #include "Scheduler.h"
24:
25: #include <assert.h>
26: #include <string.h>
27: #include <errno.h>
28: #include <unistd.h>
29: #include <stdlib.h>
30: #include <sys/param.h>
31:
32: #include "Log.h"
33: #include "timeval.h"
34:
35: // Define a bunch of class-global variables.
36:
37: Scheduler::IOTypeInfo Scheduler::read(&FDInfo::read);
38: Scheduler::IOTypeInfo Scheduler::write(&FDInfo::write);
39: unsigned int Scheduler::nfds;
40: Scheduler::FDInfo *Scheduler::fdinfo;
41: unsigned int Scheduler::nfdinfo_alloc;
42:
43: unsigned int Scheduler::ntasks;
44: Scheduler::TimedProc Scheduler::recurring_proc;
45: void *Scheduler::recurring_closure;
46: timeval Scheduler::next_task_time;
47: timeval Scheduler::recurring_interval;
48: Scheduler::onetime_task *Scheduler::first_task;
49: bool Scheduler::running;
50:
51:
52: //////////////////////////////////////////////////////////////////////////////
53: // One time task code
54:
55: void
56: Scheduler::install_onetime_task(const timeval& when,
57: TimedProc proc, void *closure)
58: {
59: onetime_task **fp = &first_task;
60: while (*fp && (*fp)->when < when)
61: fp = &(*fp)->next;
62: onetime_task *nt = new onetime_task;
63: nt->next = *fp;
64: *fp = nt;
65: nt->when = when;
66: nt->proc = proc;
67: nt->closure = closure;
68: ntasks++;
69: }
70:
71: void
72: Scheduler::remove_onetime_task(TimedProc proc, void *closure)
73: {
74: onetime_task *p, **pp = &first_task;
75: while ((p = *pp) != NULL)
76: {
77: if (p->proc == proc && p->closure == closure)
78: { *pp = p->next;
79: delete p;
80: ntasks--;
81: break;
82: }
83: pp = &p->next;
84: }
85: }
86:
87: //////////////////////////////////////////////////////////////////////////////
88: // Recurring task code
89:
90: void
91: Scheduler::install_recurring_task(const timeval& interval,
92: TimedProc proc, void *closure)
93: {
94: timeval now;
95:
96: assert(!recurring_proc);
97: assert(proc);
98: assert(interval.tv_sec >= 0);
99: assert(interval.tv_usec >= 0 && interval.tv_usec < 1000000);
100: assert(interval.tv_sec || interval.tv_usec);
101:
102: recurring_proc = proc;
103: recurring_closure = closure;
104: recurring_interval = interval;
105: ntasks++;
106: (void) gettimeofday(&now, NULL);
107: next_task_time = now + interval;
108: }
109:
110: void
111: Scheduler::remove_recurring_task(TimedProc proc, void *closure)
112: {
113: assert(proc == recurring_proc);
114: assert(closure == recurring_closure);
115:
116: recurring_proc = NULL;
117: recurring_closure = closure;
118: timerclear(&recurring_interval);
119: ntasks--;
120: }
121:
122: // do_tasks activates all timer based tasks. It also sets
123: // next_task_time to the absolute time when it should next be
124: // invoked.
125:
126: void
127: Scheduler::do_tasks()
128: {
129: if (ntasks)
130: {
131: timeval now;
132: (void) gettimeofday(&now, NULL);
133: if (recurring_proc && now >= next_task_time)
134: {
135: // Time for the next task.
136:
137: (*recurring_proc)(recurring_closure);
138: next_task_time += recurring_interval;
139:
140: // If the clock has jumped ahead or we've gotten too far behind,
141: // postpone the next task.
142:
143: if (next_task_time < now)
144: next_task_time = now + recurring_interval;
145: }
146: else
147: { timeval time_left = next_task_time - now;
148: if (recurring_interval < time_left)
149: {
150: // More time left than we started with -- clock must have
151: // run backward. Reset next_task_time to sane value.
152:
153: next_task_time = now + recurring_interval;
154: }
155: }
156:
157: while (first_task && first_task->when < now)
158: { TimedProc proc = first_task->proc;
159: void *closure = first_task->closure;
160: remove_onetime_task(proc, closure);
161: (*proc)(closure);
162: }
163: }
164: }
165:
166: // calc_timeout calculates the timeout to pass to select().
167: // It returns:
168: // NULL (if no timed tasks exist)
169: // zero time (if it's time for the next task)
170: // nonzero time (if it's not time yet)
171:
172: timeval *
173: Scheduler::calc_timeout()
174: {
175: static timeval sleep_interval;
176:
177: if (ntasks)
178: {
179: timeval wake_time;
180: if (recurring_proc)
181: wake_time = next_task_time;
182: if (!recurring_proc || first_task && first_task->when < wake_time)
183: wake_time = first_task->when;
184: timeval now;
185: (void) gettimeofday(&now, NULL);
186: sleep_interval = wake_time - now;
187: if (sleep_interval.tv_sec < 0)
188: timerclear(&sleep_interval);
189: return &sleep_interval;
190: }
191: else
192: return NULL;
193: }
194:
195: //////////////////////////////////////////////////////////////////////////////
196: // I/O handler code
197:
198: // fd_to_info converts a file descriptor to a pointer into the
199: // fdinfo array. On the way, it verifies that the array has
200: // been allocated far enough out.
201:
202: Scheduler::FDInfo *
203: Scheduler::fd_to_info(int fd)
204: {
205: assert(fd >= 0);
206: if (nfds < fd + 1)
207: {
208: if (nfdinfo_alloc < fd + 1)
209: {
210: unsigned newalloc = nfdinfo_alloc * 3 / 2 + 10;
211: if (newalloc < fd + 1)
212: newalloc = fd + 1;
213: FDInfo *newinfo = new FDInfo[newalloc];
214: for (unsigned i = 0; i < nfds; i++)
215: newinfo[i] = fdinfo[i];
216: delete [] fdinfo;
217: fdinfo = newinfo;
218: nfdinfo_alloc = newalloc;
219: }
220:
221: // Zero all new fdinfo's.
222: memset(&fdinfo[nfds], 0, (fd + 1 - nfds) * sizeof *fdinfo);
223: nfds = fd + 1;
224: }
225: return &fdinfo[fd];
226: }
227:
228: // trim_fdinfo makes the fdinfo array smaller if its last entries
229: // aren't being used. The memory isn't actually freed unless the
230: // array is completely zeroed out.
231:
232: void
233: Scheduler::trim_fdinfo()
234: {
235: for (FDInfo *fp = &fdinfo[nfds - 1]; nfds > 0; --nfds, --fp)
236: if (fp->read.handler || fp->write.handler)
237: break;
238:
239: if (!nfds)
240: { delete [] fdinfo;
241: fdinfo = NULL;
242: nfdinfo_alloc = 0;
243: }
244: }
245:
246: Scheduler::IOHandler
247: Scheduler::install_io_handler(int fd, IOHandler handler, void *closure,
248: IOTypeInfo *iotype)
249: {
250: assert(fd >= 0);
251: assert(handler);
252: FDInfo *fp = fd_to_info(fd);
253: IOHandler old_handler = (fp->*(iotype->iotype)).handler;
254: (fp->*(iotype->iotype)).handler = handler;
255: (fp->*(iotype->iotype)).closure = closure;
256: assert(!old_handler || FD_ISSET(fd, &iotype->fds));
257: if (!FD_ISSET(fd, &iotype->fds))
258: {
259: FD_SET(fd, &iotype->fds);
260: iotype->nbitsset++;
261: }
262: return old_handler;
263: }
264:
265: Scheduler::IOHandler
266: Scheduler::remove_io_handler(int fd, IOTypeInfo *iotype)
267: {
268: assert(fd >= 0 && fd < nfds);
269: FDInfo *fp = fd_to_info(fd);
270: IOHandler old_handler = (fp->*(iotype->iotype)).handler;
271: (fp->*(iotype->iotype)).handler = NULL;
272: (fp->*(iotype->iotype)).closure = NULL;
273: trim_fdinfo();
274: assert(old_handler);
275: if (FD_ISSET(fd, &iotype->fds))
276: {
277: FD_CLR(fd, &iotype->fds);
278: iotype->nbitsset--;
279: }
280: return old_handler;
281: }
282:
283: Scheduler::IOHandler
284: Scheduler::install_read_handler(int fd, IOHandler handler, void *closure)
285: {
286: return install_io_handler(fd, handler, closure, &read);
287: }
288:
289: Scheduler::IOHandler
290: Scheduler::remove_read_handler(int fd)
291: {
292: return remove_io_handler(fd, &read);
293: }
294:
295: Scheduler::IOHandler
296: Scheduler::install_write_handler(int fd, IOHandler handler, void *closure)
297: {
298: return install_io_handler(fd, handler, closure, &write);
299: }
300:
301: Scheduler::IOHandler
302: Scheduler::remove_write_handler(int fd)
303: {
304: return remove_io_handler(fd, &write);
305: }
306:
307: void
308: Scheduler::handle_io(const fd_set *fds, FDInfo::FDIOHandler FDInfo::* iotype)
309: {
310: if (fds)
311: for (int fd = 0; fd < nfds; fd++)
312: if (FD_ISSET(fd, fds))
313: { FDInfo *fp = &fdinfo[fd];
314: assert(iotype == &FDInfo::read || iotype == &FDInfo::write);
315: (fp->*iotype).handler(fd, (fp->*iotype).closure);
316: // Remember, handler may move fdinfo array.
317: }
318: }
319:
320: // Scheduling priorities defined here: writable descriptors have
321: // highest priority, followed by exceptionable descriptors, then
322: // readable descriptors, then timed tasks have the lowest priority.
323:
324: void
325: Scheduler::select()
326: {
327: fd_set readfds, writefds;
328: readfds = Scheduler::read.fds;
329: writefds = Scheduler::write.fds;
330: timeval *timeout = calc_timeout();
331:
332: int status = ::select(nfds, &readfds, &writefds, 0, timeout);
333:
334: if (status == -1 && errno != EINTR)
335: { Log::perror("select"); // Oh, no!
336: ::exit(1);
337: }
338: if (status > 0)
339: {
340: // I/O is ready -- find it and do it.
341:
342: handle_io( &writefds, &FDInfo::write );
343: handle_io( &readfds, &FDInfo::read );
344: }
345:
346: // Check for tasks now.
347:
348: do_tasks();
349: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>