/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* ********************************************************************** */ /* * General-Purpose Event List Manager * */ /* ********************************************************************** */ /* * description: These routines maintain a time-ordered list of events. * functions available: * init : Creates and initializes the data structure. * See the reference for parameters to init. * add(&event, time, id) : Adds an event to the list. * Returns: 0 if success, * -2 if event time is lower * than Lower Bound time LB * -1 else * remove(id) : Removes events (with appropriate id). * empty : Returns true if the list is empty, false otherwise. * first : Removes the element at the head of the list. * Returns a pointer to the event. * delete : Frees up all allocated storage associated * with the event list. * reference: Franta, W. R. and Maly, K., * "An efficient data structure for the * simulation event set ", CACM Vol. 20(8), * Aug 1977, pp. 596-602. * machine dependant: the constant INFINITY */ /* ********************************************************************** */ #include #include extern void *xmalloc(size_t); #define INFINITY 2147483647L /* upper bound on time */ #define TRUE 1 #define FALSE 0 /* the following parameters are set in init */ static int DU; /* number of time intervals */ static time_t LB; /* lower bound on time */ static time_t DT; /* width of interval */ static int NLIM; /* max notices per sublist */ /* * a notice points to an event. a notice has the following fields: * time = time of the event. * id = identifier for an event or class of events that may need * to be removed (other than at the front of the list). * event = pointer to the event. * isdummy = tells whether this notice points to a real event or * is just a dummy notice (one that is used to "mark off" * the time intervals that the user specifies in init). * key = points back to the key that points to this notice, * if there is one. * left = points to the notice immediately preceding this one. * right = points to the notice immediately following this one. */ struct notice { time_t time; int id; void *event; short int isdummy; struct key *key; struct notice *left; struct notice *right; }; /* current points to the front of the list of notices (events) */ struct notice *current = NULL; /* * a key points to a sublist of notices. a key has the following fields: * time = max time of notices in sublist. * numnote = number of notices in sublist. * notice = pointer to the notice with max time. * left = points to the key immediately preceding this one. * right = points to the key immediately following this one. */ struct key { time_t time; int numnote; struct notice *notice; struct key *left; struct key *right; }; /* * the index list breaks the keys into time intervals as specified in init. * the index is "shifted" one time interval whenever el_first returns an * event with a time greater than the max time of the first interval * (eg. with intervals of a day which span one week (MTWTFSS), * if el_first finds the next event is on tuesday, then * the intervals of the event list get shifted (TWTFSSM). */ struct index { struct key *key; struct index *right; }; /* index pts to the front of the index list */ static struct index *index = NULL; /* ******************* */ void el_init(du, lb, dt, nlim) /* ******************* */ int du, nlim; time_t lb, dt; { int i; time_t t; struct index *indprev, *ind; struct key *kprev, *k; struct notice *nprev, *n; if ((du < 1) || (dt < 1) || (nlim < 1)) return; DU = du + 1; LB = lb; DT = dt; NLIM = nlim; /* * initialize index, keys, and notices */ /* create first dummy notice */ n = (struct notice *)xmalloc(sizeof (struct notice)); n->time = LB; n->isdummy = TRUE; n->left = NULL; nprev = n; /* create first dummy key */ k = (struct key *)xmalloc(sizeof (struct key)); k->time = LB; k->numnote = 1; k->notice = n; k->left = NULL; kprev = k; /* make notice point to key */ n->key = k; /* no index element to allocate this time */ indprev = NULL; /* create dummy notices, dummy keys, and index elements */ t = LB; for (i = 1; i < DU; i++) { t = t + DT; n = (struct notice *)xmalloc(sizeof (struct notice)); n->time = t; n->isdummy = TRUE; n->left = nprev; nprev->right = n; nprev = n; k = (struct key *)xmalloc(sizeof (struct key)); k->time = t; k->numnote = 1; k->notice = n; k->left = kprev; kprev->right = k; kprev = k; n->key = k; ind = (struct index *)xmalloc(sizeof (struct index)); ind->key = k; if (indprev == NULL) index = ind; else indprev->right = ind; indprev = ind; } /* create last dummy notice */ n = (struct notice *)xmalloc(sizeof (struct notice)); n->time = INFINITY; n->isdummy = TRUE; n->left = nprev; n->right = NULL; nprev->right = n; /* create last dummy key */ k = (struct key *)xmalloc(sizeof (struct key)); k->time = INFINITY; k->numnote = 1; k->notice = n; k->left = kprev; k->right = NULL; kprev->right = k; n->key = k; /* create last index element */ ind = (struct index *)xmalloc(sizeof (struct index)); ind->key = k; ind->right = NULL; indprev->right = ind; current = NULL; } /* ********************** */ int el_add(event, time, id) /* ********************** */ void *event; int id; time_t time; { /* * add works slightly differently than in the reference. if the * sublist to be inserted into is full (numnote = NLIM), * the sublist is split in half. thus the size of the sublists * in this implementation normally ranges from NLIM/2 to NLIM. */ struct index *ind; struct key *k, *k2; struct notice *n, *n2; int i; /* * time may be 0 when set by next_time() on error or an * invalid time specification of job */ if ((index == NULL) || (time <= 0)) { return (-1); } if (time < LB) { return (-2); } /* allocate new notice */ n = (struct notice *)xmalloc(sizeof (struct notice)); n->time = time; n->id = id; n->event = event; n->isdummy = FALSE; n->key = NULL; /* find the right interval */ ind = index; while ((ind->key)->time <= time) ind = ind->right; /* find the right key */ k = (ind->key)->left; while (k->time > time) k = k->left; k = k->right; /* (k->time>time) and ((k->left)->time<=time) */ if (k->numnote == NLIM) { /* k's sublist is full, so split it */ k->numnote = NLIM / 2; n2 = k->notice; for (i = 1; i <= NLIM/2; i++) n2 = n2->left; /* create a key which will point to notice n2 */ k2 = (struct key *)xmalloc(sizeof (struct key)); k2->time = n2->time; k2->numnote = NLIM - NLIM/2; k2->notice = n2; k2->right = k; k2->left = k->left; k->left = k2; (k2->left)->right = k2; n2->key = k2; /* have n2 point back to k2 */ /* which of the new sublists will hold the new notice? */ if (k2->time > time) k = k2; } /* * the new notice n is ready to be inserted * k points to the appropriate sublist */ k->numnote = k->numnote + 1; n2 = k->notice; while (n2->time > time) n2 = n2->left; n->right = n2->right; n->left = n2; (n2->right)->left = n; n2->right = n; if ((current == NULL) || (current->time > time)) current = n; return (0); } /* ******************** */ void el_remove(id, flag) /* ******************** */ int id, flag; { /* * remove finds notices n that need to be removed by traversing thru * the notice list. if n is the sole element of a sublist, the * sublist is deleted. if not, an adjacent sublist is merged with * n's sublist, if that is possible. after these checks, n is removed. */ struct notice *n, *n2; struct key *k, *kl, *kr; if ((index == NULL) || (current == NULL)) return; n = current; while (n != NULL) { while ((n != NULL) && ((n->isdummy) || (n->id != id))) n = n->right; if (n != NULL) { /* n should be deleted */ if ((n->key != NULL) && ((n->key)->numnote == 1)) { /* n = sole element of a sublist */ k = n->key; (k->left)->right = k->right; (k->right)->left = k->left; free(k); } else { if (n->key != NULL) { /* n has a key pointing to it */ (n->left)->key = n->key; (n->key)->time = (n->left)->time; (n->key)->notice = n->left; } /* find the key that points to this sublist */ n2 = n; while (n2->key == NULL) n2 = n2->right; k = n2->key; k->numnote = k->numnote - 1; /* * check if two adjacent sublists can be merged * first check left, then check right */ kl = k->left; kr = k->right; if ((!(kl->notice)->isdummy) && ((kl->numnote+k->numnote) <= NLIM)) { /* delete the key to the left */ (kl->notice)->key = NULL; k->numnote += kl->numnote; (kl->left)->right = k; k->left = kl->left; free(kl); } else if ((!(k->notice)->isdummy) && ((kr->numnote+k->numnote) <= NLIM)) { /* delete this key */ (k->notice)->key = NULL; kr->numnote += k->numnote; (k->left)->right = kr; kr->left = k->left; free(k); } } /* delete n, then advance n down the list */ (n->left)->right = n->right; (n->right)->left = n->left; n2 = n->right; free(n); n = n2; } if (flag) break; } /* now reset current */ k = (index->key)->left; while (k->left != NULL) k = k->left; n = (k->notice)->right; while ((n != NULL) && (n->isdummy)) n = n->right; current = n; } /* ********************* */ int el_empty(void) /* ********************* */ { if (current == NULL) return (1); else return (0); } /* ********************* */ void * el_first(void) /* ********************* */ { struct notice *n, *fn; struct key *k, *fk; struct index *ind, *fi; int ctr, *val; time_t next_int; if ((index == NULL) || (current == NULL)) return (NULL); while ((index->key)->time < current->time) { if (DU == 2) { /* only two intervals, so relabel first one */ k = index->key; k->time += DT; (k->notice)->time += DT; continue; } /* * remove the notice, key, and index corresponding * to the first time interval. Then split the * overflow interval into a normal interval * plus an overflow interval. */ fi = index; fk = fi->key; fn = fk->notice; (fn->left)->right = fn->right; (fn->right)->left = fn->left; (fk->left)->right = fk->right; (fk->right)->left = fk->left; index = index->right; /* find where to split */ ind = index; while ((ind->right)->right != NULL) ind = ind->right; /* ind points to the next to last index interval */ k = ind->key; next_int = k->time + DT; /* upper bound on new inter. */ while (k->time < next_int) k = k->right; /* k points to the appropriate sublist of notices */ n = (k->notice)->left; ctr = 1; while (n->time >= next_int) { ctr++; n = n->left; } n = n->right; /* * n points to first notice of the new overflow interval * ctr tells how many notices are in the first sublist * of the new overflow interval * insert the new index element */ fi->right = ind->right; ind->right = fi; /* insert the new dummy key */ fk->time = next_int; fk->numnote = k->numnote - ctr + 1; fk->left = k->left; fk->right = k; (k->left)->right = fk; k->left = fk; k->numnote = ctr; /* insert the new dummy notice */ fn->time = next_int; fn->left = n->left; fn->right = n; (n->left)->right = fn; n->left = fn; } /* remove the first element of the list */ (current->left)->right = current->right; (current->right)->left = current->left; /* now update the numnote field in the appropriate key */ n = current; while (n->key == NULL) n = n->right; k = n->key; k->numnote = k->numnote - 1; /* if numnote = 0 then this key must be removed */ if (k->numnote == 0) { (k->left)->right = k->right; (k->right)->left = k->left; free(k); } /* now set current to be the head of the list */ fn = current->right; while ((fn != NULL) && (fn->isdummy)) fn = fn->right; val = current->event; free(current); current = fn; return (val); } /* ************** */ void el_delete(void) /* ************** */ { /* el_delete frees up all the space associated with the event list */ struct index *ind, *ind2; struct key *k, *k2; struct notice *n, *n2; if (index == NULL) return; ind = index; k = ind->key; while (k->left != NULL) k = k->left; n = k->notice; while (n != NULL) { n2 = n->right; free(n); n = n2; } while (k != NULL) { k2 = k->right; free(k); k = k2; } while (ind != NULL) { ind2 = ind->right; free(ind); ind = ind2; } index = NULL; current = NULL; }