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 NULL 0 /* a null pointer */ 67 #define TRUE 1 68 #define FALSE 0 69 70 /* the following parameters are set in init */ 71 static int DU; /* number of time intervals */ 72 static time_t LB; /* lower bound on time */ 73 static time_t DT; /* width of interval */ 74 static int NLIM; /* max notices per sublist */ 75 76 /* 77 * a notice points to an event. a notice has the following fields: 78 * time = time of the event. 79 * id = identifier for an event or class of events that may need 80 * to be removed (other than at the front of the list). 81 * event = pointer to the event. 82 * isdummy = tells whether this notice points to a real event or 83 * is just a dummy notice (one that is used to "mark off" 84 * the time intervals that the user specifies in init). 85 * key = points back to the key that points to this notice, 86 * if there is one. 87 * left = points to the notice immediately preceding this one. 88 * right = points to the notice immediately following this one. 89 */ 90 struct notice { time_t time; 91 int id; 92 void *event; 93 short int isdummy; 94 struct key *key; 95 struct notice *left; 96 struct notice *right; }; 97 98 /* current points to the front of the list of notices (events) */ 99 struct notice *current = NULL; 100 101 /* 102 * a key points to a sublist of notices. a key has the following fields: 103 * time = max time of notices in sublist. 104 * numnote = number of notices in sublist. 105 * notice = pointer to the notice with max time. 106 * left = points to the key immediately preceding this one. 107 * right = points to the key immediately following this one. 108 */ 109 struct key { time_t time; 110 int numnote; 111 struct notice *notice; 112 struct key *left; 113 struct key *right; }; 114 115 /* 116 * the index list breaks the keys into time intervals as specified in init. 117 * the index is "shifted" one time interval whenever el_first returns an 118 * event with a time greater than the max time of the first interval 119 * (eg. with intervals of a day which span one week (MTWTFSS), 120 * if el_first finds the next event is on tuesday, then 121 * the intervals of the event list get shifted (TWTFSSM). 122 */ 123 struct index { struct key *key; 124 struct index *right; }; 125 126 /* index pts to the front of the index list */ 127 static struct index *index = NULL; 128 129 /* ******************* */ 130 void 131 el_init(du, lb, dt, nlim) 132 /* ******************* */ 133 int du, nlim; 134 time_t lb, dt; 135 { 136 int i; 137 time_t t; 138 struct index *indprev, *ind; 139 struct key *kprev, *k; 140 struct notice *nprev, *n; 141 142 if ((du < 1) || (dt < 1) || (nlim < 1)) 143 return; 144 DU = du + 1; 145 LB = lb; 146 DT = dt; 147 NLIM = nlim; 148 149 /* 150 * initialize index, keys, and notices 151 */ 152 153 /* create first dummy notice */ 154 n = (struct notice *)xmalloc(sizeof (struct notice)); 155 n->time = LB; 156 n->isdummy = TRUE; 157 n->left = NULL; 158 nprev = n; 159 /* create first dummy key */ 160 k = (struct key *)xmalloc(sizeof (struct key)); 161 k->time = LB; 162 k->numnote = 1; 163 k->notice = n; 164 k->left = NULL; 165 kprev = k; 166 /* make notice point to key */ 167 n->key = k; 168 /* no index element to allocate this time */ 169 indprev = NULL; 170 /* create dummy notices, dummy keys, and index elements */ 171 t = LB; 172 for (i = 1; i < DU; i++) { 173 t = t + DT; 174 n = (struct notice *)xmalloc(sizeof (struct notice)); 175 n->time = t; 176 n->isdummy = TRUE; 177 n->left = nprev; 178 nprev->right = n; 179 nprev = n; 180 k = (struct key *)xmalloc(sizeof (struct key)); 181 k->time = t; 182 k->numnote = 1; 183 k->notice = n; 184 k->left = kprev; 185 kprev->right = k; 186 kprev = k; 187 n->key = k; 188 ind = (struct index *)xmalloc(sizeof (struct index)); 189 ind->key = k; 190 if (indprev == NULL) 191 index = ind; 192 else 193 indprev->right = ind; 194 indprev = ind; } 195 /* create last dummy notice */ 196 n = (struct notice *)xmalloc(sizeof (struct notice)); 197 n->time = INFINITY; 198 n->isdummy = TRUE; 199 n->left = nprev; 200 n->right = NULL; 201 nprev->right = n; 202 /* create last dummy key */ 203 k = (struct key *)xmalloc(sizeof (struct key)); 204 k->time = INFINITY; 205 k->numnote = 1; 206 k->notice = n; 207 k->left = kprev; 208 k->right = NULL; 209 kprev->right = k; 210 n->key = k; 211 /* create last index element */ 212 ind = (struct index *)xmalloc(sizeof (struct index)); 213 ind->key = k; 214 ind->right = NULL; 215 indprev->right = ind; 216 217 current = NULL; 218 } 219 220 221 /* ********************** */ 222 int 223 el_add(event, time, id) 224 /* ********************** */ 225 void *event; 226 int id; 227 time_t time; 228 { 229 /* 230 * add works slightly differently than in the reference. if the 231 * sublist to be inserted into is full (numnote = NLIM), 232 * the sublist is split in half. thus the size of the sublists 233 * in this implementation normally ranges from NLIM/2 to NLIM. 234 */ 235 236 struct index *ind; 237 struct key *k, *k2; 238 struct notice *n, *n2; 239 int i; 240 241 /* 242 * time may be 0 when set by next_time() on error or an 243 * invalid time specification of job 244 */ 245 if ((index == NULL) || (time <= 0)) { 246 return (-1); 247 } 248 if (time < LB) { 249 return (-2); 250 } 251 252 /* allocate new notice */ 253 n = (struct notice *)xmalloc(sizeof (struct notice)); 254 n->time = time; 255 n->id = id; 256 n->event = event; 257 n->isdummy = FALSE; 258 n->key = NULL; 259 260 /* find the right interval */ 261 ind = index; 262 while ((ind->key)->time <= time) ind = ind->right; 263 264 /* find the right key */ 265 k = (ind->key)->left; 266 while (k->time > time) k = k->left; 267 k = k->right; 268 269 /* (k->time>time) and ((k->left)->time<=time) */ 270 if (k->numnote == NLIM) { 271 /* k's sublist is full, so split it */ 272 k->numnote = NLIM / 2; 273 n2 = k->notice; 274 for (i = 1; i <= NLIM/2; i++) n2 = n2->left; 275 /* create a key which will point to notice n2 */ 276 k2 = (struct key *)xmalloc(sizeof (struct key)); 277 k2->time = n2->time; 278 k2->numnote = NLIM - NLIM/2; 279 k2->notice = n2; 280 k2->right = k; 281 k2->left = k->left; 282 k->left = k2; 283 (k2->left)->right = k2; 284 n2->key = k2; /* have n2 point back to k2 */ 285 /* which of the new sublists will hold the new notice? */ 286 if (k2->time > time) k = k2; } 287 288 /* 289 * the new notice n is ready to be inserted 290 * k points to the appropriate sublist 291 */ 292 k->numnote = k->numnote + 1; 293 n2 = k->notice; 294 while (n2->time > time) n2 = n2->left; 295 n->right = n2->right; 296 n->left = n2; 297 (n2->right)->left = n; 298 n2->right = n; 299 300 if ((current == NULL) || (current->time > time)) 301 current = n; 302 303 return (0); 304 } 305 306 307 /* ******************** */ 308 void 309 el_remove(id, flag) 310 /* ******************** */ 311 int id, flag; 312 { 313 /* 314 * remove finds notices n that need to be removed by traversing thru 315 * the notice list. if n is the sole element of a sublist, the 316 * sublist is deleted. if not, an adjacent sublist is merged with 317 * n's sublist, if that is possible. after these checks, n is removed. 318 */ 319 320 struct notice *n, *n2; 321 struct key *k, *kl, *kr; 322 323 if ((index == NULL) || (current == NULL)) 324 return; 325 326 n = current; 327 while (n != NULL) { 328 while ((n != NULL) && ((n->isdummy) || (n->id != id))) 329 n = n->right; 330 if (n != NULL) { 331 /* n should be deleted */ 332 if ((n->key != NULL) && ((n->key)->numnote == 1)) { 333 /* n = sole element of a sublist */ 334 k = n->key; 335 (k->left)->right = k->right; 336 (k->right)->left = k->left; 337 free(k); 338 } else { if (n->key != NULL) { 339 /* n has a key pointing to it */ 340 (n->left)->key = n->key; 341 (n->key)->time = (n->left)->time; 342 (n->key)->notice = n->left; } 343 /* find the key that points to this sublist */ 344 n2 = n; 345 while (n2->key == NULL) n2 = n2->right; 346 k = n2->key; 347 k->numnote = k->numnote - 1; 348 /* 349 * check if two adjacent sublists can be merged 350 * first check left, then check right 351 */ 352 kl = k->left; 353 kr = k->right; 354 if ((!(kl->notice)->isdummy) && 355 ((kl->numnote+k->numnote) <= NLIM)) { 356 /* delete the key to the left */ 357 (kl->notice)->key = NULL; 358 k->numnote += kl->numnote; 359 (kl->left)->right = k; 360 k->left = kl->left; 361 free(kl); 362 } else if ((!(k->notice)->isdummy) && 363 ((kr->numnote+k->numnote) 364 <= NLIM)) { 365 /* delete this key */ 366 (k->notice)->key = NULL; 367 kr->numnote += k->numnote; 368 (k->left)->right = kr; 369 kr->left = k->left; 370 free(k); } 371 } 372 /* delete n, then advance n down the list */ 373 (n->left)->right = n->right; 374 (n->right)->left = n->left; 375 n2 = n->right; 376 free(n); 377 n = n2; 378 } 379 if (flag) break; 380 } 381 /* now reset current */ 382 k = (index->key)->left; 383 while (k->left != NULL) k = k->left; 384 n = (k->notice)->right; 385 while ((n != NULL) && (n->isdummy)) n = n->right; 386 current = n; 387 } 388 389 390 /* ********************* */ 391 int 392 el_empty(void) 393 /* ********************* */ 394 { 395 if (current == NULL) 396 return (1); 397 else 398 return (0); 399 } 400 401 402 /* ********************* */ 403 void * 404 el_first(void) 405 /* ********************* */ 406 { 407 struct notice *n, *fn; 408 struct key *k, *fk; 409 struct index *ind, *fi; 410 int ctr, *val; 411 time_t next_int; 412 413 if ((index == NULL) || (current == NULL)) 414 return (NULL); 415 416 while ((index->key)->time < current->time) { 417 if (DU == 2) { 418 /* only two intervals, so relabel first one */ 419 k = index->key; 420 k->time += DT; 421 (k->notice)->time += DT; 422 continue; } 423 /* 424 * remove the notice, key, and index corresponding 425 * to the first time interval. Then split the 426 * overflow interval into a normal interval 427 * plus an overflow interval. 428 */ 429 fi = index; 430 fk = fi->key; 431 fn = fk->notice; 432 (fn->left)->right = fn->right; 433 (fn->right)->left = fn->left; 434 (fk->left)->right = fk->right; 435 (fk->right)->left = fk->left; 436 index = index->right; 437 /* find where to split */ 438 ind = index; 439 while ((ind->right)->right != NULL) ind = ind->right; 440 /* ind points to the next to last index interval */ 441 k = ind->key; 442 next_int = k->time + DT; /* upper bound on new inter. */ 443 while (k->time < next_int) k = k->right; 444 /* k points to the appropriate sublist of notices */ 445 n = (k->notice)->left; 446 ctr = 1; 447 while (n->time >= next_int) { 448 ctr++; 449 n = n->left; } 450 n = n->right; 451 /* 452 * n points to first notice of the new overflow interval 453 * ctr tells how many notices are in the first sublist 454 * of the new overflow interval 455 * insert the new index element 456 */ 457 fi->right = ind->right; 458 ind->right = fi; 459 /* insert the new dummy key */ 460 fk->time = next_int; 461 fk->numnote = k->numnote - ctr + 1; 462 fk->left = k->left; 463 fk->right = k; 464 (k->left)->right = fk; 465 k->left = fk; 466 k->numnote = ctr; 467 /* insert the new dummy notice */ 468 fn->time = next_int; 469 fn->left = n->left; 470 fn->right = n; 471 (n->left)->right = fn; 472 n->left = fn; } 473 474 /* remove the first element of the list */ 475 (current->left)->right = current->right; 476 (current->right)->left = current->left; 477 /* now update the numnote field in the appropriate key */ 478 n = current; 479 while (n->key == NULL) n = n->right; 480 k = n->key; 481 k->numnote = k->numnote - 1; 482 /* if numnote = 0 then this key must be removed */ 483 if (k->numnote == 0) { 484 (k->left)->right = k->right; 485 (k->right)->left = k->left; 486 free(k); } 487 488 /* now set current to be the head of the list */ 489 fn = current->right; 490 while ((fn != NULL) && (fn->isdummy)) 491 fn = fn->right; 492 val = current->event; 493 free(current); 494 current = fn; 495 496 return (val); 497 } 498 499 500 /* ************** */ 501 void 502 el_delete(void) 503 /* ************** */ 504 { 505 /* el_delete frees up all the space associated with the event list */ 506 507 struct index *ind, *ind2; 508 struct key *k, *k2; 509 struct notice *n, *n2; 510 511 if (index == NULL) 512 return; 513 ind = index; 514 k = ind->key; 515 while (k->left != NULL) k = k->left; 516 n = k->notice; 517 while (n != NULL) { 518 n2 = n->right; 519 free(n); 520 n = n2; } 521 while (k != NULL) { 522 k2 = k->right; 523 free(k); 524 k = k2; } 525 while (ind != NULL) { 526 ind2 = ind->right; 527 free(ind); 528 ind = ind2; } 529 530 index = NULL; 531 current = NULL; 532 } 533