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