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.c214
1 files changed, 193 insertions, 21 deletions
diff --git a/src/util/scheduler.c b/src/util/scheduler.c
index 1352fe0d8..0ff6f9612 100644
--- a/src/util/scheduler.c
+++ b/src/util/scheduler.c
@@ -174,6 +174,21 @@ struct GNUNET_SCHEDULER_Handle
174 struct Task *pending; 174 struct Task *pending;
175 175
176 /** 176 /**
177 * List of tasks waiting ONLY for a timeout event.
178 * Sorted by timeout (earliest first). Used so that
179 * we do not traverse the list of these tasks when
180 * building select sets (we just look at the head
181 * to determine the respective timeout ONCE).
182 */
183 struct Task *pending_timeout;
184
185 /**
186 * Last inserted task waiting ONLY for a timeout event.
187 * Used to (heuristically) speed up insertion.
188 */
189 struct Task *pending_timeout_last;
190
191 /**
177 * ID of the task that is running right now. 192 * ID of the task that is running right now.
178 */ 193 */
179 struct Task *active_task; 194 struct Task *active_task;
@@ -274,6 +289,15 @@ is_pending (struct GNUNET_SCHEDULER_Handle *sched,
274 min = pos->id; 289 min = pos->id;
275 pos = pos->next; 290 pos = pos->next;
276 } 291 }
292 pos = sched->pending_timeout;
293 while (pos != NULL)
294 {
295 if (pos->id == id)
296 return GNUNET_YES;
297 if (pos->id < min)
298 min = pos->id;
299 pos = pos->next;
300 }
277 for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++) 301 for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
278 { 302 {
279 pos = sched->ready[p]; 303 pos = sched->ready[p];
@@ -306,7 +330,19 @@ update_sets (struct GNUNET_SCHEDULER_Handle *sched,
306 struct GNUNET_TIME_Relative *timeout) 330 struct GNUNET_TIME_Relative *timeout)
307{ 331{
308 struct Task *pos; 332 struct Task *pos;
333 struct GNUNET_TIME_Absolute now;
334 struct GNUNET_TIME_Relative to;
309 335
336 now = GNUNET_TIME_absolute_get ();
337 pos = sched->pending_timeout;
338 if (pos != NULL)
339 {
340 to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
341 if (timeout->value > to.value)
342 *timeout = to;
343 if (pos->reason != 0)
344 *timeout = GNUNET_TIME_UNIT_ZERO;
345 }
310 pos = sched->pending; 346 pos = sched->pending;
311 while (pos != NULL) 347 while (pos != NULL)
312 { 348 {
@@ -316,12 +352,9 @@ update_sets (struct GNUNET_SCHEDULER_Handle *sched,
316 pos = pos->next; 352 pos = pos->next;
317 continue; 353 continue;
318 } 354 }
319
320 if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value) 355 if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value)
321 { 356 {
322 struct GNUNET_TIME_Relative to; 357 to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
323
324 to = GNUNET_TIME_absolute_get_remaining (pos->timeout);
325 if (timeout->value > to.value) 358 if (timeout->value > to.value)
326 *timeout = to; 359 *timeout = to;
327 } 360 }
@@ -384,24 +417,33 @@ is_ready (struct GNUNET_SCHEDULER_Handle *sched,
384 const struct GNUNET_NETWORK_FDSet *rs, 417 const struct GNUNET_NETWORK_FDSet *rs,
385 const struct GNUNET_NETWORK_FDSet *ws) 418 const struct GNUNET_NETWORK_FDSet *ws)
386{ 419{
420 enum GNUNET_SCHEDULER_Reason reason;
421
422 reason = task->reason;
387 if (now.value >= task->timeout.value) 423 if (now.value >= task->timeout.value)
388 task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT; 424 reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
389 if ( (0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) && 425 if ( (0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
390 ( (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, task->read_fd)) || 426 ( ( (task->read_fd != -1) &&
427 (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, task->read_fd)) ) ||
391 (set_overlaps (rs, task->read_set) ) ) ) 428 (set_overlaps (rs, task->read_set) ) ) )
392 task->reason |= GNUNET_SCHEDULER_REASON_READ_READY; 429 reason |= GNUNET_SCHEDULER_REASON_READ_READY;
393 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) && 430 if ((0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
394 ( (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, task->write_fd)) || 431 ( ( (task->write_fd != -1) &&
432 (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, task->write_fd)) ) ||
395 (set_overlaps (ws, task->write_set) ) ) ) 433 (set_overlaps (ws, task->write_set) ) ) )
396 task->reason |= GNUNET_SCHEDULER_REASON_WRITE_READY; 434 reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
397 if (task->reason == 0) 435 if (reason == 0)
398 return GNUNET_NO; /* not ready */ 436 return GNUNET_NO; /* not ready */
399 if (task->prereq_id != GNUNET_SCHEDULER_NO_TASK) 437 if (task->prereq_id != GNUNET_SCHEDULER_NO_TASK)
400 { 438 {
401 if (GNUNET_YES == is_pending (sched, task->prereq_id)) 439 if (GNUNET_YES == is_pending (sched, task->prereq_id))
402 return GNUNET_NO; /* prereq waiting */ 440 {
403 task->reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE; 441 task->reason = reason;
442 return GNUNET_NO; /* prereq waiting */
443 }
444 reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
404 } 445 }
446 task->reason = reason;
405 return GNUNET_YES; 447 return GNUNET_YES;
406} 448}
407 449
@@ -413,7 +455,8 @@ is_ready (struct GNUNET_SCHEDULER_Handle *sched,
413 * @param task task ready for execution 455 * @param task task ready for execution
414 */ 456 */
415static void 457static void
416queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task) 458queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle,
459 struct Task *task)
417{ 460{
418 enum GNUNET_SCHEDULER_Priority p = task->priority; 461 enum GNUNET_SCHEDULER_Priority p = task->priority;
419 if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) 462 if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
@@ -444,6 +487,20 @@ check_ready (struct GNUNET_SCHEDULER_Handle *handle,
444 487
445 now = GNUNET_TIME_absolute_get (); 488 now = GNUNET_TIME_absolute_get ();
446 prev = NULL; 489 prev = NULL;
490 pos = handle->pending_timeout;
491 while (pos != NULL)
492 {
493 next = pos->next;
494 if (now.value >= pos->timeout.value)
495 pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
496 if (0 == pos->reason)
497 break;
498 handle->pending_timeout = next;
499 if (handle->pending_timeout_last == pos)
500 handle->pending_timeout_last = NULL;
501 queue_ready_task (handle, pos);
502 pos = next;
503 }
447 pos = handle->pending; 504 pos = handle->pending;
448 while (pos != NULL) 505 while (pos != NULL)
449 { 506 {
@@ -484,6 +541,15 @@ GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
484 struct Task *pos; 541 struct Task *pos;
485 int i; 542 int i;
486 543
544 pos = sched->pending_timeout;
545 while (pos != NULL)
546 {
547 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
548 /* we don't move the task into the ready queue yet; check_ready
549 will do that later, possibly adding additional
550 readiness-factors */
551 pos = pos->next;
552 }
487 pos = sched->pending; 553 pos = sched->pending;
488 while (pos != NULL) 554 while (pos != NULL)
489 { 555 {
@@ -615,7 +681,7 @@ run_ready (struct GNUNET_SCHEDULER_Handle *sched,
615 destroy_task (pos); 681 destroy_task (pos);
616 sched->tasks_run++; 682 sched->tasks_run++;
617 } 683 }
618 while ((sched->pending == NULL) || (p >= sched->max_priority_added)); 684 while ( (sched->pending == NULL) || (p >= sched->max_priority_added) );
619} 685}
620 686
621/** 687/**
@@ -700,7 +766,9 @@ GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
700 GNUNET_SCHEDULER_REASON_STARTUP); 766 GNUNET_SCHEDULER_REASON_STARTUP);
701 last_tr = 0; 767 last_tr = 0;
702 busy_wait_warning = 0; 768 busy_wait_warning = 0;
703 while ((sched.pending != NULL) || (sched.ready_count > 0)) 769 while ((sched.pending != NULL) ||
770 (sched.pending_timeout != NULL) ||
771 (sched.ready_count > 0))
704 { 772 {
705 GNUNET_NETWORK_fdset_zero (rs); 773 GNUNET_NETWORK_fdset_zero (rs);
706 GNUNET_NETWORK_fdset_zero (ws); 774 GNUNET_NETWORK_fdset_zero (ws);
@@ -832,8 +900,10 @@ GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
832 struct Task *t; 900 struct Task *t;
833 struct Task *prev; 901 struct Task *prev;
834 enum GNUNET_SCHEDULER_Priority p; 902 enum GNUNET_SCHEDULER_Priority p;
903 int to;
835 void *ret; 904 void *ret;
836 905
906 to = 0;
837 prev = NULL; 907 prev = NULL;
838 t = sched->pending; 908 t = sched->pending;
839 while (t != NULL) 909 while (t != NULL)
@@ -843,6 +913,21 @@ GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
843 prev = t; 913 prev = t;
844 t = t->next; 914 t = t->next;
845 } 915 }
916 if (t == NULL)
917 {
918 prev = NULL;
919 to = 1;
920 t = sched->pending_timeout;
921 while (t != NULL)
922 {
923 if (t->id == task)
924 break;
925 prev = t;
926 t = t->next;
927 }
928 if (sched->pending_timeout_last == t)
929 sched->pending_timeout_last = NULL;
930 }
846 p = 0; 931 p = 0;
847 while (t == NULL) 932 while (t == NULL)
848 { 933 {
@@ -864,12 +949,25 @@ GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
864 if (prev == NULL) 949 if (prev == NULL)
865 { 950 {
866 if (p == 0) 951 if (p == 0)
867 sched->pending = t->next; 952 {
953 if (to == 0)
954 {
955 sched->pending = t->next;
956 }
957 else
958 {
959 sched->pending_timeout = t->next;
960 }
961 }
868 else 962 else
869 sched->ready[p] = t->next; 963 {
964 sched->ready[p] = t->next;
965 }
870 } 966 }
871 else 967 else
872 prev->next = t->next; 968 {
969 prev->next = t->next;
970 }
873 ret = t->callback_cls; 971 ret = t->callback_cls;
874#if DEBUG_TASKS 972#if DEBUG_TASKS
875 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 973 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -998,10 +1096,84 @@ GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
998 struct GNUNET_TIME_Relative delay, 1096 struct GNUNET_TIME_Relative delay,
999 GNUNET_SCHEDULER_Task task, void *task_cls) 1097 GNUNET_SCHEDULER_Task task, void *task_cls)
1000{ 1098{
1099#if 1
1100 /* new, optimized version */
1101 struct Task *t;
1102 struct Task *pos;
1103 struct Task *prev;
1104#if EXECINFO
1105 void *backtrace_array[MAX_TRACE_DEPTH];
1106#endif
1107
1108 GNUNET_assert (NULL != task);
1109 t = GNUNET_malloc (sizeof (struct Task));
1110 t->callback = task;
1111 t->callback_cls = task_cls;
1112#if EXECINFO
1113 t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1114 t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1115#endif
1116 t->read_fd = -1;
1117 t->write_fd = -1;
1118 t->id = ++sched->last_id;
1119#if PROFILE_DELAYS
1120 t->start_time = GNUNET_TIME_absolute_get ();
1121#endif
1122 t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1123 t->priority = sched->current_priority;
1124 /* try tail first (optimization in case we are
1125 appending to a long list of tasks with timeouts) */
1126 prev = sched->pending_timeout_last;
1127 if (prev != NULL)
1128 {
1129 if (prev->timeout.value > t->timeout.value)
1130 prev = NULL;
1131 else
1132 pos = prev->next; /* heuristic success! */
1133 }
1134 if (prev == NULL)
1135 {
1136 /* heuristic failed, do traversal of timeout list */
1137 pos = sched->pending_timeout;
1138 }
1139 while ( (pos != NULL) &&
1140 ( (pos->timeout.value <= t->timeout.value) ||
1141 (pos->reason != 0) ) )
1142 {
1143 prev = pos;
1144 pos = pos->next;
1145 }
1146 if (prev == NULL)
1147 sched->pending_timeout = t;
1148 else
1149 prev->next = t;
1150 t->next = pos;
1151 /* hyper-optimization... */
1152 sched->pending_timeout_last = t;
1153
1154#if DEBUG_TASKS
1155 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1156 "Adding task: %llu / %p\n", t->id, t->callback_cls);
1157#endif
1158#if EXECINFO
1159 int i;
1160
1161 for (i=0;i<t->num_backtrace_strings;i++)
1162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1163 "Task %u trace %d: %s\n",
1164 t->id,
1165 i,
1166 t->backtrace_strings[i]);
1167#endif
1168 return t->id;
1169
1170#else
1171 /* unoptimized version */
1001 return GNUNET_SCHEDULER_add_select (sched, 1172 return GNUNET_SCHEDULER_add_select (sched,
1002 GNUNET_SCHEDULER_PRIORITY_KEEP, 1173 GNUNET_SCHEDULER_PRIORITY_KEEP,
1003 GNUNET_SCHEDULER_NO_TASK, delay, 1174 GNUNET_SCHEDULER_NO_TASK, delay,
1004 NULL, NULL, task, task_cls); 1175 NULL, NULL, task, task_cls);
1176#endif
1005} 1177}
1006 1178
1007 1179
@@ -1045,7 +1217,7 @@ GNUNET_SCHEDULER_add_now (struct GNUNET_SCHEDULER_Handle *sched,
1045 * && (delay-ready 1217 * && (delay-ready
1046 * || any-rs-ready 1218 * || any-rs-ready
1047 * || any-ws-ready 1219 * || any-ws-ready
1048 * || (shutdown-active && run-on-shutdown) ) 1220 * || shutdown-active )
1049 * </code> 1221 * </code>
1050 * 1222 *
1051 * @param sched scheduler to use 1223 * @param sched scheduler to use