add: args.death_sem = cached_death_sem;
context: void thread_exit(int retcode){	int state;	struct thread *t = thread_get_current_thread();	struct proc *p = t->proc;	bool delete_proc = false;	unsigned int death_stack;	dprintf("thread 0x%x exiting w/return code 0x%x\n", t->id, retcode);	// boost our priority to get this over with	thread_set_priority(t->id, THREAD_HIGH_PRIORITY);	// delete the user stack region first	if(p->_aspace_id >= 0 && t->user_stack_region_id >= 0) {		region_id rid = t->user_stack_region_id;		t->user_stack_region_id = -1;		vm_delete_region(p->_aspace_id, rid);	}	if(p != kernel_proc) {		// remove this thread from the current process and add it to the kernel		// put the thread into the kernel proc until it dies		state = disable_interrupts();		GRAB_PROC_LOCK();		remove_thread_from_proc(p, t);		insert_thread_into_proc(kernel_proc, t);		if(p->main_thread == t) {			// this was main thread in this process			delete_proc = true;			hash_remove(proc_hash, p);			p->state = PROC_STATE_DEATH;		}		RELEASE_PROC_LOCK();		// swap address spaces, to make sure we're running on the kernel's pgdir		vm_aspace_swap(kernel_proc->kaspace);		restore_interrupts(state);//		dprintf("thread_exit: thread 0x%x now a kernel thread!\n", t->id);	}	// delete the process	if(delete_proc) {		if(p->num_threads > 0) {			// there are other threads still in this process,			// cycle through and signal kill on each of the threads			// XXX this can be optimized. There's got to be a better solution.			struct thread *temp_thread;			state = disable_interrupts();			GRAB_PROC_LOCK();			// we can safely walk the list because of the lock. no new threads can be created			// because of the PROC_STATE_DEATH flag on the process			temp_thread = p->thread_list;			while(temp_thread) {				struct thread *next = temp_thread->proc_next;				thread_kill_thread_nowait(temp_thread->id);				temp_thread = next;			}			RELEASE_PROC_LOCK();			restore_interrupts(state);			// Now wait for all of the threads to die			// XXX block on a semaphore			while((volatile int)p->num_threads > 0) {				thread_snooze(10000); // 10 ms			}		}		vm_put_aspace(p->aspace);		vm_delete_aspace(p->_aspace_id);		delete_owned_ports(p->id);		sem_delete_owned_sems(p->id);		vfs_free_io_context(p->ioctx);		kfree(p);	}	// delete the sem that others will use to wait on us and get the retcode	{		sem_id s = t->return_code_sem;		t->return_code_sem = -1;		delete_sem_etc(s, retcode);	}	death_stack = get_death_stack();	{		struct thread_exit_args args;		args.t = t;		args.old_kernel_stack = t->kernel_stack_region_id;		args.death_stack = death_stack;		// disable the interrupts. Must remain disabled until the kernel stack pointer can be officially switched		args.int_state = disable_interrupts();		// set the new kernel stack officially to the death stack, wont be really switched until		// the next function is called. This bookkeeping must be done now before a context switch		// happens, or the processor will interrupt to the old stack		t->kernel_stack_region_id = death_stacks[death_stack].rid;		t->kernel_stack_base = death_stacks[death_stack].address;		// we will continue in thread_exit2(), on the new stack		arch_thread_switch_kstack_and_call(t, t->kernel_stack_base + KSTACK_SIZE, thread_exit2, &args);	}	panic("never can get here\n");}