EC440 Project4: Thread Local Storage
The goal of this homework was to understand basic concepts of memory management and to provide protected memory regions for threads
A lot of the code that we needed for logic in this assignment was given through the slides covered in lecture. This helped a lot with the different aspects of logic in terms of properly allocating memory, but there were a few design choices I had to make for my own, especially for the data structure we used for mapping TLS and their threads, as well as writing the logic for the different functions. This assignment was more straightforward as we were essentially given comments that told us exactly what we were implementing, all we had to figure out was how.
To start, I had 2 global variables: initalized, and page_size. These are two that are used in tls_create as well as allocating memory for our pages
The first thing that was given was the struct for page, this has two entries for address and reference count. I had to change the type for address because of pointer arithmethic because I used C11.
Next given was the struct for thread_local_storage, the DS to hold the tls. This had 4 fields which were the TID, size in bytes, number of pages, and array of pointers to the pages.
We were given an idea for how to do our global structure for mapping thread to a TLS, with the hash_element and hash_table. The hash_element has three fields, TID, *tls, and a next pointer. This suggests a linked list format for the hash_table when resolving collisions. I believe that because we only have a max thread count of 128, we can avoid having to deal with collisions with a hash_table with 128 spots, but I added collision logic for generalization, as it makes more sense to have a smaller hash_table size.
the next given function was tls_handle_page_fault. This was partially implemented, telling us with the p_fault address to help us find the page which causes the fault. We were given comments which pretty much told us what to do. Firstly, we need to check of the signal raised for the segfault was a real segfault or if because a thread touched the forbidden memory. We do thisby iterating through every ssingle entry + collision entries to see if any of their pages have the starting address of the page which causes a page fault. If this the case, then it means that we have a segfault from touching forbidden memory, which means we call pthread_exit. If this is not the case, then we install the handler and re-raise our signal to terminate the process
The next given was tls_init(), which is used to initalize on our first run. This was all given, and in class we talked about having a TLS initalized flag that turns on. This sets our pagesize global variable and sets all of the signal handler and their flags.
One of the functions that we had to create was tls_create. The first time it runs we have to call the helper tls_init() function, and then mroeo comments were given to give us an idea of how to write this function. We first check if the currently executing thread has TLSA, and if the size is greater than 0 or not. This is part of the erroor checking routine. If this is the case, then we cannot create an additional TLS for this thread and return an error. Afterward, if not erroring out, we allocate a TLS with calloc, to get page-aligned memory. We initalize the TLS with setting the TID from our currently executing thread's pthread_self() function, and set all of the different attributes, such as number of pages, and allocating memory based on that number. We allocate all pages with mmap to make sure that they are not protected until we actively are using tls_write and tls_read, and set the reference founter to 1. I then add the threadID and TLS mapping to the global data structure, dealing with collisions in a linked list method if they occur.
The next two functions were helper functions for changing the permissions of our page to protect and unprotect our pages.
The tls_write function is another function that we were to implement. This one reads in length butes from the buffer->memory location and writes to the LSA of the current threadd at execution at point offset. I first error check, seeing if the current threadd has an LSA as we need to write to it. After that I error check to see if offset+length > size of the TLS. If these are fine, I unprotect all pages at once, and perform the write operation which was given to us. I only added one line which is to copy the page with memcopy for the copy's address with p->addresss. I reprotect as I go, and write to the page with dst. I then make sure that our current pages are protected as well.
The next is tls_read, which follows the same error_checking pattern. I check if current thread has a TLS, which is needed to read from. I unprotect all pages in this thread, and perform the slide's readoperation. Afterwards, I reprotect all pages that belong to the thread's TLS.
The next function is tls_destroy, which frees the TLS of the current thread. We check if the current thread has a TLS, and if so then remove it from the table after finding it. Cleaning up the pages, was unique, and if the page isn't shared we can free it : cannot free it if other threads are using that page, so instead we decrement the reference count. Thsi is done with a for loop that goes through all the pages of the relevant thread TLS and checks to see if the reference count should be decremented, or instead if we unmap the page with munmap and free the memory. After freeing the pages, we free all the other allocated memory associated with the thread.
The final and difficult part for me was tls_clone. We had to find the target thread and clone the TLSA for our currently executing thread. I had to find if the currently executing thread already has an LSA, and if so then it errored out. If the target thread doesn't have an LSA, then it errors out. After, I had to use calloc to set the hash_element for our hash_table and copy the thread relevant information with the cloned_TID, using calloc for the TLS as well. Then, after this TLS was set, I could take the targets infromation relevant to the TLS, and begin copying the pages in terms of sharing pointers to the same memory. I had to make sure to increment the referencec counter, and I had issues personally with the adding of entry to the global data structure. The issue I facedd was with the collisions, as my assignment did not actually add it to the linked list and instead isolated TLS' out of the hash_table. For a while. this made my crash out for test case 10. but once I went over the logic, it was a simple logic switch and fixed my issues.