Browse Source

Using Mutexes and ServiceHandler

pull/50/head
Andrea Ciliberti 3 years ago
parent
commit
9da34bd050
  1. 9
      ctru-rs/src/error.rs
  2. 40
      ctru-rs/src/gfx.rs
  3. 3
      ctru-rs/src/lib.rs
  4. 52
      ctru-rs/src/romfs.rs
  5. 52
      ctru-rs/src/services/apt.rs
  6. 62
      ctru-rs/src/services/fs.rs
  7. 48
      ctru-rs/src/services/hid.rs
  8. 45
      ctru-rs/src/services/mod.rs
  9. 65
      ctru-rs/src/services/soc.rs
  10. 58
      ctru-rs/src/services/sslc.rs
  11. 50
      ctru-rs/src/srv.rs

9
ctru-rs/src/error.rs

@ -9,7 +9,7 @@ pub type Result<T> = ::std::result::Result<T, Error>; @@ -9,7 +9,7 @@ pub type Result<T> = ::std::result::Result<T, Error>;
#[non_exhaustive]
pub enum Error {
Os(ctru_sys::Result),
ServiceAlreadyActive(&'static str),
ServiceAlreadyActive,
}
impl From<ctru_sys::Result> for Error {
@ -29,10 +29,7 @@ impl fmt::Debug for Error { @@ -29,10 +29,7 @@ impl fmt::Debug for Error {
.field("summary", &R_SUMMARY(err))
.field("level", &R_LEVEL(err))
.finish(),
Error::ServiceAlreadyActive(service) => f
.debug_tuple("ServiceAlreadyActive")
.field(&String::from(service))
.finish(),
Error::ServiceAlreadyActive => f.debug_tuple("ServiceAlreadyActive").finish(),
}
}
}
@ -44,7 +41,7 @@ impl fmt::Display for Error { @@ -44,7 +41,7 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Os(err) => write!(f, "libctru result code: 0x{:08X}", err),
Error::ServiceAlreadyActive(service) => write!(f, "Service {service} already active"),
Error::ServiceAlreadyActive => write!(f, "Service already active"),
}
}
}

40
ctru-rs/src/gfx.rs

