xref: /illumos-gate/usr/src/cmd/cron/elm.c (revision e2553d6841698282283135811d10eebeefb512bc)
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