diff --git a/kern/test/rwtest.c b/kern/test/rwtest.c index e3896d4..9fa3a33 100644 --- a/kern/test/rwtest.c +++ b/kern/test/rwtest.c @@ -1,3 +1,8 @@ +/* + * All the contents of this file are overwritten during automated + * testing. Please consider this before changing anything in this file. + */ + #include #include #include diff --git a/kern/test/synchprobs.c b/kern/test/synchprobs.c index d77aaed..07fec9a 100644 --- a/kern/test/synchprobs.c +++ b/kern/test/synchprobs.c @@ -1,6 +1,4 @@ /* - * NO NOT MODIFY THIS FILE - * * All the contents of this file are overwritten during automated * testing. Please consider this before changing anything in this file. */ @@ -11,28 +9,37 @@ #include #include #include +#include +#include #define PROBLEMS_MAX_YIELDER 16 #define PROBLEMS_MAX_SPINNER 8192 +#define SUCCESS 0 +#define FAIL 1 + /* * Shared initialization routines */ static uint32_t startcount; -static struct lock *startlock; +static struct lock *testlock; static struct cv *startcv; +static struct cv *endcv; static struct semaphore *endsem; +struct spinlock status_lock; +static bool test_status = FAIL; + static void inititems(uint32_t count) { startcount = count; - if (startlock==NULL) { - startlock = lock_create("startlock"); - if (startlock == NULL) { + if (testlock==NULL) { + testlock = lock_create("testlock"); + if (testlock == NULL) { panic("synchprobs: lock_create failed\n"); } } @@ -48,26 +55,78 @@ inititems(uint32_t count) panic("synchprobs: sem_create failed\n"); } } + spinlock_init(&status_lock); +} + +/* + * Helper function to initialize the thread pool. + */ +static +void +initialize_thread(volatile void* threads[], uint32_t index) { + if (threads[index] != NULL) { + test_status = FAIL; + } + threads[index] = curthread->t_stack; +} + +/* + * Helper function to check whether current thread is valid. + */ +static +void +check_thread(volatile void* threads[], uint32_t index) { + if (threads[index] != curthread->t_stack) { + test_status = FAIL; + } } /* * Driver code for the whalemating problem. */ +#define NMATING 10 +#define MALE 0 +#define FEMALE 1 +#define MATCHMAKER 2 +#define CHECK_TIMES 32 + +static volatile int male_start_count; +static volatile int male_end_count; +static volatile int female_start_count; +static volatile int female_end_count; +static volatile int matchmaker_start_count; +static volatile int matchmaker_end_count; +static volatile int match_count; + +static volatile int match_status[3 * NMATING]; +static volatile void* whale_threads[3 * NMATING]; +static volatile int whale_roles[3 * NMATING]; +static struct semaphore *matcher_sem; + +/* + * Enforce male_start() and male_end() called from male thread. + * Similar for female and matchmaker threads + */ +static +void +check_role(uint32_t index, int role) { + if (whale_roles[index] != role) { + test_status = FAIL; + } +} + static void male_wrapper(void * unused1, unsigned long index) { (void)unused1; random_yielder(4); - lock_acquire(startlock); + lock_acquire(testlock); + initialize_thread(whale_threads, (uint32_t)index); + whale_roles[index] = MALE; startcount--; - if (startcount == 0) { - cv_broadcast(startcv, startlock); - } else { - cv_wait(startcv, startlock); - } - lock_release(startlock); + lock_release(testlock); male((uint32_t)index); V(endsem); @@ -76,16 +135,30 @@ male_wrapper(void * unused1, unsigned long index) { void male_start(uint32_t index) { (void)index; + lock_acquire(testlock); + check_thread(whale_threads, index); + check_role(index, MALE); + male_start_count++; + cv_signal(startcv, testlock); + lock_release(testlock); random_yielder(PROBLEMS_MAX_YIELDER); random_spinner(PROBLEMS_MAX_SPINNER); kprintf_n("%s starting\n", curthread->t_name); + kprintf_t("."); } void male_end(uint32_t index) { (void)index; + lock_acquire(testlock); + check_thread(whale_threads, index); + check_role(index, MALE); + cv_signal(endcv, testlock); + male_end_count++; + lock_release(testlock); random_yielder(PROBLEMS_MAX_YIELDER); random_spinner(PROBLEMS_MAX_SPINNER); kprintf_n("%s ending\n", curthread->t_name); + kprintf_t("."); } static @@ -94,14 +167,11 @@ female_wrapper(void * unused1, unsigned long index) { (void)unused1; random_yielder(4); - lock_acquire(startlock); + lock_acquire(testlock); + initialize_thread(whale_threads, (uint32_t)index); + whale_roles[index] = FEMALE; startcount--; - if (startcount == 0) { - cv_broadcast(startcv, startlock); - } else { - cv_wait(startcv, startlock); - } - lock_release(startlock); + lock_release(testlock); female((uint32_t)index); V(endsem); @@ -110,53 +180,93 @@ female_wrapper(void * unused1, unsigned long index) { void female_start(uint32_t index) { (void) index; + lock_acquire(testlock); + check_thread(whale_threads, index); + check_role(index, FEMALE); + female_start_count++; + cv_signal(startcv, testlock); + lock_release(testlock); random_yielder(PROBLEMS_MAX_YIELDER); random_spinner(PROBLEMS_MAX_SPINNER); kprintf_n("%s starting\n", curthread->t_name); + kprintf_t("."); } void female_end(uint32_t index) { (void) index; + lock_acquire(testlock); + check_thread(whale_threads, index); + check_role(index, FEMALE); + cv_signal(endcv, testlock); + female_end_count++; + lock_release(testlock); random_yielder(PROBLEMS_MAX_YIELDER); random_spinner(PROBLEMS_MAX_SPINNER); kprintf_n("%s ending\n", curthread->t_name); + kprintf_t("."); } static void matchmaker_wrapper(void * unused1, unsigned long index) { (void)unused1; - + random_yielder(4); - lock_acquire(startlock); + lock_acquire(testlock); + initialize_thread(whale_threads, (uint32_t)index); + whale_roles[index] = MATCHMAKER; startcount--; - if (startcount == 0) { - cv_broadcast(startcv, startlock); - } else { - cv_wait(startcv, startlock); - } - lock_release(startlock); + lock_release(testlock); matchmaker((uint32_t)index); V(endsem); - + return; } void matchmaker_start(uint32_t index) { (void)index; + lock_acquire(testlock); + check_thread(whale_threads, index); + check_role(index, MATCHMAKER); + matchmaker_start_count++; + cv_signal(startcv, testlock); + lock_release(testlock); random_yielder(PROBLEMS_MAX_YIELDER); random_spinner(PROBLEMS_MAX_SPINNER); kprintf_n("%s starting\n", curthread->t_name); + kprintf_t("."); + P(matcher_sem); } void matchmaker_end(uint32_t index) { (void)index; + lock_acquire(testlock); + check_thread(whale_threads, index); + check_role(index, MATCHMAKER); + + int i = match_count * 3; + match_status[i] = male_start_count - male_end_count; + match_status[i+1] = female_start_count - female_end_count; + match_status[i+2] = matchmaker_start_count - matchmaker_end_count; + + match_count++; + matchmaker_end_count++; + cv_signal(endcv, testlock); + lock_release(testlock); random_yielder(PROBLEMS_MAX_YIELDER); random_spinner(PROBLEMS_MAX_SPINNER); kprintf_n("%s ending\n", curthread->t_name); + kprintf_t("."); } -#define NMATING 10 +static +void +check_zero(int count, const char *name) { + (void)name; + if (count != 0) { + test_status = FAIL; + } +} int whalemating(int nargs, char **args) { @@ -165,13 +275,47 @@ whalemating(int nargs, char **args) { int i, j, err = 0; char name[32]; - - inititems(3 * NMATING); - whalemating_init(); - for (i = 0; i < 3; i++) { + male_start_count = 0 ; + male_end_count = 0 ; + female_start_count = 0 ; + female_end_count = 0 ; + matchmaker_start_count = 0; + matchmaker_end_count = 0; + match_count = 0; + + startcount = 3 * NMATING; + testlock = lock_create("testlock"); + if (testlock == NULL) { + panic("sp1: lock_create failed\n"); + } + startcv = cv_create("startcv"); + if (startcv == NULL) { + panic("sp1: cv_create failed\n"); + } + endcv = cv_create("endcv"); + if (endcv == NULL) { + panic("sp1: cv_create failed\n"); + } + endsem = sem_create("endsem", 0); + if (endsem == NULL) { + panic("sp1: sem_create failed\n"); + } + matcher_sem = sem_create("matcher_sem", 0); + if (matcher_sem == NULL) { + panic("sp1: sem_create failed\n"); + } + spinlock_init(&status_lock); + test_status = FAIL; + + whalemating_init(); + + /* Start males and females only. */ + for (i = 0; i < 2; i++) { for (j = 0; j < NMATING; j++) { + kprintf_t("."); int index = (i * NMATING) + j; + whale_threads[index] = NULL; switch (i) { case 0: snprintf(name, sizeof(name), "Male Whale Thread %d", index); @@ -181,10 +325,6 @@ whalemating(int nargs, char **args) { snprintf(name, sizeof(name), "Female Whale Thread %d", index); err = thread_fork(name, NULL, female_wrapper, NULL, index); break; - case 2: - snprintf(name, sizeof(name), "Matchmaker Whale Thread %d", index); - err = thread_fork(name, NULL, matchmaker_wrapper, NULL, index); - break; } if (err) { panic("whalemating: thread_fork failed: (%s)\n", strerror(err)); @@ -192,14 +332,127 @@ whalemating(int nargs, char **args) { } } + /* Wait for males and females to start. */ + for (i = 0; i < NMATING * 2; i++) { + kprintf_t("."); + lock_acquire(testlock); + cv_wait(startcv, testlock); + lock_release(testlock); + } + + /* Make sure nothing is happening... */ + bool loop_status = SUCCESS; + for (i = 0; i < CHECK_TIMES && loop_status == SUCCESS; ) { + kprintf_t("."); + random_spinner(PROBLEMS_MAX_SPINNER); + lock_acquire(testlock); + if ((male_start_count != NMATING) || (female_start_count != NMATING) || + (matchmaker_start_count + male_end_count + female_end_count + matchmaker_end_count != 0)) { + loop_status = FAIL; + } + lock_release(testlock); + } + if (loop_status == FAIL) { + test_status = FAIL; + goto done; + } + + /* Create the matchmakers */ + for (j = 0; j < NMATING; j++) { + kprintf_t("."); + int index = (2 * NMATING) + j; + whale_threads[index] = NULL; + snprintf(name, sizeof(name), "Matchmaker Whale Thread %d", index); + err = thread_fork(name, NULL, matchmaker_wrapper, NULL, index); + if (err) { + panic("whalemating: thread_fork failed: (%s)\n", strerror(err)); + } + } + + /* + * Release a random number of matchmakers and wait for them and their + * matches to finish. + */ + int pivot = (random() % (NMATING - 2)) + 1; + for (i = 0; i < pivot; i++) { + kprintf_t("."); + V(matcher_sem); + } + for (i = 0; i < 3 * pivot; i++) { + kprintf_t("."); + lock_acquire(testlock); + cv_wait(endcv, testlock); + lock_release(testlock); + } + + /* Make sure nothing else is happening... */ + bool loop_status = SUCCESS; + for (i = 0; i < CHECK_TIMES && loop_status == SUCCESS; ) { + kprintf_t("."); + random_spinner(PROBLEMS_MAX_SPINNER); + lock_acquire(testlock); + if ((male_start_count != NMATING) || (female_start_count != NMATING) || + (male_end_count != pivot) || (female_end_count != pivot) || + (matchmaker_start_count != pivot) || (matchmaker_end_count != pivot)) { + loop_status = FAIL; + } + lock_release(testlock); + } + if (loop_status == FAIL) { + test_status = FAIL; + goto done; + } + + /* + * Release the rest of the matchmakers and wait for everyone to finish. + */ + + for (i = pivot; i < NMATING; i++) { + V(matcher_sem); + } for (i = 0; i < 3; i++) { for (j = 0; j < NMATING; j++) { + kprintf_t("."); P(endsem); } } whalemating_cleanup(); + check_zero(male_start_count - NMATING, "male"); + check_zero(female_start_count - NMATING, "female"); + check_zero(matchmaker_start_count - NMATING, "matchmaker"); + check_zero(male_start_count - male_end_count, "male"); + check_zero(female_start_count - female_end_count, "female"); + check_zero(matchmaker_start_count - matchmaker_end_count, "matchmaker"); + + if (match_count == NMATING) { + for (i = 0; i < NMATING; i++) { + kprintf_t("."); + j = i * 3; + int male = match_status[j]; + int female = match_status[j + 1]; + if (male == 0 || female == 0) { + spinlock_acquire(&status_lock); + test_status = FAIL; + spinlock_release(&status_lock); + } + } + } else { + spinlock_acquire(&status_lock); + test_status = FAIL; + spinlock_release(&status_lock); + } + +done: + lock_destroy(testlock); + cv_destroy(startcv); + cv_destroy(endcv); + sem_destroy(endsem); + sem_destroy(matcher_sem); + + success(test_status, SECRET, "sp1"); + return 0; } @@ -207,19 +460,79 @@ whalemating(int nargs, char **args) { * Driver code for the stoplight problem. */ +#define NCARS 64 +#define NUM_QUADRANTS 4 +#define UNKNOWN_CAR -1 +#define PASSED_CAR -2 + +#define GO_STRAIGHT 0 +#define TURN_LEFT 1 +#define TURN_RIGHT 2 + +static volatile int quadrant_array[NUM_QUADRANTS]; +static volatile int max_car_count; +static volatile int all_quadrant; +static volatile int car_locations[NCARS]; +static volatile int car_directions[NCARS]; +static volatile int car_turns[NCARS]; +static volatile int car_turn_times[NCARS]; +static volatile void* car_threads[NCARS]; + +static +void +initialize_car_thread(uint32_t index, uint32_t direction, uint32_t turn) { + initialize_thread(car_threads, index); + car_directions[index] = direction; + car_turns[index] = turn; + car_turn_times[index] = 0; +} + +static +void +check_intersection() { + int n = 0; + for (int i = 0; i < NUM_QUADRANTS; i++) { + if (quadrant_array[i] > 1) { + // panic("stoplight: more than 1 car in same quadrant %d\n", i); + test_status = FAIL; + } + n += quadrant_array[i]; + } + max_car_count = n > max_car_count ? n : max_car_count; +} + +/* + * When car move, must call this function and hold a lock. + * It first checks current intersection status make sure no more than one car in one quadrant. + * Then it removes current car from previous location. + * In the end, it returns current car's index for inQuadrant, to let inQuadrant update car_locations array. + */ +static +int +move(uint32_t index) { + check_thread(car_threads, index); + check_intersection(); + int pre_location = car_locations[index]; + if (pre_location != UNKNOWN_CAR && pre_location != PASSED_CAR) { + quadrant_array[pre_location]--; + } + return pre_location; +} + static void turnright_wrapper(void *index, unsigned long direction) { random_yielder(4); - lock_acquire(startlock); + lock_acquire(testlock); + initialize_car_thread((uint32_t)index, (uint32_t)direction, TURN_RIGHT); startcount--; if (startcount == 0) { - cv_broadcast(startcv, startlock); + cv_broadcast(startcv, testlock); } else { - cv_wait(startcv, startlock); + cv_wait(startcv, testlock); } - lock_release(startlock); + lock_release(testlock); turnright((uint32_t)direction, (uint32_t)index); V(endsem); @@ -230,14 +543,15 @@ void gostraight_wrapper(void *index, unsigned long direction) { random_yielder(4); - lock_acquire(startlock); + lock_acquire(testlock); + initialize_car_thread((uint32_t)index, (uint32_t)direction, GO_STRAIGHT); startcount--; if (startcount == 0) { - cv_broadcast(startcv, startlock); + cv_broadcast(startcv, testlock); } else { - cv_wait(startcv, startlock); + cv_wait(startcv, testlock); } - lock_release(startlock); + lock_release(testlock); gostraight((uint32_t)direction, (uint32_t)index); V(endsem); @@ -248,14 +562,15 @@ void turnleft_wrapper(void *index, unsigned long direction) { random_yielder(4); - lock_acquire(startlock); + lock_acquire(testlock); + initialize_car_thread((uint32_t)index, (uint32_t)direction, TURN_LEFT); startcount--; if (startcount == 0) { - cv_broadcast(startcv, startlock); + cv_broadcast(startcv, testlock); } else { - cv_wait(startcv, startlock); + cv_wait(startcv, testlock); } - lock_release(startlock); + lock_release(testlock); turnleft((uint32_t)direction, (uint32_t)index); V(endsem); @@ -264,24 +579,86 @@ turnleft_wrapper(void *index, unsigned long direction) void inQuadrant(int quadrant, uint32_t index) { - (void)index; - random_yielder(PROBLEMS_MAX_YIELDER); random_spinner(PROBLEMS_MAX_SPINNER); + lock_acquire(testlock); + int pre_quadrant = move(index); + + int target_quadrant = car_directions[index]; + switch (car_turn_times[index]) { + case 0: + if (pre_quadrant != UNKNOWN_CAR) { + test_status = FAIL; + } + break; + case 1: + if (pre_quadrant != target_quadrant) { + test_status = FAIL; + } + target_quadrant = (target_quadrant + NUM_QUADRANTS - 1) % NUM_QUADRANTS; + break; + case 2: + target_quadrant = (target_quadrant + NUM_QUADRANTS - 1) % NUM_QUADRANTS; + if (pre_quadrant != target_quadrant) { + test_status = FAIL; + } + target_quadrant = (target_quadrant + NUM_QUADRANTS - 1) % NUM_QUADRANTS; + break; + default: + test_status = FAIL; + break; + } + if (quadrant != target_quadrant) { + test_status = FAIL; + } + car_turn_times[index]++; + + if (quadrant_array[quadrant] > 0) { + // panic("%s inQuadrant %d which already has another car\n", curthread->t_name, quadrant); + test_status = FAIL; + } + quadrant_array[quadrant]++; + car_locations[index] = quadrant; + all_quadrant++; + + lock_release(testlock); kprintf_n("%s in quadrant %d\n", curthread->t_name, quadrant); } void leaveIntersection(uint32_t index) { - (void)index; - random_yielder(PROBLEMS_MAX_YIELDER); random_spinner(PROBLEMS_MAX_SPINNER); + lock_acquire(testlock); + move(index); + + switch (car_turns[index]) { + case GO_STRAIGHT: + if (car_turn_times[index] != 2) { + test_status = FAIL; + } + break; + case TURN_LEFT: + if (car_turn_times[index] != 3) { + test_status = FAIL; + } + break; + case TURN_RIGHT: + if (car_turn_times[index] != 1) { + test_status = FAIL; + } + break; + default: + test_status = FAIL; + break; + } + + car_locations[index] = PASSED_CAR; + lock_release(testlock); kprintf_n("%s left the intersection\n", curthread->t_name); } -#define NCARS 64 - +// TODO: should we delete this? struct semaphore * stoplightMenuSemaphore; int stoplight(int nargs, char **args) { @@ -292,6 +669,21 @@ int stoplight(int nargs, char **args) { inititems(NCARS); stoplight_init(); + test_status = SUCCESS; + + max_car_count = 0; + all_quadrant = 0; + int required_quadrant = 0; + + for (i = 0; i < NUM_QUADRANTS; i++) { + quadrant_array[i] = 0; + } + + for (i = 0; i < NCARS; i++) { + car_locations[i] = UNKNOWN_CAR; + car_threads[i] = NULL; + car_directions[i] = -1; + } for (i = 0; i < NCARS; i++) { @@ -301,26 +693,59 @@ int stoplight(int nargs, char **args) { snprintf(name, sizeof(name), "Car Thread %d", i); switch (turn) { - case 0: + case GO_STRAIGHT: err = thread_fork(name, NULL, gostraight_wrapper, (void *)i, direction); + required_quadrant += 2; break; - case 1: + case TURN_LEFT: err = thread_fork(name, NULL, turnleft_wrapper, (void *)i, direction); + required_quadrant += 3; break; - case 2: + case TURN_RIGHT: err = thread_fork(name, NULL, turnright_wrapper, (void *)i, direction); + required_quadrant += 1; break; } if (err) { panic("stoplight: thread_fork failed: (%s)\n", strerror(err)); } } - + for (i = 0; i < NCARS; i++) { P(endsem); } stoplight_cleanup(); + if (test_status == FAIL) { + success(test_status, SECRET, "sp2"); + return 0; + } + + // Check all cars pass the intersection + int passed = 0; + for (i = 0; i < NCARS; i++) { + passed += car_locations[i] == PASSED_CAR ? 1 : 0; + } + if (passed != NCARS) { + // panic("stoplight: not all cars pass the intersection, total: %d, passed: %d\n", NCARS, passed); + success(FAIL, SECRET, "sp2"); + return 0; + } + + // Check hit quadrant times is same as required, for example, student can force all cars turn right + if (all_quadrant != required_quadrant) { + // panic("stoplight: you may make wrong turn for some cars\n"); + success(FAIL, SECRET, "sp2"); + return 0; + } + + if (max_car_count <= 1) { + test_status = FAIL; + // panic("stoplight: only one car in the intersection at same time, did you use big lock?\n"); + } + + success(test_status, SECRET, "sp2"); + return 0; } diff --git a/kern/test/synchtest.c b/kern/test/synchtest.c index 31a14b9..be94df6 100644 --- a/kern/test/synchtest.c +++ b/kern/test/synchtest.c @@ -29,6 +29,9 @@ /* * Synchronization test code. + * + * All the contents of this file are overwritten during automated + * testing. Please consider this before changing anything in this file. */ #include @@ -117,6 +120,7 @@ semtest(int nargs, char **args) sem_destroy(donesem); } } + spinlock_init(&status_lock); test_status = FAIL; kprintf_n("If this hangs, it's broken: "); @@ -249,6 +253,7 @@ locktest(int nargs, char **args) sem_destroy(donesem); } } + spinlock_init(&status_lock); test_status = SUCCESS; for (i=0; i