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.5.2.3 2012/05/10 14:44:22 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:
111: if (!t || !TASK_ROOT(t))
112: return (void*) -1;
113:
114: switch (TASK_TYPE(t)) {
115: case taskREAD:
116: #ifdef __NetBSD__
117: EV_SET(&chg[0], TASK_FD(t), EVFILT_READ, EV_DELETE, 0, 0, (intptr_t) TASK_FD(t));
118: #else
119: EV_SET(&chg[0], TASK_FD(t), EVFILT_READ, EV_DELETE, 0, 0, (void*) TASK_FD(t));
120: #endif
121: kevent(TASK_ROOT(t)->root_kq, chg, 1, NULL, 0, &timeout);
122: break;
123: case taskWRITE:
124: #ifdef __NetBSD__
125: EV_SET(&chg[0], TASK_FD(t), EVFILT_WRITE, EV_DELETE, 0, 0, (intptr_t) TASK_FD(t));
126: #else
127: EV_SET(&chg[0], TASK_FD(t), EVFILT_WRITE, EV_DELETE, 0, 0, (void*) TASK_FD(t));
128: #endif
129: kevent(TASK_ROOT(t)->root_kq, chg, 1, NULL, 0, &timeout);
130: break;
131: default:
132: break;
133: }
134:
135: return NULL;
136: }
137:
138: /*
139: * sched_hook_read() - Default READ hook
140: *
141: * @task = current task
142: * @arg = unused
143: * return: <0 errors and 0 ok
144: */
145: void *
146: sched_hook_read(void *task, void *arg __unused)
147: {
148: sched_task_t *t = task;
149: struct kevent chg[1];
150: struct timespec timeout = { 0, 0 };
151:
152: if (!t || !TASK_ROOT(t))
153: return (void*) -1;
154:
155: #ifdef __NetBSD__
156: EV_SET(&chg[0], TASK_FD(t), EVFILT_READ, EV_ADD, 0, 0, (intptr_t) TASK_FD(t));
157: #else
158: EV_SET(&chg[0], TASK_FD(t), EVFILT_READ, EV_ADD, 0, 0, (void*) TASK_FD(t));
159: #endif
160: if (kevent(TASK_ROOT(t)->root_kq, chg, 1, NULL, 0, &timeout) == -1) {
161: if (TASK_ROOT(t)->root_hooks.hook_exec.exception)
162: TASK_ROOT(t)->root_hooks.hook_exec.exception(TASK_ROOT(t), NULL);
163: else
164: LOGERR;
165: return (void*) -1;
166: }
167:
168: return NULL;
169: }
170:
171: /*
172: * sched_hook_write() - Default WRITE hook
173: *
174: * @task = current task
175: * @arg = unused
176: * return: <0 errors and 0 ok
177: */
178: void *
179: sched_hook_write(void *task, void *arg __unused)
180: {
181: sched_task_t *t = task;
182: struct kevent chg[1];
183: struct timespec timeout = { 0, 0 };
184:
185: if (!t || !TASK_ROOT(t))
186: return (void*) -1;
187:
188: #ifdef __NetBSD__
189: EV_SET(&chg[0], TASK_FD(t), EVFILT_WRITE, EV_ADD, 0, 0, (intptr_t) TASK_FD(t));
190: #else
191: EV_SET(&chg[0], TASK_FD(t), EVFILT_WRITE, EV_ADD, 0, 0, (void*) TASK_FD(t));
192: #endif
193: if (kevent(TASK_ROOT(t)->root_kq, chg, 1, NULL, 0, &timeout) == -1) {
194: if (TASK_ROOT(t)->root_hooks.hook_exec.exception)
195: TASK_ROOT(t)->root_hooks.hook_exec.exception(TASK_ROOT(t), NULL);
196: else
197: LOGERR;
198: return (void*) -1;
199: }
200:
201: return NULL;
202: }
203:
204: /*
205: * sched_hook_fetch() - Default FETCH hook
206: *
207: * @root = root task
208: * @arg = unused
209: * return: NULL error or !=NULL fetched task
210: */
211: void *
212: sched_hook_fetch(void *root, void *arg __unused)
213: {
214: sched_root_task_t *r = root;
215: sched_task_t *task;
216: struct timespec now, m, mtmp;
217: struct timespec *timeout;
218: struct kevent evt[1], res[KQ_EVENTS];
219: register int i;
220: int en;
221:
222: if (!r)
223: return NULL;
224:
225: /* get new task by queue priority */
226: while ((task = TAILQ_FIRST(&r->root_event))) {
227: #ifdef HAVE_LIBPTHREAD
228: pthread_mutex_lock(&r->root_mtx[taskEVENT]);
229: #endif
230: TAILQ_REMOVE(&r->root_event, task, task_node);
231: #ifdef HAVE_LIBPTHREAD
232: pthread_mutex_unlock(&r->root_mtx[taskEVENT]);
233: #endif
234: task->task_type = taskUNUSE;
235: #ifdef HAVE_LIBPTHREAD
236: pthread_mutex_lock(&r->root_mtx[taskUNUSE]);
237: #endif
238: TAILQ_INSERT_TAIL(&r->root_unuse, task, task_node);
239: #ifdef HAVE_LIBPTHREAD
240: pthread_mutex_unlock(&r->root_mtx[taskUNUSE]);
241: #endif
242: return task;
243: }
244: while ((task = TAILQ_FIRST(&r->root_ready))) {
245: #ifdef HAVE_LIBPTHREAD
246: pthread_mutex_lock(&r->root_mtx[taskREADY]);
247: #endif
248: TAILQ_REMOVE(&r->root_ready, task, task_node);
249: #ifdef HAVE_LIBPTHREAD
250: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
251: #endif
252: task->task_type = taskUNUSE;
253: #ifdef HAVE_LIBPTHREAD
254: pthread_mutex_lock(&r->root_mtx[taskUNUSE]);
255: #endif
256: TAILQ_INSERT_TAIL(&r->root_unuse, task, task_node);
257: #ifdef HAVE_LIBPTHREAD
258: pthread_mutex_unlock(&r->root_mtx[taskUNUSE]);
259: #endif
260: return task;
261: }
262:
263: #ifdef TIMER_WITHOUT_SORT
264: clock_gettime(CLOCK_MONOTONIC, &now);
265:
266: sched_timespecclear(&r->root_wait);
267: TAILQ_FOREACH(task, &r->root_timer, task_node) {
268: if (!sched_timespecisset(&r->root_wait))
269: r->root_wait = TASK_TS(task);
270: else if (sched_timespeccmp(&TASK_TS(task), &r->root_wait, -) < 0)
271: r->root_wait = TASK_TS(task);
272: }
273:
274: if (TAILQ_FIRST(&r->root_timer)) {
275: m = r->root_wait;
276: sched_timespecsub(&m, &now, &mtmp);
277: r->root_wait = mtmp;
278: } else {
279: /* set wait INFTIM */
280: sched_timespecinf(&r->root_wait);
281: }
282: #else
283: if (!TAILQ_FIRST(&r->root_eventlo) && (task = TAILQ_FIRST(&r->root_timer))) {
284: clock_gettime(CLOCK_MONOTONIC, &now);
285:
286: m = TASK_TS(task);
287: sched_timespecsub(&m, &now, &mtmp);
288: r->root_wait = mtmp;
289: } else {
290: /* set wait INFTIM */
291: sched_timespecinf(&r->root_wait);
292: }
293: #endif
294: /* if present member of eventLo, set NOWAIT */
295: if (TAILQ_FIRST(&r->root_eventlo))
296: sched_timespecclear(&r->root_wait);
297:
298: if (r->root_wait.tv_sec != -1 && r->root_wait.tv_nsec != -1)
299: timeout = &r->root_wait;
300: else if (sched_timespecisinf(&r->root_poll))
301: timeout = NULL;
302: else
303: timeout = &r->root_poll;
304: if ((en = kevent(r->root_kq, NULL, 0, res, KQ_EVENTS, timeout)) == -1) {
305: if (r->root_hooks.hook_exec.exception) {
306: if (r->root_hooks.hook_exec.exception(r, NULL))
307: return NULL;
308: } else if (errno != EINTR)
309: LOGERR;
310: return NULL;
311: }
312:
313: now.tv_sec = now.tv_nsec = 0;
314: /* Go and catch the cat into pipes ... */
315: for (i = 0; i < en; i++) {
316: memcpy(evt, &res[i], sizeof evt);
317: evt->flags = EV_DELETE;
318: /* Put read/write task to ready queue */
319: switch (res[i].filter) {
320: case EVFILT_READ:
321: TAILQ_FOREACH(task, &r->root_read, task_node) {
322: if (TASK_FD(task) != ((intptr_t) res[i].udata))
323: continue;
324: /* remove read handle */
325: #ifdef HAVE_LIBPTHREAD
326: pthread_mutex_lock(&r->root_mtx[taskREAD]);
327: #endif
328: TAILQ_REMOVE(&r->root_read, task, task_node);
329: #ifdef HAVE_LIBPTHREAD
330: pthread_mutex_unlock(&r->root_mtx[taskREAD]);
331: #endif
332: if (r->root_hooks.hook_exec.exception && res[i].flags & EV_EOF) {
333: if (r->root_hooks.hook_exec.exception(r, (void*) EV_EOF)) {
334: task->task_type = taskUNUSE;
335: #ifdef HAVE_LIBPTHREAD
336: pthread_mutex_lock(&r->root_mtx[taskUNUSE]);
337: #endif
338: TAILQ_INSERT_TAIL(&r->root_unuse, task, task_node);
339: #ifdef HAVE_LIBPTHREAD
340: pthread_mutex_unlock(&r->root_mtx[taskUNUSE]);
341: #endif
342: } else {
343: task->task_type = taskREADY;
344: #ifdef HAVE_LIBPTHREAD
345: pthread_mutex_lock(&r->root_mtx[taskREADY]);
346: #endif
347: TAILQ_INSERT_TAIL(&r->root_ready, task, task_node);
348: #ifdef HAVE_LIBPTHREAD
349: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
350: #endif
351: }
352: } else {
353: task->task_type = taskREADY;
354: #ifdef HAVE_LIBPTHREAD
355: pthread_mutex_lock(&r->root_mtx[taskREADY]);
356: #endif
357: TAILQ_INSERT_TAIL(&r->root_ready, task, task_node);
358: #ifdef HAVE_LIBPTHREAD
359: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
360: #endif
361: }
362: break;
363: }
364: break;
365: case EVFILT_WRITE:
366: TAILQ_FOREACH(task, &r->root_write, task_node) {
367: if (TASK_FD(task) != ((intptr_t) res[i].udata))
368: continue;
369: /* remove write handle */
370: #ifdef HAVE_LIBPTHREAD
371: pthread_mutex_lock(&r->root_mtx[taskWRITE]);
372: #endif
373: TAILQ_REMOVE(&r->root_write, task, task_node);
374: #ifdef HAVE_LIBPTHREAD
375: pthread_mutex_unlock(&r->root_mtx[taskWRITE]);
376: #endif
377: if (r->root_hooks.hook_exec.exception && res[i].flags & EV_EOF) {
378: if (r->root_hooks.hook_exec.exception(r, (void*) EV_EOF)) {
379: task->task_type = taskUNUSE;
380: #ifdef HAVE_LIBPTHREAD
381: pthread_mutex_lock(&r->root_mtx[taskUNUSE]);
382: #endif
383: TAILQ_INSERT_TAIL(&r->root_unuse, task, task_node);
384: #ifdef HAVE_LIBPTHREAD
385: pthread_mutex_unlock(&r->root_mtx[taskUNUSE]);
386: #endif
387: } else {
388: task->task_type = taskREADY;
389: #ifdef HAVE_LIBPTHREAD
390: pthread_mutex_lock(&r->root_mtx[taskREADY]);
391: #endif
392: TAILQ_INSERT_TAIL(&r->root_ready, task, task_node);
393: #ifdef HAVE_LIBPTHREAD
394: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
395: #endif
396: }
397: } else {
398: task->task_type = taskREADY;
399: #ifdef HAVE_LIBPTHREAD
400: pthread_mutex_lock(&r->root_mtx[taskREADY]);
401: #endif
402: TAILQ_INSERT_TAIL(&r->root_ready, task, task_node);
403: #ifdef HAVE_LIBPTHREAD
404: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
405: #endif
406: }
407: break;
408: }
409: break;
410: }
411: if (kevent(r->root_kq, evt, 1, NULL, 0, &now) == -1) {
412: if (r->root_hooks.hook_exec.exception) {
413: if (r->root_hooks.hook_exec.exception(r, NULL))
414: return NULL;
415: } else
416: LOGERR;
417: }
418: }
419:
420: /* timer update & put in ready queue */
421: clock_gettime(CLOCK_MONOTONIC, &now);
422:
423: TAILQ_FOREACH(task, &r->root_timer, task_node)
424: if (sched_timespeccmp(&now, &TASK_TS(task), -) >= 0) {
425: #ifdef HAVE_LIBPTHREAD
426: pthread_mutex_lock(&r->root_mtx[taskTIMER]);
427: #endif
428: TAILQ_REMOVE(&r->root_timer, task, task_node);
429: #ifdef HAVE_LIBPTHREAD
430: pthread_mutex_unlock(&r->root_mtx[taskTIMER]);
431: #endif
432: task->task_type = taskREADY;
433: #ifdef HAVE_LIBPTHREAD
434: pthread_mutex_lock(&r->root_mtx[taskREADY]);
435: #endif
436: TAILQ_INSERT_TAIL(&r->root_ready, task, task_node);
437: #ifdef HAVE_LIBPTHREAD
438: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
439: #endif
440: }
441:
442: /* put eventlo priority task to ready queue, if there is no ready task or
443: reach max missed fetch-rotate */
444: if ((task = TAILQ_FIRST(&r->root_eventlo))) {
445: if (!TAILQ_FIRST(&r->root_ready) || r->root_eventlo_miss > MAX_EVENTLO_MISS) {
446: r->root_eventlo_miss = 0;
447:
448: #ifdef HAVE_LIBPTHREAD
449: pthread_mutex_lock(&r->root_mtx[taskEVENTLO]);
450: #endif
451: TAILQ_REMOVE(&r->root_eventlo, task, task_node);
452: #ifdef HAVE_LIBPTHREAD
453: pthread_mutex_unlock(&r->root_mtx[taskEVENTLO]);
454: #endif
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: } else
464: r->root_eventlo_miss++;
465: } else
466: r->root_eventlo_miss = 0;
467:
468: /* OK, lets get ready task !!! */
469: task = TAILQ_FIRST(&r->root_ready);
470: if (!(task))
471: return NULL;
472:
473: #ifdef HAVE_LIBPTHREAD
474: pthread_mutex_lock(&r->root_mtx[taskREADY]);
475: #endif
476: TAILQ_REMOVE(&r->root_ready, task, task_node);
477: #ifdef HAVE_LIBPTHREAD
478: pthread_mutex_unlock(&r->root_mtx[taskREADY]);
479: #endif
480: task->task_type = taskUNUSE;
481: #ifdef HAVE_LIBPTHREAD
482: pthread_mutex_lock(&r->root_mtx[taskUNUSE]);
483: #endif
484: TAILQ_INSERT_TAIL(&r->root_unuse, task, task_node);
485: #ifdef HAVE_LIBPTHREAD
486: pthread_mutex_unlock(&r->root_mtx[taskUNUSE]);
487: #endif
488: return task;
489: }
490:
491: /*
492: * sched_hook_exception() - Default EXCEPTION hook
493: *
494: * @root = root task
495: * @arg = custom handling: if arg == EV_EOF or other value; default: arg == NULL log errno
496: * return: <0 errors and 0 ok
497: */
498: void *
499: sched_hook_exception(void *root, void *arg)
500: {
501: sched_root_task_t *r = root;
502:
503: if (!r)
504: return NULL;
505:
506: /* custom exception handling ... */
507: if (arg) {
508: if (arg == (void*) EV_EOF)
509: return NULL;
510: return (void*) -1; /* raise scheduler error!!! */
511: }
512:
513: /* if error hook exists */
514: if (r->root_hooks.hook_root.error)
515: return (r->root_hooks.hook_root.error(root, (void*) ((intptr_t) errno)));
516:
517: /* default case! */
518: LOGERR;
519: return NULL;
520: }
521:
522: /*
523: * sched_hook_condition() - Default CONDITION hook
524: *
525: * @root = root task
526: * @arg = killState from schedRun()
527: * return: NULL kill scheduler loop or !=NULL ok
528: */
529: void *
530: sched_hook_condition(void *root, void *arg)
531: {
532: sched_root_task_t *r = root;
533:
534: if (!r)
535: return NULL;
536:
537: return (void*) (r->root_cond - *(intptr_t*) arg);
538: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>