diff options
Diffstat (limited to 'src/util/scheduler.c')
-rw-r--r-- | src/util/scheduler.c | 214 |
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 | */ |
415 | static void | 457 | static void |
416 | queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task) | 458 | queue_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 |