> > 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

Reply via email to