I have always found that building fun things from scratch in my spare time is the best way to cure my ignorance. Coincidentally, it also seems to be one of the best ways to cure boredom and disillusionment with technology that many people complain about these days. I have been lucky enough to have somebody point me to this snippet from "Surely you're joking, Mr. Feynman" very early during my university career:
Then I had another thought: Physics disgusts me a little bit now, but I used to enjoy doing physics. Why did I enjoy it? I used to play with it. I used to do whatever I felt like doing - it didn't have to do with whether it was important for the development of nuclear physics, but whether it was interesting and amusing for me to play with. When I was in high school, I'd see water running out of a faucet growing narrower, and wonder if I could figure out what determines that curve. I found it was rather easy to do. I didn't have to do it; it wasn't important for the future of science; somebody else had already done it. That didn't make any difference. I'd invent things and play with things for my own entertainment.
Pursuing this strategy at least part time has worked wonders for me. It allowed me to avoid the issue completely and I have always had an absolute blast doing it. It has also led me to unique places and opened up great opportunities as a byproduct.
This post is about one of these things that I played with for no good reason
other than pure fun. At one point, I decided that I would like to know how
threading works and that the best way to do it was to write a threading library
entirely from scratch. I mean the stuff that hides behind all those
calls that you know and love from C. Amazingly, I was able to chew through all
this bit by bit and build something reasonably simple that still did all those
things that mystified me in the past.
The posts linked below describe a more or less complete threading library written in simple C and based on Linux syscalls. The library (almost) does not use glibc, but it takes some inspiration from it. I start from the basics like calling syscalls from C, heap management, printing text, etc. Then, I move to thread creation, cancelation, joining, and scheduling on top of that. It all ends with synchronization primitives: a mutex implementation with priority inheritance, as well as condition variables and RW locks.
Table of Contents
- Syscalls, memory, and your first therad
- The pointer to self and thread-local storage
- Futexes, mutexes, and memory sychronization
- Joining threads and dynamic initialization
- Scheduling and task priority
- RW Locks
- Condition variables
- Final thoughts