From d20c4de1c56fae56015ca64bee4c1d56e4511c00 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sun, 23 Jul 2017 16:32:33 -0700 Subject: [PATCH] Safety fixes For OpenVR needs to be both initialized before Vulkan is, and shut down before Vulkan is. --- examples/test.rs | 2 +- src/lib.rs | 38 ++++++++++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/examples/test.rs b/examples/test.rs index bc464e5..00bf5b6 100644 --- a/examples/test.rs +++ b/examples/test.rs @@ -16,7 +16,7 @@ fn print_matrix_4x3(offset: u32, mat: [[f32; 4]; 3]) { } fn main() { - let context = match openvr::init(openvr::ApplicationType::Other) { + let context = match unsafe { openvr::init(openvr::ApplicationType::Other) } { Ok(ivr) => ivr, Err(err) => { println!("Failed to initialize openvr {:?}", err); diff --git a/src/lib.rs b/src/lib.rs index 0ccb1ab..5cce1f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ extern crate openvr_sys; use std::sync::atomic::{Ordering, AtomicBool, ATOMIC_BOOL_INIT}; use std::{fmt, error, ptr, mem}; use std::ffi::{CStr, CString}; +use std::cell::Cell; use openvr_sys as sys; @@ -25,22 +26,27 @@ static INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT; /// Initialize OpenVR /// /// # Panics +/// /// When the library has already been initialized -pub fn init(ty: ApplicationType) -> Result { +/// +/// # Safety +/// +/// The `Context` MUST be dropped or shut down with `Context::shutdown` before shutting down the graphics API. +pub unsafe fn init(ty: ApplicationType) -> Result { if INITIALIZED.swap(true, Ordering::Acquire) { panic!("OpenVR has already been initialized!"); } let mut error = sys::EVRInitError_VRInitError_None; - unsafe { sys::VR_InitInternal(&mut error, ty as sys::EVRApplicationType) }; + sys::VR_InitInternal(&mut error, ty as sys::EVRApplicationType); if error != sys::EVRInitError_VRInitError_None { return Err(InitError(error)); } - if !unsafe { sys::VR_IsInterfaceVersionValid(sys::IVRSystem_Version.as_ptr() as *const i8) } { - unsafe { sys::VR_ShutdownInternal() } + if !sys::VR_IsInterfaceVersionValid(sys::IVRSystem_Version.as_ptr() as *const i8) { + sys::VR_ShutdownInternal(); return Err(InitError(sys::EVRInitError_VRInitError_Init_InterfaceNotFound)); } - Ok(Context {}) + Ok(Context { live: Cell::new(true) }) } pub struct System<'a>(&'a sys::VR_IVRSystem_FnTable); @@ -50,7 +56,7 @@ pub struct RenderModels<'a>(&'a sys::VR_IVRRenderModels_FnTable); /// Entry points into OpenVR. /// /// At most one of this object may exist at a time. -pub struct Context {} +pub struct Context { live: Cell } fn load(suffix: &[u8]) -> Result<*const T, InitError> { let mut magic = Vec::from(b"FnTable:".as_ref()); @@ -71,8 +77,24 @@ impl Context { impl Drop for Context { fn drop(&mut self) { - unsafe { sys::VR_ShutdownInternal() } - INITIALIZED.store(false, Ordering::Release); + unsafe { self.shutdown() } + } +} + +impl Context { + /// Shut down OpenVR. Repeated calls are safe. + /// + /// Called implicitly by `Context::drop`. This MUST be called before shutting down the graphics API, or OpenVR may + /// invoke undefined behavior. + /// + /// # Safety + /// + /// No OpenVR calls may be made after this has been called unless a new `Context` is subsequently constructed. + pub unsafe fn shutdown(&self) { + if self.live.replace(false) { + sys::VR_ShutdownInternal(); + INITIALIZED.store(false, Ordering::Release); + } } }