I am a Junior double majoring in Mathematics and Computer Science & Engineering at the University of Michigan. Some of my interests include Mixed Martial Arts, Chess, Playing Guitar, Reading/Writing and Contributing to Open Source Projects. In my three years at UofM, I've been a part of the navigation subteam for the University of Michigan's Autonomous Robotic Vehichle Team, actively participated in the university's Blue Jitsu club, and served as a Feature Lead for the University's Collaborative Lab for Advancing Work in Space.
I am currently working as an Embedded Software Engineering intern at Stryker. During my twelve week internship I worked on the 1788 platform. Specifically, I built an automated testing system for the Embedded Software team, quantified the update times for different camera types, and developed software to perform common image processing procedures.
I built a server that took in network request to read, write, create, and delete files. The
server was able to concurrently handle up to thirty requests at a time. To acheive this, I
used the boost library’s thread
module to handle the concurrency.
To process network requests, I used the operating system’s socket API. This allowed me to
have more control over how which messages the server would respond to. In writing this server
module, I learned about the Reactor pattern
and how it can be used to handle multiple network requests concurrently. I also studied the
mechanics of reader-writer locks
and how they can be used to allow multiple readers to access a resource while only allowing
one writer to access it at a time. This was crucial in ensuring that the server could handle
multiple requests at once without corrupting the file system. However, there were still some
optimizations that could be made to the server.
One of the problems that reader-writer locks have is that, to ensure the integrity of every
inode in the file system, the server must lock every inode before reading or writing to it.
This would be tolerable if disk I/O was rare, but since I decided not to cache any inodes in
memory, the server had to read the inode from disk every time it needed to access it. In turn,
this meant that the server had to lock the inode it was modifying even if it was only reading
from it. To solve this problem, I made use of boost’s upgrade_lock
class. This class allows
a thread to lock a resource in read mode and, when it needs to write to it, atomically upgrade
the lock to write mode. This greatly improved the server’s performance and allowed it to handle
more requests at once.
I wrote a C++ program that intercepted load and stores made by user code and carried out the actions a kernel’s paging systems might implement to deal with the instruction. As any useful pager would, mine handled reading and writing pages to the computer’s disk when they were no longer being actively used by the user. Moreover, it fetched disk files that the user requested, giving the user a powerful tool and an improvement in fetch latency.
While this was challenging enough by itself, up until this point the pager did not emulate how real-world pagers deal with processes forking and, in the process, copying their arenas. To better reflect the behavior of real-world pagers, my pager supports spawning child processes with nonempty arenas, allowing it to share pages with the process that spawned it. To handle shared pages, I implemented the copy-on-write sharing policy in my pager. This allows an arbitrary number of processes to share the same physical page until one of them writes to it. Writing to it causes the pager to make a copy of it and assign the newly modified physical page to the writing process.
I wrote a C++ library that allowed users to create and run threads as well as synchronization
and mutual exclusion primitives. Other than utilizing linux’s setcontext
, swapcontext
,
and makecontext
functions, I used no external utilities to implement this library. Following
the well-known Operating Systems principle of separating mechanisms from policy, my library
supports an arbitrary thread prioritization policy. Moreover, it provides the user with a lock
class to provide mutual exclusion and a condition_variable
class for synchronization. As with
most non-trivial software engineering projects, testing my library was a crucial part of
this project’s development cycle. As such, I learned about
Mesa
and Hoare
style monitors.
Seeing as modern personal computers usually have more than one CPU core to work with, my library also supported running threads on different CPUs. This allows for concurrent programs to run in parallel, thus reducing the runtime of a multithreaded program.