I think the glibc docs even explicitly allow the kind of locking Rust does:
Functions marked with const
as an MT-Safety issue non-atomically modify internal objects that are better regarded as constant, because a substantial portion of the GNU C Library accesses them without synchronization. Unlike race
, that causes both readers and writers of internal objects to be regarded as MT-Unsafe and AS-Unsafe, this mark is applied to writers only. Writers remain equally MT- and AS-Unsafe to call, but the then-mandatory constness of objects they modify enables readers to be regarded as MT-Safe and AS-Safe (as long as no other reasons for them to be unsafe remain), since the lack of synchronization is not a problem when the objects are effectively constant.
The identifier that follows the const
mark will appear by itself as a safety note in readers. Programs that wish to work around this safety issue, so as to call writers, may use a non-recursve rwlock
associated with the identifier, and guard all calls to functions marked with const
followed by the identifier with a write lock, and all calls to functions marked with the identifier by itself with a read lock. The non-recursive locking removes the MT-Safety problem, but it trades one AS-Safety problem for another, so use in asynchronous signals remains undefined.
So, MT-Unsafe const:env
means that if all calls to functions with a const:env
/env
designation are guarded by a non-recurisve rwlock, that removes the MT-Safety problem.
It seems to me like what Rust does is sound according to the glibc spec -- but it is extremely fragile and non-compositional; the moment anything else in the program calls an env
function without getting the Rust-specific lock, we are in UB territory. There also is no compositional fix that would still let us mutate the global environment.
So this becomes a tradeoff between
- Letting pure Rust programs (i.e., not linking in any non-Rust code that might access the environment) mutate the environment.
- Making Rust safety compositional so that it still applies even when linking in non-Rust code that accesses the environment.
We cannot have both. If we were pre-1.0 I personally would strongly argue for 2; non-compositional safety is a disaster. But is there a good way we can get there now?
Note that if we were to make Rust set_env
only mutate the Rust-specific environment (akin to Java), then programs in the first category would not change behavior, and programs in the second category would be safe (but programmers might be surprised that the non-Rust code does not see the environment changes). I think the only programs negatively affected by this (in the strict sense that programs with UB cannot become any worse, even though they might of course happen to work in practice) are programs that use set_env
from Rust before spawning any threads, and later have non-Rust code access the environment. So maybe there is a way forward along these lines?