-
Notifications
You must be signed in to change notification settings - Fork 0
HaroldLee/Rome2
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
/** @mainpage 15-410 Project 2 @author Kaili Li (kailili) @author Qi Liu (qiliu) Implementation: Part1 Thread library 1. Hash table We use a hash table to contain the threads information. As the thread tid generated by the kernal will increase and not repeat. There will be less collision in the table. As a result, we expect to find one thread item with constant time. 2. How to seperate thread stack. We put a blank virtual memory page between every two threads' stack. The blank page will not be allocated to any thread. When a thread go across its boundry, it may step in the thread stack below and will corrupt other thread's data. If there is a blank page, it will cause a page fault. And the exception handler would find the cross-border behavior and terminate the thread. Another aspect is that we never put thread information(such as tid) on its stack. Relying on the stack to store the information may spread error from one thread to another thread. I will explain this in the 'How to get tid section'. 3. Exception stack Each thread will be allocated with one exception stack(4K Byte) in the front of its stack. Thus, the handler of different threads will execute on different stack. 4. Resource recycle When a thread exit, we will not release its stack immediately. We will put it into a free list. When new thread is created, we will find from the free list first. 5. How to get tid It is true that there are some quick way to get current thread's tid. For example, put the tid on its stack. However, we will not use the method. We do want the stack corruption error to spread to other thread. Let me explain it with an example, suppose thread 7's stack is corrupted, and cause a fault. If the handler, use the tid from the stack, it will get some wrong answers, 12 maybe. As a result, the handler may try to terminate thread 12. We want thr_gettid() always return with a right value. Part 2: Autostack The following is the stack architecture in our implementation: +++++++++++++++++++ + + + root stack + + + +++++++++++++++++++ + + + thread1 + + security space + + (4k) + + + +++++++++++++++++++ + + + thread1 + + handler stack + + (4k) + + + +++++++++++++++++++ + + + thread1 + + user stack + + + +++++++++++++++++++ + + + thread2 + + security space + + (4k) + + + +++++++++++++++++++ + + + thread2 + + handler stack + + (4k) + + + +++++++++++++++++++ + + + thread2 + + user stack + + + +++++++++++++++++++ + + + ...... + + + +++++++++++++++++++ + + + ...... + + + +++++++++++++++++++ + + + root + + handler stack + + + +++++++++++++++++++ At the bottom (up) of the stack, is the user stack for the root thread, this is not allocated by us, what we do is just to record the base and top of this space, and use for further stack growth. if the "thr_init" is not called, there will not be a limit of the stack size. So we do not stop the stack grow to a very large size ("ROOT_HDLR_STACK" in autostack.c). Below the "ROOT_HDLR_STACK" is the handler stack for root thread, this is for the root thread's excption handler function. After the thr_init has been called, we will set a max value for the user stack space, then we will record it. and the stack size of any thread will be limited by this maxvalue. However, we do not give user the maximum space at once, instead, we give one page (4k) to user. When it encounters page fault, we give out one page more. In this way, we give page by page until the stack size grows to the max size set by "thr_init". The reason we do new_page all space user apply is that it may really need that much space, so we can save physical memory (not save logical space) in this way. Every thread has its own user stack and handler stack. Our consideration is that exception handler may be alseo interrupted by aother thread, and that thread may also encounters page fault...so we can not only have a "public space" for all handlers. Above the handler stack, is a "security space" for a thread. We have this space is based on following consideration. Suppose we do not have "security space", and we want to create 3 threads, 1, 2, 3. Suppose 2 is created first, 1 later. Then when stack of thread 1 grow to the space of thread 2, it will not encounter a page fault because this space has been paged out when 2 created. Then 2' stack will be accessed by 1 without any warning to user. Part3: Mutex and condition variables We do not implement the mutex to satisify bounded waiting. The code section between mutex_lock and mutex_unlock is expected to be short. Some method to implement bouned-waiting tend to make the mutex_lock complex(more code). Our implementation: When a thread try to get a lock and fail, it will yield to the thread who is currently hold the lock. So, the thread do not need to just wait and do nothing. Conditon varialbe: We have simple link list for the condition queue. Thread will be but in this queue and deschedule it self; on the other hand, another thread will first dequeue a thread if there is, and then record its thread id, then make it runnable in a loop until the make runnable returns SUCCESS. This is a safe implementation because when dequeue has not been finished, the "make run" thread will wait until it finish and then awake it. */
About
Legion
Resources
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published