--- /dev/null
+/* The main thread set its priority to PRI_MIN and creates 7 threads
+ (thread 1..7) with priorities PRI_MIN + 3, 6, 9, 12, ...
+ The main thread initializes 8 locks: lock 0..7 and acquires lock 0.
+
+ When thread[i] starts, it first acquires lock[i] (unless i == 7.)
+ Subsequently, thread[i] attempts to acquire lock[i-1], which is held by
+ thread[i-1], except for lock[0], which is held by the main thread.
+ Because the lock is held, thread[i] donates its priority to thread[i-1],
+ which donates to thread[i-2], and so on until the main thread
+ receives the donation.
+
+ After threads[1..7] have been created and are blocked on locks[0..7],
+ the main thread releases lock[0], unblocking thread[1], and being
+ preempted by it.
+ Thread[1] then completes acquiring lock[0], then releases lock[0],
+ then releases lock[1], unblocking thread[2], etc.
+ Thread[7] finally acquires & releases lock[7] and exits, allowing
+ thread[6], then thread[5] etc. to run and exit until finally the
+ main thread exits.
+
+ In addition, interloper threads are created at priority levels
+ p = PRI_MIN + 2, 5, 8, 11, ... which should not be run until the
+ corresponding thread with priority p + 1 has finished.
+
+ Written by Godmar Back <gback@cs.vt.edu> */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+
+#define NESTING_DEPTH 8
+
+struct lock_pair
+ {
+ struct lock *second;
+ struct lock *first;
+ };
+
+static thread_func donor_thread_func;
+static thread_func interloper_thread_func;
+
+void
+test_priority_donate_chain (void)
+{
+ int i;
+ struct lock locks[NESTING_DEPTH - 1];
+ struct lock_pair lock_pairs[NESTING_DEPTH];
+
+ /* This test does not work with the MLFQS. */
+ ASSERT (!thread_mlfqs);
+
+ thread_set_priority (PRI_MIN);
+
+ for (i = 0; i < NESTING_DEPTH - 1; i++)
+ lock_init (&locks[i]);
+
+ lock_acquire (&locks[0]);
+ msg ("%s got lock.", thread_name ());
+
+ for (i = 1; i < NESTING_DEPTH; i++)
+ {
+ char name[16];
+ int thread_priority;
+
+ snprintf (name, sizeof name, "thread %d", i);
+ thread_priority = PRI_MIN + i * 3;
+ lock_pairs[i].first = i < NESTING_DEPTH - 1 ? locks + i: NULL;
+ lock_pairs[i].second = locks + i - 1;
+
+ thread_create (name, thread_priority, donor_thread_func, lock_pairs + i);
+ msg ("%s should have priority %d. Actual priority: %d.",
+ thread_name (), thread_priority, thread_get_priority ());
+
+ snprintf (name, sizeof name, "interloper %d", i);
+ thread_create (name, thread_priority - 1, interloper_thread_func, NULL);
+ }
+
+ lock_release (&locks[0]);
+ msg ("%s finishing with priority %d.", thread_name (),
+ thread_get_priority ());
+}
+
+static void
+donor_thread_func (void *locks_)
+{
+ struct lock_pair *locks = locks_;
+
+ if (locks->first)
+ lock_acquire (locks->first);
+
+ lock_acquire (locks->second);
+ msg ("%s got lock", thread_name ());
+
+ lock_release (locks->second);
+ msg ("%s should have priority %d. Actual priority: %d",
+ thread_name (), (NESTING_DEPTH - 1) * 3,
+ thread_get_priority ());
+
+ if (locks->first)
+ lock_release (locks->first);
+
+ msg ("%s finishing with priority %d.", thread_name (),
+ thread_get_priority ());
+}
+
+static void
+interloper_thread_func (void *arg_ UNUSED)
+{
+ msg ("%s finished.", thread_name ());
+}
+
+// vim: sw=2
--- /dev/null
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(priority-donate-chain) begin
+(priority-donate-chain) main got lock.
+(priority-donate-chain) main should have priority 3. Actual priority: 3.
+(priority-donate-chain) main should have priority 6. Actual priority: 6.
+(priority-donate-chain) main should have priority 9. Actual priority: 9.
+(priority-donate-chain) main should have priority 12. Actual priority: 12.
+(priority-donate-chain) main should have priority 15. Actual priority: 15.
+(priority-donate-chain) main should have priority 18. Actual priority: 18.
+(priority-donate-chain) main should have priority 21. Actual priority: 21.
+(priority-donate-chain) thread 1 got lock
+(priority-donate-chain) thread 1 should have priority 21. Actual priority: 21
+(priority-donate-chain) thread 2 got lock
+(priority-donate-chain) thread 2 should have priority 21. Actual priority: 21
+(priority-donate-chain) thread 3 got lock
+(priority-donate-chain) thread 3 should have priority 21. Actual priority: 21
+(priority-donate-chain) thread 4 got lock
+(priority-donate-chain) thread 4 should have priority 21. Actual priority: 21
+(priority-donate-chain) thread 5 got lock
+(priority-donate-chain) thread 5 should have priority 21. Actual priority: 21
+(priority-donate-chain) thread 6 got lock
+(priority-donate-chain) thread 6 should have priority 21. Actual priority: 21
+(priority-donate-chain) thread 7 got lock
+(priority-donate-chain) thread 7 should have priority 21. Actual priority: 21
+(priority-donate-chain) thread 7 finishing with priority 21.
+(priority-donate-chain) interloper 7 finished.
+(priority-donate-chain) thread 6 finishing with priority 18.
+(priority-donate-chain) interloper 6 finished.
+(priority-donate-chain) thread 5 finishing with priority 15.
+(priority-donate-chain) interloper 5 finished.
+(priority-donate-chain) thread 4 finishing with priority 12.
+(priority-donate-chain) interloper 4 finished.
+(priority-donate-chain) thread 3 finishing with priority 9.
+(priority-donate-chain) interloper 3 finished.
+(priority-donate-chain) thread 2 finishing with priority 6.
+(priority-donate-chain) interloper 2 finished.
+(priority-donate-chain) thread 1 finishing with priority 3.
+(priority-donate-chain) interloper 1 finished.
+(priority-donate-chain) main finishing with priority 0.
+(priority-donate-chain) end
+EOF
+pass;