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