> > How about, however, something like (hypotetically) rewriting the Linux
> > kernel in Rust? (with limited amounts of unsafe code)
>
> I think it's a bit unfair that in your example, you insist on wanting
> fine-grained global RW locks, when in fact linux has been systematically
> backing away from those in favour of RCU. Indeed, the docs on the RW
> locks[1] say:
> NOTE! We are working hard to remove reader-writer spinlocks in most
> cases, so please don't add a new one without consensus. (Instead,
> see Documentation/RCU/rcu.txt for complete information.)
>
> RCU, as you know, is very different, and .. I actually don't know if
> it's easy to encode in rust's current rules or not. But I think it's
> neither helped by the rules you were proposing either.
Well, in many cases RCU is just used on data structures, so a special RcuList,
RcuHashTable, etc. implemented with unsafe code would probably do the job.
> I think kernels are dealing with way more complication than we can
> encode into any set of rules: weird IO memory mappings and barrier
> rules[2], per-CPU structures, interrupt safety, custom allocators and
> lifetime patterns, all sorts of stuff that we can't handle "optimally".
> I would expect a kernel written in rust to involve a lot of looking at
> our type system for tools-that-might-help, but ultimately when facing a
> performance-vs-safety trade, or a hardware-reality-vs-rust-types trade,
> to go with performance and reality, and larger blocks of supporting
> "unsafe" code they've reasoned carefully about. That's what they do now
> and, somehow, they manage to code with a level of attention-to-detail
> and minimalism wherein it seems to mostly work.
Yes, although most of those specific issues can probably be isolated in
snippets of unsafe code.
However, it's enough to consider the kernel as an implementation of POSIX file
system calls using a RAM disk to have a broader issue: the natural and simplest
way to express it in current Rust is a "filesystem task" accessing task-local
data implemented with garbage collected @ boxes, but that would have equivalent
serialization and thus performance to a big kernel lock.
Otherwise, one needs multiple tasks concurrently mutating shared objects, which
requires either global GC or global reference counting that needs both the
ability to nest mutable pointers in ways that are statically acyclic due to
their types, and probably some sort of mutable RcTree class that would allow
reparenting with O(tree_height) checks of acyclicity (like Linux VFS does
manually for rename on directories).
BTW, there is in fact a probably better way than what I originally proposed to
do more general acyclic reference counting:
1. Add a DoesntStronglyPointTo<T> kind (with a shorter name) that would allow
to declare RcMut<T> as RcMut<T : DoesntStronglyPointTo<RcMut<T>>>, and would
have the compiler check that there is no sequence of non-weak-reference
pointers going from T to RcMut<T>
2. Enhance RcMut<T> to RcMut<T, U : DoesntStronglyPointTo<U>>, that would hold
a (T, U) pair, allow to get a &(T, U) pointer while reading, but only a (&T,
&mut U) pointer pair when writing, making T immutable and U mutable
3. Add RcList, RcTree for those structures (and RWARC versions)
Then, one would put the cyclic pointers in the T part, which would be OK since
they are immutable, and put everything acyclic in the U part with the
DoesntStronglyPointTo bound checking that it's indeed acyclic.
This avoids the magic of having the compiler infer which pointers can be
allowed to mutate, instead having the user indicate them and the compiler give
an error if the user gets it wrong.
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev