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: hooks.c,v 1.6.4.2 2012/05/30 08:34:44 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: /*
51: * sched_hook_init() - Default INIT hook
52: *
53: * @root = root task
54: * @arg = unused
55: * return: <0 errors and 0 ok
56: */
57: void *
58: sched_hook_init(void *root, void *arg __unused)
59: {
60: sched_root_task_t *r = root;
61:
62: if (!r)
63: return (void*) -1;
64:
65: r->root_kq = kqueue();
66: if (r->root_kq == -1) {
67: LOGERR;
68: return (void*) -1;
69: }
70:
71: return NULL;
72: }
73:
74: /*
75: * sched_hook_fini() - Default FINI hook
76: *
77: * @root = root task
78: * @arg = unused
79: * return: <0 errors and 0 ok
80: */
81: void *
82: sched_hook_fini(void *root, void *arg __unused)
83: {
84: sched_root_task_t *r = root;
85:
86: if (!r)
87: return (void*) -1;
88:
89: if (r->root_kq > 2) {
90: close(r->root_kq);
91: r->root_kq = 0;
92: }
93:
94: return NULL;
95: }
96:
97: /*
98: * sched_hook_cancel() - Default CANCEL hook
99: *
100: * @task = current task
101: * @arg = unused
102: * return: <0 errors and 0 ok
103: */
104: void *
105: sched_hook_cancel(void *task, void *arg __unused)
106: {
107: sched_task_t *t = task;
108: struct kevent chg[1];
109: struct timespec timeout = { 0, 0 };
110: uintptr_t ident;
111:
112: if (!t || !TASK_ROOT(t))
113: return (void*) -1;
114:
115: switch (TASK_TYPE(t)) {
116: case taskREAD:
117: #ifdef __NetBSD__
118: EV_SET(&chg[0], TASK_FD(t), EVFILT_READ, EV_DELETE, 0, 0, (intptr_t) TASK_FD(t));
119: #else
120: EV_SET(&chg[0], TASK_FD(t), EVFILT_READ, EV_DELETE, 0, 0, (void*) TASK_FD(t));
121: #endif
122: kevent(TASK_ROOT(t)->root_kq, chg, 1, NULL, 0, &timeout);
123: break;
124: case taskWRITE:
125: #ifdef __NetBSD__
126: EV_SET(&chg[0], TASK_FD(t), EVFILT_WRITE, EV_DELETE, 0, 0, (intptr_t) TASK_FD(t));
127: #else
128: EV_SET(&chg[0], TASK_FD(t), EVFILT_WRITE, EV_DELETE, 0, 0, (void*) TASK_FD(t));
129: #endif
130: kevent(TASK_ROOT(t)->root_kq, chg, 1, NULL, 0, &timeout);
131: break;
132: case taskALARM:
133: if (TASK_DATA(t))
134: ident = (uintptr_t) TASK_DATA(t);
135: else
136: ident = (uintptr_t) TASK_FUNC(t);
137: #ifdef __NetBSD__
138: EV_SET(&chg[0], ident, EVFILT_TIMER, EV_DELETE, 0, 0, (intptr_t) ident);
139: #else
140: EV_SET(&chg[0], ident, EVFILT_TIMER, EV_DELETE, 0, 0, (void*) ident);
141: #endif
142: kevent(TASK_ROOT(t)->root_kq, chg, 1, NULL, 0, &timeout);
143: break;
144: default:
145: break;
146: }
147:
148: return NULL;
149: }
150:
151: /*
152: * sched_hook_read() - Default READ hook
153: *
154: * @task = current task
155: * @arg = unused
156: * return: <0 errors and 0 ok
157: */
158: void *
159: sched_hook_read(void *task, void *arg __unused)
160: {
161: sched_task_t *t = task;
162: struct kevent chg[1];
163: struct timespec timeout = { 0, 0 };
164:
165: if (!t || !TASK_ROOT(t))
166: return (void*) -1;
167:
168: #ifdef __NetBSD__
169: EV_SET(&chg[0], TASK_FD(t), EVFILT_READ, EV_ADD, 0, 0, (intptr_t) TASK_FD(t));
170: #else
171: EV_SET(&chg[0], TASK_FD(t), EVFILT_READ, EV_ADD, 0, 0, (void*) TASK_FD(t));
172: #endif
173: if (kevent(TASK_ROOT(t)->root_kq, chg, 1, NULL, 0, &timeout) == -1) {
174: if (TASK_ROOT(t)->root_hooks.hook_exec.exception)
175: TASK_ROOT(t)->root_hooks.hook_exec.exception(TASK_ROOT(t), NULL);
176: else
177: LOGERR;
178: return (void*) -1;
179: }
180:
181: return NULL;
182: }
183:
184: /*
185: * sched_hook_write() - Default WRITE hook
186: *
187: * @task = current task
188: * @arg = unused
189: * return: <0 errors and 0 ok
190: */
191: void *
192: sched_hook_write(void *task, void *arg __unused)
193: {
194: sched_task_t *t = task;
195: struct kevent chg[1];
196: struct timespec timeout = { 0, 0 };
197:
198: if (!t || !TASK_ROOT(t))
199: return (void*) -1;
200:
201: #ifdef __NetBSD__
202: EV_SET(&chg[0], TASK_FD(t), EVFILT_WRITE, EV_ADD, 0, 0, (intptr_t) TASK_FD(t));
203: #else
204: EV_SET(&chg[0], TASK_FD(t), EVFILT_WRITE, EV_ADD, 0, 0, (void*) TASK_FD(t));
205: #endif
206: if (kevent(TASK_ROOT(t)->root_kq, chg, 1, NULL, 0, &timeout) == -1) {
207: if (TASK_ROOT(t)->root_hooks.hook_exec.exception)
208: TASK_ROOT(t)->root_hooks.hook_exec.exception(TASK_ROOT(t), NULL);
209: else
210: LOGERR;
211: return (void*) -1;
212: }
213:
214: return NULL;
215: }
216:
217: /*
218: * sched_hook_alarm() - Default ALARM hook
219: *
220: * @task = current task
221: * @arg = unused
222: * return: <0 errors and 0 ok
223: */
224: void *
225: sched_hook_alarm(void *task, void *arg __unused)
226: {
227: sched_task_t *t = task;
228: struct kevent chg[1];
229: struct timespec timeout = { 0, 0 };
230: uintptr_t ident;
231:
232: if (!t || !TASK_ROOT(t))
233: return (void*) -1;
234:
235: if (TASK_DATA(t))
236: ident = (uintptr_t) TASK_DATA(t);
237: else
238: ident = (uintptr_t) TASK_FUNC(t);
239:
240: #ifdef __NetBSD__
241: EV_SET(&chg[0], ident, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0,
242: t->task_val.ts.tv_sec * 1000 + t->task_val.ts.tv_nsec / 1000000,
243: (intptr_t) ident);
244: #else
245: EV_SET(&chg[0], ident, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0,
246: t->task_val.ts.tv_sec * 1000 + t->task_val.ts.tv_nsec / 1000000,
247: (void*) ident);
248: #endif
249: if (kevent(TASK_ROOT(t)->root_kq, chg, 1, NULL, 0, &timeout) == -1) {
250: if (TASK_ROOT(t)->root_hooks.hook_exec.exception)
251: TASK_ROOT(t)->root_hooks.hook_exec.exception(TASK_ROOT(t), NULL);
252: else
253: LOGERR;
254: return (void*) -1;
255: }
256:
257: return NULL;
258: }
259:
260: /*
261: * sched_hook_fetch() - Default FETCH hook
262: *
263: * @root = root task
264: * @arg = unused
265: * return: NULL error or !=NULL fetched task
266: */
267: void *
268: sched_hook_fetch(void *root, void *arg __unused)
269: {
270: sched_root_task_t *r = root;
271: sched_task_t *task, *tmp;
272: struct timespec now, m, mtmp;
273: struct timespec *timeout;
274: struct kevent evt[1], res[KQ_EVENTS];
275: register int i;
276: int en;
277: uintptr_t ident;
278:
279: if (!r)
280: return NULL;
281:
282: /* get new task by queue priority */
283: while ((task = TAILQ_FIRST(&r->root_event))) {
284: #ifdef HAVE_LIBPTHREAD
285: pthread_mutex_lock(&r->root_mtx[taskEVENT]);
286: #endif
287: TAILQ_REMOVE(&r->root_event, task, task_node);
288: #ifdef HAVE_LIBPTHREAD
289: pthread_mutex_unlock(&r->root_mtx[taskEVENT]);
290: #endif
291: task->task_type = taskUNUSE;
292: #ifdef HAVE_LIBPTHREAD
293: pthread_mutex_lock(&r->root_mtx[taskUNUSE]);
294: #endif
295: TAILQ_INSERT_TAIL(&r->root_unuse, task, task_node);
296: #ifdef HAVE_LIBPTHREAD
297: pthread_mutex_unlock(&r->root_mtx[taskUNUSE]);
298: #endif
299: return task;
300: }
301: while ((task = TAILQ_FIRST(&r->root_ready))) {
302: #ifdef HAVE_LIBPTHREAD
303: pthread_mutex_lock(&r->root_mtx[taskREADY]);
304: #endif
305: TAILQ_REMOVE(&r->root_ready, task, task_node);
306: #ifdef HAVE_LIBPTHREAD
307: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
308: #endif
309: task->task_type = taskUNUSE;
310: #ifdef HAVE_LIBPTHREAD
311: pthread_mutex_lock(&r->root_mtx[taskUNUSE]);
312: #endif
313: TAILQ_INSERT_TAIL(&r->root_unuse, task, task_node);
314: #ifdef HAVE_LIBPTHREAD
315: pthread_mutex_unlock(&r->root_mtx[taskUNUSE]);
316: #endif
317: return task;
318: }
319:
320: #ifdef TIMER_WITHOUT_SORT
321: clock_gettime(CLOCK_MONOTONIC, &now);
322:
323: sched_timespecclear(&r->root_wait);
324: TAILQ_FOREACH(task, &r->root_timer, task_node) {
325: if (!sched_timespecisset(&r->root_wait))
326: r->root_wait = TASK_TS(task);
327: else if (sched_timespeccmp(&TASK_TS(task), &r->root_wait, -) < 0)
328: r->root_wait = TASK_TS(task);
329: }
330:
331: if (TAILQ_FIRST(&r->root_timer)) {
332: m = r->root_wait;
333: sched_timespecsub(&m, &now, &mtmp);
334: r->root_wait = mtmp;
335: } else {
336: /* set wait INFTIM */
337: sched_timespecinf(&r->root_wait);
338: }
339: #else
340: if (!TAILQ_FIRST(&r->root_eventlo) && (task = TAILQ_FIRST(&r->root_timer))) {
341: clock_gettime(CLOCK_MONOTONIC, &now);
342:
343: m = TASK_TS(task);
344: sched_timespecsub(&m, &now, &mtmp);
345: r->root_wait = mtmp;
346: } else {
347: /* set wait INFTIM */
348: sched_timespecinf(&r->root_wait);
349: }
350: #endif
351: /* if present member of eventLo, set NOWAIT */
352: if (TAILQ_FIRST(&r->root_eventlo))
353: sched_timespecclear(&r->root_wait);
354:
355: if (r->root_wait.tv_sec != -1 && r->root_wait.tv_nsec != -1)
356: timeout = &r->root_wait;
357: else if (sched_timespecisinf(&r->root_poll))
358: timeout = NULL;
359: else
360: timeout = &r->root_poll;
361: if ((en = kevent(r->root_kq, NULL, 0, res, KQ_EVENTS, timeout)) == -1) {
362: if (r->root_hooks.hook_exec.exception) {
363: if (r->root_hooks.hook_exec.exception(r, NULL))
364: return NULL;
365: } else if (errno != EINTR)
366: LOGERR;
367: return NULL;
368: }
369:
370: now.tv_sec = now.tv_nsec = 0;
371: /* Go and catch the cat into pipes ... */
372: for (i = 0; i < en; i++) {
373: memcpy(evt, &res[i], sizeof evt);
374: evt->flags = EV_DELETE;
375: /* Put read/write task to ready queue */
376: switch (res[i].filter) {
377: case EVFILT_READ:
378: TAILQ_FOREACH_SAFE(task, &r->root_read, task_node, tmp) {
379: if (TASK_FD(task) != ((intptr_t) res[i].udata))
380: continue;
381: /* remove read handle */
382: #ifdef HAVE_LIBPTHREAD
383: pthread_mutex_lock(&r->root_mtx[taskREAD]);
384: #endif
385: TAILQ_REMOVE(&r->root_read, task, task_node);
386: #ifdef HAVE_LIBPTHREAD
387: pthread_mutex_unlock(&r->root_mtx[taskREAD]);
388: #endif
389: if (r->root_hooks.hook_exec.exception && res[i].flags & EV_EOF) {
390: if (r->root_hooks.hook_exec.exception(r, (void*) EV_EOF)) {
391: task->task_type = taskUNUSE;
392: #ifdef HAVE_LIBPTHREAD
393: pthread_mutex_lock(&r->root_mtx[taskUNUSE]);
394: #endif
395: TAILQ_INSERT_TAIL(&r->root_unuse, task, task_node);
396: #ifdef HAVE_LIBPTHREAD
397: pthread_mutex_unlock(&r->root_mtx[taskUNUSE]);
398: #endif
399: } else {
400: task->task_type = taskREADY;
401: #ifdef HAVE_LIBPTHREAD
402: pthread_mutex_lock(&r->root_mtx[taskREADY]);
403: #endif
404: TAILQ_INSERT_TAIL(&r->root_ready, task, task_node);
405: #ifdef HAVE_LIBPTHREAD
406: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
407: #endif
408: }
409: } else {
410: task->task_type = taskREADY;
411: #ifdef HAVE_LIBPTHREAD
412: pthread_mutex_lock(&r->root_mtx[taskREADY]);
413: #endif
414: TAILQ_INSERT_TAIL(&r->root_ready, task, task_node);
415: #ifdef HAVE_LIBPTHREAD
416: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
417: #endif
418: }
419: break;
420: }
421: break;
422: case EVFILT_WRITE:
423: TAILQ_FOREACH_SAFE(task, &r->root_write, task_node, tmp) {
424: if (TASK_FD(task) != ((intptr_t) res[i].udata))
425: continue;
426: /* remove write handle */
427: #ifdef HAVE_LIBPTHREAD
428: pthread_mutex_lock(&r->root_mtx[taskWRITE]);
429: #endif
430: TAILQ_REMOVE(&r->root_write, task, task_node);
431: #ifdef HAVE_LIBPTHREAD
432: pthread_mutex_unlock(&r->root_mtx[taskWRITE]);
433: #endif
434: if (r->root_hooks.hook_exec.exception && res[i].flags & EV_EOF) {
435: if (r->root_hooks.hook_exec.exception(r, (void*) EV_EOF)) {
436: task->task_type = taskUNUSE;
437: #ifdef HAVE_LIBPTHREAD
438: pthread_mutex_lock(&r->root_mtx[taskUNUSE]);
439: #endif
440: TAILQ_INSERT_TAIL(&r->root_unuse, task, task_node);
441: #ifdef HAVE_LIBPTHREAD
442: pthread_mutex_unlock(&r->root_mtx[taskUNUSE]);
443: #endif
444: } else {
445: task->task_type = taskREADY;
446: #ifdef HAVE_LIBPTHREAD
447: pthread_mutex_lock(&r->root_mtx[taskREADY]);
448: #endif
449: TAILQ_INSERT_TAIL(&r->root_ready, task, task_node);
450: #ifdef HAVE_LIBPTHREAD
451: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
452: #endif
453: }
454: } else {
455: task->task_type = taskREADY;
456: #ifdef HAVE_LIBPTHREAD
457: pthread_mutex_lock(&r->root_mtx[taskREADY]);
458: #endif
459: TAILQ_INSERT_TAIL(&r->root_ready, task, task_node);
460: #ifdef HAVE_LIBPTHREAD
461: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
462: #endif
463: }
464: break;
465: }
466: break;
467: case EVFILT_TIMER:
468: TAILQ_FOREACH_SAFE(task, &r->root_alarm, task_node, tmp) {
469: if (TASK_DATA(task))
470: ident = (uintptr_t) TASK_DATA(task);
471: else
472: ident = (uintptr_t) TASK_FUNC(task);
473: if (ident != ((uintptr_t) res[i].udata))
474: continue;
475: /* remove alarm handle */
476: #ifdef HAVE_LIBPTHREAD
477: pthread_mutex_lock(&r->root_mtx[taskALARM]);
478: #endif
479: TAILQ_REMOVE(&r->root_alarm, task, task_node);
480: #ifdef HAVE_LIBPTHREAD
481: pthread_mutex_unlock(&r->root_mtx[taskALARM]);
482: #endif
483: task->task_type = taskREADY;
484: #ifdef HAVE_LIBPTHREAD
485: pthread_mutex_lock(&r->root_mtx[taskREADY]);
486: #endif
487: TAILQ_INSERT_TAIL(&r->root_ready, task, task_node);
488: #ifdef HAVE_LIBPTHREAD
489: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
490: #endif
491: break;
492: }
493: break;
494: }
495: if (kevent(r->root_kq, evt, 1, NULL, 0, &now) == -1) {
496: if (r->root_hooks.hook_exec.exception) {
497: if (r->root_hooks.hook_exec.exception(r, NULL))
498: return NULL;
499: } else
500: LOGERR;
501: }
502: }
503:
504: /* timer update & put in ready queue */
505: clock_gettime(CLOCK_MONOTONIC, &now);
506:
507: TAILQ_FOREACH_SAFE(task, &r->root_timer, task_node, tmp)
508: if (sched_timespeccmp(&now, &TASK_TS(task), -) >= 0) {
509: #ifdef HAVE_LIBPTHREAD
510: pthread_mutex_lock(&r->root_mtx[taskTIMER]);
511: #endif
512: TAILQ_REMOVE(&r->root_timer, task, task_node);
513: #ifdef HAVE_LIBPTHREAD
514: pthread_mutex_unlock(&r->root_mtx[taskTIMER]);
515: #endif
516: task->task_type = taskREADY;
517: #ifdef HAVE_LIBPTHREAD
518: pthread_mutex_lock(&r->root_mtx[taskREADY]);
519: #endif
520: TAILQ_INSERT_TAIL(&r->root_ready, task, task_node);
521: #ifdef HAVE_LIBPTHREAD
522: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
523: #endif
524: }
525:
526: /* put eventlo priority task to ready queue, if there is no ready task or
527: reach max missed fetch-rotate */
528: if ((task = TAILQ_FIRST(&r->root_eventlo))) {
529: if (!TAILQ_FIRST(&r->root_ready) || r->root_eventlo_miss > MAX_EVENTLO_MISS) {
530: r->root_eventlo_miss = 0;
531:
532: #ifdef HAVE_LIBPTHREAD
533: pthread_mutex_lock(&r->root_mtx[taskEVENTLO]);
534: #endif
535: TAILQ_REMOVE(&r->root_eventlo, task, task_node);
536: #ifdef HAVE_LIBPTHREAD
537: pthread_mutex_unlock(&r->root_mtx[taskEVENTLO]);
538: #endif
539: task->task_type = taskREADY;
540: #ifdef HAVE_LIBPTHREAD
541: pthread_mutex_lock(&r->root_mtx[taskREADY]);
542: #endif
543: TAILQ_INSERT_TAIL(&r->root_ready, task, task_node);
544: #ifdef HAVE_LIBPTHREAD
545: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
546: #endif
547: } else
548: r->root_eventlo_miss++;
549: } else
550: r->root_eventlo_miss = 0;
551:
552: /* OK, lets get ready task !!! */
553: task = TAILQ_FIRST(&r->root_ready);
554: if (!(task))
555: return NULL;
556:
557: #ifdef HAVE_LIBPTHREAD
558: pthread_mutex_lock(&r->root_mtx[taskREADY]);
559: #endif
560: TAILQ_REMOVE(&r->root_ready, task, task_node);
561: #ifdef HAVE_LIBPTHREAD
562: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
563: #endif
564: task->task_type = taskUNUSE;
565: #ifdef HAVE_LIBPTHREAD
566: pthread_mutex_lock(&r->root_mtx[taskUNUSE]);
567: #endif
568: TAILQ_INSERT_TAIL(&r->root_unuse, task, task_node);
569: #ifdef HAVE_LIBPTHREAD
570: pthread_mutex_unlock(&r->root_mtx[taskUNUSE]);
571: #endif
572: return task;
573: }
574:
575: /*
576: * sched_hook_exception() - Default EXCEPTION hook
577: *
578: * @root = root task
579: * @arg = custom handling: if arg == EV_EOF or other value; default: arg == NULL log errno
580: * return: <0 errors and 0 ok
581: */
582: void *
583: sched_hook_exception(void *root, void *arg)
584: {
585: sched_root_task_t *r = root;
586:
587: if (!r)
588: return NULL;
589:
590: /* custom exception handling ... */
591: if (arg) {
592: if (arg == (void*) EV_EOF)
593: return NULL;
594: return (void*) -1; /* raise scheduler error!!! */
595: }
596:
597: /* if error hook exists */
598: if (r->root_hooks.hook_root.error)
599: return (r->root_hooks.hook_root.error(root, (void*) ((intptr_t) errno)));
600:
601: /* default case! */
602: LOGERR;
603: return NULL;
604: }
605:
606: /*
607: * sched_hook_condition() - Default CONDITION hook
608: *
609: * @root = root task
610: * @arg = killState from schedRun()
611: * return: NULL kill scheduler loop or !=NULL ok
612: */
613: void *
614: sched_hook_condition(void *root, void *arg)
615: {
616: sched_root_task_t *r = root;
617:
618: if (!r)
619: return NULL;
620:
621: return (void*) (r->root_cond - *(intptr_t*) arg);
622: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>