I think what's really special about the main thread is that Rust (and I believe in some cases the OS) forces the process to exit if the main thread completes. I think the difference in panic handling is mostly down to that. I think the description in the docs for std::thread describe this distinction the most explicitly.[1]
Fundamentally panic recovery works the same way in all threads—for both the main thread and spawned threads the standard library implements panic handling by wrapping the user code in catch_unwind().[2][3] It's more or less possible to override the standard library's behavior for the main thread by wrapping all the code in your main() function in a catch_unwind() and then implementing whatever fallback behavior you want, like waiting for other threads to exit before terminating. In some cases something like this happens automatically, for instance if the main thread spawns other threads using std::thread::scope.[4]
This article falls into the trap of conflating the Wirecutter's misapplication of filtration standards with irrelevant minutiae about which terms and diameters they cite for the filter classes. So alongside a pretty cogent description of how fine-matter filtration works by particle size, there's the claim that "a 'PM2.5 filter' … isn’t a thing," despite the PM2.5 class of fine particulate matter being the range specifically mentioned in the Ikea product description in the screenshot. A cursory search will turn up lots of results for filters which show that this is a pretty common term. Where the Wirecutter review actually goes wrong is in taking 2.5 microns as the lower bound of the particulate range, whereas it's conventionally the upper bound.
Then there's the idea that "Neither size mentioned (0.3 microns or 2.5 microns) has any relationship to either of the design specs" [the EU E12 and H13 standards]. When I google "hepa" my first hit is a US EPA page giving the specification for the most penetrating particle size of HEPA filters as 0.3 microns, rather than the 0.15 microns given in the article (from the empirical research or EU standards, I'm not sure which). This is from North America, but then, the Wirecutter is an American review site. It's worth considering this kind of (IMO) misfire in light of the article making the least charitable possible inference, that the Wirecutter deliberately set out to discredit the Ikea product because it couldn't give an affiliate link.
I think the OP is confusing the runtime and image format a bit here. At runtime OverlayFS can use metadata-only copy up to describe changed files, but the container image is still defined as a sequence of layers where each layer is a tar file. There's no special handling for metadata-only changes of a file from a parent layer. As the OCI image spec puts it [1]:
> Additions and Modifications are represented the same in the changeset tar archive.
This is an excellent writeup, but I'm curious about how dependent the microbenchmark is on the scripting engine doing no real work. My off-the-cuff guess is that the unshared version might still perform better with other workloads (it would have higher throughput and avoid variance caused by fluctuating degrees of cache contention) but it might be that the observable overhead from hammering a single cache line would disappear if each thread had more varied work to do. And of course if the scripting engine benefits substantially from caching internal state across invocations (things like incrementally optimized JIT code) then having 24 instances can incur substantial extra work.
Fundamentally panic recovery works the same way in all threads—for both the main thread and spawned threads the standard library implements panic handling by wrapping the user code in catch_unwind().[2][3] It's more or less possible to override the standard library's behavior for the main thread by wrapping all the code in your main() function in a catch_unwind() and then implementing whatever fallback behavior you want, like waiting for other threads to exit before terminating. In some cases something like this happens automatically, for instance if the main thread spawns other threads using std::thread::scope.[4]
[1]: https://doc.rust-lang.org/std/thread/#the-threading-model [2]: https://github.com/rust-lang/rust/blob/7042c269c166191cd5d8d... [3]: https://github.com/rust-lang/rust/blob/7042c269c166191cd5d8d... [4]: https://doc.rust-lang.org/std/thread/fn.scope.html