Skip to content
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 context current when dropping glow::Context to allow for debug callback cleanup #6114

Merged
merged 1 commit into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Bottom level categories:

#### Naga

* Support constant evaluation for `firstLeadingBit` and `firstTrailingBit` numeric built-ins in WGSL. Front-ends that translate to these built-ins also benefit from constant evaluation. By @ErichDonGubler in [#5101](https://github.com/gfx-rs/wgpu/pull/5101).
- Support constant evaluation for `firstLeadingBit` and `firstTrailingBit` numeric built-ins in WGSL. Front-ends that translate to these built-ins also benefit from constant evaluation. By @ErichDonGubler in [#5101](https://github.com/gfx-rs/wgpu/pull/5101).

### Bug Fixes

Expand All @@ -59,8 +59,13 @@ Bottom level categories:
- Fix crash when dropping the surface after the device. By @wumpf in [#6052](https://github.com/gfx-rs/wgpu/pull/6052)
- Fix error message that is thrown in create_render_pass to no longer say `compute_pass`. By @matthew-wong1 [#6041](https://github.com/gfx-rs/wgpu/pull/6041)

#### GLES / OpenGL

- Fix GL debug message callbacks not being properly cleaned up (causing UB). By @Imberflur in [#6114](https://github.com/gfx-rs/wgpu/pull/6114)

### Changes

- `wgpu_hal::gles::Adapter::new_external` now requires the context to be current when dropping the adapter and related objects. By @Imberflur in [#6114](https://github.com/gfx-rs/wgpu/pull/6114).
- Reduce the amount of debug and trace logs emitted by wgpu-core and wgpu-hal. By @nical in [#6065](https://github.com/gfx-rs/wgpu/issues/6065)

### Dependency Updates
Expand Down
50 changes: 42 additions & 8 deletions wgpu-hal/src/gles/egl.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use glow::HasContext;
use once_cell::sync::Lazy;
use parking_lot::{Mutex, MutexGuard, RwLock};
use parking_lot::{MappedMutexGuard, Mutex, MutexGuard, RwLock};

use std::{collections::HashMap, ffi, os::raw, ptr, rc::Rc, sync::Arc, time::Duration};
use std::{
collections::HashMap, ffi, mem::ManuallyDrop, os::raw, ptr, rc::Rc, sync::Arc, time::Duration,
};

/// The amount of time to wait while trying to obtain a lock to the adapter context
const CONTEXT_LOCK_TIMEOUT_SECS: u64 = 1;
Expand Down Expand Up @@ -295,6 +297,7 @@ impl EglContext {
.make_current(self.display, self.pbuffer, self.pbuffer, Some(self.raw))
.unwrap();
}

fn unmake_current(&self) {
self.instance
.make_current(self.display, None, None, None)
Expand All @@ -305,7 +308,7 @@ impl EglContext {
/// A wrapper around a [`glow::Context`] and the required EGL context that uses locking to guarantee
/// exclusive access when shared with multiple threads.
pub struct AdapterContext {
glow: Mutex<glow::Context>,
glow: Mutex<ManuallyDrop<glow::Context>>,
egl: Option<EglContext>,
}

Expand Down Expand Up @@ -346,14 +349,39 @@ impl AdapterContext {
}
}

impl Drop for AdapterContext {
fn drop(&mut self) {
struct CurrentGuard<'a>(&'a EglContext);
impl Drop for CurrentGuard<'_> {
fn drop(&mut self) {
self.0.unmake_current();
}
}

// Context must be current when dropped. See safety docs on
// `glow::HasContext`.
//
// NOTE: This is only set to `None` by `Adapter::new_external` which
// requires the context to be current when anything that may be holding
// the `Arc<AdapterShared>` is dropped.
let _guard = self.egl.as_ref().map(|egl| {
egl.make_current();
CurrentGuard(egl)
});
let glow = self.glow.get_mut();
// SAFETY: Field not used after this.
unsafe { ManuallyDrop::drop(glow) };
}
}

struct EglContextLock<'a> {
instance: &'a Arc<EglInstance>,
display: khronos_egl::Display,
}

/// A guard containing a lock to an [`AdapterContext`]
pub struct AdapterContextLock<'a> {
glow: MutexGuard<'a, glow::Context>,
glow: MutexGuard<'a, ManuallyDrop<glow::Context>>,
egl: Option<EglContextLock<'a>>,
}

Expand Down Expand Up @@ -387,10 +415,12 @@ impl AdapterContext {
///
/// > **Note:** Calling this function **will** still lock the [`glow::Context`] which adds an
/// > extra safe-guard against accidental concurrent access to the context.
pub unsafe fn get_without_egl_lock(&self) -> MutexGuard<glow::Context> {
self.glow
pub unsafe fn get_without_egl_lock(&self) -> MappedMutexGuard<glow::Context> {
let guard = self
.glow
.try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
.expect("Could not lock adapter context. This is most-likely a deadlock.")
.expect("Could not lock adapter context. This is most-likely a deadlock.");
MutexGuard::map(guard, |glow| &mut **glow)
}

/// Obtain a lock to the EGL context and get handle to the [`glow::Context`] that can be used to
Expand Down Expand Up @@ -1052,6 +1082,8 @@ impl crate::Instance for Instance {
unsafe { gl.debug_message_callback(super::gl_debug_message_callback) };
}

// Avoid accidental drop when the context is not current.
let gl = ManuallyDrop::new(gl);
Comment on lines +1085 to +1086
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a stale comment? You're not avoiding a drop when the context is not current, instead you're forcing it to be current Before dropping.

Copy link
Contributor Author

@Imberflur Imberflur Aug 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is noting the reason to wrap in ManuallyDrop before calling unmake_current on the line below. This prevents the glow::Context from being dropped if there is some panic between when we call unmake_current and when gl is placed into the AdapterContext (where the drop impl will handle making the context current before dropping glow::Context).

FWIW the comment could probably be clarified.

Copy link
Contributor

@MarijnS95 MarijnS95 Aug 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarification is happening in #6152 where I ran into a few conflicts with this PR. Panics (except inside the drop handler) should still cause Drop to be called while unwinding. I'll add a mention that the only skipped case is between let gl and when Self {} is finally constructed.

Fwiw when a debug callback is specified by the user (in this case wgpu), it's common to skip messages when panicking:

if thread::panicking() {
return vk::FALSE;
}

Not sure if that can be done in EGL/WGL as well? Otherwise it seems this solution is a litle bit overengineered if it's just about making the context current in drop()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Panics (except inside the drop handler) should still cause Drop to be called while unwinding

Yes, this is why it needs to be wrapped in ManuallyDrop in the intermittent time before AdapterContext is constructed since panics would drop the glow::Context which could lead to UB if it isn't current.

Fwiw when a debug callback is specified by the user (in this case wgpu), it's common to skip messages when panicking:

Something like this won't help here with the way the Drop impl for glow::Context works, by the time the callback is invoked there is already UB.

inner.egl.unmake_current();

unsafe {
Expand All @@ -1073,13 +1105,15 @@ impl super::Adapter {
/// - The underlying OpenGL ES context must be current.
/// - The underlying OpenGL ES context must be current when interfacing with any objects returned by
/// wgpu-hal from this adapter.
/// - The underlying OpenGL ES context must be current when dropping this adapter and when
/// dropping any objects returned from this adapter.
cwfitzgerald marked this conversation as resolved.
Show resolved Hide resolved
pub unsafe fn new_external(
fun: impl FnMut(&str) -> *const ffi::c_void,
) -> Option<crate::ExposedAdapter<super::Api>> {
let context = unsafe { glow::Context::from_loader_function(fun) };
unsafe {
Self::expose(AdapterContext {
glow: Mutex::new(context),
glow: Mutex::new(ManuallyDrop::new(context)),
egl: None,
})
}
Expand Down
24 changes: 22 additions & 2 deletions wgpu-hal/src/gles/wgl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
use std::{
collections::HashSet,
ffi::{c_void, CStr, CString},
mem,
mem::{self, ManuallyDrop},
os::raw::c_int,
ptr,
sync::{
Expand Down Expand Up @@ -134,11 +134,29 @@ unsafe impl Send for WglContext {}
unsafe impl Sync for WglContext {}

struct Inner {
gl: glow::Context,
gl: ManuallyDrop<glow::Context>,
device: InstanceDevice,
context: WglContext,
}

impl Drop for Inner {
fn drop(&mut self) {
struct CurrentGuard<'a>(&'a WglContext);
impl Drop for CurrentGuard<'_> {
fn drop(&mut self) {
self.0.unmake_current().unwrap();
}
}

// Context must be current when dropped. See safety docs on
// `glow::HasContext`.
self.context.make_current(self.device.dc).unwrap();
let _guard = CurrentGuard(&self.context);
// SAFETY: Field not used after this.
unsafe { ManuallyDrop::drop(&mut self.gl) };
}
}

unsafe impl Send for Inner {}
unsafe impl Sync for Inner {}

Expand Down Expand Up @@ -497,6 +515,8 @@ impl crate::Instance for Instance {
unsafe { gl.debug_message_callback(super::gl_debug_message_callback) };
}

// Avoid accidental drop when the context is not current.
let gl = ManuallyDrop::new(gl);
context.unmake_current().map_err(|e| {
crate::InstanceError::with_source(
String::from("unable to unset the current WGL context"),
Expand Down
Loading