From 079fac47d7d68bd62c9c3ab6ebdf04ca379823ed Mon Sep 17 00:00:00 2001 From: ulfvonbelow Date: Sat, 28 Jan 2023 15:30:54 -0600 Subject: [PATCH 1/2] UTIL: add test demonstrating scheduler bug, don't run it by default. These demonstrate a bug in the scheduler by which a task can prevent any other task from running for an arbitrarily long time despite regularly yielding to the scheduler. It is caused by a faulty check in GNUNET_SCHEDULER_do_work that assumes that the task that was the last in the queue when the pass began will still be in the same relative position when the pass ends, and uses this assumption to detect the end of the current pass. This assumption fails when the last task of the current pass is canceled after the pass has started. It also fails when we schedule a higher-priority task to run immediately, causing work_priority to immediately switch such that we now process a queue that doesn't contain the pass-ending task we're looking for. These tests are built, but not run by 'make check' yet, since they currently fail. You can manually verify that they do currently fail. --- src/util/Makefile.am | 12 +++++ src/util/test_scheduler_hogging_cancel.c | 51 ++++++++++++++++++++ src/util/test_scheduler_hogging_priority.c | 55 ++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 src/util/test_scheduler_hogging_cancel.c create mode 100644 src/util/test_scheduler_hogging_priority.c diff --git a/src/util/Makefile.am b/src/util/Makefile.am index ed01558eb..81b8a93b7 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -174,6 +174,8 @@ endif noinst_PROGRAMS = \ gnunet-config-diff \ test_common_logging_dummy \ + test_scheduler_hogging_cancel \ + test_scheduler_hogging_priority \ gnunet-crypto-tvg if ENABLE_TEST_RUN @@ -587,6 +589,16 @@ test_scheduler_delay_SOURCES = \ test_scheduler_delay_LDADD = \ libgnunetutil.la +test_scheduler_hogging_cancel_SOURCES = \ + test_scheduler_hogging_cancel.c +test_scheduler_hogging_cancel_LDADD = \ + libgnunetutil.la + +test_scheduler_hogging_priority_SOURCES = \ + test_scheduler_hogging_priority.c +test_scheduler_hogging_priority_LDADD = \ + libgnunetutil.la + test_service_SOURCES = \ test_service.c test_service_LDADD = \ diff --git a/src/util/test_scheduler_hogging_cancel.c b/src/util/test_scheduler_hogging_cancel.c new file mode 100644 index 000000000..7611338b3 --- /dev/null +++ b/src/util/test_scheduler_hogging_cancel.c @@ -0,0 +1,51 @@ +#include "gnunet_util_lib.h" +#include + +static int count = 0; +static int final_count; +static struct GNUNET_SCHEDULER_Task *t4; + +static void end (void *cls) +{ + final_count = count; + count = 5000; + GNUNET_SCHEDULER_shutdown (); +} + +static void self_rescheduling (void *cls) +{ + if (0 == count) + { + GNUNET_SCHEDULER_cancel (t4); + GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_MILLISECONDS, + GNUNET_SCHEDULER_PRIORITY_URGENT, + &end, + NULL); + sleep (1); + /* end should be added to ready queue on next scheduler pass for certain + now */ + } + if (++count < 5000) + { + GNUNET_SCHEDULER_add_now (&self_rescheduling, NULL); + } +} + +static void to_be_canceled (void *cls) +{ + /* Don't run me! */ +} + + +static void init (void *cls) +{ + GNUNET_SCHEDULER_add_now (&self_rescheduling, NULL); + t4 = GNUNET_SCHEDULER_add_now (&to_be_canceled, NULL); +} + + +int main (int argc, char **argv) +{ + GNUNET_SCHEDULER_run (&init, NULL); + return final_count < 5000 ? 0 : 1; +} diff --git a/src/util/test_scheduler_hogging_priority.c b/src/util/test_scheduler_hogging_priority.c new file mode 100644 index 000000000..217a39ce7 --- /dev/null +++ b/src/util/test_scheduler_hogging_priority.c @@ -0,0 +1,55 @@ +#include "gnunet_util_lib.h" +#include + +static int count = 0; +static int final_count; + +static void end (void *cls) +{ + final_count = count; + count = 5000; + GNUNET_SCHEDULER_shutdown (); +} + +static void self_rescheduling (void *cls) +{ + if (count == 0) + { + GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_MILLISECONDS, + GNUNET_SCHEDULER_PRIORITY_URGENT, + &end, + NULL); + sleep(1); + /* end should be added to ready queue on next scheduler pass for certain + now */ + } + if (++count < 5000) + { + GNUNET_SCHEDULER_add_now (&self_rescheduling, NULL); + } +} + + +static void noop (void *cls) +{ +} + +static void indirection (void *cls) +{ + GNUNET_SCHEDULER_add_with_reason_and_priority (&self_rescheduling, NULL, + GNUNET_SCHEDULER_REASON_STARTUP, + GNUNET_SCHEDULER_PRIORITY_HIGH); +} + +static void init (void *cls) +{ + GNUNET_SCHEDULER_add_now (&indirection, NULL); + GNUNET_SCHEDULER_add_now (&noop, NULL); +} + + +int main (int argc, char **argv) +{ + GNUNET_SCHEDULER_run (&init, NULL); + return final_count < 5000 ? 0 : 1; +} -- 2.38.1