1: /*************************************************************************
2: * (C) 2011 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
3: * by Michael Pounov <misho@openbsd-bg.org>
4: *
5: * $Author: misho $
6: * $Id: aitsched.c,v 1.9 2012/05/30 08:52:45 misho Exp $
7: *
8: **************************************************************************
9: The ELWIX and AITNET software is distributed under the following
10: terms:
11:
12: All of the documentation and software included in the ELWIX and AITNET
13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
14:
15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
16: by Michael Pounov <misho@elwix.org>. All rights reserved.
17:
18: Redistribution and use in source and binary forms, with or without
19: modification, are permitted provided that the following conditions
20: are met:
21: 1. Redistributions of source code must retain the above copyright
22: notice, this list of conditions and the following disclaimer.
23: 2. Redistributions in binary form must reproduce the above copyright
24: notice, this list of conditions and the following disclaimer in the
25: documentation and/or other materials provided with the distribution.
26: 3. All advertising materials mentioning features or use of this software
27: must display the following acknowledgement:
28: This product includes software developed by Michael Pounov <misho@elwix.org>
29: ELWIX - Embedded LightWeight unIX and its contributors.
30: 4. Neither the name of AITNET nor the names of its contributors
31: may be used to endorse or promote products derived from this software
32: without specific prior written permission.
33:
34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37: ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44: SUCH DAMAGE.
45: */
46: #include "global.h"
47: #include "hooks.h"
48:
49:
50: #pragma GCC visibility push(hidden)
51:
52: int sched_Errno;
53: char sched_Error[STRSIZ];
54:
55: #pragma GCC visibility pop
56:
57:
58: // sched_GetErrno() Get error code of last operation
59: inline int
60: sched_GetErrno()
61: {
62: return sched_Errno;
63: }
64:
65: // sched_GetError() Get error text of last operation
66: inline const char *
67: sched_GetError()
68: {
69: return sched_Error;
70: }
71:
72: // sched_SetErr() Set error to variables for internal use!!!
73: inline void
74: sched_SetErr(int eno, char *estr, ...)
75: {
76: va_list lst;
77:
78: sched_Errno = eno;
79: memset(sched_Error, 0, sizeof sched_Error);
80: va_start(lst, estr);
81: vsnprintf(sched_Error, sizeof sched_Error, estr, lst);
82: va_end(lst);
83: }
84:
85: /* Init and prepare scheduler functions */
86:
87: /*
88: * schedRegisterHooks() - Register IO handles and bind tasks to it
89: *
90: * @root = root task
91: * return: -1 error or 0 ok
92: */
93: int
94: schedRegisterHooks(sched_root_task_t * __restrict root)
95: {
96: assert(root);
97:
98: if (root->root_hooks.hook_root.fini)
99: root->root_hooks.hook_root.fini(root, NULL);
100: memset(&root->root_hooks, 0, sizeof root->root_hooks);
101:
102: root->root_hooks.hook_add.read = sched_hook_read;
103: root->root_hooks.hook_add.write = sched_hook_write;
104: root->root_hooks.hook_add.alarm = sched_hook_alarm;
105:
106: root->root_hooks.hook_exec.cancel = sched_hook_cancel;
107: root->root_hooks.hook_exec.fetch = sched_hook_fetch;
108: root->root_hooks.hook_exec.exception = sched_hook_exception;
109:
110: root->root_hooks.hook_root.init = sched_hook_init;
111: root->root_hooks.hook_root.fini = sched_hook_fini;
112: return 0;
113: }
114:
115: /*
116: * schedInit() - Init scheduler
117: *
118: * @data = optional data if !=NULL
119: * @datlen = data len if data is set
120: * return: allocated root task if ok or NULL error
121: */
122: sched_root_task_t *
123: schedInit(void ** __restrict data, size_t datlen)
124: {
125: sched_root_task_t *root = NULL;
126: int (*func)(sched_root_task_t *);
127: #ifdef HAVE_LIBPTHREAD
128: register int i;
129: #endif
130:
131: root = malloc(sizeof(sched_root_task_t));
132: if (!root) {
133: LOGERR;
134: } else {
135: memset(root, 0, sizeof(sched_root_task_t));
136:
137: /* INFINIT polling period by default */
138: sched_timespecinf(&root->root_poll);
139:
140: #ifdef HAVE_LIBPTHREAD
141: for (i = 0; i < taskMAX; i++)
142: if (pthread_mutex_init(&root->root_mtx[i], NULL)) {
143: LOGERR;
144: while (i)
145: pthread_mutex_destroy(&root->root_mtx[--i]);
146: free(root);
147: return NULL;
148: }
149:
150: for (i = 0; i < taskMAX; i++)
151: pthread_mutex_lock(&root->root_mtx[i]);
152: #endif
153:
154: TAILQ_INIT(&root->root_read);
155: TAILQ_INIT(&root->root_write);
156: TAILQ_INIT(&root->root_alarm);
157: TAILQ_INIT(&root->root_timer);
158: TAILQ_INIT(&root->root_event);
159: TAILQ_INIT(&root->root_eventlo);
160: TAILQ_INIT(&root->root_ready);
161: TAILQ_INIT(&root->root_unuse);
162:
163: #ifdef HAVE_LIBPTHREAD
164: for (i = 0; i < taskMAX; i++)
165: pthread_mutex_unlock(&root->root_mtx[i]);
166: #endif
167:
168: if (data && *data) {
169: if (datlen) {
170: root->root_data.iov_base = *data;
171: root->root_data.iov_len = datlen;
172: } else { /* if datlen == 0, switch to callbacks init mode */
173: /* little hack :) for correct initialization of scheduler */
174: func = (int(*)(sched_root_task_t*)) data;
175: func(root);
176: }
177: }
178:
179: if (root->root_hooks.hook_root.init)
180: root->root_hooks.hook_root.init(root, NULL);
181: }
182:
183: return root;
184: }
185:
186: /*
187: * schedEnd() - End scheduler & free all resources
188: *
189: * @root = root task
190: * return: -1 error or 0 ok
191: */
192: int
193: schedEnd(sched_root_task_t ** __restrict root)
194: {
195: sched_task_t *task, *tmp;
196: #ifdef HAVE_LIBPTHREAD
197: register int i;
198: #endif
199:
200: if (!root || !*root)
201: return -1;
202:
203: TAILQ_FOREACH_SAFE(task, &(*root)->root_read, task_node, tmp) {
204: schedCancel(task);
205: }
206: TAILQ_FOREACH_SAFE(task, &(*root)->root_write, task_node, tmp) {
207: schedCancel(task);
208: }
209: TAILQ_FOREACH_SAFE(task, &(*root)->root_alarm, task_node, tmp) {
210: schedCancel(task);
211: }
212: TAILQ_FOREACH_SAFE(task, &(*root)->root_timer, task_node, tmp) {
213: schedCancel(task);
214: }
215: TAILQ_FOREACH_SAFE(task, &(*root)->root_event, task_node, tmp) {
216: schedCancel(task);
217: }
218: TAILQ_FOREACH_SAFE(task, &(*root)->root_eventlo, task_node, tmp) {
219: schedCancel(task);
220: }
221: TAILQ_FOREACH_SAFE(task, &(*root)->root_ready, task_node, tmp) {
222: schedCancel(task);
223: }
224:
225: #ifdef HAVE_LIBPTHREAD
226: pthread_mutex_lock(&(*root)->root_mtx[taskUNUSE]);
227: #endif
228: while ((task = TAILQ_FIRST(&(*root)->root_unuse))) {
229: TAILQ_REMOVE(&(*root)->root_unuse, task, task_node);
230: free(task);
231: }
232: #ifdef HAVE_LIBPTHREAD
233: pthread_mutex_unlock(&(*root)->root_mtx[taskUNUSE]);
234: #endif
235:
236: if ((*root)->root_hooks.hook_root.fini)
237: (*root)->root_hooks.hook_root.fini(*root, NULL);
238:
239: #ifdef HAVE_LIBPTHREAD
240: for (i = 0; i < taskMAX; i++)
241: pthread_mutex_destroy(&(*root)->root_mtx[i]);
242: #endif
243:
244: free(*root);
245: *root = NULL;
246: return 0;
247: }
248:
249: /*
250: * schedCall() - Call task execution function
251: *
252: * @task = current task
253: * return: !=NULL error or =NULL ok
254: */
255: inline void *
256: schedCall(sched_task_t * __restrict task)
257: {
258: void *ptr = (void*) -1;
259:
260: if (!task)
261: return ptr;
262:
263: if (!TASK_ISLOCKED(task))
264: TASK_LOCK(task);
265:
266: task->task_id++;
267: ptr = task->task_func(task);
268:
269: TASK_UNLOCK(task);
270: return ptr;
271: }
272:
273: /*
274: * schedFetch() - Fetch ready task
275: *
276: * @root = root task
277: * return: =NULL error or !=NULL ready task
278: */
279: inline void *
280: schedFetch(sched_root_task_t * __restrict root)
281: {
282: void *ptr;
283:
284: if (!root)
285: return NULL;
286:
287: if (root->root_hooks.hook_exec.fetch)
288: ptr = root->root_hooks.hook_exec.fetch(root, NULL);
289: else
290: ptr = NULL;
291:
292: return ptr;
293: }
294:
295: /*
296: * schedCancel() - Cancel task from scheduler
297: *
298: * @task = task
299: * return: -1 error or 0 ok
300: */
301: int
302: schedCancel(sched_task_t * __restrict task)
303: {
304: sched_queue_t *queue;
305:
306: if (!task || !TASK_ROOT(task))
307: return -1;
308:
309: if (TASK_ROOT(task)->root_hooks.hook_exec.cancel)
310: if (TASK_ROOT(task)->root_hooks.hook_exec.cancel(task, NULL))
311: return -1;
312:
313: switch (TASK_TYPE(task)) {
314: case taskREAD:
315: queue = &TASK_ROOT(task)->root_read;
316: break;
317: case taskWRITE:
318: queue = &TASK_ROOT(task)->root_write;
319: break;
320: case taskALARM:
321: queue = &TASK_ROOT(task)->root_alarm;
322: break;
323: case taskTIMER:
324: queue = &TASK_ROOT(task)->root_timer;
325: break;
326: case taskEVENT:
327: queue = &TASK_ROOT(task)->root_event;
328: break;
329: case taskEVENTLO:
330: queue = &TASK_ROOT(task)->root_eventlo;
331: break;
332: case taskREADY:
333: queue = &TASK_ROOT(task)->root_ready;
334: break;
335: default:
336: queue = NULL;
337: }
338: if (queue) {
339: #ifdef HAVE_LIBPTHREAD
340: pthread_mutex_lock(&TASK_ROOT(task)->root_mtx[TASK_TYPE(task)]);
341: #endif
342: TAILQ_REMOVE(queue, task, task_node);
343: #ifdef HAVE_LIBPTHREAD
344: pthread_mutex_unlock(&TASK_ROOT(task)->root_mtx[TASK_TYPE(task)]);
345: #endif
346: }
347: if (TASK_TYPE(task) != taskUNUSE)
348: _sched_unuseTask(task);
349:
350: return 0;
351: }
352:
353: /*
354: * schedCancelby() - Cancel task from scheduler by criteria
355: *
356: * @root = root task
357: * @type = cancel from queue type, if =taskMAX cancel same task from all queues
358: * @criteria = find task by criteria [CRITERIA_CALL|CRITERIA_ARG|CRITERIA_FD|CRITERIA_VAL|CRITERIA_TS]
359: * @param = search parameter
360: * @hook = custom cleanup hook function, may be NULL
361: * return: -1 error, -2 error in sub-stage cancel execution, -3 error from custom hook or 0 ok
362: */
363: int
364: schedCancelby(sched_root_task_t * __restrict root, sched_task_type_t type,
365: u_char criteria, void *param, sched_hook_func_t hook)
366: {
367: sched_task_t *task, *tmp;
368: sched_queue_t *queue;
369: register int flg = 0;
370:
371: if (!root)
372: return -1;
373: if (type == taskMAX) {
374: if (schedCancelby(root, taskREAD, criteria, param, hook))
375: return -2;
376: if (schedCancelby(root, taskWRITE, criteria, param, hook))
377: return -2;
378: if (schedCancelby(root, taskALARM, criteria, param, hook))
379: return -2;
380: if (schedCancelby(root, taskTIMER, criteria, param, hook))
381: return -2;
382: if (schedCancelby(root, taskEVENT, criteria, param, hook))
383: return -2;
384: if (schedCancelby(root, taskEVENTLO, criteria, param, hook))
385: return -2;
386: if (schedCancelby(root, taskREADY, criteria, param, hook))
387: return -2;
388: return 0;
389: }
390: switch (type) {
391: case taskREAD:
392: queue = &root->root_read;
393: break;
394: case taskWRITE:
395: queue = &root->root_write;
396: break;
397: case taskALARM:
398: queue = &root->root_alarm;
399: break;
400: case taskTIMER:
401: queue = &root->root_timer;
402: break;
403: case taskEVENT:
404: queue = &root->root_event;
405: break;
406: case taskEVENTLO:
407: queue = &root->root_eventlo;
408: break;
409: case taskREADY:
410: queue = &root->root_ready;
411: break;
412: default:
413: return 0;
414: }
415:
416: #ifdef HAVE_LIBPTHREAD
417: pthread_mutex_lock(&root->root_mtx[type]);
418: #endif
419: TAILQ_FOREACH_SAFE(task, queue, task_node, tmp) {
420: flg ^= flg;
421: switch (criteria) {
422: case CRITERIA_CALL:
423: if (TASK_FUNC(task) == (sched_task_func_t) param)
424: flg = 1;
425: break;
426: case CRITERIA_ARG:
427: if (TASK_ARG(task) == param)
428: flg = 1;
429: break;
430: case CRITERIA_FD:
431: if (TASK_FD(task) == (intptr_t) param)
432: flg = 1;
433: break;
434: case CRITERIA_VAL:
435: if (TASK_VAL(task) == (u_long) param)
436: flg = 1;
437: break;
438: case CRITERIA_TS:
439: if (!sched_timespeccmp(&TASK_TS(task), (struct timespec*) param, -))
440: flg = 1;
441: break;
442: default:
443: sched_SetErr(EINVAL, "Invalid parameter criteria %d", criteria);
444: flg = -1;
445: }
446: if (flg < 0)
447: break;
448: /* cancel choosen task */
449: if (flg > 0) {
450: if (TASK_ROOT(task)->root_hooks.hook_exec.cancel)
451: if (TASK_ROOT(task)->root_hooks.hook_exec.cancel(task, NULL)) {
452: flg = -1;
453: break;
454: }
455: /* custom hook */
456: if (hook)
457: if (hook(task, NULL)) {
458: flg = -3;
459: break;
460: }
461:
462: TAILQ_REMOVE(queue, task, task_node);
463: if (TASK_TYPE(task) != taskUNUSE)
464: _sched_unuseTask(task);
465:
466: flg ^= flg; /* ok */
467: }
468: }
469: #ifdef HAVE_LIBPTHREAD
470: pthread_mutex_unlock(&root->root_mtx[type]);
471: #endif
472: return flg;
473: }
474:
475: /*
476: * schedRun() - Scheduler *run loop*
477: *
478: * @root = root task
479: * @killState = kill condition variable, if !=0 stop scheduler loop
480: * return: -1 error or 0 ok
481: */
482: int
483: schedRun(sched_root_task_t *root, volatile intptr_t * __restrict killState)
484: {
485: sched_task_t *task;
486:
487: if (!root)
488: return -1;
489:
490: if (root->root_hooks.hook_exec.run)
491: if (root->root_hooks.hook_exec.run(root, NULL))
492: return -1;
493:
494: if (killState) {
495: if (root->root_hooks.hook_exec.condition)
496: /* condition scheduler loop */
497: while (root && root->root_hooks.hook_exec.fetch &&
498: root->root_hooks.hook_exec.condition &&
499: root->root_hooks.hook_exec.condition(root, (void*) killState)) {
500: if ((task = root->root_hooks.hook_exec.fetch(root, NULL)))
501: schedCall(task);
502: }
503: else
504: /* trigger scheduler loop */
505: while (!*killState && root && root->root_hooks.hook_exec.fetch) {
506: if ((task = root->root_hooks.hook_exec.fetch(root, NULL)))
507: schedCall(task);
508: }
509: } else
510: /* infinite scheduler loop */
511: while (root && root->root_hooks.hook_exec.fetch)
512: if ((task = root->root_hooks.hook_exec.fetch(root, NULL)))
513: schedCall(task);
514:
515: return 0;
516: }
517:
518: /*
519: * schedPolling() - Polling timeout period if no timer task is present
520: *
521: * @root = root task
522: * @ts = timeout polling period, if ==NULL INFINIT timeout
523: * @tsold = old timeout polling if !=NULL
524: * return: -1 error or 0 ok
525: */
526: inline int
527: schedPolling(sched_root_task_t * __restrict root, struct timespec * __restrict ts,
528: struct timespec * __restrict tsold)
529: {
530: if (!root)
531: return -1;
532:
533: if (tsold)
534: *tsold = root->root_poll;
535:
536: if (!ts)
537: sched_timespecinf(&root->root_poll);
538: else
539: root->root_poll = *ts;
540:
541: return 0;
542: }
543:
544: /*
545: * schedTermCondition() - Activate hook for scheduler condition kill
546: *
547: * @root = root task
548: * @condValue = condition value, kill schedRun() if condValue == killState
549: * return: -1 error ok 0 ok
550: */
551: inline int
552: schedTermCondition(sched_root_task_t * __restrict root, intptr_t condValue)
553: {
554: if (!root)
555: return -1;
556:
557: root->root_cond = condValue;
558: root->root_hooks.hook_exec.condition = sched_hook_condition;
559: return 0;
560: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>