-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make safe WindowHandle
s to imilictly ref-count
#158
Comments
The display stuff is not that prone, but there's alacritty/alacritty#7146 at least. With display you can create type depending on it before actually running the loop, so it'll have the current lifetime of event loop, however it'll still help if there wasn't a lifetime at all. |
Alternative is for apps to require |
Other point for that is that libraries based on raw-window-handle can adopt to that with just a single line change basically, since it works the same as And keep in mind that |
I haven't read the entire Matrix thread yet, so I don't fully understand the context behind this, but I think there's a way around this without I use I'm against adding any kind of window closing code to this library. There's no common way to destroy windows on all platforms, it would add a lot of dependencies and it probably wouldn't work anyways. I know there was talk of adding a lifetime to |
@notgull you'll make it depend on winit actually and it won't really work in MT context because you can
This is more restrictive to what I propose and I'm not sure how easy it is to use a thing like that.
This is not about closing either, it's just an indicator that you have handles alive for the library. Consumers don't need to even know about that, they should just store the So the end code will be safe and the same as it was using the
No, I won't add it. Besides, keep in mind that we don't solve innert problems here because they are not an actual problems, the problem is dropping after the window and inability to use the current handle in the case where you can't pass the entire window to some other lib, since it's just makes it pretty ugly. Like #[derive(Clone)]
struct RefCountedHandle {
window_handle: Arc<RawWindowHandle>, // It's still not send/sync, but it's ref-counted because it can be used on other thread.
} Maybe some other prototype will be better, but the thing is that winit will know that you have something still using the handle when it's dropping its window or exiting the loop, thus it'll be able to force you drop the window and not let you out unless you clear resources or by use In the end you can port e.g. glutin and wgpu to that with literally zero changes and remove all |
I'm sorry, I'm having trouble following with what you are proposing. Is the idea to add a reference counted window handle with an indicator as to whether or not the window is still alive? And this indicator is shared between In my opinion Rust offers us the tools to work with this scenario and we should try to use these tools first instead of trying to invent our own bespoke primitives. I don't see how composition is overly restrictive. For your struct MySurface {
inner: Surface<Window>
}
impl MySurface {
fn window(&self) -> &Window {
self.inner.get_ref()
}
fn sw_surface(&self) -> &Surface<Window> {
&self.inner
}
} You can access the window just like you would in your original code. The only downside is that you don't get |
But I need Like I know that you can do what you suggest as alternative, it's just doesn't really work in my opinion, because the constraints a different, and it requires a lot of changes to all the libraries, where with my approach you just replace |
Adds the `get_ref` and `get_mut` functions, which can be used to get references (mutable or otherwise) to the underlying window handle. cc rust-windowing/raw-window-handle#158 (comment) Signed-off-by: John Nunley <[email protected]>
I don't think that "friction with the current GUI ecosystem" should be evaluated as a constraint for this design. The prior way of handling window handles was the equivalent of passing raw pointers to functions. Normally I'd be a fan of the path of least harm, but unfortunately many of the crates that have built their APIs on top of this unsoundness. The solution is to break the APIs so they actually pass around real-life windows instead of pointers. The solution shouldn't be to staple on refcounting to the existing system. I have serious doubts that this can be implemented without introducing a deadlock, leak or other bug to this system. Composition is just types inside of other types, so it's bug free right off the bat. We should be taking a lesson from the Rust HTTP client fiasco here. My vote is to use the tools the rest of the ecosystem uses, in contrast to settling further into a system that's unsound.
This can happen with the current system. Most graphics surfaces are implemented like this: struct MyGraphicsSurface<W> {
window: W,
gorey_system_bits: Foobar
} So this can just be done: impl<W> MyGraphicsSurface<W> {
fn get_ref(&self) -> &W {
&self.window
}
fn get_mut(&mut self) -> &mut W {
&mut self.window
}
} See rust-windowing/softbuffer#193 for an example of this in practice. I'm requesting @grovesNL and any other interested I understand why |
Just popping in to second the notion that breaks that improve soundness are absolutely worth considering strongly. |
I'm not sure if I understand the soundness problem with the existing API, but in general wgpu would prefer approaches that doesn't require a generic parameter or lifetime for windows if possible (instead preferring explicitly or implicitly refcounted window handles). Edit: actually I misspoke - looks like wgpu already has the lifetime generic (I missed that change) so this might be ok https://github.com/gfx-rs/wgpu/blob/f27bb443c16634596618e9b2969ee6822cc7a696/wgpu/src/lib.rs#L366 |
There's no soundness issues with what we have now, mostly convenience and boilerplate kind of thing. I don't like the current approach because generally the thing should be ref-counted and winit has its handles ref-counted already. However, to make things safe, we'd explicitly need to wrap into ref-counted object and libraries to be generic over The borrowed lifetime approach doesn't work, only Like in the end both work the same, I just propose to move the ref-counting into |
Since no one else has spoken up on behalf of SDL yet in this thread maybe I should add that this whole thing would probably be a pain in the butt to work in with an SDL based window situation. |
I never worked with |
Well SDL, the C API, lets you
This means that the FFI bindings later to SDL can currently basically support this crate without breaking the general rule that "bindings crates should be just bindings and avoid original code/abstractions". However, if the ref counting were mandatory then that would require the ffi bindings to not support directly getting the window handle at all, and the "rusty" safe layer of SDL would need to keep an extra ref counted thing on top of other stuff it's probably already ref counting (eg: having an SDL window open keeps the SDL api open, this is usually handled in rust with a cluster of Arc values). In total not an impossible blocker, but as someone who works with SDL mostly and not really with winit because I always think that winit is absurdly complex, this doesn't seem like an obvious win. |
I'm not touching Again, winit has nothing to do with all of that, it'll work fine with whatever and provide safe handles. |
Ah, I follow now. Then don't worry about everything I just said. |
If we added a reference-counting handle like
Is that what you're proposing when you say that In that past, we have decided against having that in this crate, since it would require a lot of dependencies, and a major selling point for this crate is to avoid such dependencies! |
@madsmtm yeah, something like that would be fine by me. Right now the owned handle is exposed via
|
The problem with the existing API (by this I mean the API that the latest released version of The API that
Can you please elaborate on this? If it's a matter of friction, I think investing into a safer system is better than stapling reference counting on an unsafe system and creating technical debt.
Not all windowing systems are
This approach demonstrably works in both I think we should be making the GUI ecosystem simpler by streamlining it and adopting Rusty idioms, rather than trying to force in new tricks for short-term wins in patch noise. |
Please, read the rust-windowing/winit#3367 (comment) . There will be
What I suggest is as safe as what you provide, since I propose to create
Refcount of
Yes, but it's a lot of work and it's unsound in e.g. winit because it doesn't linger window because of not doing proper ref-counting. Like what you suggest is already unsound if you do naive impl and not do ref-count. |
I'll have to see it to believe it. I'm worried that needing to coordinate across RWH, winit and graphics APIs to make sure something is safe will be a lot of effort. Also, it sounds nearly impossible to verify without ecosystem-wide crater tests.
My main point is that we're specializing the API for
I'll admit this, but it's work in favor of a system that works better with Rust's existing idioms than to keep with our current flawed design.
What platforms does this apply to?The platforms that I am familiar with (X11, Windows) can cause slight annoyances but not anything unsound. |
rust-windowing/winit#3317 all? If you drop event loop before the But again, it's not needed for winit at all, because the way new API work will be fine with whatever we have now, it's just libraries should be more aware of such a thing. |
Mostly ergonomics - the extra type parameter adds some extra friction (especially if the type has to be written out anywhere, like when window and surface creation might happen in different places, or surface is held in a struct). It's not a big deal though, it just makes things slightly more difficult which feels like it could be avoidable. I might be forgetting why raw window handle prefers borrowed handles in the first place, but it feels like it could be reasonable to always pass owned raw window handles between crates (even removing the lifetime if possible) assuming we can reliably refcount inside that handle. I don't know all the nuances about how this needs to work on each platform, but some platforms already internally refcount anyway, for example:
|
ref counting like bumping |
Yeah that's true, I was just thinking about cases like the SDL use case mentioned by @Lokathor if we did want to support it directly on this owned handle type (without wrapping platform handles in |
As I said, all have nothing to do with The only argument against generic is that I don't have strong opinion here anyway given that some folks want to pass |
Adds the `get_ref` and `get_mut` functions, which can be used to get references (mutable or otherwise) to the underlying window handle. cc rust-windowing/raw-window-handle#158 (comment) Signed-off-by: John Nunley <[email protected]>
This issue was discussed in our weekly winit maintainer's meeting. As we've determined that |
The current handle can only work safely when the window is either end up in
OnceCell
, doneBox::leak
, or send in some sort of wrapper to other thread. However the most common way write multi window is doing something like thatSuch state is being owned by
EventLoop
or defined as part of the captured value in the closure, so it can't have correct lifetime. Rust doesn't allow to haveSurface
depend onWindow
and be stored in the same struct, but just in the right order.This could can work, but with e.g. GBM/drm stuff it may segfault, for example, see Smithay/smithay#1102 as an example.
One solution could be to have a
WindowHandle
even owned one somehow retain the original object so it can't drop in the wrong order.This can work really nice when the
Window
itself is not owned by the user, but rather by the library, so to remove theWindow
they'll have to call failing destructor and the library could just deny you dropping it because you still hold reference to it.Such design doesn't really need a lifetime, since you just can't drop the original object. Yes it does require the library serving the handle to retain the resources, but it's safe.
Even if the handle gets invalidated, it's often safe to use, so it's not an issue and irrelevant to the suggestion.
Some sketch of the API and semantics based on the rust-windowing/winit#3367 .
If we state that to safely impl the window must be dropped only when it has only a single reference(from itself) it means that it can't also outlive the event loop, since it'll continue to run until all the windows are closed.
So the end API could be safe in all cases, unlike the current one where you're forced to store data in a special way due to lifetime.
Ref rust-windowing/winit#3365
Ref rust-windowing/winit#3317
The text was updated successfully, but these errors were encountered: