821 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			821 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2015
 | |
|  *	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.
 | |
|  */
 | |
| 
 | |
| #include <types.h>
 | |
| #include <lib.h>
 | |
| #include <spinlock.h>
 | |
| #include <synch.h>
 | |
| #include <thread.h>
 | |
| #include <current.h>
 | |
| #include <clock.h>
 | |
| #include <test.h>
 | |
| 
 | |
| /*
 | |
|  * Unit tests for semaphores.
 | |
|  *
 | |
|  * We test 21 correctness criteria, each stated in a comment at the
 | |
|  * top of each test.
 | |
|  *
 | |
|  * Note that these tests go inside the semaphore abstraction to
 | |
|  * validate the internal state.
 | |
|  *
 | |
|  * All tests (apart from those that crash) attempt to clean up after
 | |
|  * running, to avoid leaking memory and leaving extra threads lying
 | |
|  * around. Tests with a cleanup phase call ok() before going to it in
 | |
|  * case the cleanup crashes -- this should not happen of course but if
 | |
|  * it does it should be distinguished from the main part of the test
 | |
|  * itself dying.
 | |
|  */
 | |
| 
 | |
| #define NAMESTRING "some-silly-name"
 | |
| 
 | |
| ////////////////////////////////////////////////////////////
 | |
| // support code
 | |
| 
 | |
| static unsigned waiters_running = 0;
 | |
| static struct spinlock waiters_lock = SPINLOCK_INITIALIZER;
 | |
| 
 | |
| static
 | |
| void
 | |
| ok(void)
 | |
