2024-09-10 13:03:02 -04:00

367 lines
8.4 KiB
C

/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
* The President and Fellows of Harvard College.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Synchronization test code.
*/
#include <types.h>
#include <lib.h>
#include <clock.h>
#include <thread.h>
#include <synch.h>
#include <test.h>
#define NSEMLOOPS 63
#define NLOCKLOOPS 120
#define NCVLOOPS 5
#define NTHREADS 32
static volatile unsigned long testval1;
static volatile unsigned long testval2;
static volatile unsigned long testval3;
static struct semaphore *testsem;
static struct lock *testlock;
static struct cv *testcv;
static struct semaphore *donesem;
static void inititems(void) {
if (testsem == NULL) {
testsem = sem_create("testsem", 2);
if (testsem == NULL) {
panic("synchtest: sem_create failed\n");
}
}
if (testlock == NULL) {
testlock = lock_create("testlock");
if (testlock == NULL) {
panic("synchtest: lock_create failed\n");
}
}
if (testcv == NULL) {
testcv = cv_create("testlock");
if (testcv == NULL) {
panic("synchtest: cv_create failed\n");
}
}
if (donesem == NULL) {
donesem = sem_create("donesem", 0);
if (donesem == NULL) {
panic("synchtest: sem_create failed\n");
}
}
}
static void semtestthread(void *junk, unsigned long num) {
int i;
(void)junk;
/*
* Only one of these should print at a time.
*/
P(testsem);
kprintf("Thread %2lu: ", num);
for (i = 0; i < NSEMLOOPS; i++) {
kprintf("%c", (int)num + 64);
}
kprintf("\n");
V(donesem);
}
int semtest(int nargs, char **args) {
int i, result;
(void)nargs;
(void)args;
inititems();
kprintf("Starting semaphore test...\n");
kprintf("If this hangs, it's broken: ");
P(testsem);
P(testsem);
kprintf("ok\n");
for (i = 0; i < NTHREADS; i++) {
result = thread_fork("semtest", NULL, semtestthread, NULL, i);
if (result) {
panic("semtest: thread_fork failed: %s\n", strerror(result));
}
}
for (i = 0; i < NTHREADS; i++) {
V(testsem);
P(donesem);
}
/* so we can run it again */
V(testsem);
V(testsem);
kprintf("Semaphore test done.\n");
return 0;
}
static void fail(unsigned long num, const char *msg) {
kprintf("thread %lu: Mismatch on %s\n", num, msg);
kprintf("Test failed\n");
lock_release(testlock);
V(donesem);
thread_exit();
}
static void locktestthread(void *junk, unsigned long num) {
int i;
(void)junk;
for (i = 0; i < NLOCKLOOPS; i++) {
lock_acquire(testlock);
testval1 = num;
testval2 = num * num;
testval3 = num % 3;
if (testval2 != testval1 * testval1) {
fail(num, "testval2/testval1");
}
if (testval2 % 3 != (testval3 * testval3) % 3) {
fail(num, "testval2/testval3");
}
if (testval3 != testval1 % 3) {
fail(num, "testval3/testval1");
}
if (testval1 != num) {
fail(num, "testval1/num");
}
if (testval2 != num * num) {
fail(num, "testval2/num");
}
if (testval3 != num % 3) {
fail(num, "testval3/num");
}
lock_release(testlock);
}
V(donesem);
}
int locktest(int nargs, char **args) {
int i, result;
(void)nargs;
(void)args;
inititems();
kprintf("Starting lock test...\n");
for (i = 0; i < NTHREADS; i++) {
result = thread_fork("synchtest", NULL, locktestthread, NULL, i);
if (result) {
panic("locktest: thread_fork failed: %s\n", strerror(result));
}
}
for (i = 0; i < NTHREADS; i++) {
P(donesem);
}
kprintf("Lock test done.\n");
return 0;
}
static void cvtestthread(void *junk, unsigned long num) {
int i;
volatile int j;
struct timespec ts1, ts2;
(void)junk;
for (i = 0; i < NCVLOOPS; i++) {
lock_acquire(testlock);
while (testval1 != num) {
gettime(&ts1);
cv_wait(testcv, testlock);
gettime(&ts2);
/* ts2 -= ts1 */
timespec_sub(&ts2, &ts1, &ts2);
/* Require at least 2000 cpu cycles (we're 25mhz) */
if (ts2.tv_sec == 0 && ts2.tv_nsec < 40 * 2000) {
kprintf("cv_wait took only %u ns\n", ts2.tv_nsec);
kprintf("That's too fast... you must be "
"busy-looping\n");
V(donesem);
thread_exit();
}
}
kprintf("Thread %lu\n", num);
testval1 = (testval1 + NTHREADS - 1) % NTHREADS;
/*
* loop a little while to make sure we can measure the
* time waiting on the cv.
*/
for (j = 0; j < 3000; j++)
;
cv_broadcast(testcv, testlock);
lock_release(testlock);
}
V(donesem);
}
int cvtest(int nargs, char **args) {
int i, result;
(void)nargs;
(void)args;
inititems();
kprintf("Starting CV test...\n");
kprintf("Threads should print out in reverse order.\n");
testval1 = NTHREADS - 1;
for (i = 0; i < NTHREADS; i++) {
result = thread_fork("synchtest", NULL, cvtestthread, NULL, i);
if (result) {
panic("cvtest: thread_fork failed: %s\n", strerror(result));
}
}
for (i = 0; i < NTHREADS; i++) {
P(donesem);
}
kprintf("CV test done\n");
return 0;
}
////////////////////////////////////////////////////////////
/*
* Try to find out if going to sleep is really atomic.
*
* What we'll do is rotate through NCVS lock/CV pairs, with one thread
* sleeping and the other waking it up. If we miss a wakeup, the sleep
* thread won't go around enough times.
*/
#define NCVS 250
#define NLOOPS 40
static struct cv *testcvs[NCVS];
static struct lock *testlocks[NCVS];
static struct semaphore *gatesem;
static struct semaphore *exitsem;
static void sleepthread(void *junk1, unsigned long junk2) {
unsigned i, j;
(void)junk1;
(void)junk2;
for (j = 0; j < NLOOPS; j++) {
for (i = 0; i < NCVS; i++) {
lock_acquire(testlocks[i]);
V(gatesem);
cv_wait(testcvs[i], testlocks[i]);
lock_release(testlocks[i]);
}
kprintf("sleepthread: %u\n", j);
}
V(exitsem);
}
static void wakethread(void *junk1, unsigned long junk2) {
unsigned i, j;
(void)junk1;
(void)junk2;
for (j = 0; j < NLOOPS; j++) {
for (i = 0; i < NCVS; i++) {
P(gatesem);
lock_acquire(testlocks[i]);
cv_signal(testcvs[i], testlocks[i]);
lock_release(testlocks[i]);
}
kprintf("wakethread: %u\n", j);
}
V(exitsem);
}
int cvtest2(int nargs, char **args) {
unsigned i;
int result;
(void)nargs;
(void)args;
for (i = 0; i < NCVS; i++) {
testlocks[i] = lock_create("cvtest2 lock");
testcvs[i] = cv_create("cvtest2 cv");
}
gatesem = sem_create("gatesem", 0);
exitsem = sem_create("exitsem", 0);
kprintf("cvtest2...\n");
result = thread_fork("cvtest2", NULL, sleepthread, NULL, 0);
if (result) {
panic("cvtest2: thread_fork failed\n");
}
result = thread_fork("cvtest2", NULL, wakethread, NULL, 0);
if (result) {
panic("cvtest2: thread_fork failed\n");
}
P(exitsem);
P(exitsem);
sem_destroy(exitsem);
sem_destroy(gatesem);
exitsem = gatesem = NULL;
for (i = 0; i < NCVS; i++) {
lock_destroy(testlocks[i]);
cv_destroy(testcvs[i]);
testlocks[i] = NULL;
testcvs[i] = NULL;
}
kprintf("cvtest2 done\n");
return 0;
}