1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 32 /* ********************************************************************** */ 33 /* * General-Purpose Event List Manager * */ 34 /* ********************************************************************** */ 35 /* 36 * description: These routines maintain a time-ordered list of events. 37 * functions available: 38 * init : Creates and initializes the data structure. 39 * See the reference for parameters to init. 40 * add(&event, time, id) : Adds an event to the list. 41 * Returns: 0 if success, 42 * -2 if event time is lower 43 * than Lower Bound time LB 44 * -1 else 45 * remove(id) : Removes events (with appropriate id). 46 * empty : Returns true if the list is empty, false otherwise. 47 * first : Removes the element at the head of the list. 48 * Returns a pointer to the event. 49 * delete : Frees up all allocated storage associated 50 * with the event list. 51 * reference: Franta, W. R. and Maly, K., 52 * "An efficient data structure for the 53 * simulation event set ", CACM Vol. 20(8), 54 * Aug 1977, pp. 596-602. 55 * machine dependant: the constant INFINITY 56 */ 57 /* ********************************************************************** */ 58 59 60 #include <sys/types.h> 61 #include <stdlib.h> 62 63 extern void *xmalloc(size_t); 64 65 #define INFINITY 2147483647L /* upper bound on time */ 66 #define TRUE 1 67 #define FALSE 0 68 69 /* the following parameters are set in init */ 70 static int DU; /* number of time intervals */ 71 static time_t LB; /* lower bound on time */ 72 static time_t DT; /* width of interval */ 73 static int NLIM; /* max notices per sublist */ 74 75 /* 76 * a notice points to an event. a notice has the following fields: 77 * time = time of the event. 78 * id = identifier for an event or class of events that may need 79 * to be removed (other than at the front of the list). 80 * event = pointer to the event. 81 * isdummy = tells whether this notice points to a real event or 82 * is just a dummy notice (one that is used to "mark off" 83 * the time intervals that the user specifies in init). 84 * key = points back to the key that points to this notice, 85 * if there is one. 86 * left = points to the notice immediately preceding this one. 87 * right = points to the notice immediately following this one. 88 */ 89 struct notice { time_t time; 90 int id; 91 void *event; 92 short int isdummy; 93 struct key *key; 94 struct notice *left; 95 struct notice *right; }; 96 97 /* current points to the front of the list of notices (events) */ 98 struct notice *current = NULL; 99 100 /* 101 * a key points to a sublist of notices. a key has the following fields: 102 * time = max time of notices in sublist. 103 * numnote = number of notices in sublist. 104 * notice = pointer to the notice with max time. 105 * left = points to the key immediately preceding this one. 106 * right = points to the key immediately following this one. 107 */ 108 struct key { time_t time; 109 int numnote; 110 struct notice *notice; 111 struct key *left; 112 struct key *right; }; 113 114 /* 115 * the index list breaks the keys into time intervals as specified in init. 116 * the index is "shifted" one time interval whenever el_first returns an 117 * event with a time greater than the max time of the first interval 118 * (eg. with intervals of a day which span one week (MTWTFSS), 119 * if el_first finds the next event is on tuesday, then 120 * the intervals of the event list get shifted (TWTFSSM). 121 */ 122 struct index { struct key *key; 123 struct index *right; }; 124 125 /* index pts to the front of the index list */ 126 static struct index *index = NULL; 127 128 /* ******************* */ 129 void 130 el_init(du, lb, dt, nlim) 131 /* ******************* */ 132 int du, nlim; 133 time_t lb, dt; 134 { 135 int i; 136 time_t t; 137 struct index *indprev, *ind; 138 struct key *kprev, *k; 139 struct notice *nprev, *n; 140 141 if ((du < 1) || (dt < 1) || (nlim < 1)) 142 return; 143 DU = du + 1; 144 LB = lb; 145 DT = dt; 146 NLIM = nlim; 147 148 /* 149 * initialize index, keys, and notices 150 */ 151 152 /* create first dummy notice */ 153 n = (struct notice *)xmalloc(sizeof (struct notice)); 154 n->time = LB; 155 n->isdummy = TRUE; 156 n->left = NULL; 157 nprev = n; 158 /* create first dummy key */ 159 k = (struct key *)xmalloc(sizeof (struct key)); 160 k->time = LB; 161 k->numnote = 1; 162 k->notice = n; 163 k->left = NULL; 164 kprev = k; 165 /* make notice point to key */ 166 n->key = k; 167 /* no index element to allocate this time */ 168 indprev = NULL; 169 /* create dummy notices, dummy keys, and index elements */ 170 t = LB; 171 for (i = 1; i < DU; i++) { 172 t = t + DT; 173 n = (struct notice *)xmalloc(sizeof (struct notice)); 174 n->time = t; 175 n->isdummy = TRUE; 176 n->left = nprev; 177 nprev->right = n; 178 nprev = n; 179 k = (struct key *)xmalloc(sizeof (struct key)); 180 k->time = t; 181 k->numnote = 1; 182 k->notice = n; 183 k->left = kprev; 184 kprev->right = k; 185 kprev = k; 186 n->key = k; 187 ind = (struct index *)xmalloc(sizeof (struct index)); 188 ind->key = k; 189 if (indprev == NULL) 190 index = ind; 191 else 192 indprev->right = ind; 193 indprev = ind; } 194 /* create last dummy notice */ 195 n = (struct notice *)xmalloc(sizeof (struct notice)); 196 n->time = INFINITY; 197 n->isdummy = TRUE; 198 n->left = nprev; 199 n->right = NULL; 200 nprev->right = n; 201 /* create last dummy key */ 202 k = (struct key *)xmalloc(sizeof (struct key)); 203 k->time = INFINITY; 204 k->numnote = 1; 205 k->notice = n; 206 k->left = kprev; 207 k->right = NULL; 208 kprev->right = k; 209 n->key = k; 210 /* create last index element */ 211 ind = (struct index *)xmalloc(sizeof (struct index)); 212 ind->key = k; 213 ind->right = NULL; 214 indprev->right = ind; 215 216 current = NULL; 217 } 218 219 220 /* ********************** */ 221 int 222 el_add(event, time, id) 223 /* ********************** */ 224 void *event; 225 int id; 226 time_t time; 227 { 228 /* 229 * add works slightly differently than in the reference. if the 230 * sublist to be inserted into is full (numnote = NLIM), 231 * the sublist is split in half. thus the size of the sublists 232 * in this implementation normally ranges from NLIM/2 to NLIM. 233 */ 234 235 struct index *ind; 236 struct key *k, *k2; 237 struct notice *n, *n2; 238 int i; 239 240 /* 241 * time may be 0 when set by next_time() on error or an 242 * invalid time specification of job 243 */ 244 if ((index == NULL) || (time <= 0)) { 245 return (-1); 246 } 247 if (time < LB) { 248 return (-2); 249 } 250 251 /* allocate new notice */ 252 n = (struct notice *)xmalloc(sizeof (struct notice)); 253 n->time = time; 254 n->id = id; 255 n->event = event; 256 n->isdummy = FALSE; 257 n->key = NULL; 258 259 /* find the right interval */ 260 ind = index; 261 while ((ind->key)->time <= time) ind = ind->right; 262 263 /* find the right key */ 264 k = (ind->key)->left; 265 while (k->time > time) k = k->left; 266 k = k->right; 267 268 /* (k->time>time) and ((k->left)->time<=time) */ 269 if (k->numnote == NLIM) { 270 /* k's sublist is full, so split it */ 271 k->numnote = NLIM / 2; 272 n2 = k->notice; 273 for (i = 1; i <= NLIM/2; i++) n2 = n2->left; 274 /* create a key which will point to notice n2 */ 275 k2 = (struct key *)xmalloc(sizeof (struct key)); 276 k2->time = n2->time; 277 k2->numnote = NLIM - NLIM/2; 278 k2->notice = n2; 279 k2->right = k; 280 k2->left = k->left; 281 k->left = k2; 282 (k2->left)->right = k2; 283 n2->key = k2; /* have n2 point back to k2 */ 284 /* which of the new sublists will hold the new notice? */ 285 if (k2->time > time) k = k2; } 286 287 /* 288 * the new notice n is ready to be inserted 289 * k points to the appropriate sublist 290 */ 291 k->numnote = k->numnote + 1; 292 n2 = k->notice; 293 while (n2->time > time) n2 = n2->left; 294 n->right = n2->right; 295 n->left = n2; 296 (n2->right)->left = n; 297 n2->right = n; 298 299 if ((current == NULL) || (current->time > time)) 300 current = n; 301 302 return (0); 303 } 304 305 306 /* ******************** */ 307 void 308 el_remove(id, flag) 309 /* ******************** */ 310 int id, flag; 311 { 312 /* 313 * remove finds notices n that need to be removed by traversing thru 314 * the notice list. if n is the sole element of a sublist, the 315 * sublist is deleted. if not, an adjacent sublist is merged with 316 * n's sublist, if that is possible. after these checks, n is removed. 317 */ 318 319 struct notice *n, *n2; 320 struct key *k, *kl, *kr; 321 322 if ((index == NULL) || (current == NULL)) 323 return; 324 325 n = current; 326 while (n != NULL) { 327 while ((n != NULL) && ((n->isdummy) || (n->id != id))) 328 n = n->right; 329 if (n != NULL) { 330 /* n should be deleted */ 331 if ((n->key != NULL) && ((n->key)->numnote == 1)) { 332 /* n = sole element of a sublist */ 333 k = n->key; 334 (k->left)->right = k->right; 335 (k->right)->left = k->left; 336 free(k); 337 } else { if (n->key != NULL) { 338 /* n has a key pointing to it */ 339 (n->left)->key = n->key; 340 (n->key)->time = (n->left)->time; 341 (n->key)->notice = n->left; } 342 /* find the key that points to this sublist */ 343 n2 = n; 344 while (n2->key == NULL) n2 = n2->right; 345 k = n2->key; 346 k->numnote = k->numnote - 1; 347 /* 348 * check if two adjacent sublists can be merged 349 * first check left, then check right 350 */ 351 kl = k->left; 352 kr = k->right; 353 if ((!(kl->notice)->isdummy) && 354 ((kl->numnote+k->numnote) <= NLIM)) { 355 /* delete the key to the left */ 356 (kl->notice)->key = NULL; 357 k->numnote += kl->numnote; 358 (kl->left)->right = k; 359 k->left = kl->left; 360 free(kl); 361 } else if ((!(k->notice)->isdummy) && 362 ((kr->numnote+k->numnote) 363 <= NLIM)) { 364 /* delete this key */ 365 (k->notice)->key = NULL; 366 kr->numnote += k->numnote; 367 (k->left)->right = kr; 368 kr->left = k->left; 369 free(k); } 370 } 371 /* delete n, then advance n down the list */ 372 (n->left)->right = n->right; 373 (n->right)->left = n->left; 374 n2 = n->right; 375 free(n); 376 n = n2; 377 } 378 if (flag) break; 379 } 380 /* now reset current */ 381 k = (index->key)->left; 382 while (k->left != NULL) k = k->left; 383 n = (k->notice)->right; 384 while ((n != NULL) && (n->isdummy)) n = n->right; 385 current = n; 386 } 387 388 389 /* ********************* */ 390 int 391 el_empty(void) 392 /* ********************* */ 393 { 394 if (current == NULL) 395 return (1); 396 else 397 return (0); 398 } 399 400 401 /* ********************* */ 402 void * 403 el_first(void) 404 /* ********************* */ 405 { 406 struct notice *n, *fn; 407 struct key *k, *fk; 408 struct index *ind, *fi; 409 int ctr, *val; 410 time_t next_int; 411 412 if ((index == NULL) || (current == NULL)) 413 return (NULL); 414 415 while ((index->key)->time < current->time) { 416 if (DU == 2) { 417 /* only two intervals, so relabel first one */ 418 k = index->key; 419 k->time += DT; 420 (k->notice)->time += DT; 421 continue; } 422 /* 423 * remove the notice, key, and index corresponding 424 * to the first time interval. Then split the 425 * overflow interval into a normal interval 426 * plus an overflow interval. 427 */ 428 fi = index; 429 fk = fi->key; 430 fn = fk->notice; 431 (fn->left)->right = fn->right; 432 (fn->right)->left = fn->left; 433 (fk->left)->right = fk->right; 434 (fk->right)->left = fk->left; 435 index = index->right; 436 /* find where to split */ 437 ind = index; 438 while ((ind->right)->right != NULL) ind = ind->right; 439 /* ind points to the next to last index interval */ 440 k = ind->key; 441 next_int = k->time + DT; /* upper bound on new inter. */ 442 while (k->time < next_int) k = k->right; 443 /* k points to the appropriate sublist of notices */ 444 n = (k->notice)->left; 445 ctr = 1; 446 while (n->time >= next_int) { 447 ctr++; 448 n = n->left; } 449 n = n->right; 450 /* 451 * n points to first notice of the new overflow interval 452 * ctr tells how many notices are in the first sublist 453 * of the new overflow interval 454 * insert the new index element 455 */ 456 fi->right = ind->right; 457 ind->right = fi; 458 /* insert the new dummy key */ 459 fk->time = next_int; 460 fk->numnote = k->numnote - ctr + 1; 461 fk->left = k->left; 462 fk->right = k; 463 (k->left)->right = fk; 464 k->left = fk; 465 k->numnote = ctr; 466 /* insert the new dummy notice */ 467 fn->time = next_int; 468 fn->left = n->left; 469 fn->right = n; 470 (n->left)->right = fn; 471 n->left = fn; } 472 473 /* remove the first element of the list */ 474 (current->left)->right = current->right; 475 (current->right)->left = current->left; 476 /* now update the numnote field in the appropriate key */ 477 n = current; 478 while (n->key == NULL) n = n->right; 479 k = n->key; 480 k->numnote = k->numnote - 1; 481 /* if numnote = 0 then this key must be removed */ 482 if (k->numnote == 0) { 483 (k->left)->right = k->right; 484 (k->right)->left = k->left; 485 free(k); } 486 487 /* now set current to be the head of the list */ 488 fn = current->right; 489 while ((fn != NULL) && (fn->isdummy)) 490 fn = fn->right; 491 val = current->event; 492 free(current); 493 current = fn; 494 495 return (val); 496 } 497 498 499 /* ************** */ 500 void 501 el_delete(void) 502 /* ************** */ 503 { 504 /* el_delete frees up all the space associated with the event list */ 505 506 struct index *ind, *ind2; 507 struct key *k, *k2; 508 struct notice *n, *n2; 509 510 if (index == NULL) 511 return; 512 ind = index; 513 k = ind->key; 514 while (k->left != NULL) k = k->left; 515 n = k->notice; 516 while (n != NULL) { 517 n2 = n->right; 518 free(n); 519 n = n2; } 520 while (k != NULL) { 521 k2 = k->right; 522 free(k); 523 k = k2; } 524 while (ind != NULL) { 525 ind2 = ind->right; 526 free(ind); 527 ind = ind2; } 528 529 index = NULL; 530 current = NULL; 531 } 532