I love spelunking into unknown codebases with nothing but find and grep. It’s one of the most valuable skills one can develop as a programmer imo and in this video you can see how I approach it.
This video focuses on debugging GUI event handling. At first the bug seemed related to the app’s waveform selection, but I then realized it was a more general topic with the SerenityOS GUI UX — selecting a dropdown entry retains focus, and requires an explicit escape key.
Ultimately I made progress accidentally by hitting the keyboard while the selection was still active, revealing to me that fact (which I hadn’t noticed before).
You can see my general debugging flow:
Get things building
How to run app from command line (to see stdout)?
How to print to stdout?
Using debug prints to understand the GUI event handling
Overall I’m quite impressed with SerenityOS. I only realized after looking into the code exactly how much code they had written and how fully featured the system is. Well done to the team.
After calling fork(), a parent process gets its entire address space write protected to facilitate COW. This causes page faults.
This makes fork() unsafe to call from anywhere in a process with realtime deadlines — including non realtime threads! Usually non RT can do what they want, but that is an interesting exception.
On modern glibc, system() doesn’t use fork(), it uses posix_spawn(). But is posix_spawn() safe from a non RT thread?
posix_spawn() doesn’t COW — the parent/child literally share memory — so the page fault issue doesn’t apply. However the parent is suspended to prevent races between the child and parent. This seems RT unsafe…
However, only the caller thread of the parent is suspended, meaning the RT threads are not suspended and continue running with no page faults.
So it is safe to use system() or posix_spawn() from a non RT thread.
Turns out, we do have them. libstdc++ contains a heuristic kludge where it checks if libpthread is linked into the process and conditionally executes an atomic add or non-atomic add depending on the result. Post 2020, it doesn’t directly use the libpthread hack, but uses glibc’s native support for this.
These kinds of things always make me sad about C++ (especially compared to how nice and clean things seem to be in Rust world), but at least it’s nice knowing that the committee did consider it and there are good reasons why to not have this in the C++ stdlib. The most compelling reason is that in the absence of a borrow checker, it is all too easy to silently use a non-atomic shared_ptr in multithreaded code.