aboutsummaryrefslogtreecommitdiff
path: root/src/util/scheduler.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/scheduler.c')
-rw-r--r--src/util/scheduler.c886
1 files changed, 886 insertions, 0 deletions
diff --git a/src/util/scheduler.c b/src/util/scheduler.c
new file mode 100644
index 000000000..0d30910e0
--- /dev/null
+++ b/src/util/scheduler.c
@@ -0,0 +1,886 @@
1/*
2 This file is part of GNUnet
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * @file util/scheduler/scheduler.c
23 * @brief schedule computations using continuation passing style
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_scheduler_lib.h"
29#include "gnunet_signal_lib.h"
30#include "gnunet_time_lib.h"
31
32/**
33 * Linked list of pending tasks.
34 */
35struct Task
36{
37 /**
38 * This is a linked list.
39 */
40 struct Task *next;
41
42 /**
43 * Function to run when ready.
44 */
45 GNUNET_SCHEDULER_Task callback;
46
47 /**
48 * Closure for the callback.
49 */
50 void *callback_cls;
51
52 /**
53 * Set of file descriptors this task is waiting
54 * for for reading. Once ready, this is updated
55 * to reflect the set of file descriptors ready
56 * for operation.
57 */
58 fd_set read_set;
59
60 /**
61 * Set of file descriptors this task is waiting
62 * for for writing. Once ready, this is updated
63 * to reflect the set of file descriptors ready
64 * for operation.
65 */
66 fd_set write_set;
67
68 /**
69 * Unique task identifier.
70 */
71 GNUNET_SCHEDULER_TaskIdentifier id;
72
73 /**
74 * Identifier of a prerequisite task.
75 */
76 GNUNET_SCHEDULER_TaskIdentifier prereq_id;
77
78 /**
79 * Absolute timeout value for the task, or
80 * GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
81 */
82 struct GNUNET_TIME_Absolute timeout;
83
84 /**
85 * Why is the task ready? Set after task is added to ready queue.
86 * Initially set to zero. All reasons that have already been
87 * satisfied (i.e. read or write ready) will be set over time.
88 */
89 enum GNUNET_SCHEDULER_Reason reason;
90
91 /**
92 * Task priority.
93 */
94 enum GNUNET_SCHEDULER_Priority priority;
95
96 /**
97 * highest-numbered file descriptor in read_set or write_set plus one
98 */
99 int nfds;
100
101 /**
102 * Should this task be run on shutdown?
103 */
104 int run_on_shutdown;
105
106};
107
108
109/**
110 * Handle for the scheduling service.
111 */
112struct GNUNET_SCHEDULER_Handle
113{
114
115 /**
116 * List of tasks waiting for an event.
117 */
118 struct Task *pending;
119
120 /**
121 * List of tasks ready to run right now,
122 * grouped by importance.
123 */
124 struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT];
125
126 /**
127 * Identity of the last task queued. Incremented for each task to
128 * generate a unique task ID (it is virtually impossible to start
129 * more than 2^64 tasks during the lifetime of a process).
130 */
131 GNUNET_SCHEDULER_TaskIdentifier last_id;
132
133 /**
134 * Highest number so that all tasks with smaller identifiers
135 * have already completed. Also the lowest number of a task
136 * still waiting to be executed.
137 */
138 GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
139
140 /**
141 * GNUNET_NO if we are running normally,
142 * GNUNET_YES if we are in shutdown mode.
143 */
144 int shutdown;
145
146 /**
147 * Number of tasks on the ready list.
148 */
149 unsigned int ready_count;
150
151 /**
152 * Priority of the task running right now. Only
153 * valid while a task is running.
154 */
155 enum GNUNET_SCHEDULER_Priority current_priority;
156
157};
158
159
160/**
161 * Check that the given priority is legal (and return it).
162 */
163static enum GNUNET_SCHEDULER_Priority
164check_priority (enum GNUNET_SCHEDULER_Priority p)
165{
166 if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
167 return p;
168 GNUNET_assert (0);
169 return 0; /* make compiler happy */
170}
171
172
173/**
174 * Update the timeout value so that it is smaller than min.
175 */
176static void
177update_timeout (struct timeval *tv, struct GNUNET_TIME_Relative min)
178{
179 if (((tv->tv_sec * 1000) + (tv->tv_usec / 1000)) > min.value)
180 {
181 tv->tv_sec = min.value / 1000;
182 tv->tv_usec = (min.value - tv->tv_sec * 1000) * 1000;
183 }
184}
185
186
187/**
188 * Set the given file descriptor bit in the given set and update max
189 * to the maximum of the existing max and fd+1.
190 */
191static void
192set_fd (int fd, int *max, fd_set * set)
193{
194 if (*max <= fd)
195 *max = fd + 1;
196 FD_SET (fd, set);
197}
198
199
200/**
201 * Is a task with this identifier still pending? Also updates
202 * "lowest_pending_id" as a side-effect (for faster checks in the
203 * future), but only if the return value is "GNUNET_NO" (and
204 * the "lowest_pending_id" check failed).
205 *
206 * @return GNUNET_YES if so, GNUNET_NO if not
207 */
208static int
209is_pending (struct GNUNET_SCHEDULER_Handle *sched,
210 GNUNET_SCHEDULER_TaskIdentifier id)
211{
212 struct Task *pos;
213 enum GNUNET_SCHEDULER_Priority p;
214 GNUNET_SCHEDULER_TaskIdentifier min;
215
216 if (id < sched->lowest_pending_id)
217 return GNUNET_NO;
218 min = -1; /* maximum value */
219 pos = sched->pending;
220 while (pos != NULL)
221 {
222 if (pos->id == id)
223 return GNUNET_YES;
224 if (pos->id < min)
225 min = pos->id;
226 pos = pos->next;
227 }
228 for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
229 {
230 pos = sched->ready[p];
231 while (pos != NULL)
232 {
233 if (pos->id == id)
234 return GNUNET_YES;
235 if (pos->id < min)
236 min = pos->id;
237 pos = pos->next;
238 }
239 }
240 sched->lowest_pending_id = min;
241 return GNUNET_NO;
242}
243
244
245/**
246 * Update all sets and timeout for select.
247 */
248static void
249update_sets (struct GNUNET_SCHEDULER_Handle *sched,
250 int *max, fd_set * rs, fd_set * ws, struct timeval *tv)
251{
252 int i;
253 struct Task *pos;
254
255 pos = sched->pending;
256 while (pos != NULL)
257 {
258 if ((pos->prereq_id != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) &&
259 (GNUNET_YES == is_pending (sched, pos->prereq_id)))
260 {
261 pos = pos->next;
262 continue;
263 }
264
265 if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value)
266 update_timeout (tv,
267 GNUNET_TIME_absolute_get_remaining (pos->timeout));
268 for (i = 0; i < pos->nfds; i++)
269 {
270 if (FD_ISSET (i, &pos->read_set))
271 set_fd (i, max, rs);
272 if (FD_ISSET (i, &pos->write_set))
273 set_fd (i, max, ws);
274 }
275 pos = pos->next;
276 }
277}
278
279
280/**
281 * Check if the ready set overlaps with the set we want to have ready.
282 * If so, update the want set (set all FDs that are ready). If not,
283 * return GNUNET_NO.
284 *
285 * @param maxfd highest FD that needs to be checked.
286 * @return GNUNET_YES if there was some overlap
287 */
288static int
289set_overlaps (const fd_set * ready, fd_set * want, int maxfd)
290{
291 int i;
292
293 for (i = 0; i < maxfd; i++)
294 if (FD_ISSET (i, want) && FD_ISSET (i, ready))
295 {
296 /* copy all over (yes, there maybe unrelated bits,
297 but this should not hurt well-written clients) */
298 memcpy (want, ready, sizeof (fd_set));
299 return GNUNET_YES;
300 }
301 return GNUNET_NO;
302}
303
304
305/**
306 * Check if the given task is eligible to run now.
307 * Also set the reason why it is eligible.
308 *
309 * @return GNUNET_YES if we can run it, GNUNET_NO if not.
310 */
311static int
312is_ready (struct GNUNET_SCHEDULER_Handle *sched,
313 struct Task *task,
314 struct GNUNET_TIME_Absolute now,
315 const fd_set * rs, const fd_set * ws)
316{
317 if ((GNUNET_NO == task->run_on_shutdown) && (GNUNET_YES == sched->shutdown))
318 return GNUNET_NO;
319 if ((GNUNET_YES == task->run_on_shutdown) &&
320 (GNUNET_YES == sched->shutdown))
321 task->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
322 if (now.value >= task->timeout.value)
323 task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
324 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
325 (rs != NULL) && (set_overlaps (rs, &task->read_set, task->nfds)))
326 task->reason |= GNUNET_SCHEDULER_REASON_READ_READY;
327 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
328 (ws != NULL) && (set_overlaps (ws, &task->write_set, task->nfds)))
329 task->reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
330 if (task->reason == 0)
331 return GNUNET_NO; /* not ready */
332 if (task->prereq_id != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
333 {
334 if (GNUNET_YES == is_pending (sched, task->prereq_id))
335 return GNUNET_NO; /* prereq waiting */
336 task->reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
337 }
338 return GNUNET_YES;
339}
340
341
342/**
343 * Put a task that is ready for execution into the ready queue.
344 */
345static void
346queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task)
347{
348 task->next = handle->ready[check_priority (task->priority)];
349 handle->ready[check_priority (task->priority)] = task;
350 handle->ready_count++;
351}
352
353
354/**
355 * Check which tasks are ready and move them
356 * to the respective ready queue.
357 */
358static void
359check_ready (struct GNUNET_SCHEDULER_Handle *handle,
360 const fd_set * rs, const fd_set * ws)
361{
362 struct Task *pos;
363 struct Task *prev;
364 struct Task *next;
365 struct GNUNET_TIME_Absolute now;
366
367 now = GNUNET_TIME_absolute_get ();
368 prev = NULL;
369 pos = handle->pending;
370 while (pos != NULL)
371 {
372 next = pos->next;
373 if (GNUNET_YES == is_ready (handle, pos, now, rs, ws))
374 {
375 if (prev == NULL)
376 handle->pending = next;
377 else
378 prev->next = next;
379 queue_ready_task (handle, pos);
380 pos = next;
381 continue;
382 }
383 prev = pos;
384 pos = next;
385 }
386}
387
388
389/**
390 * Run at least one task in the highest-priority queue that is not
391 * empty. Keep running tasks until we are either no longer running
392 * "URGENT" tasks or until we have at least one "pending" task (which
393 * may become ready, hence we should select on it). Naturally, if
394 * there are no more ready tasks, we also return.
395 */
396static void
397run_ready (struct GNUNET_SCHEDULER_Handle *sched)
398{
399 enum GNUNET_SCHEDULER_Priority p;
400 struct Task *pos;
401 struct GNUNET_SCHEDULER_TaskContext tc;
402
403 do
404 {
405 if (sched->ready_count == 0)
406 return;
407 GNUNET_assert (sched->ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
408 /* yes, p>0 is correct, 0 is "KEEP" which should
409 always be an empty queue (see assertion)! */
410 for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
411 {
412 pos = sched->ready[p];
413 if (pos != NULL)
414 break;
415 }
416 GNUNET_assert (pos != NULL); /* ready_count wrong? */
417 sched->ready[p] = pos->next;
418 sched->ready_count--;
419 sched->current_priority = p;
420 GNUNET_assert (pos->priority == p);
421 tc.sched = sched;
422 tc.reason = pos->reason;
423 tc.read_ready = &pos->read_set;
424 tc.write_ready = &pos->write_set;
425 pos->callback (pos->callback_cls, &tc);
426 GNUNET_free (pos);
427 }
428 while ((sched->pending == NULL) || (p == GNUNET_SCHEDULER_PRIORITY_URGENT));
429}
430
431
432/**
433 * Have we (ever) received a SIGINT/TERM/QUIT/HUP?
434 */
435static volatile int sig_shutdown;
436
437
438/**
439 * Signal handler called for signals that should cause us to shutdown.
440 */
441static void
442sighandler_shutdown ()
443{
444 sig_shutdown = 1;
445}
446
447
448/**
449 * Initialize a scheduler using this thread. This function will
450 * return when either a shutdown was initiated (via signal) and all
451 * tasks marked to "run_on_shutdown" have been completed or when all
452 * tasks in general have been completed.
453 *
454 * @param task task to run immediately
455 * @param cls closure of task
456 */
457void
458GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *cls)
459{
460 struct GNUNET_SCHEDULER_Handle sched;
461 fd_set rs;
462 fd_set ws;
463 int max;
464 struct timeval tv;
465 int ret;
466 struct GNUNET_SIGNAL_Context *shc_int;
467 struct GNUNET_SIGNAL_Context *shc_term;
468 struct GNUNET_SIGNAL_Context *shc_quit;
469 struct GNUNET_SIGNAL_Context *shc_hup;
470 struct Task *tpos;
471
472 sig_shutdown = 0;
473 shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
474 shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
475 shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
476 shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
477 memset (&sched, 0, sizeof (sched));
478 sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
479 GNUNET_SCHEDULER_add_continuation (&sched,
480 GNUNET_YES,
481 task,
482 cls, GNUNET_SCHEDULER_REASON_STARTUP);
483 while ((GNUNET_NO == sched.shutdown) &&
484 (!sig_shutdown) &&
485 ((sched.pending != NULL) || (sched.ready_count > 0)))
486 {
487 FD_ZERO (&rs);
488 FD_ZERO (&ws);
489 max = 0;
490 tv.tv_sec = 0x7FFFFFFF;
491 tv.tv_usec = 0;
492 if (sched.ready_count > 0)
493 {
494 /* no blocking, more work already ready! */
495 tv.tv_sec = 0;
496 tv.tv_usec = 0;
497 }
498 update_sets (&sched, &max, &rs, &ws, &tv);
499 ret = SELECT (max, &rs, &ws, NULL, &tv);
500 if (ret == -1)
501 {
502 if (errno == EINTR)
503 continue;
504 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
505 break;
506 }
507 check_ready (&sched, &rs, &ws);
508 run_ready (&sched);
509 }
510 if (sig_shutdown)
511 sched.shutdown = GNUNET_YES;
512 GNUNET_SIGNAL_handler_uninstall (shc_int);
513 GNUNET_SIGNAL_handler_uninstall (shc_term);
514 GNUNET_SIGNAL_handler_uninstall (shc_quit);
515 GNUNET_SIGNAL_handler_uninstall (shc_hup);
516 do
517 {
518 run_ready (&sched);
519 check_ready (&sched, NULL, NULL);
520 }
521 while (sched.ready_count > 0);
522 while (NULL != (tpos = sched.pending))
523 {
524 sched.pending = tpos->next;
525 GNUNET_free (tpos);
526 }
527}
528
529
530/**
531 * Request the shutdown of a scheduler. This function can be used to
532 * stop a scheduling thread when created with the
533 * "GNUNET_SCHEDULER_init_thread" function or from within the signal
534 * handler for signals causing shutdowns.
535 */
536void
537GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
538{
539 sched->shutdown = GNUNET_YES;
540}
541
542
543/**
544 * Get information about the current load of this scheduler. Use this
545 * function to determine if an elective task should be added or simply
546 * dropped (if the decision should be made based on the number of
547 * tasks ready to run).
548 *
549 * @param sched scheduler to query
550 * @return number of tasks pending right now
551 */
552unsigned int
553GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
554 enum GNUNET_SCHEDULER_Priority p)
555{
556 struct Task *pos;
557 unsigned int ret;
558
559 if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
560 return sched->ready_count;
561 if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
562 p = sched->current_priority;
563 ret = 0;
564 pos = sched->ready[p];
565 while (pos != NULL)
566 {
567 pos = pos->next;
568 ret++;
569 }
570 return ret;
571}
572
573
574/**
575 * Cancel the task with the specified identifier.
576 * The task must not yet have run.
577 *
578 * @param sched scheduler to use
579 * @param task id of the task to cancel
580 */
581void *
582GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
583 GNUNET_SCHEDULER_TaskIdentifier task)
584{
585 struct Task *t;
586 struct Task *prev;
587 enum GNUNET_SCHEDULER_Priority p;
588 void *ret;
589
590 prev = NULL;
591 t = sched->pending;
592 while (t != NULL)
593 {
594 if (t->id == task)
595 break;
596 prev = t;
597 t = t->next;
598 }
599 p = 0;
600 while (t == NULL)
601 {
602 p++;
603 GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
604 prev = NULL;
605 t = sched->ready[p];
606 while (t != NULL)
607 {
608 if (t->id == task)
609 {
610 sched->ready_count--;
611 break;
612 }
613 prev = t;
614 t = t->next;
615 }
616 }
617 if (prev == NULL)
618 {
619 if (p == 0)
620 sched->pending = t->next;
621 else
622 sched->ready[p] = t->next;
623 }
624 else
625 prev->next = t->next;
626 ret = t->callback_cls;
627 GNUNET_free (t);
628 return ret;
629}
630
631
632/**
633 * Continue the current execution with the given function. This is
634 * similar to the other "add" functions except that there is no delay
635 * and the reason code can be specified.
636 *
637 * @param sched scheduler to use
638 * @param main main function of the task
639 * @param cls closure of task
640 * @param reason reason for task invocation
641 */
642void
643GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
644 int run_on_shutdown,
645 GNUNET_SCHEDULER_Task main,
646 void *cls,
647 enum GNUNET_SCHEDULER_Reason reason)
648{
649 struct Task *task;
650
651 task = GNUNET_malloc (sizeof (struct Task));
652 task->callback = main;
653 task->callback_cls = cls;
654 task->id = ++sched->last_id;
655 task->reason = reason;
656 task->priority = sched->current_priority;
657 task->run_on_shutdown = run_on_shutdown;
658 queue_ready_task (sched, task);
659}
660
661
662/**
663 * Schedule a new task to be run after the specified
664 * prerequisite task has completed.
665 *
666 * @param sched scheduler to use
667 * @param run_on_shutdown run on shutdown?
668 * @param prio how important is this task?
669 * @param prerequisite_task run this task after the task with the given
670 * task identifier completes (and any of our other
671 * conditions, such as delay, read or write-readyness
672 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
673 * on completion of other tasks.
674 * @param main main function of the task
675 * @param cls closure of task
676 * @return unique task identifier for the job
677 * only valid until "main" is started!
678 */
679GNUNET_SCHEDULER_TaskIdentifier
680GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
681 int run_on_shutdown,
682 enum GNUNET_SCHEDULER_Priority prio,
683 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
684 GNUNET_SCHEDULER_Task main, void *cls)
685{
686 return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
687 prerequisite_task,
688 GNUNET_TIME_UNIT_ZERO,
689 0, NULL, NULL, main, cls);
690}
691
692
693/**
694 * Schedule a new task to be run with a specified delay. The task
695 * will be scheduled for execution once the delay has expired and the
696 * prerequisite task has completed.
697 *
698 * @param sched scheduler to use
699 * @param run_on_shutdown run on shutdown? You can use this
700 * argument to run a function only during shutdown
701 * by setting delay to -1. Set this
702 * argument to GNUNET_NO to skip this task if
703 * the user requested process termination.
704 * @param prio how important is this task?
705 * @param prerequisite_task run this task after the task with the given
706 * task identifier completes (and any of our other
707 * conditions, such as delay, read or write-readyness
708 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
709 * on completion of other tasks.
710 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
711 * @param main main function of the task
712 * @param cls closure of task
713 * @return unique task identifier for the job
714 * only valid until "main" is started!
715 */
716GNUNET_SCHEDULER_TaskIdentifier
717GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
718 int run_on_shutdown,
719 enum GNUNET_SCHEDULER_Priority prio,
720 GNUNET_SCHEDULER_TaskIdentifier
721 prerequisite_task,
722 struct GNUNET_TIME_Relative delay,
723 GNUNET_SCHEDULER_Task main, void *cls)
724{
725 return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
726 prerequisite_task, delay,
727 0, NULL, NULL, main, cls);
728}
729
730
731/**
732 * Schedule a new task to be run with a specified delay or when the
733 * specified file descriptor is ready for reading. The delay can be
734 * used as a timeout on the socket being ready. The task will be
735 * scheduled for execution once either the delay has expired or the
736 * socket operation is ready.
737 *
738 * @param sched scheduler to use
739 * @param run_on_shutdown run on shutdown? Set this
740 * argument to GNUNET_NO to skip this task if
741 * the user requested process termination.
742 * @param prio how important is this task?
743 * @param prerequisite_task run this task after the task with the given
744 * task identifier completes (and any of our other
745 * conditions, such as delay, read or write-readyness
746 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
747 * on completion of other tasks.
748 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
749 * @param rfd read file-descriptor
750 * @param main main function of the task
751 * @param cls closure of task
752 * @return unique task identifier for the job
753 * only valid until "main" is started!
754 */
755GNUNET_SCHEDULER_TaskIdentifier
756GNUNET_SCHEDULER_add_read (struct GNUNET_SCHEDULER_Handle * sched,
757 int run_on_shutdown,
758 enum GNUNET_SCHEDULER_Priority prio,
759 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
760 struct GNUNET_TIME_Relative delay,
761 int rfd, GNUNET_SCHEDULER_Task main, void *cls)
762{
763 fd_set rs;
764
765 GNUNET_assert (rfd >= 0);
766 FD_ZERO (&rs);
767 FD_SET (rfd, &rs);
768 return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
769 prerequisite_task, delay,
770 rfd + 1, &rs, NULL, main, cls);
771}
772
773
774/**
775 * Schedule a new task to be run with a specified delay or when the
776 * specified file descriptor is ready for writing. The delay can be
777 * used as a timeout on the socket being ready. The task will be
778 * scheduled for execution once either the delay has expired or the
779 * socket operation is ready.
780 *
781 * @param sched scheduler to use
782 * @param run_on_shutdown run on shutdown? Set this
783 * argument to GNUNET_NO to skip this task if
784 * the user requested process termination.
785 * @param prio how important is this task?
786 * @param prerequisite_task run this task after the task with the given
787 * task identifier completes (and any of our other
788 * conditions, such as delay, read or write-readyness
789 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
790 * on completion of other tasks.
791 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
792 * @param wfd write file-descriptor
793 * @param main main function of the task
794 * @param cls closure of task
795 * @return unique task identifier for the job
796 * only valid until "main" is started!
797 */
798GNUNET_SCHEDULER_TaskIdentifier
799GNUNET_SCHEDULER_add_write (struct GNUNET_SCHEDULER_Handle * sched,
800 int run_on_shutdown,
801 enum GNUNET_SCHEDULER_Priority prio,
802 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
803 struct GNUNET_TIME_Relative delay,
804 int wfd, GNUNET_SCHEDULER_Task main, void *cls)
805{
806 fd_set ws;
807
808 GNUNET_assert (wfd >= 0);
809 FD_ZERO (&ws);
810 FD_SET (wfd, &ws);
811 return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
812 prerequisite_task, delay,
813 wfd + 1, NULL, &ws, main, cls);
814}
815
816
817/**
818 * Schedule a new task to be run with a specified delay or when any of
819 * the specified file descriptor sets is ready. The delay can be used
820 * as a timeout on the socket(s) being ready. The task will be
821 * scheduled for execution once either the delay has expired or any of
822 * the socket operations is ready. This is the most general
823 * function of the "add" family. Note that the "prerequisite_task"
824 * must be satisfied in addition to any of the other conditions. In
825 * other words, the task will be started when
826 * <code>
827 * (prerequisite-run)
828 * && (delay-ready
829 * || any-rs-ready
830 * || any-ws-ready
831 * || (shutdown-active && run-on-shutdown) )
832 * </code>
833 *
834 * @param sched scheduler to use
835 * @param run_on_shutdown run on shutdown? Set this
836 * argument to GNUNET_NO to skip this task if
837 * the user requested process termination.
838 * @param prio how important is this task?
839 * @param prerequisite_task run this task after the task with the given
840 * task identifier completes (and any of our other
841 * conditions, such as delay, read or write-readyness
842 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
843 * on completion of other tasks.
844 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
845 * @param nfds highest-numbered file descriptor in any of the two sets plus one
846 * @param rs set of file descriptors we want to read (can be NULL)
847 * @param ws set of file descriptors we want to write (can be NULL)
848 * @param main main function of the task
849 * @param cls closure of task
850 * @return unique task identifier for the job
851 * only valid until "main" is started!
852 */
853GNUNET_SCHEDULER_TaskIdentifier
854GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
855 int run_on_shutdown,
856 enum GNUNET_SCHEDULER_Priority prio,
857 GNUNET_SCHEDULER_TaskIdentifier
858 prerequisite_task,
859 struct GNUNET_TIME_Relative delay,
860 int nfds, const fd_set * rs, const fd_set * ws,
861 GNUNET_SCHEDULER_Task main, void *cls)
862{
863 struct Task *task;
864
865 task = GNUNET_malloc (sizeof (struct Task));
866 task->callback = main;
867 task->callback_cls = cls;
868 if ((rs != NULL) && (nfds > 0))
869 memcpy (&task->read_set, rs, sizeof (fd_set));
870 if ((ws != NULL) && (nfds > 0))
871 memcpy (&task->write_set, ws, sizeof (fd_set));
872 task->id = ++sched->last_id;
873 task->prereq_id = prerequisite_task;
874 task->timeout = GNUNET_TIME_relative_to_absolute (delay);
875 task->priority =
876 check_priority ((prio ==
877 GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->
878 current_priority : prio);
879 task->nfds = nfds;
880 task->run_on_shutdown = run_on_shutdown;
881 task->next = sched->pending;
882 sched->pending = task;
883 return task->id;
884}
885
886/* end of scheduler.c */