| {
 | |
| 	kprintf("Test passed; now cleaning up.\n");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Wrapper for sem_create when we aren't explicitly tweaking it.
 | |
|  */
 | |
| static
 | |
| struct semaphore *
 | |
| makesem(unsigned count)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 
 | |
| 	sem = sem_create(NAMESTRING, count);
 | |
| 	if (sem == NULL) {
 | |
| 		panic("semunit: whoops: sem_create failed\n");
 | |
| 	}
 | |
| 	return sem;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * A thread that just waits on a semaphore.
 | |
|  */
 | |
| static
 | |
| void
 | |
| waiter(void *vsem, unsigned long junk)
 | |
| {
 | |
| 	struct semaphore *sem = vsem;
 | |
| 	(void)junk;
 | |
| 
 | |
| 	P(sem);
 | |
| 
 | |
| 	spinlock_acquire(&waiters_lock);
 | |
| 	KASSERT(waiters_running > 0);
 | |
| 	waiters_running--;
 | |
| 	spinlock_release(&waiters_lock);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Set up a waiter.
 | |
|  */
 | |
| static
 | |
| void
 | |
| makewaiter(struct semaphore *sem)
 | |
| {
 | |
| 	int result;
 | |
| 
 | |
| 	spinlock_acquire(&waiters_lock);
 | |
| 	waiters_running++;
 | |
| 	spinlock_release(&waiters_lock);
 | |
| 
 | |
| 	result = thread_fork("semunit waiter", NULL, waiter, sem, 0);
 | |
| 	if (result) {
 | |
| 		panic("semunit: thread_fork failed\n");
 | |
| 	}
 | |
| 	kprintf("Sleeping for waiter to run\n");
 | |
| 	clocksleep(1);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Spinlocks don't natively provide this, because it only makes sense
 | |
|  * under controlled conditions.
 | |
|  *
 | |
|  * Note that we should really read the holder atomically; but because
 | |
|  * we're using this under controlled conditions, it doesn't actually
 | |
|  * matter -- nobody is supposed to be able to touch the holder while
 | |
|  * we're checking it, or the check wouldn't be reliable; and, provided
 | |
|  * clocksleep works, nobody can.
 | |
|  */
 | |
| static
 | |
| bool
 | |
| spinlock_not_held(struct spinlock *splk)
 | |
| {
 | |
| 	return splk->splk_holder == NULL;
 | |
| }
 | |
| 
 | |
| ////////////////////////////////////////////////////////////
 | |
| // tests
 | |
| 
 | |
| /*
 | |
|  * 1. After a successful sem_create:
 | |
|  *     - sem_name compares equal to the passed-in name
 | |
|  *     - sem_name is not the same pointer as the passed-in name
 | |
|  *     - sem_wchan is not null
 | |
|  *     - sem_lock is not held and has no owner
 | |
|  *     - sem_count is the passed-in count
 | |
|  */
 | |
| int
 | |
| semu1(int nargs, char **args)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 	const char *name = NAMESTRING;
 | |
| 
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	sem = sem_create(name, 56);
 | |
| 	if (sem == NULL) {
 | |
| 		panic("semu1: whoops: sem_create failed\n");
 | |
| 	}
 | |
| 	KASSERT(!strcmp(sem->sem_name, name));
 | |
| 	KASSERT(sem->sem_name != name);
 | |
| 	KASSERT(sem->sem_wchan != NULL);
 | |
| 	KASSERT(spinlock_not_held(&sem->sem_lock));
 | |
| 	KASSERT(sem->sem_count == 56);
 | |
| 
 | |
| 	ok();
 | |
| 	/* clean up */
 | |
| 	sem_destroy(sem);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2. Passing a null name to sem_create asserts or crashes.
 | |
|  */
 | |
| int
 | |
| semu2(int nargs, char **args)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	kprintf("This should crash with a kernel null dereference\n");
 | |
| 	sem = sem_create(NULL, 44);
 | |
| 	(void)sem;
 | |
| 	panic("semu2: sem_create accepted a null name\n");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 3. Passing a null semaphore to sem_destroy asserts or crashes.
 | |
|  */
 | |
| int
 | |
| semu3(int nargs, char **args)
 | |
| {
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	kprintf("This should assert that sem != NULL\n");
 | |
| 	sem_destroy(NULL);
 | |
| 	panic("semu3: sem_destroy accepted a null semaphore\n");
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * 4. sem_count is an unsigned type.
 | |
|  */
 | |
| int
 | |
| semu4(int nargs, char **args)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	/* Create a semaphore with count 0. */
 | |
| 	sem = makesem(0);
 | |
| 	/* Decrement the count. */
 | |
| 	sem->sem_count--;
 | |
| 	/* This value should be positive. */
 | |
| 	KASSERT(sem->sem_count > 0);
 | |
| 
 | |
| 	/* Clean up. */
 | |
| 	ok();
 | |
| 	sem_destroy(sem);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 5. A semaphore can be successfully initialized with a count of at
 | |
|  * least 0xf0000000.
 | |
|  */
 | |
| int
 | |
| semu5(int nargs, char **args)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	sem = sem_create(NAMESTRING, 0xf0000000U);
 | |
| 	if (sem == NULL) {
 | |
| 		/* This might not be an innocuous malloc shortage. */
 | |
| 		panic("semu5: sem_create failed\n");
 | |
| 	}
 | |
| 	KASSERT(sem->sem_count == 0xf0000000U);
 | |
| 
 | |
| 	/* Clean up. */
 | |
| 	ok();
 | |
| 	sem_destroy(sem);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 6. Passing a semaphore with a waiting thread to sem_destroy asserts
 | |
|  * (in the wchan code).
 | |
|  */
 | |
| int
 | |
| semu6(int nargs, char **args)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	sem = makesem(0);
 | |
| 	makewaiter(sem);
 | |
| 	kprintf("This should assert that the wchan's threadlist is empty\n");
 | |
| 	sem_destroy(sem);
 | |
| 	panic("semu6: wchan_destroy with waiters succeeded\n");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 7. Calling V on a semaphore does not block the caller, regardless
 | |
|  * of the semaphore count.
 | |
|  */
 | |
| int
 | |
| semu7(int nargs, char **args)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 	struct spinlock lk;
 | |
| 
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	sem = makesem(0);
 | |
| 
 | |
| 	/*
 | |
| 	 * Check for blocking by taking a spinlock; if we block while
 | |
| 	 * holding a spinlock, wchan_sleep will assert.
 | |
| 	 */
 | |
| 	spinlock_init(&lk);
 | |
| 	spinlock_acquire(&lk);
 | |
| 
 | |
| 	/* try with count 0, count 1, and count 2, just for completeness */
 | |
| 	V(sem);
 | |
| 	V(sem);
 | |
| 	V(sem);
 | |
| 
 | |
| 	/* Clean up. */
 | |
| 	ok();
 | |
| 	spinlock_release(&lk);
 | |
| 	spinlock_cleanup(&lk);
 | |
| 	sem_destroy(sem);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 8/9. After calling V on a semaphore with no threads waiting:
 | |
|  *    - sem_name is unchanged
 | |
|  *    - sem_wchan is unchanged
 | |
|  *    - sem_lock is (still) unheld and has no owner
 | |
|  *    - sem_count is increased by one
 | |
|  *
 | |
|  * This is true even if we are in an interrupt handler.
 | |
|  */
 | |
| static
 | |
| void
 | |
| do_semu89(bool interrupthandler)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 	struct wchan *wchan;
 | |
| 	const char *name;
 | |
| 
 | |
| 	sem = makesem(0);
 | |
| 
 | |
| 	/* check preconditions */
 | |
| 	name = sem->sem_name;
 | |
| 	wchan = sem->sem_wchan;
 | |
| 	KASSERT(!strcmp(name, NAMESTRING));
 | |
| 	KASSERT(spinlock_not_held(&sem->sem_lock));
 | |
| 
 | |
| 	/*
 | |
| 	 * The right way to this is to set up an actual interrupt,
 | |
| 	 * e.g. an interprocessor interrupt, and hook onto it to run
 | |
| 	 * the V() in the actual interrupt handler. However, that
 | |
| 	 * requires a good bit of infrastructure that we don't
 | |
| 	 * have. Instead we'll fake it by explicitly setting
 | |
| 	 * curthread->t_in_interrupt.
 | |
| 	 */
 | |
| 	if (interrupthandler) {
 | |
| 		KASSERT(curthread->t_in_interrupt == false);
 | |
| 		curthread->t_in_interrupt = true;
 | |
| 	}
 | |
| 
 | |
| 	V(sem);
 | |
| 
 | |
| 	if (interrupthandler) {
 | |
| 		KASSERT(curthread->t_in_interrupt == true);
 | |
| 		curthread->t_in_interrupt = false;
 | |
| 	}
 | |
| 
 | |
| 	/* check postconditions */
 | |
| 	KASSERT(name == sem->sem_name);
 | |
| 	KASSERT(!strcmp(name, NAMESTRING));
 | |
| 	KASSERT(wchan == sem->sem_wchan);
 | |
| 	KASSERT(spinlock_not_held(&sem->sem_lock));
 | |
| 	KASSERT(sem->sem_count == 1);
 | |
| 
 | |
| 	/* clean up */
 | |
| 	ok();
 | |
| 	sem_destroy(sem);
 | |
| }
 | |
| 
 | |
| int
 | |
| semu8(int nargs, char **args)
 | |
| {
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	do_semu89(false /*interrupthandler*/);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| semu9(int nargs, char **args)
 | |
| {
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	do_semu89(true /*interrupthandler*/);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 10/11. After calling V on a semaphore with one thread waiting, and giving
 | |
|  * it time to run:
 | |
|  *    - sem_name is unchanged
 | |
|  *    - sem_wchan is unchanged
 | |
|  *    - sem_lock is (still) unheld and has no owner
 | |
|  *    - sem_count is still 0
 | |
|  *    - the other thread does in fact run
 | |
|  *
 | |
|  * This is true even if we are in an interrupt handler.
 | |
|  */
 | |
| static
 | |
| int
 | |
| do_semu1011(bool interrupthandler)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 	struct wchan *wchan;
 | |
| 	const char *name;
 | |
| 
 | |
| 	sem = makesem(0);
 | |
| 	makewaiter(sem);
 | |
| 
 | |
| 	/* check preconditions */
 | |
| 	name = sem->sem_name;
 | |
| 	wchan = sem->sem_wchan;
 | |
| 	KASSERT(!strcmp(name, NAMESTRING));
 | |
| 	KASSERT(spinlock_not_held(&sem->sem_lock));
 | |
| 	spinlock_acquire(&waiters_lock);
 | |
| 	KASSERT(waiters_running == 1);
 | |
| 	spinlock_release(&waiters_lock);
 | |
| 
 | |
| 	/* see above */
 | |
| 	if (interrupthandler) {
 | |
| 		KASSERT(curthread->t_in_interrupt == false);
 | |
| 		curthread->t_in_interrupt = true;
 | |
| 	}
 | |
| 
 | |
| 	V(sem);
 | |
| 
 | |
| 	if (interrupthandler) {
 | |
| 		KASSERT(curthread->t_in_interrupt == true);
 | |
| 		curthread->t_in_interrupt = false;
 | |
| 	}
 | |
| 
 | |
| 	/* give the waiter time to exit */
 | |
| 	clocksleep(1);
 | |
| 
 | |
| 	/* check postconditions */
 | |
| 	KASSERT(name == sem->sem_name);
 | |
| 	KASSERT(!strcmp(name, NAMESTRING));
 | |
| 	KASSERT(wchan == sem->sem_wchan);
 | |
| 	KASSERT(spinlock_not_held(&sem->sem_lock));
 | |
| 	KASSERT(sem->sem_count == 0);
 | |
| 	spinlock_acquire(&waiters_lock);
 | |
| 	KASSERT(waiters_running == 0);
 | |
| 	spinlock_release(&waiters_lock);
 | |
| 
 | |
| 	/* clean up */
 | |
| 	ok();
 | |
| 	sem_destroy(sem);
 | |
| 	return 0;
 | |
| 
 | |
| }
 | |
| 
 | |
| int
 | |
| semu10(int nargs, char **args)
 | |
| {
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	do_semu1011(false /*interrupthandler*/);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| semu11(int nargs, char **args)
 | |
| {
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	do_semu1011(true /*interrupthandler*/);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * 12/13. After calling V on a semaphore with two threads waiting, and
 | |
|  * giving it time to run:
 | |
|  *    - sem_name is unchanged
 | |
|  *    - sem_wchan is unchanged
 | |
|  *    - sem_lock is (still) unheld and has no owner
 | |
|  *    - sem_count is still 0
 | |
|  *    - one of the other threads does in fact run
 | |
|  *    - the other one does not
 | |
|  */
 | |
| static
 | |
| void
 | |
| semu1213(bool interrupthandler)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 	struct wchan *wchan;
 | |
| 	const char *name;
 | |
| 
 | |
| 	sem = makesem(0);
 | |
| 	makewaiter(sem);
 | |
| 	makewaiter(sem);
 | |
| 
 | |
| 	/* check preconditions */
 | |
| 	name = sem->sem_name;
 | |
| 	wchan = sem->sem_wchan;
 | |
| 	KASSERT(!strcmp(name, NAMESTRING));
 | |
| 	wchan = sem->sem_wchan;
 | |
| 	KASSERT(spinlock_not_held(&sem->sem_lock));
 | |
| 	spinlock_acquire(&waiters_lock);
 | |
| 	KASSERT(waiters_running == 2);
 | |
| 	spinlock_release(&waiters_lock);
 | |
| 
 | |
| 	/* see above */
 | |
| 	if (interrupthandler) {
 | |
| 		KASSERT(curthread->t_in_interrupt == false);
 | |
| 		curthread->t_in_interrupt = true;
 | |
| 	}
 | |
| 
 | |
| 	V(sem);
 | |
| 
 | |
| 	if (interrupthandler) {
 | |
| 		KASSERT(curthread->t_in_interrupt == true);
 | |
| 		curthread->t_in_interrupt = false;
 | |
| 	}
 | |
| 
 | |
| 	/* give the waiter time to exit */
 | |
| 	clocksleep(1);
 | |
| 
 | |
| 	/* check postconditions */
 | |
| 	KASSERT(name == sem->sem_name);
 | |
| 	KASSERT(!strcmp(name, NAMESTRING));
 | |
| 	KASSERT(wchan == sem->sem_wchan);
 | |
| 	KASSERT(spinlock_not_held(&sem->sem_lock));
 | |
| 	KASSERT(sem->sem_count == 0);
 | |
| 	spinlock_acquire(&waiters_lock);
 | |
| 	KASSERT(waiters_running == 1);
 | |
| 	spinlock_release(&waiters_lock);
 | |
| 
 | |
| 	/* clean up */
 | |
| 	ok();
 | |
| 	V(sem);
 | |
| 	clocksleep(1);
 | |
| 	spinlock_acquire(&waiters_lock);
 | |
| 	KASSERT(waiters_running == 0);
 | |
| 	spinlock_release(&waiters_lock);
 | |
| 	sem_destroy(sem);
 | |
| }
 | |
| 
 | |
| int
 | |
| semu12(int nargs, char **args)
 | |
| {
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	semu1213(false /*interrupthandler*/);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| semu13(int nargs, char **args)
 | |
| {
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	semu1213(true /*interrupthandler*/);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 14. Calling V on a semaphore whose count is the maximum allowed value
 | |
|  * asserts.
 | |
|  */
 | |
| int
 | |
| semu14(int nargs, char **args)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	kprintf("This should assert that sem_count is > 0.\n");
 | |
| 	sem = makesem(0);
 | |
| 
 | |
| 	/*
 | |
| 	 * The maximum value is (unsigned)-1. Get this by decrementing
 | |
| 	 * from 0.
 | |
| 	 */
 | |
| 	sem->sem_count--;
 | |
| 	V(sem);
 | |
| 	KASSERT(sem->sem_count == 0);
 | |
| 	panic("semu14: V tolerated count wraparound\n");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 15. Calling V on a null semaphore asserts.
 | |
|  */
 | |
| int
 | |
| semu15(int nargs, char **args)
 | |
| {
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	kprintf("This should assert that the semaphore isn't null.\n");
 | |
| 	V(NULL);
 | |
| 	panic("semu15: V tolerated null semaphore\n");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 16. Calling P on a semaphore with count > 0 does not block the caller.
 | |
|  */
 | |
| int
 | |
| semu16(int nargs, char **args)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 	struct spinlock lk;
 | |
| 
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	sem = makesem(1);
 | |
| 
 | |
| 	/* As above, check for improper blocking by taking a spinlock. */
 | |
| 	spinlock_init(&lk);
 | |
| 	spinlock_acquire(&lk);
 | |
| 
 | |
| 	P(sem);
 | |
| 
 | |
| 	ok();
 | |
| 	spinlock_release(&lk);
 | |
| 	spinlock_cleanup(&lk);
 | |
| 	sem_destroy(sem);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 17. Calling P on a semaphore with count == 0 does block the caller.
 | |
|  */
 | |
| 
 | |
| static struct thread *semu17_thread;
 | |
| 
 | |
| static
 | |
| void
 | |
| semu17_sub(void *semv, unsigned long junk)
 | |
| {
 | |
| 	struct semaphore *sem = semv;
 | |
| 
 | |
| 	(void)junk;
 | |
| 
 | |
| 	semu17_thread = curthread;
 | |
| 
 | |
| 	/* precondition */
 | |
| 	KASSERT(sem->sem_count == 0);
 | |
| 
 | |
| 	P(sem);
 | |
| }
 | |
| 
 | |
| int
 | |
| semu17(int nargs, char **args)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 	int result;
 | |
| 
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	semu17_thread = NULL;
 | |
| 
 | |
| 	sem = makesem(0);
 | |
| 	result = thread_fork("semu17_sub", NULL, semu17_sub, sem, 0);
 | |
| 	if (result) {
 | |
| 		panic("semu17: whoops: thread_fork failed\n");
 | |
| 	}
 | |
| 	kprintf("Waiting for subthread...\n");
 | |
| 	clocksleep(1);
 | |
| 
 | |
| 	/* The subthread should be blocked. */
 | |
| 	KASSERT(semu17_thread != NULL);
 | |
| 	KASSERT(semu17_thread->t_state == S_SLEEP);
 | |
| 
 | |
| 	/* Clean up. */
 | |
| 	ok();
 | |
| 	V(sem);
 | |
| 	clocksleep(1);
 | |
| 	sem_destroy(sem);
 | |
| 	semu17_thread = NULL;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 18. After calling P on a semaphore with count > 0:
 | |
|  *    - sem_name is unchanged
 | |
|  *    - sem_wchan is unchanged
 | |
|  *    - sem_lock is unheld and has no owner
 | |
|  *    - sem_count is one less
 | |
|  */
 | |
| int
 | |
| semu18(int nargs, char **args)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 	struct wchan *wchan;
 | |
| 	const char *name;
 | |
| 
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	sem = makesem(1);
 | |
| 
 | |
| 	/* preconditions */
 | |
| 	name = sem->sem_name;
 | |
| 	KASSERT(!strcmp(name, NAMESTRING));
 | |
| 	wchan = sem->sem_wchan;
 | |
| 	KASSERT(spinlock_not_held(&sem->sem_lock));
 | |
| 	KASSERT(sem->sem_count == 1);
 | |
| 
 | |
| 	P(sem);
 | |
| 	
 | |
| 	/* postconditions */
 | |
| 	KASSERT(name == sem->sem_name);
 | |
| 	KASSERT(!strcmp(name, NAMESTRING));
 | |
| 	KASSERT(wchan == sem->sem_wchan);
 | |
| 	KASSERT(spinlock_not_held(&sem->sem_lock));
 | |
| 	KASSERT(sem->sem_count == 0);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 19. After calling P on a semaphore with count == 0 and another
 | |
|  * thread uses V exactly once to cause a wakeup:
 | |
|  *    - sem_name is unchanged
 | |
|  *    - sem_wchan is unchanged
 | |
|  *    - sem_lock is unheld and has no owner
 | |
|  *    - sem_count is still 0
 | |
|  */
 | |
| 
 | |
| static
 | |
| void
 | |
| semu19_sub(void *semv,  unsigned long junk)
 | |
| {
 | |
| 	struct semaphore *sem = semv;
 | |
| 
 | |
| 	(void)junk;
 | |
| 
 | |
| 	kprintf("semu19: waiting for parent to sleep\n");
 | |
| 	clocksleep(1);
 | |
| 	/*
 | |
| 	 * We could assert here that the parent *is* sleeping; but for
 | |
| 	 * that we'd need its thread pointer and it's not worth the
 | |
| 	 * trouble.
 | |
| 	 */
 | |
| 	V(sem);
 | |
| }
 | |
| 
 | |
| int
 | |
| semu19(int nargs, char **args)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 	struct wchan *wchan;
 | |
| 	const char *name;
 | |
| 	int result;
 | |
| 
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	sem = makesem(0);
 | |
| 	result = thread_fork("semu19_sub", NULL, semu19_sub, sem, 0);
 | |
| 	if (result) {
 | |
| 		panic("semu19: whoops: thread_fork failed\n");
 | |
| 	}
 | |
| 
 | |
| 	/* preconditions */
 | |
| 	name = sem->sem_name;
 | |
| 	KASSERT(!strcmp(name, NAMESTRING));
 | |
| 	wchan = sem->sem_wchan;
 | |
| 	KASSERT(spinlock_not_held(&sem->sem_lock));
 | |
| 	KASSERT(sem->sem_count == 0);
 | |
| 
 | |
| 	P(sem);
 | |
| 
 | |
| 	/* postconditions */
 | |
| 	KASSERT(name == sem->sem_name);
 | |
| 	KASSERT(!strcmp(name, NAMESTRING));
 | |
| 	KASSERT(wchan == sem->sem_wchan);
 | |
| 	KASSERT(spinlock_not_held(&sem->sem_lock));
 | |
| 	KASSERT(sem->sem_count == 0);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 20/21. Calling P in an interrupt handler asserts, regardless of the
 | |
|  * count.
 | |
|  */
 | |
| int
 | |
| semu20(int nargs, char **args)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	kprintf("This should assert that we aren't in an interrupt\n");
 | |
| 
 | |
| 	sem = makesem(0);
 | |
| 	/* as above */
 | |
| 	curthread->t_in_interrupt = true;
 | |
| 	P(sem);
 | |
| 	panic("semu20: P tolerated being in an interrupt handler\n");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| semu21(int nargs, char **args)
 | |
| {
 | |
| 	struct semaphore *sem;
 | |
| 
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	kprintf("This should assert that we aren't in an interrupt\n");
 | |
| 
 | |
| 	sem = makesem(1);
 | |
| 	/* as above */
 | |
| 	curthread->t_in_interrupt = true;
 | |
| 	P(sem);
 | |
| 	panic("semu21: P tolerated being in an interrupt handler\n");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 22. Calling P on a null semaphore asserts.
 | |
|  */
 | |
| int
 | |
| semu22(int nargs, char **args)
 | |
| {
 | |
| 	(void)nargs; (void)args;
 | |
| 
 | |
| 	kprintf("This should assert that the semaphore isn't null.\n");
 | |
| 	P(NULL);
 | |
| 	panic("semu22: P tolerated null semaphore\n");
 | |
| 	return 0;
 | |
| }
 |