Live data from Hacker News

Giving up on wlroots-rs

way-cooler.org

11–20 of 126 posts

Re: Giving up on wlroots-rs

#11

I know very little about Rust or Wayland (I suppose I'm not the target audience) but I got lost very quickly here: > A Wayland “output” is the resource that represents a display device. Commonly this means it handles a computer monitor. This resource could disappear at any time in the life cycle of the application. This is easy enough to imagine: all it takes is a yank of the display’s power cord and the monitor goes…

Picture a pointer to video memory. Or, simpler, picture a pointer to an SHM section that either side of the SHM IPC conversation can deallocate. From both sides’ perspective, that pointer is probably implemented as both an SHM section, but also an SHM pointer to the section, such that either side can set the SHM pointer to NULL, and then (if they managed to do that) proceed to tell the SHM infrastructure to unmap all mappings of the SHM section.

When this sort of structure is used, there are certainly going to be guarantees in place (probably by using some sort of session/transaction functions that compare-and-swap an atomic SHM spinlock controlling the SHM pointer) such that the SHM pointer won’t go NULL [and the thing it points to won’t be deallocated] in the middle of either side writing to it. But those functions aren’t actually consuming the resource and spitting out a new temporary one (i.e. a “handle” to the resource, as the article’s author implemented in their Rust wrapper library originally.) Instead, they’re just functions that block either side from writing to the pointer as long as you’re in them—sort of like disabling interrupts in a critical section.

How do you model the ownership of such a C-FFI-runtime-guarded IPC SHM volatile pointer-to-pointer, in Rust? Is there an idiomatic translation for it?

Because this sort of thing comes up all the time in the context of kernel handles to buffers, and I would be surprised if the folks writing OSes in Rust haven’t hit on it before.

IIRC, there’s an IPC abstraction called a ‘blackboard’ (sort of related to a tuple space) that is the generalization of this SHM model, so it might also help to ask how you’d model an IPC ‘blackboard’ in Rust.

Re: Giving up on wlroots-rs

#12

I know very little about Rust or Wayland (I suppose I'm not the target audience) but I got lost very quickly here: > A Wayland “output” is the resource that represents a display device. Commonly this means it handles a computer monitor. This resource could disappear at any time in the life cycle of the application. This is easy enough to imagine: all it takes is a yank of the display’s power cord and the monitor goes…

I think you missed this part:

> [the resource handle] can only be dropped between event callbacks, wlroots/Wayland is callback based

So "at any time" means between any two user callbacks, but not during them.

Error states really aren't a solution IMO, as they tend to pollute the entire interface in that methods that would be expected to work unconditionally can now fail or panic. Either you have to check each method invocation (entirely unnecessarily, since if a resource isn't in an error state at the start of your callback it won't go into one during it) or you make the absence of error state a precondition, in which case you're guaranteed to miss one or two cases and panic when the user yanks out the cord.

Re: Giving up on wlroots-rs

#13
I can't speak to every issue which the author might have encountered, but there is a better solution to the lifetime management problem than the two mentioned in the article.

Instead of this:

    fn some_wlroots_callback(output_handle: OutputHandle,
                             surface_handle: SurfaceHandle) {
        output_handle.run(|output| {
            surface_handle.run(|surface| {
                // maybe some more nested layers...
            }).unwrap()
        }).unwrap()
    }
One can do this:

    fn some_wlroots_callback(
        ctx: CallbackContext,
        output_handle: OutputHandle,
        surface_handle: SurfaceHandle
    ) {
        let output = ctx.get(output_handle);
        let surface = ctx.get(surface_handle);
    }
This is safe, because the lifetime of "output" and "surface" can be bound to the lifetime of the "ctx" (whose lifetime is controlled by the library: the library simply has to make sure that "ctx" is not accessible outside of a callback).

edit: Realised you can't tell that there's an implicit lifetime in `CallbackContext` here:

    struct CallbackContext { ... };
OR

    type CallbackContext = &'a CallbackContextImpl;

Re: Giving up on wlroots-rs

#14
post #4

Question (for any more knowledgable readers here) from someone with a somewhat shallow understanding of the topics discussed: Does this end up being primarily a negative reflection on the general structure of: (a) Rust (b) wl-roots (c) Wayland (d) all three (e) none of the above, it's merely the incidental reality of trying to write code that's compatible/usable across multiple language ecosystems and none of the 3 p…

Speaking as someone who dabbled in Wayland in Rust before (but a few years ago so my knowledge might be outdated), has not tried wl-roots, and uses Rust a lot

(a) Only when talking about interacting with C libraries written in a certain style. So not in general, and not with most C libraries, but yes for implementing wayland compositors.

(b) Don't know

(c) Absolutely a negative reflection, there are two ugly things going on here. The official wayland libraries use a ownership style that really only makes sense in C imho, and despite the claim that wayland is just a protocol you can't actually not use the official libraries, because the drivers only work with the official libraries.

Re: Giving up on wlroots-rs

#15
"A Wayland “output” is the resource that represents a display device. Commonly this means it handles a computer monitor. This resource could disappear at any time in the life cycle of the application. This is easy enough to imagine: all it takes is a yank of the display’s power cord and the monitor goes away. [Except it can't; it can only disappear between callbacks?] This is basically the exact opposite of the Rust memory model. Rust likes to own things and give compile-time defined borrows of that memory. This is runtime lifetime management that must be managed in some way."

Something leads me to believe that there is something very wrong with the architecture of wlroots-rs. The output should be attached to a callback parameter or something, maybe? I don't know enough about Wayland to say, but something ain't right.

Re: Giving up on wlroots-rs

#16
> When the benefit at the end of the day is just so I don’t have to write C, that doesn’t really make it worth it.

This line not only summarizes the content of the article, but I'm afraid it may also describe the actual situation of porting C/C++ code to Rust.

Re: Giving up on wlroots-rs

#17
post #4

Question (for any more knowledgable readers here) from someone with a somewhat shallow understanding of the topics discussed: Does this end up being primarily a negative reflection on the general structure of: (a) Rust (b) wl-roots (c) Wayland (d) all three (e) none of the above, it's merely the incidental reality of trying to write code that's compatible/usable across multiple language ecosystems and none of the 3 p…

I read it as friction between how Rust sees its application model and the real world. Total ownership of a resource can't be maintained if some jackass goes and pulls the physical hardware out from under you. So now the Rust implementation gets crufted up with a zillion checks for unexpected changes to the underlying system that are clunky in Rust because that's supposed to be an exotic condition, not something you're doing on practically every line.

Re: Giving up on wlroots-rs

#18
post #14
post #4

Question (for any more knowledgable readers here) from someone with a somewhat shallow understanding of the topics discussed: Does this end up being primarily a negative reflection on the general structure of: (a) Rust (b) wl-roots (c) Wayland (d) all three (e) none of the above, it's merely the incidental reality of trying to write code that's compatible/usable across multiple language ecosystems and none of the 3 p…

Speaking as someone who dabbled in Wayland in Rust before (but a few years ago so my knowledge might be outdated), has not tried wl-roots, and uses Rust a lot (a) Only when talking about interacting with C libraries written in a certain style. So not in general, and not with most C libraries, but yes for implementing wayland compositors. (b) Don't know (c) Absolutely a negative reflection , there are two ugly things…

>(c) Absolutely a negative reflection, there are two ugly things going on here. The official wayland libraries use a ownership style that really only makes sense in C imho, and despite the claim that wayland is just a protocol you can't actually not use the official libraries, because the drivers only work with the official libraries.

Not really sure what you're talking about here. Wayland is just a protocol, and a pretty simple one to boot. There also exists a pure Rust implementation of the Wayland protocol:

https://github.com/Smithay/smithay

The problem is that Rust and libwayland, the C implementation of Wayland, don't get along well, and that wlroots is designed to work with libwayland and inherits a lot of those design decisions that make it difficult to deal with in Rust. And to be honest, Rust is special in this regard. Other wlroots bindings exist for Go, Haskell, Common Lisp, OCaml, and Chicken Scheme - and all seem to do fine.

I think this points more to a failing in Rust, in that it's not designed to cope with this particular model well. Since this is a fairly common model, that seems like a big flaw.

Re: Giving up on wlroots-rs

#19

A good post, thanks for sharing. > I want to make one (mildly controversial) thing clear: rewriting a library for the sake of only using Rust is not good engineering. Strong agree. > A literal rewrite of a project to Rust is not interesting, it’s not useful, it just causes churn and splits ecosystems. Time would be better spent either working with existing solutions that already have the effort put in to make them co…

I agree and was going to say the same. That there was a huge challenge in writing code to manage memory ownership, is not surprising because memory ownership is so fluid and adhoc in C and its derivatives. And when you are asked to make that ownership explicit, if the original designers hadn't been thinking about it, you get a lot of cases which they would not have considered. I would like to see a compositor written…

Not a wayland, wlroots or rust expert, but dollars to donuts says that it's not "memory" management[1] at issue, but resource allocation on the other side of the graphics driver. Vertex arrays, textures, framebuffers, shaders et. al. all need some kind of allocation strategy, can in the modern world often be shared between process contexts, and they really don't fit well into the metaphors of either C or Rust.

But where in C you can just do it anyway, Rust is likely to flip out over your wrapper object abstractions.

[1] I mean sure, technically it is memory management, but you know what I mean.

Re: Giving up on wlroots-rs

#20
post #11

I know very little about Rust or Wayland (I suppose I'm not the target audience) but I got lost very quickly here: > A Wayland “output” is the resource that represents a display device. Commonly this means it handles a computer monitor. This resource could disappear at any time in the life cycle of the application. This is easy enough to imagine: all it takes is a yank of the display’s power cord and the monitor goes…

Picture a pointer to video memory. Or, simpler, picture a pointer to an SHM section that either side of the SHM IPC conversation can deallocate. From both sides’ perspective, that pointer is probably implemented as both an SHM section, but also an SHM pointer to the section, such that either side can set the SHM pointer to NULL, and then (if they managed to do that) proceed to tell the SHM infrastructure to unmap all…

Quick answer, without understanding all the details: a weak-pointer-like structure that performed all the necessary locking and checking before giving out access to the underlying SHM.

Rust's borrow checking is more or less a formalization of C++'s RAII style; in my experience, solutions for that translate relatively simply.

Post reply on HN