From: Ben Pfaff Date: Thu, 18 May 2006 02:55:09 +0000 (+0000) Subject: Add a test for P1 that checks that multiple threads can properly wake X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=51caf1a5a76244a0932e1d492b03c071cfe2f406;p=pintos-anon Add a test for P1 that checks that multiple threads can properly wake up at the same clock tick. --- diff --git a/TODO b/TODO index 53a7060..d230106 100644 --- a/TODO +++ b/TODO @@ -26,41 +26,6 @@ Add a system call "get_kernel_memory_information". User programs could engage in a variety of activities and notice leaks by checking the kernel memory statistics. -From: "Godmar Back" -Subject: multiple threads waking up at same clock tick -To: "Ben Pfaff" -Date: Wed, 1 Mar 2006 08:14:47 -0500 - -Greg Benson points out another potential TODO item for P1. - ----- -One thing I recall: - -The alarm tests do not test to see if multiple threads are woken up if -their timers have expired. That is, students can write a solution -that just wakes up the first thread on the sleep queue rather than -check for additional threads. Of course, the next thread will be -woken up on the next tick. Also, this might be hard to test. - ---- -Way to test this: (from Godmar Back) - -Thread A with high priority spins until 'ticks' changes, then calls to -timer_sleep(X), Thread B with lower priority is then resumed, calls -set_priority to make its priority equal to that of thread A, then -calls timer_sleep(X), all of that before the next clock interrupt -arrives. - -On wakeup, each thread records wake-up time and calls yield -immediately, forcing the scheduler to switch to the other -equal-priority thread. Both wake-up times must be the same (and match -the planned wake-up time.) - -PS: -I actually tested it and it's hard to pass with the current ips setting. -The bounds on how quickly a thread would need to be able to return after -sleep appear too tight. Need another idea. - From: "Godmar Back" For reasons I don't currently understand, some of our students seem diff --git a/src/tests/threads/Make.tests b/src/tests/threads/Make.tests index 5a7b400..0b6d532 100644 --- a/src/tests/threads/Make.tests +++ b/src/tests/threads/Make.tests @@ -2,16 +2,18 @@ # Test names. tests/threads_TESTS = $(addprefix tests/threads/,alarm-single \ -alarm-multiple alarm-priority alarm-zero alarm-negative \ -priority-change priority-donate-one priority-donate-multiple \ -priority-donate-multiple2 priority-donate-nest priority-donate-sema \ -priority-fifo priority-preempt priority-sema priority-condvar \ -mlfqs-load-1 mlfqs-load-60 mlfqs-load-avg mlfqs-recent-1 mlfqs-fair-2 \ -mlfqs-fair-20 mlfqs-nice-2 mlfqs-nice-10) +alarm-multiple alarm-simultaneous alarm-priority alarm-zero \ +alarm-negative priority-change priority-donate-one \ +priority-donate-multiple priority-donate-multiple2 \ +priority-donate-nest priority-donate-sema priority-fifo \ +priority-preempt priority-sema priority-condvar mlfqs-load-1 \ +mlfqs-load-60 mlfqs-load-avg mlfqs-recent-1 mlfqs-fair-2 mlfqs-fair-20 \ +mlfqs-nice-2 mlfqs-nice-10) # Sources for tests. tests/threads_SRC = tests/threads/tests.c tests/threads_SRC += tests/threads/alarm-wait.c +tests/threads_SRC += tests/threads/alarm-simultaneous.c tests/threads_SRC += tests/threads/alarm-priority.c tests/threads_SRC += tests/threads/alarm-zero.c tests/threads_SRC += tests/threads/alarm-negative.c diff --git a/src/tests/threads/Rubric.alarm b/src/tests/threads/Rubric.alarm index b2d44a3..61abe85 100644 --- a/src/tests/threads/Rubric.alarm +++ b/src/tests/threads/Rubric.alarm @@ -1,7 +1,8 @@ Functionality and robustness of alarm clock: -5 alarm-single -5 alarm-multiple -5 alarm-priority +4 alarm-single +4 alarm-multiple +4 alarm-simultaneous +4 alarm-priority 1 alarm-zero 1 alarm-negative diff --git a/src/tests/threads/alarm-simultaneous.c b/src/tests/threads/alarm-simultaneous.c new file mode 100644 index 0000000..7db5418 --- /dev/null +++ b/src/tests/threads/alarm-simultaneous.c @@ -0,0 +1,94 @@ +/* Creates N threads, each of which sleeps a different, fixed + duration, M times. Records the wake-up order and verifies + that it is valid. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/malloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "devices/timer.h" + +static void test_sleep (int thread_cnt, int iterations); + +void +test_alarm_simultaneous (void) +{ + test_sleep (5, 5); +} + +/* Information about the test. */ +struct sleep_test + { + int64_t start; /* Current time at start of test. */ + int iterations; /* Number of iterations per thread. */ + int *output_pos; /* Current position in output buffer. */ + }; + +static void sleeper (void *); + +/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */ +static void +test_sleep (int thread_cnt, int iterations) +{ + struct sleep_test test; + int *output; + int i; + + /* This test does not work with the MLFQS. */ + ASSERT (!enable_mlfqs); + + msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations); + msg ("Each thread sleeps 10 ticks each time."); + msg ("Within an iteration, all threads should wake up on the same tick."); + + /* Allocate memory. */ + output = malloc (sizeof *output * iterations * thread_cnt * 2); + if (output == NULL) + PANIC ("couldn't allocate memory for test"); + + /* Initialize test. */ + test.start = timer_ticks () + 100; + test.iterations = iterations; + test.output_pos = output; + + /* Start threads. */ + ASSERT (output != NULL); + for (i = 0; i < thread_cnt; i++) + { + char name[16]; + snprintf (name, sizeof name, "thread %d", i); + thread_create (name, PRI_DEFAULT, sleeper, &test); + } + + /* Wait long enough for all the threads to finish. */ + timer_sleep (100 + iterations * 10 + 100); + + /* Print completion order. */ + msg ("iteration 0, thread 0: woke up after %d ticks", output[0]); + for (i = 1; i < test.output_pos - output; i++) + msg ("iteration %d, thread %d: woke up %d ticks later", + i / thread_cnt, i % thread_cnt, output[i] - output[i - 1]); + + free (output); +} + +/* Sleeper thread. */ +static void +sleeper (void *test_) +{ + struct sleep_test *test = test_; + int i; + + /* Make sure we're at the beginning of a timer tick. */ + timer_sleep (1); + + for (i = 1; i <= test->iterations; i++) + { + int64_t sleep_until = test->start + i * 10; + timer_sleep (sleep_until - timer_ticks ()); + *test->output_pos++ = timer_ticks () - test->start; + thread_yield (); + } +} diff --git a/src/tests/threads/alarm-simultaneous.ck b/src/tests/threads/alarm-simultaneous.ck new file mode 100644 index 0000000..510c07d --- /dev/null +++ b/src/tests/threads/alarm-simultaneous.ck @@ -0,0 +1,36 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(alarm-simultaneous) begin +(alarm-simultaneous) Creating 5 threads to sleep 5 times each. +(alarm-simultaneous) Each thread sleeps 10 ticks each time. +(alarm-simultaneous) Within an iteration, all threads should wake up on the same tick. +(alarm-simultaneous) iteration 0, thread 0: woke up after 10 ticks +(alarm-simultaneous) iteration 0, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 0, thread 2: woke up 0 ticks later +(alarm-simultaneous) iteration 0, thread 3: woke up 0 ticks later +(alarm-simultaneous) iteration 0, thread 4: woke up 0 ticks later +(alarm-simultaneous) iteration 1, thread 0: woke up 10 ticks later +(alarm-simultaneous) iteration 1, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 1, thread 2: woke up 0 ticks later +(alarm-simultaneous) iteration 1, thread 3: woke up 0 ticks later +(alarm-simultaneous) iteration 1, thread 4: woke up 0 ticks later +(alarm-simultaneous) iteration 2, thread 0: woke up 10 ticks later +(alarm-simultaneous) iteration 2, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 2, thread 2: woke up 0 ticks later +(alarm-simultaneous) iteration 2, thread 3: woke up 0 ticks later +(alarm-simultaneous) iteration 2, thread 4: woke up 0 ticks later +(alarm-simultaneous) iteration 3, thread 0: woke up 10 ticks later +(alarm-simultaneous) iteration 3, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 3, thread 2: woke up 0 ticks later +(alarm-simultaneous) iteration 3, thread 3: woke up 0 ticks later +(alarm-simultaneous) iteration 3, thread 4: woke up 0 ticks later +(alarm-simultaneous) iteration 4, thread 0: woke up 10 ticks later +(alarm-simultaneous) iteration 4, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 4, thread 2: woke up 0 ticks later +(alarm-simultaneous) iteration 4, thread 3: woke up 0 ticks later +(alarm-simultaneous) iteration 4, thread 4: woke up 0 ticks later +(alarm-simultaneous) end +EOF diff --git a/src/tests/threads/alarm-wait.c b/src/tests/threads/alarm-wait.c index 8030d20..458e987 100644 --- a/src/tests/threads/alarm-wait.c +++ b/src/tests/threads/alarm-wait.c @@ -145,7 +145,6 @@ sleeper (void *t_) { int64_t sleep_until = test->start + i * t->duration; timer_sleep (sleep_until - timer_ticks ()); - lock_acquire (&test->output_lock); *test->output_pos++ = t->id; lock_release (&test->output_lock); diff --git a/src/tests/threads/tests.c b/src/tests/threads/tests.c index b4b12cd..ca99eb6 100644 --- a/src/tests/threads/tests.c +++ b/src/tests/threads/tests.c @@ -13,6 +13,7 @@ static const struct test tests[] = { {"alarm-single", test_alarm_single}, {"alarm-multiple", test_alarm_multiple}, + {"alarm-simultaneous", test_alarm_simultaneous}, {"alarm-priority", test_alarm_priority}, {"alarm-zero", test_alarm_zero}, {"alarm-negative", test_alarm_negative}, diff --git a/src/tests/threads/tests.h b/src/tests/threads/tests.h index 46d99f8..ed9fbac 100644 --- a/src/tests/threads/tests.h +++ b/src/tests/threads/tests.h @@ -7,6 +7,7 @@ typedef void test_func (void); extern test_func test_alarm_single; extern test_func test_alarm_multiple; +extern test_func test_alarm_simultaneous; extern test_func test_alarm_priority; extern test_func test_alarm_zero; extern test_func test_alarm_negative;