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.33.4.1 2023/08/31 15:22:12 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 - 2023
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: int
60: sched_GetErrno()
61: {
62: return sched_Errno;
63: }
64:
65: // sched_GetError() Get error text of last operation
66: const char *
67: sched_GetError()
68: {
69: return sched_Error;
70: }
71:
72: // sched_SetErr() Set error to variables for internal use!!!
73: 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:
86: /* string support functions directly imported from OpenBSD */
87:
88: #ifndef HAVE_STRLCAT
89: /*
90: * Appends src to string dst of size siz (unlike strncat, siz is the
91: * full size of dst, not space left). At most siz-1 characters
92: * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
93: * Returns strlen(src) + MIN(siz, strlen(initial dst)).
94: * If retval >= siz, truncation occurred.
95: */
96: size_t
97: strlcat(char * __restrict dst, const char * __restrict src, size_t siz)
98: {
99: char *d = dst;
100: const char *s = src;
101: size_t n = siz;
102: size_t dlen;
103:
104: /* Find the end of dst and adjust bytes left but don't go past end */
105: while (n-- != 0 && *d != '\0')
106: d++;
107: dlen = d - dst;
108: n = siz - dlen;
109:
110: if (n == 0)
111: return(dlen + strlen(s));
112: while (*s != '\0') {
113: if (n != 1) {
114: *d++ = *s;
115: n--;
116: }
117: s++;
118: }
119: *d = '\0';
120:
121: return(dlen + (s - src)); /* count does not include NUL */
122: }
123: #endif
124: #ifndef HAVE_STRLCPY
125: /*
126: * Copy src to string dst of size siz. At most siz-1 characters
127: * will be copied. Always NUL terminates (unless siz == 0).
128: * Returns strlen(src); if retval >= siz, truncation occurred.
129: */
130: size_t
131: strlcpy(char * __restrict dst, const char * __restrict src, size_t siz)
132: {
133: char *d = dst;
134: const char *s = src;
135: size_t n = siz;
136:
137: /* Copy as many bytes as will fit */
138: if (n != 0) {
139: while (--n != 0) {
140: if ((*d++ = *s++) == '\0')
141: break;
142: }
143: }
144:
145: /* Not enough room in dst, add NUL and traverse rest of src */
146: if (n == 0) {
147: if (siz != 0)
148: *d = '\0'; /* NUL-terminate dst */
149: while (*s++)
150: ;
151: }
152:
153: return(s - src - 1); /* count does not include NUL */
154: }
155: #endif
156:
157:
158: /* Init and prepare scheduler functions */
159:
160: /*
161: * schedRegisterHooks() - Register IO handles and bind tasks to it
162: *
163: * @root = root task
164: * return: -1 error or 0 ok
165: */
166: int
167: schedRegisterHooks(sched_root_task_t * __restrict root)
168: {
169: assert(root);
170:
171: if (root->root_hooks.hook_root.fini)
172: root->root_hooks.hook_root.fini(root, NULL);
173: memset(&root->root_hooks, 0, sizeof root->root_hooks);
174:
175: root->root_hooks.hook_add.read = sched_hook_read;
176: root->root_hooks.hook_add.write = sched_hook_write;
177: #if defined(HAVE_TIMER_CREATE) && defined(HAVE_TIMER_SETTIME) && defined(HAVE_TIMER_DELETE)
178: root->root_hooks.hook_add.rtc = sched_hook_rtc;
179: #endif
180: #if SUP_ENABLE == KQ_SUPPORT
181: root->root_hooks.hook_add.alarm = sched_hook_alarm;
182: root->root_hooks.hook_add.node = sched_hook_node;
183: root->root_hooks.hook_add.proc = sched_hook_proc;
184: #ifdef EVFILT_USER
185: root->root_hooks.hook_add.user = sched_hook_user;
186: #endif
187: #endif /* KQ_SUPPORT */
188: #ifdef HAVE_LIBPTHREAD
189: root->root_hooks.hook_add.thread = sched_hook_thread;
190: #endif
191: root->root_hooks.hook_add.signal = sched_hook_signal;
192:
193: root->root_hooks.hook_exec.cancel = sched_hook_cancel;
194: root->root_hooks.hook_exec.fetch = sched_hook_fetch;
195: root->root_hooks.hook_exec.exception = sched_hook_exception;
196:
197: root->root_hooks.hook_root.init = sched_hook_init;
198: root->root_hooks.hook_root.fini = sched_hook_fini;
199: return 0;
200: }
201:
202: /*
203: * schedInit() - Init scheduler
204: *
205: * @data = optional data if !=NULL
206: * @datlen = data len if data is set
207: * return: allocated root task if ok or NULL error
208: */
209: sched_root_task_t *
210: schedInit(void ** __restrict data, size_t datlen)
211: {
212: sched_root_task_t *root = NULL;
213: int (*func)(sched_root_task_t *);
214: #if SUP_ENABLE == KQ_SUPPORT
215: sigset_t ss;
216: #endif
217: #ifdef HAVE_LIBPTHREAD
218: register int i;
219: #endif
220:
221: root = e_malloc(sizeof(sched_root_task_t));
222: if (!root) {
223: LOGERR;
224: } else {
225: memset(root, 0, sizeof(sched_root_task_t));
226:
227: /* set default maximum regular task hit misses */
228: root->root_miss = MAX_TASK_MISS;
229:
230: /* INFINIT polling period by default */
231: sched_timespecinf(&root->root_poll);
232:
233: #ifdef HAVE_LIBPTHREAD
234: if (pthread_mutex_init(&root->root_sigmtx, NULL)) {
235: LOGERR;
236: e_free(root);
237: return NULL;
238: }
239:
240: for (i = 0; i < taskMAX; i++)
241: if ((errno = pthread_mutex_init(&root->root_mtx[i], NULL))) {
242: LOGERR;
243: while (i)
244: pthread_mutex_destroy(&root->root_mtx[--i]);
245: e_free(root);
246: return NULL;
247: }
248:
249: for (i = 0; i < taskMAX; i++)
250: pthread_mutex_lock(&root->root_mtx[i]);
251: #endif
252:
253: TAILQ_INIT(&root->root_read);
254: TAILQ_INIT(&root->root_write);
255: TAILQ_INIT(&root->root_timer);
256: TAILQ_INIT(&root->root_alarm);
257: TAILQ_INIT(&root->root_rtc);
258: TAILQ_INIT(&root->root_node);
259: TAILQ_INIT(&root->root_proc);
260: TAILQ_INIT(&root->root_signal);
261: TAILQ_INIT(&root->root_aio);
262: TAILQ_INIT(&root->root_lio);
263: TAILQ_INIT(&root->root_user);
264: TAILQ_INIT(&root->root_event);
265: TAILQ_INIT(&root->root_task);
266: TAILQ_INIT(&root->root_suspend);
267: TAILQ_INIT(&root->root_ready);
268: TAILQ_INIT(&root->root_unuse);
269: TAILQ_INIT(&root->root_thread);
270:
271: #ifdef HAVE_LIBPTHREAD
272: for (i = 0; i < taskMAX; i++)
273: pthread_mutex_unlock(&root->root_mtx[i]);
274:
275: pthread_sigmask(SIG_BLOCK, NULL, &root->root_oldset);
276: #else
277: sigprocmask(SIG_BLOCK, NULL, &root->root_oldset);
278: #endif
279:
280: if (data && *data) {
281: if (datlen) {
282: root->root_data.iov_base = *data;
283: root->root_data.iov_len = datlen;
284: } else { /* if datlen == 0, switch to callbacks init mode */
285: /* little hack :) for correct initialization of scheduler */
286: func = (int(*)(sched_root_task_t*)) data;
287: func(root);
288: }
289: }
290:
291: if (root->root_hooks.hook_root.init)
292: root->root_hooks.hook_root.init(root, NULL);
293: }
294:
295: return root;
296: }
297:
298: /*
299: * schedEnd() - End scheduler & free all resources
300: *
301: * @root = root task
302: * return: -1 error or 0 ok
303: */
304: int
305: schedEnd(sched_root_task_t ** __restrict root)
306: {
307: sched_task_t *task, *tmp;
308: #ifdef HAVE_LIBPTHREAD
309: register int i;
310: #endif
311:
312: if (!root || !*root)
313: return -1;
314:
315: #if 0
316: TAILQ_FOREACH_SAFE(task, &(*root)->root_read, task_node, tmp)
317: printf("read=%p\n", task);
318: TAILQ_FOREACH_SAFE(task, &(*root)->root_write, task_node, tmp)
319: printf("write=%p\n", task);
320: TAILQ_FOREACH_SAFE(task, &(*root)->root_timer, task_node, tmp)
321: printf("timer=%p\n", task);
322: TAILQ_FOREACH_SAFE(task, &(*root)->root_alarm, task_node, tmp)
323: printf("alarm=%p\n", task);
324: TAILQ_FOREACH_SAFE(task, &(*root)->root_rtc, task_node, tmp)
325: printf("rtc=%p\n", task);
326: TAILQ_FOREACH_SAFE(task, &(*root)->root_node, task_node, tmp)
327: printf("node=%p\n", task);
328: TAILQ_FOREACH_SAFE(task, &(*root)->root_proc, task_node, tmp)
329: printf("proc=%p\n", task);
330: TAILQ_FOREACH_SAFE(task, &(*root)->root_signal, task_node, tmp)
331: printf("signal=%p\n", task);
332: TAILQ_FOREACH_SAFE(task, &(*root)->root_aio, task_node, tmp)
333: printf("aio=%p\n", task);
334: TAILQ_FOREACH_SAFE(task, &(*root)->root_lio, task_node, tmp)
335: printf("lio=%p\n", task);
336: TAILQ_FOREACH_SAFE(task, &(*root)->root_user, task_node, tmp)
337: printf("user=%p\n", task);
338: TAILQ_FOREACH_SAFE(task, &(*root)->root_event, task_node, tmp)
339: printf("event=%p\n", task);
340: TAILQ_FOREACH_SAFE(task, &(*root)->root_suspend, task_node, tmp)
341: printf("suspend=%p\n", task);
342: TAILQ_FOREACH_SAFE(task, &(*root)->root_ready, task_node, tmp)
343: printf("ready=%p\n", task);
344: TAILQ_FOREACH_SAFE(task, &(*root)->root_thread, task_node, tmp)
345: printf("thread=%p\n", task);
346: TAILQ_FOREACH_SAFE(task, &(*root)->root_task, task_node, tmp)
347: printf("task=%p\n", task);
348: TAILQ_FOREACH_SAFE(task, &(*root)->root_unuse, task_node, tmp)
349: printf("unuse=%p\n", task);
350: fflush(stdout);
351: #endif
352:
353: TAILQ_FOREACH_SAFE(task, &(*root)->root_read, task_node, tmp)
354: schedCancel(task);
355: TAILQ_FOREACH_SAFE(task, &(*root)->root_write, task_node, tmp)
356: schedCancel(task);
357: TAILQ_FOREACH_SAFE(task, &(*root)->root_timer, task_node, tmp)
358: schedCancel(task);
359: TAILQ_FOREACH_SAFE(task, &(*root)->root_alarm, task_node, tmp)
360: schedCancel(task);
361: TAILQ_FOREACH_SAFE(task, &(*root)->root_rtc, task_node, tmp)
362: schedCancel(task);
363: TAILQ_FOREACH_SAFE(task, &(*root)->root_node, task_node, tmp)
364: schedCancel(task);
365: TAILQ_FOREACH_SAFE(task, &(*root)->root_proc, task_node, tmp)
366: schedCancel(task);
367: TAILQ_FOREACH_SAFE(task, &(*root)->root_signal, task_node, tmp)
368: schedCancel(task);
369: TAILQ_FOREACH_SAFE(task, &(*root)->root_aio, task_node, tmp)
370: schedCancel(task);
371: TAILQ_FOREACH_SAFE(task, &(*root)->root_lio, task_node, tmp)
372: schedCancel(task);
373: TAILQ_FOREACH_SAFE(task, &(*root)->root_user, task_node, tmp)
374: schedCancel(task);
375: TAILQ_FOREACH_SAFE(task, &(*root)->root_event, task_node, tmp)
376: schedCancel(task);
377: TAILQ_FOREACH_SAFE(task, &(*root)->root_suspend, task_node, tmp)
378: schedCancel(task);
379: TAILQ_FOREACH_SAFE(task, &(*root)->root_ready, task_node, tmp)
380: schedCancel(task);
381: TAILQ_FOREACH_SAFE(task, &(*root)->root_thread, task_node, tmp)
382: schedCancel(task);
383: TAILQ_FOREACH_SAFE(task, &(*root)->root_task, task_node, tmp)
384: schedCancel(task);
385:
386: SCHED_QLOCK((*root), taskUNUSE);
387: TAILQ_FOREACH_SAFE(task, &(*root)->root_unuse, task_node, tmp) {
388: TAILQ_REMOVE(&(*root)->root_unuse, task, task_node);
389: e_free(task);
390: }
391: SCHED_QUNLOCK((*root), taskUNUSE);
392:
393: if ((*root)->root_hooks.hook_root.fini)
394: (*root)->root_hooks.hook_root.fini(*root, NULL);
395:
396: #ifdef HAVE_LIBPTHREAD
397: for (i = 0; i < taskMAX; i++) {
398: SCHED_QTRYLOCK(*root, i);
399: SCHED_QUNLOCK(*root, i);
400: pthread_mutex_destroy(&(*root)->root_mtx[i]);
401: }
402:
403: if ((*root)->root_sigthr) {
404: pthread_cancel((*root)->root_sigthr);
405: #ifdef __linux__
406: (*root)->root_sigthr = 0L;
407: #else
408: (*root)->root_sigthr = NULL;
409: #endif
410: pthread_sigmask(SIG_SETMASK, &(*root)->root_oldset, NULL);
411: pthread_mutex_unlock(&(*root)->root_sigmtx);
412: }
413: pthread_mutex_destroy(&(*root)->root_sigmtx);
414: #else
415: sigprocmask(SIG_SETMASK, &(*root)->root_oldset, NULL);
416: #endif
417:
418: e_free(*root);
419: *root = NULL;
420: return 0;
421: }
422:
423: /*
424: * schedCall() - Call task execution function
425: *
426: * @task = current task
427: * return: !=NULL error or =NULL ok
428: */
429: void *
430: schedCall(sched_task_t * __restrict task)
431: {
432: void *ptr = (void*) -1;
433:
434: if (!task)
435: return ptr;
436:
437: if (!TASK_ISLOCKED(task))
438: TASK_LOCK(task);
439:
440: ptr = task->task_func(task);
441:
442: TASK_UNLOCK(task);
443: return ptr;
444: }
445:
446: /*
447: * schedFetch() - Fetch ready task
448: *
449: * @root = root task
450: * return: =NULL error or !=NULL ready task
451: */
452: void *
453: schedFetch(sched_root_task_t * __restrict root)
454: {
455: void *ptr;
456:
457: if (!root)
458: return NULL;
459:
460: if (root->root_hooks.hook_exec.fetch)
461: ptr = root->root_hooks.hook_exec.fetch(root, NULL);
462: else
463: ptr = NULL;
464:
465: return ptr;
466: }
467:
468: /*
469: * schedTrigger() - Triggering USER task
470: *
471: * @task = task
472: * return: -1 error or 0 ok
473: */
474: int
475: schedTrigger(sched_task_t * __restrict task)
476: {
477: #if SUP_ENABLE != KQ_SUPPORT
478: sched_SetErr(ENOTSUP, "disabled kqueue support");
479: return -1;
480: #else
481: #ifndef EVFILT_USER
482: sched_SetErr(ENOTSUP, "Not supported kevent() filter");
483: return -1;
484: #else
485: struct kevent chg[1];
486: struct timespec timeout = { 0, 0 };
487:
488: if (!task || !TASK_ROOT(task))
489: return -1;
490:
491: #ifdef __NetBSD__
492: EV_SET(chg, TASK_VAL(task), EVFILT_USER, 0, NOTE_TRIGGER, 0, (intptr_t) TASK_VAL(task));
493: #else
494: EV_SET(chg, TASK_VAL(task), EVFILT_USER, 0, NOTE_TRIGGER, 0, (void*) TASK_VAL(task));
495: #endif
496: if (kevent(TASK_ROOT(task)->root_kq, chg, 1, NULL, 0, &timeout) == -1) {
497: LOGERR;
498: return -1;
499: }
500:
501: return 0;
502: #endif
503: #endif /* KQ_SUPPORT */
504: }
505:
506: /*
507: * schedQuery() - Query task in scheduler
508: *
509: * @task = task
510: * return: -1 error, 0 found and 1 not found
511: */
512: int
513: schedQuery(sched_task_t * __restrict task)
514: {
515: sched_queue_t *queue;
516: sched_task_t *t;
517:
518: if (!task || !TASK_ROOT(task))
519: return -1; /* error */
520:
521: switch (TASK_TYPE(task)) {
522: case taskREAD:
523: queue = &TASK_ROOT(task)->root_read;
524: break;
525: case taskWRITE:
526: queue = &TASK_ROOT(task)->root_write;
527: break;
528: case taskTIMER:
529: queue = &TASK_ROOT(task)->root_timer;
530: break;
531: case taskALARM:
532: queue = &TASK_ROOT(task)->root_alarm;
533: break;
534: case taskRTC:
535: queue = &TASK_ROOT(task)->root_rtc;
536: break;
537: case taskNODE:
538: queue = &TASK_ROOT(task)->root_node;
539: break;
540: case taskPROC:
541: queue = &TASK_ROOT(task)->root_proc;
542: break;
543: case taskSIGNAL:
544: queue = &TASK_ROOT(task)->root_signal;
545: break;
546: case taskAIO:
547: queue = &TASK_ROOT(task)->root_aio;
548: break;
549: case taskLIO:
550: queue = &TASK_ROOT(task)->root_lio;
551: break;
552: case taskUSER:
553: queue = &TASK_ROOT(task)->root_user;
554: break;
555: case taskEVENT:
556: queue = &TASK_ROOT(task)->root_event;
557: break;
558: case taskTASK:
559: queue = &TASK_ROOT(task)->root_task;
560: break;
561: case taskSUSPEND:
562: queue = &TASK_ROOT(task)->root_suspend;
563: break;
564: case taskREADY:
565: queue = &TASK_ROOT(task)->root_ready;
566: break;
567: case taskTHREAD:
568: queue = &TASK_ROOT(task)->root_thread;
569: break;
570: default:
571: return 1; /* not in queue */
572: }
573: if (queue)
574: TAILQ_FOREACH(t, queue, task_node)
575: if (TASK_ID(t) == TASK_ID(task))
576: return 0; /* found */
577:
578: return 1; /* not in queue */
579: }
580:
581: /*
582: * schedQueryby() - Query task in scheduler by criteria
583: *
584: * @root = root task
585: * @type = query from queue type, if =taskMAX query same task from all queues
586: * @criteria = find task by criteria
587: * [ CRITERIA_ANY|CRITERIA_CALL|CRITERIA_ARG|CRITERIA_FD|CRITERIA_VAL|
588: * CRITERIA_ID|CRITERIA_TS|CRITERIA_DATA|CRITERIA_DATLEN ]
589: * @param = search parameter
590: * return: NULL not found or !=NULL task
591: */
592: sched_task_t *
593: schedQueryby(sched_root_task_t * __restrict root, sched_task_type_t type,
594: u_char criteria, void *param)
595: {
596: sched_task_t *task;
597: sched_queue_t *queue;
598:
599: if (!root)
600: return NULL;
601: /* if type == taskMAX check in all queues */
602: if (type == taskMAX) {
603: if ((task = schedQueryby(root, taskREAD, criteria, param)))
604: return task;
605: if ((task = schedQueryby(root, taskWRITE, criteria, param)))
606: return task;
607: if ((task = schedQueryby(root, taskTIMER, criteria, param)))
608: return task;
609: if ((task = schedQueryby(root, taskALARM, criteria, param)))
610: return task;
611: if ((task = schedQueryby(root, taskRTC, criteria, param)))
612: return task;
613: if ((task = schedQueryby(root, taskNODE, criteria, param)))
614: return task;
615: if ((task = schedQueryby(root, taskPROC, criteria, param)))
616: return task;
617: if ((task = schedQueryby(root, taskSIGNAL, criteria, param)))
618: return task;
619: if ((task = schedQueryby(root, taskAIO, criteria, param)))
620: return task;
621: if ((task = schedQueryby(root, taskLIO, criteria, param)))
622: return task;
623: if ((task = schedQueryby(root, taskUSER, criteria, param)))
624: return task;
625: if ((task = schedQueryby(root, taskEVENT, criteria, param)))
626: return task;
627: if ((task = schedQueryby(root, taskTASK, criteria, param)))
628: return task;
629: if ((task = schedQueryby(root, taskSUSPEND, criteria, param)))
630: return task;
631: if ((task = schedQueryby(root, taskREADY, criteria, param)))
632: return task;
633: if ((task = schedQueryby(root, taskTHREAD, criteria, param)))
634: return task;
635: return NULL; /* not found */
636: }
637: /* choosen queue */
638: switch (type) {
639: case taskREAD:
640: queue = &root->root_read;
641: break;
642: case taskWRITE:
643: queue = &root->root_write;
644: break;
645: case taskTIMER:
646: queue = &root->root_timer;
647: break;
648: case taskALARM:
649: queue = &root->root_alarm;
650: break;
651: case taskRTC:
652: queue = &root->root_rtc;
653: break;
654: case taskNODE:
655: queue = &root->root_node;
656: break;
657: case taskPROC:
658: queue = &root->root_proc;
659: break;
660: case taskSIGNAL:
661: queue = &root->root_signal;
662: break;
663: case taskAIO:
664: queue = &root->root_aio;
665: break;
666: case taskLIO:
667: queue = &root->root_lio;
668: break;
669: case taskUSER:
670: queue = &root->root_user;
671: break;
672: case taskEVENT:
673: queue = &root->root_event;
674: break;
675: case taskTASK:
676: queue = &root->root_task;
677: break;
678: case taskSUSPEND:
679: queue = &root->root_suspend;
680: break;
681: case taskREADY:
682: queue = &root->root_ready;
683: break;
684: case taskTHREAD:
685: queue = &root->root_thread;
686: break;
687: default:
688: return NULL; /* not found */
689: }
690:
691: TAILQ_FOREACH(task, queue, task_node) {
692: switch (criteria) {
693: case CRITERIA_ANY:
694: return task; /* found */
695: case CRITERIA_CALL:
696: if (TASK_FUNC(task) == (sched_task_func_t) param)
697: return task; /* found */
698: break;
699: case CRITERIA_ARG:
700: if (TASK_ARG(task) == param)
701: return task; /* found */
702: break;
703: case CRITERIA_FD:
704: if (TASK_FD(task) == (intptr_t) param)
705: return task; /* found */
706: break;
707: case CRITERIA_ID:
708: case CRITERIA_VAL:
709: if (TASK_VAL(task) == (u_long) param)
710: return task; /* found */
711: break;
712: case CRITERIA_TS:
713: if (!sched_timespeccmp(&TASK_TS(task),
714: (struct timespec*) param, -))
715: return task; /* found */
716: break;
717: case CRITERIA_DATA:
718: if (TASK_DATA(task) == param)
719: return task; /* found */
720: break;
721: case CRITERIA_DATLEN:
722: if (TASK_DATLEN(task) == (size_t) param)
723: return task; /* found */
724: break;
725: default:
726: sched_SetErr(EINVAL, "Invalid parameter criteria %d",
727: criteria);
728: return NULL; /* not found */
729: }
730: }
731:
732: return NULL; /* not found */
733: }
734:
735: /*
736: * schedCancel() - Cancel task from scheduler
737: *
738: * @task = task
739: * return: -1 error or 0 ok
740: */
741: int
742: schedCancel(sched_task_t * __restrict task)
743: {
744: sched_queue_t *queue;
745:
746: if (!task || !TASK_ROOT(task))
747: return -1;
748:
749: if (TASK_ROOT(task)->root_hooks.hook_exec.cancel)
750: if (TASK_ROOT(task)->root_hooks.hook_exec.cancel(task, NULL))
751: return -1;
752:
753: switch (TASK_TYPE(task)) {
754: case taskREAD:
755: queue = &TASK_ROOT(task)->root_read;
756: break;
757: case taskWRITE:
758: queue = &TASK_ROOT(task)->root_write;
759: break;
760: case taskTIMER:
761: queue = &TASK_ROOT(task)->root_timer;
762: break;
763: case taskALARM:
764: queue = &TASK_ROOT(task)->root_alarm;
765: break;
766: case taskRTC:
767: queue = &TASK_ROOT(task)->root_rtc;
768: break;
769: case taskNODE:
770: queue = &TASK_ROOT(task)->root_node;
771: break;
772: case taskPROC:
773: queue = &TASK_ROOT(task)->root_proc;
774: break;
775: case taskSIGNAL:
776: queue = &TASK_ROOT(task)->root_signal;
777: break;
778: case taskAIO:
779: queue = &TASK_ROOT(task)->root_aio;
780: break;
781: case taskLIO:
782: queue = &TASK_ROOT(task)->root_lio;
783: break;
784: case taskUSER:
785: queue = &TASK_ROOT(task)->root_user;
786: break;
787: case taskEVENT:
788: queue = &TASK_ROOT(task)->root_event;
789: break;
790: case taskTASK:
791: queue = &TASK_ROOT(task)->root_task;
792: break;
793: case taskSUSPEND:
794: queue = &TASK_ROOT(task)->root_suspend;
795: break;
796: case taskREADY:
797: queue = &TASK_ROOT(task)->root_ready;
798: break;
799: case taskTHREAD:
800: queue = &TASK_ROOT(task)->root_thread;
801: break;
802: default:
803: queue = NULL;
804: }
805: if (queue)
806: remove_task_from(task, queue);
807: if (TASK_TYPE(task) != taskUNUSE)
808: sched_unuseTask(task);
809:
810: return 0;
811: }
812:
813: /*
814: * schedCancelby() - Cancel task from scheduler by criteria
815: *
816: * @root = root task
817: * @type = cancel from queue type, if =taskMAX cancel same task from all queues
818: * @criteria = find task by criteria
819: * [ CRITERIA_ANY|CRITERIA_CALL|CRITERIA_ARG|CRITERIA_FD|CRITERIA_VAL|
820: * CRITERIA_ID|CRITERIA_TS|CRITERIA_DATA|CRITERIA_DATLEN ]
821: * @param = search parameter
822: * @hook = custom cleanup hook function, may be NULL
823: * return: -1 error, -2 error in sub-stage cancel execution, -3 error from custom hook or 0 ok
824: */
825: int
826: schedCancelby(sched_root_task_t * __restrict root, sched_task_type_t type,
827: u_char criteria, void *param, sched_hook_func_t hook)
828: {
829: sched_task_t *task, *tmp;
830: sched_queue_t *queue;
831: register int flg = 0;
832:
833: if (!root)
834: return -1;
835: /* if type == taskMAX check in all queues */
836: if (type == taskMAX) {
837: if (schedCancelby(root, taskREAD, criteria, param, hook))
838: return -2;
839: if (schedCancelby(root, taskWRITE, criteria, param, hook))
840: return -2;
841: if (schedCancelby(root, taskTIMER, criteria, param, hook))
842: return -2;
843: if (schedCancelby(root, taskALARM, criteria, param, hook))
844: return -2;
845: if (schedCancelby(root, taskRTC, criteria, param, hook))
846: return -2;
847: if (schedCancelby(root, taskNODE, criteria, param, hook))
848: return -2;
849: if (schedCancelby(root, taskPROC, criteria, param, hook))
850: return -2;
851: if (schedCancelby(root, taskSIGNAL, criteria, param, hook))
852: return -2;
853: if (schedCancelby(root, taskAIO, criteria, param, hook))
854: return -2;
855: if (schedCancelby(root, taskLIO, criteria, param, hook))
856: return -2;
857: if (schedCancelby(root, taskUSER, criteria, param, hook))
858: return -2;
859: if (schedCancelby(root, taskEVENT, criteria, param, hook))
860: return -2;
861: if (schedCancelby(root, taskTASK, criteria, param, hook))
862: return -2;
863: if (schedCancelby(root, taskSUSPEND, criteria, param, hook))
864: return -2;
865: if (schedCancelby(root, taskREADY, criteria, param, hook))
866: return -2;
867: if (schedCancelby(root, taskTHREAD, criteria, param, hook))
868: return -2;
869: return 0;
870: }
871: /* choosen queue */
872: switch (type) {
873: case taskREAD:
874: queue = &root->root_read;
875: break;
876: case taskWRITE:
877: queue = &root->root_write;
878: break;
879: case taskTIMER:
880: queue = &root->root_timer;
881: break;
882: case taskALARM:
883: queue = &root->root_alarm;
884: break;
885: case taskRTC:
886: queue = &root->root_rtc;
887: break;
888: case taskNODE:
889: queue = &root->root_node;
890: break;
891: case taskPROC:
892: queue = &root->root_proc;
893: break;
894: case taskSIGNAL:
895: queue = &root->root_signal;
896: break;
897: case taskAIO:
898: queue = &root->root_aio;
899: break;
900: case taskLIO:
901: queue = &root->root_lio;
902: break;
903: case taskUSER:
904: queue = &root->root_user;
905: break;
906: case taskEVENT:
907: queue = &root->root_event;
908: break;
909: case taskTASK:
910: queue = &root->root_task;
911: break;
912: case taskSUSPEND:
913: queue = &root->root_suspend;
914: break;
915: case taskREADY:
916: queue = &root->root_ready;
917: break;
918: case taskTHREAD:
919: queue = &root->root_thread;
920: break;
921: default:
922: return 0;
923: }
924:
925: SCHED_QLOCK(root, type);
926: TAILQ_FOREACH_SAFE(task, queue, task_node, tmp) {
927: flg ^= flg;
928: switch (criteria) {
929: case CRITERIA_ANY:
930: flg = 1;
931: break;
932: case CRITERIA_CALL:
933: if (TASK_FUNC(task) == (sched_task_func_t) param)
934: flg = 1;
935: break;
936: case CRITERIA_ARG:
937: if (TASK_ARG(task) == param)
938: flg = 1;
939: break;
940: case CRITERIA_FD:
941: if (TASK_FD(task) == (intptr_t) param)
942: flg = 1;
943: break;
944: case CRITERIA_ID:
945: case CRITERIA_VAL:
946: if (TASK_VAL(task) == (u_long) param)
947: flg = 1;
948: break;
949: case CRITERIA_TS:
950: if (!sched_timespeccmp(&TASK_TS(task), (struct timespec*) param, -))
951: flg = 1;
952: break;
953: case CRITERIA_DATA:
954: if (TASK_DATA(task) == param)
955: flg = 1;
956: break;
957: case CRITERIA_DATLEN:
958: if (TASK_DATLEN(task) == (size_t) param)
959: flg = 1;
960: break;
961: default:
962: sched_SetErr(EINVAL, "Invalid parameter criteria %d", criteria);
963: flg = -1;
964: }
965: if (flg < 0) /* error */
966: break;
967: /* cancel choosen task */
968: if (flg > 0) {
969: if (TASK_ROOT(task)->root_hooks.hook_exec.cancel)
970: if (TASK_ROOT(task)->root_hooks.hook_exec.cancel(task, NULL)) {
971: flg = -1;
972: break;
973: }
974: /* custom hook */
975: if (hook)
976: if (hook(task, NULL)) {
977: flg = -3;
978: break;
979: }
980:
981: TAILQ_REMOVE(queue, task, task_node);
982: if (TASK_TYPE(task) != taskUNUSE)
983: sched_unuseTask(task);
984:
985: flg ^= flg; /* ok */
986: }
987: }
988: SCHED_QUNLOCK(root, type);
989:
990: return flg;
991: }
992:
993: /*
994: * schedRun() - Scheduler *run loop*
995: *
996: * @root = root task
997: * @killState = kill condition variable, if !=0 stop scheduler loop
998: * return: -1 error or 0 ok
999: */
1000: int
1001: schedRun(sched_root_task_t *root, volatile intptr_t * __restrict killState)
1002: {
1003: sched_task_t *task;
1004:
1005: if (!root)
1006: return -1;
1007:
1008: if (root->root_hooks.hook_exec.run)
1009: if (root->root_hooks.hook_exec.run(root, NULL))
1010: return -1;
1011:
1012: if (killState) {
1013: if (root->root_hooks.hook_exec.condition)
1014: /* condition scheduler loop */
1015: while (root && root->root_hooks.hook_exec.fetch &&
1016: root->root_hooks.hook_exec.condition &&
1017: root->root_hooks.hook_exec.condition(root, (void*) killState)) {
1018: if ((task = root->root_hooks.hook_exec.fetch(root, NULL)))
1019: root->root_ret = schedCall(task);
1020: }
1021: else
1022: /* trigger scheduler loop */
1023: while (!*killState && root && root->root_hooks.hook_exec.fetch) {
1024: if ((task = root->root_hooks.hook_exec.fetch(root, NULL)))
1025: root->root_ret = schedCall(task);
1026: }
1027: } else
1028: /* infinite scheduler loop */
1029: while (root && root->root_hooks.hook_exec.fetch)
1030: if ((task = root->root_hooks.hook_exec.fetch(root, NULL)))
1031: root->root_ret = schedCall(task);
1032:
1033: return 0;
1034: }
1035:
1036: /*
1037: * schedPolling() - Polling timeout period if no timer task is present
1038: *
1039: * @root = root task
1040: * @ts = timeout polling period, if ==NULL INFINIT timeout
1041: * @tsold = old timeout polling if !=NULL
1042: * return: -1 error or 0 ok
1043: */
1044: int
1045: schedPolling(sched_root_task_t * __restrict root, struct timespec * __restrict ts,
1046: struct timespec * __restrict tsold)
1047: {
1048: if (!root)
1049: return -1;
1050:
1051: if (tsold)
1052: *tsold = root->root_poll;
1053:
1054: if (!ts)
1055: sched_timespecinf(&root->root_poll);
1056: else
1057: root->root_poll = *ts;
1058:
1059: return 0;
1060: }
1061:
1062: /*
1063: * schedTermCondition() - Activate hook for scheduler condition kill
1064: *
1065: * @root = root task
1066: * @condValue = condition value, kill schedRun() if condValue == killState
1067: * return: -1 error or 0 ok
1068: */
1069: int
1070: schedTermCondition(sched_root_task_t * __restrict root, intptr_t * __restrict condValue)
1071: {
1072: if (!root && !condValue)
1073: return -1;
1074:
1075: *root->root_cond = *condValue;
1076: root->root_hooks.hook_exec.condition = sched_hook_condition;
1077: return 0;
1078: }
1079:
1080: /*
1081: * schedResumeby() - Resume suspended task
1082: *
1083: * @root = root task
1084: * @criteria = find task by criteria
1085: * [CRITERIA_ANY|CRITERIA_ID|CRITERIA_VAL|CRITERIA_DATA]
1086: * @param = search parameter (sched_task_t *task| unsigned long id)
1087: * return: -1 error or 0 resumed ok
1088: */
1089: int
1090: schedResumeby(sched_root_task_t * __restrict root, u_char criteria, void *param)
1091: {
1092: sched_task_t *task, *tmp;
1093: register int flg = 0;
1094:
1095: if (!root)
1096: return -1;
1097:
1098: SCHED_QLOCK(root, taskSUSPEND);
1099: TAILQ_FOREACH_SAFE(task, &root->root_suspend, task_node, tmp) {
1100: flg ^= flg;
1101: switch (criteria) {
1102: case CRITERIA_ANY:
1103: flg = 1;
1104: break;
1105: case CRITERIA_ID:
1106: case CRITERIA_VAL:
1107: if (TASK_VAL(task) == (u_long) param)
1108: flg = 1;
1109: break;
1110: case CRITERIA_DATA:
1111: if (TASK_ID(task) == (sched_task_t*) param)
1112: flg = 1;
1113: break;
1114: default:
1115: sched_SetErr(EINVAL, "Invalid parameter criteria %d", criteria);
1116: flg = -1;
1117: }
1118: if (flg < 0)
1119: break;
1120: /* resume choosen task */
1121: if (flg > 0) {
1122: if (root->root_hooks.hook_exec.resume)
1123: if (root->root_hooks.hook_exec.resume(task, NULL)) {
1124: flg = -1;
1125: break;
1126: }
1127:
1128: TAILQ_REMOVE(&root->root_suspend, task, task_node);
1129:
1130: task->task_type = taskREADY;
1131: insert_task_to(task, &root->root_ready);
1132:
1133: flg ^= flg; /* ok */
1134: }
1135: }
1136: SCHED_QUNLOCK(root, taskSUSPEND);
1137:
1138: return flg;
1139: }
1140:
1141: static void *
1142: _sched_sigDisp(void *arg)
1143: {
1144: sched_root_task_t *root = arg;
1145: sched_task_t *task, *tmp;
1146: int sig, flg;
1147: sigset_t ss;
1148:
1149: sigfillset(&ss);
1150: while (root->root_sigthr) {
1151: if (sigwait(&ss, &sig))
1152: continue;
1153:
1154: pthread_mutex_lock(&root->root_sigmtx);
1155: if (!sigismember(&root->root_sigset, sig)) {
1156: pthread_mutex_unlock(&root->root_sigmtx);
1157: continue;
1158: }
1159:
1160: flg = 0;
1161: TAILQ_FOREACH_SAFE(task, &root->root_signal, task_node, tmp) {
1162: if (TASK_VAL(task) == (uintptr_t) sig) {
1163: if (!flg) {
1164: TASK_RET(task) = 0;
1165: TASK_FLAG(task) = 0;
1166:
1167: /* remove signal handle */
1168: transit_task2ready(task, &root->root_signal);
1169: }
1170: flg++;
1171: }
1172: }
1173: if (flg < 2)
1174: sigdelset(&root->root_sigset, sig);
1175:
1176: pthread_mutex_unlock(&root->root_sigmtx);
1177: }
1178:
1179: return NULL;
1180: }
1181:
1182: /*
1183: * schedSignalDispatch() - Activate or Deactivate signal dispatcher
1184: *
1185: * @root = root task
1186: * @on = Activate or =0 deactivate
1187: * return: -1 error or 0 ok
1188: */
1189: int
1190: schedSignalDispatch(sched_root_task_t * __restrict root, int on)
1191: {
1192: sigset_t ss;
1193: struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 };
1194: #ifndef HAVE_LIBPTHREAD
1195: sched_SetErr(ENOTSUP, "Library has not support pthreads");
1196: return -1;
1197: #else
1198: pthread_attr_t attr;
1199: #endif
1200:
1201: #if SUP_ENABLE == KQ_SUPPORT
1202: return 0;
1203: #endif
1204:
1205: if (!on) {
1206: pthread_cancel(root->root_sigthr);
1207: #ifdef __linux__
1208: root->root_sigthr = 0L;
1209: #else
1210: root->root_sigthr = NULL;
1211: #endif
1212: return 0;
1213: }
1214:
1215: /* if we have not using polling, then we must turn on */
1216: if (sched_timespecisinf(&root->root_poll))
1217: schedPolling(root, &ts, NULL);
1218:
1219: pthread_attr_init(&attr);
1220: pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1221: #ifdef SCHED_RR
1222: pthread_attr_setschedpolicy(&attr, SCHED_RR);
1223: #else
1224: pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
1225: #endif
1226:
1227: sigfillset(&ss);
1228: pthread_sigmask(SIG_BLOCK, &ss, NULL);
1229:
1230: if (pthread_create(&root->root_sigthr, &attr, _sched_sigDisp, root)) {
1231: sched_SetErr(errno, "pthread_create(SignalDispatch) #%d - %s",
1232: errno, strerror(errno));
1233: #ifdef __linux__
1234: root->root_sigthr = 0L;
1235: #else
1236: root->root_sigthr = NULL;
1237: #endif
1238: pthread_sigmask(SIG_SETMASK, &root->root_oldset, NULL);
1239: return -1;
1240: }
1241:
1242: return 0;
1243: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>