X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pintos-anon;a=blobdiff_plain;f=src%2Fthreads%2Fthread.c;h=d68c123fb2476420d09557f4f52d4a6fb12949f4;hp=ba1cae365f711f44474d6206bffb4e4ca25ccddc;hb=09fa5c11aff62bd728d4fe8de28a7abaf252fed3;hpb=d4c30c6a386fe850e7eed1025e459fbc82a0b6e2 diff --git a/src/threads/thread.c b/src/threads/thread.c index ba1cae3..d68c123 100644 --- a/src/threads/thread.c +++ b/src/threads/thread.c @@ -24,6 +24,10 @@ that are ready to run but not actually running. */ static struct list ready_list; +/* List of all processes. Processes are added to this list + when they are first scheduled and removed when they exit. */ +static struct list all_list; + /* Idle thread. */ static struct thread *idle_thread; @@ -52,9 +56,7 @@ static unsigned thread_ticks; /* # of timer ticks since last yield. */ /* If false (default), use round-robin scheduler. If true, use multi-level feedback queue scheduler. - Controlled by kernel command-line options "-o mlfqs". - Note that the command line is not parsed until well after - thread_init() is called. */ + Controlled by kernel command-line option "-o mlfqs". */ bool thread_mlfqs; static void kernel_thread (thread_func *, void *aux); @@ -66,7 +68,7 @@ static void init_thread (struct thread *, const char *name, int priority); static bool is_thread (struct thread *) UNUSED; static void *alloc_frame (struct thread *, size_t size); static void schedule (void); -void schedule_tail (struct thread *prev); +void thread_schedule_tail (struct thread *prev); static tid_t allocate_tid (void); /* Initializes the threading system by transforming the code @@ -80,10 +82,6 @@ static tid_t allocate_tid (void); allocator before trying to create any threads with thread_create(). - The kernel command line is not parsed until *after* this - function returns, so that when this function runs, - thread_mlfqs is always false. - It is not safe to call thread_current() until this function finishes. */ void @@ -93,6 +91,7 @@ thread_init (void) lock_init (&tid_lock); list_init (&ready_list); + list_init (&all_list); /* Set up a thread structure for the running thread. */ initial_thread = running_thread (); @@ -102,22 +101,14 @@ thread_init (void) } /* Starts preemptive thread scheduling by enabling interrupts. - Also creates the idle thread. - - By the time this function runs, thread_mlfqs has been properly - initialized to its final value. */ + Also creates the idle thread. */ void thread_start (void) { - /* Create the idle thread with maximum priority. This ensures - that it will be scheduled soon after interrupts are enabled. - The idle thread will block almost immediately upon - scheduling, and subsequently it will never appear on the - ready list, so the priority here is otherwise - unimportant. */ + /* Create the idle thread. */ struct semaphore idle_started; sema_init (&idle_started, 0); - thread_create ("idle", PRI_MAX, idle, &idle_started); + thread_create ("idle", PRI_MIN, idle, &idle_started); /* Start preemptive thread scheduling. */ intr_enable (); @@ -180,6 +171,7 @@ thread_create (const char *name, int priority, struct switch_entry_frame *ef; struct switch_threads_frame *sf; tid_t tid; + enum intr_level old_level; ASSERT (function != NULL); @@ -192,6 +184,11 @@ thread_create (const char *name, int priority, init_thread (t, name, priority); tid = t->tid = allocate_tid (); + /* Prepare thread for first run by initializing its stack. + Do this atomically so intermediate values for the 'stack' + member cannot be observed. */ + old_level = intr_disable (); + /* Stack frame for kernel_thread(). */ kf = alloc_frame (t, sizeof *kf); kf->eip = NULL; @@ -205,6 +202,9 @@ thread_create (const char *name, int priority, /* Stack frame for switch_threads(). */ sf = alloc_frame (t, sizeof *sf); sf->eip = switch_entry; + sf->ebp = 0; + + intr_set_level (old_level); /* Add to run queue. */ thread_unblock (t); @@ -230,7 +230,12 @@ thread_block (void) /* Transitions a blocked thread T to the ready-to-run state. This is an error if T is not blocked. (Use thread_yield() to - make the running thread ready.) */ + make the running thread ready.) + + This function does not preempt the running thread. This can + be important: if the caller had disabled interrupts itself, + it may expect that it can atomically unblock a thread and + update other data. */ void thread_unblock (struct thread *t) { @@ -289,9 +294,11 @@ thread_exit (void) process_exit (); #endif - /* Just set our status to dying and schedule another process. - We will be destroyed during the call to schedule_tail(). */ + /* Remove thread from all threads list, set our status to dying, + and schedule another process. That process will destroy us + when it calls thread_schedule_tail(). */ intr_disable (); + list_remove (&thread_current()->allelem); thread_current ()->status = THREAD_DYING; schedule (); NOT_REACHED (); @@ -315,6 +322,23 @@ thread_yield (void) intr_set_level (old_level); } +/* Invoke function 'func' on all threads, passing along 'aux'. + This function must be called with interrupts off. */ +void +thread_foreach (thread_action_func *func, void *aux) +{ + struct list_elem *e; + + ASSERT (intr_get_level () == INTR_OFF); + + for (e = list_begin (&all_list); e != list_end (&all_list); + e = list_next (e)) + { + struct thread *t = list_entry (e, struct thread, allelem); + func (t, aux); + } +} + /* Sets the current thread's priority to NEW_PRIORITY. */ void thread_set_priority (int new_priority) @@ -394,7 +418,7 @@ idle (void *idle_started_ UNUSED) See [IA32-v2a] "HLT", [IA32-v2b] "STI", and [IA32-v3a] 7.11.1 "HLT Instruction". */ - asm ("sti; hlt"); + asm volatile ("sti; hlt" : : : "memory"); } } @@ -435,6 +459,8 @@ is_thread (struct thread *t) static void init_thread (struct thread *t, const char *name, int priority) { + enum intr_level old_level; + ASSERT (t != NULL); ASSERT (PRI_MIN <= priority && priority <= PRI_MAX); ASSERT (name != NULL); @@ -445,6 +471,10 @@ init_thread (struct thread *t, const char *name, int priority) t->stack = (uint8_t *) t + PGSIZE; t->priority = priority; t->magic = THREAD_MAGIC; + + old_level = intr_disable (); + list_push_back (&all_list, &t->allelem); + intr_set_level (old_level); } /* Allocates a SIZE-byte frame at the top of thread T's stack and @@ -491,7 +521,7 @@ next_thread_to_run (void) After this function and its caller returns, the thread switch is complete. */ void -schedule_tail (struct thread *prev) +thread_schedule_tail (struct thread *prev) { struct thread *cur = running_thread (); @@ -525,8 +555,8 @@ schedule_tail (struct thread *prev) running to some other state. This function finds another thread to run and switches to it. - It's not safe to call printf() until schedule_tail() has - completed. */ + It's not safe to call printf() until thread_schedule_tail() + has completed. */ static void schedule (void) { @@ -540,7 +570,7 @@ schedule (void) if (cur != next) prev = switch_threads (cur, next); - schedule_tail (prev); + thread_schedule_tail (prev); } /* Returns a tid to use for a new thread. */