Skip to content

Commit e9dafc8

Browse files
authored
Rely on drop order (#360)
Certain fields must be dropped before the source window/display is dropped. Before, this was done using `Option<T>`, which is a bit annoying, and it also wasn't handled properly (e.g. `WaylandDisplayImpl` also has to be dropped after the `WlSurface`).
1 parent 80cdb1b commit e9dafc8

2 files changed

Lines changed: 40 additions & 58 deletions

File tree

src/backends/wayland/mod.rs

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,18 @@ struct State;
2222

2323
#[derive(Debug)]
2424
pub struct WaylandDisplayImpl<D: ?Sized> {
25-
conn: Option<Connection>,
25+
conn: Connection,
2626
event_queue: Mutex<EventQueue<State>>,
2727
qh: QueueHandle<State>,
2828
shm: wl_shm::WlShm,
2929

3030
/// The object that owns the display handle.
3131
///
3232
/// This has to be dropped *after* the `conn` field, because the `conn` field implicitly borrows
33-
/// this.
33+
/// this. This is done by placing this field last, see:
34+
/// <https://doc.rust-lang.org/std/ops/trait.Drop.html#drop-order>
3435
_display: D,
35-
}
36-
37-
impl<D: HasDisplayHandle + ?Sized> WaylandDisplayImpl<D> {
38-
fn conn(&self) -> &Connection {
39-
self.conn.as_ref().unwrap()
40-
}
36+
// NO FIELDS AFTER THIS!
4137
}
4238

4339
impl<D: HasDisplayHandle + ?Sized> ContextInterface<D> for Arc<WaylandDisplayImpl<D>> {
@@ -64,7 +60,7 @@ impl<D: HasDisplayHandle + ?Sized> ContextInterface<D> for Arc<WaylandDisplayImp
6460
globals.destroy();
6561

6662
Ok(Arc::new(WaylandDisplayImpl {
67-
conn: Some(conn),
63+
conn,
6864
event_queue: Mutex::new(event_queue),
6965
qh,
7066
shm,
@@ -78,23 +74,24 @@ impl<D: ?Sized> Drop for WaylandDisplayImpl<D> {
7874
if self.shm.version() >= 2 {
7975
self.shm.release();
8076
}
81-
// Make sure the connection is dropped first.
82-
self.conn = None;
8377
}
8478
}
8579

8680
#[derive(Debug)]
87-
pub struct WaylandImpl<D: ?Sized, W: ?Sized> {
88-
display: Arc<WaylandDisplayImpl<D>>,
89-
surface: Option<wl_surface::WlSurface>,
81+
pub struct WaylandImpl<D: ?Sized, W> {
82+
surface: wl_surface::WlSurface,
9083
buffers: Option<(WaylandBuffer, WaylandBuffer)>,
9184
size: Option<(NonZeroI32, NonZeroI32)>,
9285

9386
/// The pointer to the window object.
9487
///
9588
/// This has to be dropped *after* the `surface` field, because the `surface` field implicitly
96-
/// borrows this.
89+
/// borrows this. This is done by placing this field last, see:
90+
/// <https://doc.rust-lang.org/std/ops/trait.Drop.html#drop-order>
9791
window_handle: W,
92+
/// Drop the display last.
93+
display: Arc<WaylandDisplayImpl<D>>,
94+
// NO FIELDS AFTER THIS!
9895
}
9996

10097
impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> SurfaceInterface<D, W>
@@ -120,11 +117,11 @@ impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> SurfaceInterface<D, W>
120117
)
121118
}
122119
.swbuf_err("Failed to create proxy for surface ID.")?;
123-
let surface = wl_surface::WlSurface::from_id(display.conn(), surface_id)
120+
let surface = wl_surface::WlSurface::from_id(&display.conn, surface_id)
124121
.swbuf_err("Failed to create proxy for surface ID.")?;
125122
Ok(Self {
126123
display: display.clone(),
127-
surface: Some(surface),
124+
surface,
128125
buffers: Default::default(),
129126
size: None,
130127
window_handle: window,
@@ -221,7 +218,7 @@ impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> SurfaceInterface<D, W>
221218
let age = back.age;
222219
Ok(BufferImpl {
223220
event_queue: &self.display.event_queue,
224-
surface: self.surface.as_ref().unwrap(),
221+
surface: &self.surface,
225222
front,
226223
back,
227224
width,
@@ -231,13 +228,6 @@ impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> SurfaceInterface<D, W>
231228
}
232229
}
233230

234-
impl<D: ?Sized, W: ?Sized> Drop for WaylandImpl<D, W> {
235-
fn drop(&mut self) {
236-
// Make sure the surface is dropped first.
237-
self.surface = None;
238-
}
239-
}
240-
241231
#[derive(Debug)]
242232
pub struct BufferImpl<'surface> {
243233
event_queue: &'surface Mutex<EventQueue<State>>,

src/backends/x11.rs

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use x11rb::xcb_ffi::XCBConnection;
4040
#[derive(Debug)]
4141
pub struct X11DisplayImpl<D: ?Sized> {
4242
/// The handle to the XCB connection.
43-
connection: Option<XCBConnection>,
43+
connection: XCBConnection,
4444

4545
/// SHM extension is available.
4646
is_shm_available: bool,
@@ -53,7 +53,11 @@ pub struct X11DisplayImpl<D: ?Sized> {
5353
/// Without `&mut`, the underlying connection cannot be closed without other unsafe behavior.
5454
/// With `&mut`, the connection can be dropped without us knowing about it. Therefore, we
5555
/// cannot provide `&mut` access to this field.
56+
///
57+
/// This has to be dropped *after* the `conn` field. This is done by placing this field last:
58+
/// <https://doc.rust-lang.org/std/ops/trait.Drop.html#drop-order>
5659
_display: D,
60+
// NO FIELDS AFTER THIS!
5761
}
5862

5963
impl<D: HasDisplayHandle + ?Sized> ContextInterface<D> for Arc<X11DisplayImpl<D>> {
@@ -110,28 +114,17 @@ impl<D: HasDisplayHandle + ?Sized> ContextInterface<D> for Arc<X11DisplayImpl<D>
110114
let supported_visuals = supported_visuals(&connection);
111115

112116
Ok(Arc::new(X11DisplayImpl {
113-
connection: Some(connection),
117+
connection,
114118
is_shm_available,
115119
supported_visuals,
116120
_display: display,
117121
}))
118122
}
119123
}
120124

121-
impl<D: ?Sized> X11DisplayImpl<D> {
122-
fn connection(&self) -> &XCBConnection {
123-
self.connection
124-
.as_ref()
125-
.expect("X11DisplayImpl::connection() called after X11DisplayImpl::drop()")
126-
}
127-
}
128-
129125
/// The handle to an X11 drawing context.
130126
#[derive(Debug)]
131-
pub struct X11Impl<D: ?Sized, W: ?Sized> {
132-
/// X display this window belongs to.
133-
display: Arc<X11DisplayImpl<D>>,
134-
127+
pub struct X11Impl<D: ?Sized, W> {
135128
/// The window to draw to.
136129
window: xproto::Window,
137130

@@ -154,7 +147,13 @@ pub struct X11Impl<D: ?Sized, W: ?Sized> {
154147
size: Option<(NonZeroU16, NonZeroU16)>,
155148

156149
/// Keep the window alive.
150+
/// NOTE: Drop this second-to-last!
157151
window_handle: W,
152+
153+
/// X display this window belongs to.
154+
/// NOTE: Drop this last!
155+
display: Arc<X11DisplayImpl<D>>,
156+
// NO FIELDS AFTER THIS!
158157
}
159158

160159
/// The buffer that is being drawn to.
@@ -219,13 +218,13 @@ impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> SurfaceInterface<D, W> fo
219218
let display2 = display.clone();
220219
let tokens = {
221220
let geometry_token = display2
222-
.connection()
221+
.connection
223222
.get_geometry(window)
224223
.swbuf_err("Failed to send geometry request")?;
225224
let window_attrs_token = if window_handle.visual_id.is_none() {
226225
Some(
227226
display2
228-
.connection()
227+
.connection
229228
.get_window_attributes(window)
230229
.swbuf_err("Failed to send window attributes request")?,
231230
)
@@ -238,11 +237,11 @@ impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> SurfaceInterface<D, W> fo
238237

239238
// Create a new graphics context to draw to.
240239
let gc = display
241-
.connection()
240+
.connection
242241
.generate_id()
243242
.swbuf_err("Failed to generate GC ID")?;
244243
display
245-
.connection()
244+
.connection
246245
.create_gc(
247246
gc,
248247
window,
@@ -344,7 +343,7 @@ impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> SurfaceInterface<D, W> fo
344343
if self.size != Some((width, height)) {
345344
self.buffer_presented = false;
346345
self.buffer
347-
.resize(self.display.connection(), num_bytes)
346+
.resize(&self.display.connection, num_bytes)
348347
.swbuf_err("Failed to resize X11 buffer")?;
349348

350349
// We successfully resized the buffer.
@@ -358,11 +357,11 @@ impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> SurfaceInterface<D, W> fo
358357
tracing::trace!("next_buffer: window={:X}", self.window);
359358

360359
// Finish waiting on the previous `shm::PutImage` request, if any.
361-
self.buffer.finish_wait(self.display.connection())?;
360+
self.buffer.finish_wait(&self.display.connection)?;
362361

363362
// We can now safely call `next_buffer` on the buffer.
364363
Ok(BufferImpl {
365-
connection: self.display.connection(),
364+
connection: &self.display.connection,
366365
window: self.window,
367366
gc: self.gc,
368367
depth: self.depth,
@@ -382,7 +381,7 @@ impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> SurfaceInterface<D, W> fo
382381
// TODO: Is it worth it to do SHM here? Probably not.
383382
let reply = self
384383
.display
385-
.connection()
384+
.connection
386385
.get_image(
387386
xproto::ImageFormat::Z_PIXMAP,
388387
self.window,
@@ -775,25 +774,18 @@ impl Drop for ShmSegment {
775774
}
776775
}
777776

778-
impl<D: ?Sized> Drop for X11DisplayImpl<D> {
779-
fn drop(&mut self) {
780-
// Make sure that the x11rb connection is dropped before its source is.
781-
self.connection = None;
782-
}
783-
}
784-
785-
impl<D: ?Sized, W: ?Sized> Drop for X11Impl<D, W> {
777+
impl<D: ?Sized, W> Drop for X11Impl<D, W> {
786778
fn drop(&mut self) {
787779
// If we used SHM, make sure it's detached from the server.
788780
if let Buffer::Shm(mut shm) = mem::replace(
789781
&mut self.buffer,
790782
Buffer::Wire(util::PixelBuffer(Vec::new())),
791783
) {
792784
// If we were in the middle of processing a buffer, wait for it to finish.
793-
shm.finish_wait(self.display.connection()).ok();
785+
shm.finish_wait(&self.display.connection).ok();
794786

795787
if let Some((segment, seg_id)) = shm.seg.take() {
796-
if let Ok(token) = self.display.connection().shm_detach(seg_id) {
788+
if let Ok(token) = self.display.connection.shm_detach(seg_id) {
797789
token.ignore_error();
798790
}
799791

@@ -803,7 +795,7 @@ impl<D: ?Sized, W: ?Sized> Drop for X11Impl<D, W> {
803795
}
804796

805797
// Close the graphics context that we created.
806-
if let Ok(token) = self.display.connection().free_gc(self.gc) {
798+
if let Ok(token) = self.display.connection.free_gc(self.gc) {
807799
token.ignore_error();
808800
}
809801
}

0 commit comments

Comments
 (0)