@ -1,12 +1,13 @@ @@ -1,12 +1,13 @@
//! LCD screens manipulation helper
use std::cell::RefCell;
use std::lazy::SyncLazy;
use std::marker::PhantomData;
use std::ops::Drop;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;
use crate::error::{Error, Result};
use crate::services::gspgpu::{self, FramebufferFormat};
use crate::services::ServiceHandler;
/// Trait implemented by TopScreen and BottomScreen for common methods
pub trait Screen {
@ -72,9 +73,10 @@ pub enum Side { @@ -72,9 +73,10 @@ pub enum Side {
pub struct Gfx {
pub top_screen: RefCell<TopScreen>,
pub bottom_screen: RefCell<BottomScreen>,
_service_handler: ServiceHandler,
}
static GFX_ACTIVE: AtomicBool = AtomicBool::new(false);
static GFX_ACTIVE: SyncLazy<Mutex<usize>> = SyncLazy::new(|| Mutex::new(0));
impl Gfx {
/// Initialize the Gfx module with the chosen framebuffer formats for the top and bottom
@ -86,20 +88,23 @@ impl Gfx { @@ -86,20 +88,23 @@ impl Gfx {
bottom_fb_fmt: FramebufferFormat,
use_vram_buffers: bool,
) -> Result<Self> {
match GFX_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) {
Ok(_) => {
unsafe {
let _service_handler = ServiceHandler::new(
&GFX_ACTIVE,
false,
|| unsafe {
ctru_sys::gfxInit(top_fb_fmt.into(), bottom_fb_fmt.into(), use_vram_buffers);
}
Ok(Gfx {
Ok(())
},
|| unsafe { ctru_sys::gfxExit() },
)?;
Ok(Self {
top_screen: RefCell::new(TopScreen),
bottom_screen: RefCell::new(BottomScreen),
_service_handler,
})
}
Err(_) => Err(Error::ServiceAlreadyActive("Gfx")),
}
}
/// Creates a new Gfx instance with default init values
/// It's the same as calling: `Gfx::with_formats(FramebufferFormat::Bgr8, FramebufferFormat::Bgr8, false)
@ -212,14 +217,6 @@ impl From<Side> for ctru_sys::gfx3dSide_t { @@ -212,14 +217,6 @@ impl From<Side> for ctru_sys::gfx3dSide_t {
}
}
impl Drop for Gfx {
fn drop(&mut self) {
unsafe { ctru_sys::gfxExit() };
GFX_ACTIVE.store(false, Ordering::SeqCst);
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -227,9 +224,6 @@ mod tests { @@ -227,9 +224,6 @@ mod tests {
#[test]
fn gfx_duplicate() {
// We don't need to build a `Gfx` because the test runner has one already
match Gfx::init() {
Err(Error::ServiceAlreadyActive("Gfx")) => return,
_ => panic!(),
}
assert!(matches!(Gfx::init(), Err(Error::ServiceAlreadyActive)))
}
}

3
ctru-rs/src/lib.rs

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
#![feature(test)]
#![feature(custom_test_frameworks)]
#![test_runner(test_runner::run)]
#![feature(once_cell)]
extern "C" fn services_deinit() {
unsafe {
@ -28,6 +29,7 @@ pub fn init() { @@ -28,6 +29,7 @@ pub fn init() {
use std::panic::PanicInfo;
#[cfg(not(test))]
let main_thread = thread::current().id();
// Panic Hook setup
@ -36,6 +38,7 @@ pub fn init() { @@ -36,6 +38,7 @@ pub fn init() {
default_hook(info);
// Only for panics in the main thread
#[cfg(not(test))]
if main_thread == thread::current().id() && console::Console::exists() {
println!("\nPress SELECT to exit the software");

52
ctru-rs/src/romfs.rs

@ -11,39 +11,39 @@ @@ -11,39 +11,39 @@
//! ```
use std::ffi::CStr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::lazy::SyncLazy;
use std::sync::Mutex;
use crate::error::Error;
use crate::services::ServiceHandler;
#[non_exhaustive]
pub struct RomFS;
pub struct RomFS {
_service_handler: ServiceHandler,
}
static ROMFS_ACTIVE: AtomicBool = AtomicBool::new(false);
static ROMFS_ACTIVE: SyncLazy<Mutex<usize>> = SyncLazy::new(|| Mutex::new(0));
impl RomFS {
pub fn init() -> crate::Result<Self> {
match ROMFS_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) {
Ok(_) => {
let _service_handler = ServiceHandler::new(
&ROMFS_ACTIVE,
true,
|| {
let mount_name = CStr::from_bytes_with_nul(b"romfs\0").unwrap();
let result = unsafe { ctru_sys::romfsMountSelf(mount_name.as_ptr()) };
if result < 0 {
Err(result.into())
} else {
Ok(Self)
}
}
Err(_) => Err(Error::ServiceAlreadyActive("RomFS")),
}
let r = unsafe { ctru_sys::romfsMountSelf(mount_name.as_ptr()) };
if r < 0 {
return Err(r.into());
}
}
impl Drop for RomFS {
fn drop(&mut self) {
Ok(())
},
|| {
let mount_name = CStr::from_bytes_with_nul(b"romfs\0").unwrap();
unsafe { ctru_sys::romfsUnmount(mount_name.as_ptr()) };
},
)?;
ROMFS_ACTIVE.store(false, Ordering::SeqCst);
Ok(Self { _service_handler })
}
}
@ -54,10 +54,14 @@ mod tests { @@ -54,10 +54,14 @@ mod tests {
#[test]
fn romfs_duplicate() {
let _romfs = RomFS::init().unwrap();
let lock = *ROMFS_ACTIVE.lock().unwrap();
match RomFS::init() {
Err(Error::ServiceAlreadyActive("RomFS")) => return,
_ => panic!(),
}
assert_eq!(lock, 1);
drop(_romfs);
let lock = *ROMFS_ACTIVE.lock().unwrap();
assert_eq!(lock, 0);
}
}

52
ctru-rs/src/services/apt.rs

@ -1,25 +1,38 @@ @@ -1,25 +1,38 @@
use std::sync::atomic::{AtomicBool, Ordering};
use std::lazy::SyncLazy;
use std::sync::Mutex;
use crate::error::Error;
use crate::services::ServiceHandler;
#[non_exhaustive]
pub struct Apt(());
pub struct Apt {
_service_handler: ServiceHandler,
}
static APT_ACTIVE: AtomicBool = AtomicBool::new(false);
static APT_ACTIVE: SyncLazy<Mutex<usize>> = SyncLazy::new(|| Mutex::new(0));
impl Apt {
pub fn init() -> crate::Result<Self> {
match APT_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) {
Ok(_) => {
let _service_handler = ServiceHandler::new(
&APT_ACTIVE,
true,
|| {
let r = unsafe { ctru_sys::aptInit() };
if r < 0 {
Err(r.into())
} else {
Ok(Self(()))
}
}
Err(_) => Err(Error::ServiceAlreadyActive("Apt")),
return Err(r.into());
}
Ok(())
},
// `socExit` returns an error code. There is no documentantion of when errors could happen,
// but we wouldn't be able to handle them in the `Drop` implementation anyways.
// Surely nothing bad will happens :D
|| unsafe {
// The socket buffer is freed automatically by `socExit`
ctru_sys::aptExit();
},
)?;
Ok(Self { _service_handler })
}
pub fn main_loop(&self) -> bool {
@ -36,14 +49,6 @@ impl Apt { @@ -36,14 +49,6 @@ impl Apt {
}
}
impl Drop for Apt {
fn drop(&mut self) {
unsafe { ctru_sys::aptExit() };
APT_ACTIVE.store(false, Ordering::SeqCst);
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -51,9 +56,8 @@ mod tests { @@ -51,9 +56,8 @@ mod tests {
#[test]
fn apt_duplicate() {
// We don't need to build a `Apt` because the test runner has one already
match Apt::init() {
Err(Error::ServiceAlreadyActive("Apt")) => return,
_ => panic!(),
}
let lock = *APT_ACTIVE.lock().unwrap();
assert_eq!(lock, 1);
}
}

62
ctru-rs/src/services/fs.rs

@ -9,15 +9,16 @@ use std::io::Error as IoError; @@ -9,15 +9,16 @@ use std::io::Error as IoError;
use std::io::ErrorKind as IoErrorKind;
use std::io::Result as IoResult;
use std::io::{Read, Seek, SeekFrom, Write};
use std::lazy::SyncLazy;
use std::mem;
use std::path::{Path, PathBuf};
use std::ptr;
use std::slice;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::sync::{Arc, Mutex};
use widestring::{WideCStr, WideCString};
use crate::error::Error;
use crate::services::ServiceHandler;
bitflags! {
#[derive(Default)]
@ -86,9 +87,11 @@ pub enum ArchiveID { @@ -86,9 +87,11 @@ pub enum ArchiveID {
///
/// The service exits when all instances of this struct go out of scope.
#[non_exhaustive]
pub struct Fs(());
pub struct Fs {
_service_handler: ServiceHandler,
}
static FS_ACTIVE: AtomicBool = AtomicBool::new(false);
static FS_ACTIVE: SyncLazy<Mutex<usize>> = SyncLazy::new(|| Mutex::new(0));
/// Handle to an open filesystem archive.
///
@ -308,17 +311,27 @@ impl Fs { @@ -308,17 +311,27 @@ impl Fs {
/// as many times as desired and the service will not exit until all
/// instances of Fs drop out of scope.
pub fn init() -> crate::Result<Self> {
match FS_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) {
Ok(_) => {
let _service_handler = ServiceHandler::new(
&FS_ACTIVE,
true,
|| {
let r = unsafe { ctru_sys::fsInit() };
if r < 0 {
Err(r.into())
} else {
Ok(Self(()))
}
}
Err(_) => Err(Error::ServiceAlreadyActive("Fs")),
return Err(r.into());
}
Ok(())
},
// `socExit` returns an error code. There is no documentantion of when errors could happen,
// but we wouldn't be able to handle them in the `Drop` implementation anyways.
// Surely nothing bad will happens :D
|| unsafe {
// The socket buffer is freed automatically by `socExit`
ctru_sys::fsExit();
},
)?;
Ok(Self { _service_handler })
}
/// Returns a handle to the SDMC (memory card) Archive.
@ -998,16 +1011,6 @@ impl Seek for File { @@ -998,16 +1011,6 @@ impl Seek for File {
}
}
impl Drop for Fs {
fn drop(&mut self) {
unsafe {
ctru_sys::fsExit();
}
FS_ACTIVE.store(false, Ordering::SeqCst);
}
}
impl Drop for Archive {
fn drop(&mut self) {
unsafe {
@ -1083,9 +1086,14 @@ mod tests { @@ -1083,9 +1086,14 @@ mod tests {
fn fs_duplicate() {
let _fs = Fs::init().unwrap();
match Fs::init() {
Err(Error::ServiceAlreadyActive("Fs")) => return,
_ => panic!(),
}
let lock = *FS_ACTIVE.lock().unwrap();
assert_eq!(lock, 1);
drop(_fs);
let lock = *FS_ACTIVE.lock().unwrap();
assert_eq!(lock, 0);
}
}

48
ctru-rs/src/services/hid.rs

@ -4,9 +4,10 @@ @@ -4,9 +4,10 @@
//! and circle pad information. It also provides information from the sound volume slider,
//! the accelerometer, and the gyroscope.
use std::sync::atomic::{AtomicBool, Ordering};
use std::lazy::SyncLazy;
use std::sync::Mutex;
use crate::error::Error;
use crate::services::ServiceHandler;
bitflags::bitflags! {
/// A set of flags corresponding to the button and directional pad
@ -49,9 +50,11 @@ bitflags::bitflags! { @@ -49,9 +50,11 @@ bitflags::bitflags! {
///
/// This service requires no special permissions to use.
#[non_exhaustive]
pub struct Hid(());
pub struct Hid {
_service_handler: ServiceHandler,
}
static HID_ACTIVE: AtomicBool = AtomicBool::new(false);
static HID_ACTIVE: SyncLazy<Mutex<usize>> = SyncLazy::new(|| Mutex::new(0));
/// Represents user input to the touchscreen.
#[non_exhaustive]
@ -70,17 +73,23 @@ pub struct CirclePosition(ctru_sys::circlePosition); @@ -70,17 +73,23 @@ pub struct CirclePosition(ctru_sys::circlePosition);
/// rare in practice.
impl Hid {
pub fn init() -> crate::Result<Self> {
match HID_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) {
Ok(_) => {
let _service_handler = ServiceHandler::new(
&HID_ACTIVE,
true,
|| {
let r = unsafe { ctru_sys::hidInit() };
if r < 0 {
Err(r.into())
} else {
Ok(Self(()))
}
}
Err(_) => Err(Error::ServiceAlreadyActive("Hid")),
return Err(r.into());
}
Ok(())
},
|| unsafe {
ctru_sys::hidExit();
},
)?;
Ok(Self { _service_handler })
}
/// Scans the HID service for all user input occurring on the current
@ -160,14 +169,6 @@ impl CirclePosition { @@ -160,14 +169,6 @@ impl CirclePosition {
}
}
impl Drop for Hid {
fn drop(&mut self) {
unsafe { ctru_sys::hidExit() };
HID_ACTIVE.store(false, Ordering::SeqCst);
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -175,9 +176,8 @@ mod tests { @@ -175,9 +176,8 @@ mod tests {
#[test]
fn hid_duplicate() {
// We don't need to build a `Hid` because the test runner has one already
match Hid::init() {
Err(Error::ServiceAlreadyActive("Hid")) => return,
_ => panic!(),
}
let lock = *HID_ACTIVE.lock().unwrap();
assert_eq!(lock, 1);
}
}

45
ctru-rs/src/services/mod.rs

@ -9,3 +9,48 @@ pub mod sslc; @@ -9,3 +9,48 @@ pub mod sslc;
pub use self::apt::Apt;
pub use self::hid::Hid;
pub use self::sslc::SslC;
use crate::Error;
use std::sync::Mutex;
pub(crate) struct ServiceHandler {
counter: &'static Mutex<usize>,
close: Box<dyn Fn()>,
}
impl ServiceHandler {
pub fn new<S, E>(
counter: &'static Mutex<usize>,
allow_multiple: bool,
start: S,
close: E,
) -> crate::Result<Self>
where
S: FnOnce() -> crate::Result<()>,
E: Fn() + 'static,
{
let mut value = counter.lock().unwrap(); // todo: handle poisoning
if *value == 0 {
start()?;
} else if !allow_multiple {
return Err(Error::ServiceAlreadyActive);
}
*value += 1;
Ok(Self {
counter,
close: Box::new(close),
})
}
}
impl Drop for ServiceHandler {
fn drop(&mut self) {
let mut value = self.counter.lock().unwrap(); // should probably handle poisoning - could just map_err to ignore it.
*value -= 1;
if *value == 0 {
(self.close)();
}
}
}

65
ctru-rs/src/services/soc.rs

@ -1,18 +1,18 @@ @@ -1,18 +1,18 @@
use libc::{free, memalign};
use libc::memalign;
use std::lazy::SyncLazy;
use std::net::Ipv4Addr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;
use crate::error::Error;
use ctru_sys::{socExit, socInit};
use crate::services::ServiceHandler;
/// Soc service. Initializing this service will enable the use of network sockets and utilities
/// such as those found in `std::net`. The service will be closed when this struct is is dropped.
#[non_exhaustive]
pub struct Soc {
soc_mem: *mut u32,
_service_handler: ServiceHandler,
}
static SOC_ACTIVE: AtomicBool = AtomicBool::new(false);
static SOC_ACTIVE: SyncLazy<Mutex<usize>> = SyncLazy::new(|| Mutex::new(0));
impl Soc {
/// Initialize the Soc service with a default buffer size of 0x100000 bytes
@ -31,20 +31,28 @@ impl Soc { @@ -31,20 +31,28 @@ impl Soc {
///
/// This function will return an error if the `Soc` service is already initialized
pub fn init_with_buffer_size(num_bytes: usize) -> crate::Result<Self> {
match SOC_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) {
Ok(_) => unsafe {
let soc_mem = memalign(0x1000, num_bytes) as *mut u32;
let r = socInit(soc_mem, num_bytes as u32);
let _service_handler = ServiceHandler::new(
&SOC_ACTIVE,
true,
|| {
let soc_mem = unsafe { memalign(0x1000, num_bytes) } as *mut u32;
let r = unsafe { ctru_sys::socInit(soc_mem, num_bytes as u32) };
if r < 0 {
free(soc_mem as *mut _);
Err(r.into())
} else {
Ok(Self { soc_mem })
return Err(r.into());
}
Ok(())
},
Err(_) => Err(Error::ServiceAlreadyActive("Soc")),
}
// `socExit` returns an error code. There is no documentantion of when errors could happen,
// but we wouldn't be able to handle them in the `Drop` implementation anyways.
// Surely nothing bad will happens :D
|| unsafe {
// The socket buffer is freed automatically by `socExit`
ctru_sys::socExit();
},
)?;
Ok(Self { _service_handler })
}
/// IP Address of the Nintendo 3DS system.
@ -54,17 +62,6 @@ impl Soc { @@ -54,17 +62,6 @@ impl Soc {
}
}
impl Drop for Soc {
fn drop(&mut self) {
unsafe {
socExit();
free(self.soc_mem as *mut _);
}
SOC_ACTIVE.store(false, Ordering::SeqCst);
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -72,10 +69,14 @@ mod tests { @@ -72,10 +69,14 @@ mod tests {
#[test]
fn soc_duplicate() {
let _soc = Soc::init().unwrap();
let lock = *SOC_ACTIVE.lock().unwrap();
match Soc::init() {
Err(Error::ServiceAlreadyActive("Soc")) => return,
_ => panic!(),
}
assert_eq!(lock, 1);
drop(_soc);
let lock = *SOC_ACTIVE.lock().unwrap();
assert_eq!(lock, 0);
}
}

58
ctru-rs/src/services/sslc.rs

@ -1,28 +1,41 @@ @@ -1,28 +1,41 @@
// TODO: Implement remaining functions
use std::sync::atomic::{AtomicBool, Ordering};
use std::lazy::SyncLazy;
use std::sync::Mutex;
use crate::error::Error;
use crate::services::ServiceHandler;
#[non_exhaustive]
pub struct SslC(());
pub struct SslC {
_service_handler: ServiceHandler,
}
static SSLC_ACTIVE: AtomicBool = AtomicBool::new(false);
static SSLC_ACTIVE: SyncLazy<Mutex<usize>> = SyncLazy::new(|| Mutex::new(0));
impl SslC {
/// Initialize sslc
pub fn init() -> crate::Result<Self> {
match SSLC_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) {
Ok(_) => {
let _service_handler = ServiceHandler::new(
&SSLC_ACTIVE,
true,
|| {
let r = unsafe { ctru_sys::sslcInit(0) };
if r < 0 {
Err(r.into())
} else {
Ok(Self(()))
}
}
Err(_) => Err(Error::ServiceAlreadyActive("SslC")),
return Err(r.into());
}
Ok(())
},
// `socExit` returns an error code. There is no documentantion of when errors could happen,
// but we wouldn't be able to handle them in the `Drop` implementation anyways.
// Surely nothing bad will happens :D
|| unsafe {
// The socket buffer is freed automatically by `socExit`
ctru_sys::sslcExit();
},
)?;
Ok(Self { _service_handler })
}
/// Fill `buf` with `buf.len()` random bytes
@ -36,14 +49,6 @@ impl SslC { @@ -36,14 +49,6 @@ impl SslC {
}
}
impl Drop for SslC {
fn drop(&mut self) {
unsafe { ctru_sys::sslcExit() };
SSLC_ACTIVE.store(false, Ordering::SeqCst);
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -52,9 +57,14 @@ mod tests { @@ -52,9 +57,14 @@ mod tests {
fn sslc_duplicate() {
let _sslc = SslC::init().unwrap();
match SslC::init() {
Err(Error::ServiceAlreadyActive("SslC")) => return,
_ => panic!(),
}
let lock = *SSLC_ACTIVE.lock().unwrap();
assert_eq!(lock, 1);
drop(_sslc);
let lock = *SSLC_ACTIVE.lock().unwrap();
assert_eq!(lock, 0);
}
}

50
ctru-rs/src/srv.rs

@ -1,33 +1,34 @@ @@ -1,33 +1,34 @@
use std::sync::atomic::{AtomicBool, Ordering};
use std::lazy::SyncLazy;
use std::sync::Mutex;
use crate::error::Error;
use crate::services::ServiceHandler;
#[non_exhaustive]
pub struct Srv(());
pub struct Srv {
_service_handler: ServiceHandler,
}
static SRV_ACTIVE: AtomicBool = AtomicBool::new(false);
static SRV_ACTIVE: SyncLazy<Mutex<usize>> = SyncLazy::new(|| Mutex::new(0));
impl Srv {
pub fn init() -> crate::Result<Self> {
match SRV_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) {
Ok(_) => {
let _service_handler = ServiceHandler::new(
&SRV_ACTIVE,
true,
|| {
let r = unsafe { ctru_sys::srvInit() };
if r < 0 {
Err(r.into())
} else {
Ok(Self(()))
}
}
Err(_) => Err(Error::ServiceAlreadyActive("Srv")),
return Err(r.into());
}
}
}
impl Drop for Srv {
fn drop(&mut self) {
unsafe { ctru_sys::srvExit() };
Ok(())
},
|| unsafe {
ctru_sys::srvExit();
},
)?;
SRV_ACTIVE.store(false, Ordering::SeqCst);
Ok(Self { _service_handler })
}
}
@ -39,9 +40,14 @@ mod tests { @@ -39,9 +40,14 @@ mod tests {
fn srv_duplicate() {
let _srv = Srv::init().unwrap();
match Srv::init() {
Err(Error::ServiceAlreadyActive("Srv")) => return,
_ => panic!(),
}
let lock = *SRV_ACTIVE.lock().unwrap();
assert_eq!(lock, 1);
drop(_srv);
let lock = *SRV_ACTIVE.lock().unwrap();
assert_eq!(lock, 0);
}
}

Loading…
Cancel
Save