Annotation of fam/fam/Scheduler.c++, Revision 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>