This is a spin-off from
DM-15500: I needed to move some code out of Mask.cc; in order to do that, I needed to refactor it a bit, and from there it snowballed into a near rewrite of one corner of Mask's implementation, including adding a mutex to guard access to global state. Since that brings it pretty far from DM-15500's original goal, it makes sense to make that a separate review and merge.
The end result is intended to be entirely backwards compatible with the old Mask API and behavior, and that takes some other cleanup work off the table. But I think the result is still a much safer and easier-to-understand implementation.