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. 50
      ctru-rs/src/gfx.rs
  3. 3
      ctru-rs/src/lib.rs
  4. 56
      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>;
#[non_exhaustive] #[non_exhaustive]
pub enum Error { pub enum Error {
Os(ctru_sys::Result), Os(ctru_sys::Result),
ServiceAlreadyActive(&'static str), ServiceAlreadyActive,
} }
impl From<ctru_sys::Result> for Error { impl From<ctru_sys::Result> for Error {
@ -29,10 +29,7 @@ impl fmt::Debug for Error {
.field("summary", &R_SUMMARY(err)) .field("summary", &R_SUMMARY(err))
.field("level", &R_LEVEL(err)) .field("level", &R_LEVEL(err))
.finish(), .finish(),
Error::ServiceAlreadyActive(service) => f Error::ServiceAlreadyActive => f.debug_tuple("ServiceAlreadyActive").finish(),
.debug_tuple("ServiceAlreadyActive")
.field(&String::from(service))
.finish(),
} }
} }
} }
@ -44,7 +41,7 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
Error::Os(err) => write!(f, "libctru result code: 0x{:08X}", err), 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"),
} }
} }
} }

50
ctru-rs/src/gfx.rs

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

3
ctru-rs/src/lib.rs

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

56
ctru-rs/src/romfs.rs

@ -11,39 +11,39 @@
//! ``` //! ```
use std::ffi::CStr; 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] #[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 { impl RomFS {
pub fn init() -> crate::Result<Self> { pub fn init() -> crate::Result<Self> {
match ROMFS_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) { let _service_handler = ServiceHandler::new(
Ok(_) => { &ROMFS_ACTIVE,
true,
|| {
let mount_name = CStr::from_bytes_with_nul(b"romfs\0").unwrap(); let mount_name = CStr::from_bytes_with_nul(b"romfs\0").unwrap();
let result = unsafe { ctru_sys::romfsMountSelf(mount_name.as_ptr()) }; let r = unsafe { ctru_sys::romfsMountSelf(mount_name.as_ptr()) };
if r < 0 {
if result < 0 { return Err(r.into());
Err(result.into())
} else {
Ok(Self)
} }
}
Err(_) => Err(Error::ServiceAlreadyActive("RomFS")),
}
}
}
impl Drop for RomFS { Ok(())
fn drop(&mut self) { },
let mount_name = CStr::from_bytes_with_nul(b"romfs\0").unwrap(); || {
unsafe { ctru_sys::romfsUnmount(mount_name.as_ptr()) }; 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 {
#[test] #[test]
fn romfs_duplicate() { fn romfs_duplicate() {
let _romfs = RomFS::init().unwrap(); let _romfs = RomFS::init().unwrap();
let lock = *ROMFS_ACTIVE.lock().unwrap();
assert_eq!(lock, 1);
drop(_romfs);
let lock = *ROMFS_ACTIVE.lock().unwrap();
match RomFS::init() { assert_eq!(lock, 0);
Err(Error::ServiceAlreadyActive("RomFS")) => return,
_ => panic!(),
}
} }
} }

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

@ -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] #[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 { impl Apt {
pub fn init() -> crate::Result<Self> { pub fn init() -> crate::Result<Self> {
match APT_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) { let _service_handler = ServiceHandler::new(
Ok(_) => { &APT_ACTIVE,
true,
|| {
let r = unsafe { ctru_sys::aptInit() }; let r = unsafe { ctru_sys::aptInit() };
if r < 0 { if r < 0 {
Err(r.into()) return Err(r.into());
} else {
Ok(Self(()))
} }
}
Err(_) => Err(Error::ServiceAlreadyActive("Apt")), 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 { pub fn main_loop(&self) -> bool {
@ -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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -51,9 +56,8 @@ mod tests {
#[test] #[test]
fn apt_duplicate() { fn apt_duplicate() {
// We don't need to build a `Apt` because the test runner has one already // We don't need to build a `Apt` because the test runner has one already
match Apt::init() { let lock = *APT_ACTIVE.lock().unwrap();
Err(Error::ServiceAlreadyActive("Apt")) => return,
_ => panic!(), assert_eq!(lock, 1);
}
} }
} }

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

@ -9,15 +9,16 @@ use std::io::Error as IoError;
use std::io::ErrorKind as IoErrorKind; use std::io::ErrorKind as IoErrorKind;
use std::io::Result as IoResult; use std::io::Result as IoResult;
use std::io::{Read, Seek, SeekFrom, Write}; use std::io::{Read, Seek, SeekFrom, Write};
use std::lazy::SyncLazy;
use std::mem; use std::mem;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::ptr; use std::ptr;
use std::slice; use std::slice;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex};
use std::sync::Arc;
use widestring::{WideCStr, WideCString}; use widestring::{WideCStr, WideCString};
use crate::error::Error; use crate::services::ServiceHandler;
bitflags! { bitflags! {
#[derive(Default)] #[derive(Default)]
@ -86,9 +87,11 @@ pub enum ArchiveID {
/// ///
/// The service exits when all instances of this struct go out of scope. /// The service exits when all instances of this struct go out of scope.
#[non_exhaustive] #[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. /// Handle to an open filesystem archive.
/// ///
@ -308,17 +311,27 @@ impl Fs {
/// as many times as desired and the service will not exit until all /// as many times as desired and the service will not exit until all
/// instances of Fs drop out of scope. /// instances of Fs drop out of scope.
pub fn init() -> crate::Result<Self> { pub fn init() -> crate::Result<Self> {
match FS_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) { let _service_handler = ServiceHandler::new(
Ok(_) => { &FS_ACTIVE,
true,
|| {
let r = unsafe { ctru_sys::fsInit() }; let r = unsafe { ctru_sys::fsInit() };
if r < 0 { if r < 0 {
Err(r.into()) return Err(r.into());
} else {
Ok(Self(()))
} }
}
Err(_) => Err(Error::ServiceAlreadyActive("Fs")), 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. /// Returns a handle to the SDMC (memory card) Archive.
@ -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 { impl Drop for Archive {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
@ -1083,9 +1086,14 @@ mod tests {
fn fs_duplicate() { fn fs_duplicate() {
let _fs = Fs::init().unwrap(); let _fs = Fs::init().unwrap();
match Fs::init() { let lock = *FS_ACTIVE.lock().unwrap();
Err(Error::ServiceAlreadyActive("Fs")) => return,
_ => panic!(), 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 @@
//! and circle pad information. It also provides information from the sound volume slider, //! and circle pad information. It also provides information from the sound volume slider,
//! the accelerometer, and the gyroscope. //! 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! { bitflags::bitflags! {
/// A set of flags corresponding to the button and directional pad /// A set of flags corresponding to the button and directional pad
@ -49,9 +50,11 @@ bitflags::bitflags! {
/// ///
/// This service requires no special permissions to use. /// This service requires no special permissions to use.
#[non_exhaustive] #[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. /// Represents user input to the touchscreen.
#[non_exhaustive] #[non_exhaustive]
@ -70,17 +73,23 @@ pub struct CirclePosition(ctru_sys::circlePosition);
/// rare in practice. /// rare in practice.
impl Hid { impl Hid {
pub fn init() -> crate::Result<Self> { pub fn init() -> crate::Result<Self> {
match HID_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) { let _service_handler = ServiceHandler::new(
Ok(_) => { &HID_ACTIVE,
true,
|| {
let r = unsafe { ctru_sys::hidInit() }; let r = unsafe { ctru_sys::hidInit() };
if r < 0 { if r < 0 {
Err(r.into()) return Err(r.into());
} else {
Ok(Self(()))
} }
}
Err(_) => Err(Error::ServiceAlreadyActive("Hid")), Ok(())
} },
|| unsafe {
ctru_sys::hidExit();
},
)?;
Ok(Self { _service_handler })
} }
/// Scans the HID service for all user input occurring on the current /// Scans the HID service for all user input occurring on the current
@ -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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -175,9 +176,8 @@ mod tests {
#[test] #[test]
fn hid_duplicate() { fn hid_duplicate() {
// We don't need to build a `Hid` because the test runner has one already // We don't need to build a `Hid` because the test runner has one already
match Hid::init() { let lock = *HID_ACTIVE.lock().unwrap();
Err(Error::ServiceAlreadyActive("Hid")) => return,
_ => panic!(), assert_eq!(lock, 1);
}
} }
} }

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

@ -9,3 +9,48 @@ pub mod sslc;
pub use self::apt::Apt; pub use self::apt::Apt;
pub use self::hid::Hid; pub use self::hid::Hid;
pub use self::sslc::SslC; 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 @@
use libc::{free, memalign}; use libc::memalign;
use std::lazy::SyncLazy;
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex;
use crate::error::Error; use crate::services::ServiceHandler;
use ctru_sys::{socExit, socInit};
/// Soc service. Initializing this service will enable the use of network sockets and utilities /// 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. /// such as those found in `std::net`. The service will be closed when this struct is is dropped.
#[non_exhaustive] #[non_exhaustive]
pub struct Soc { 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 { impl Soc {
/// Initialize the Soc service with a default buffer size of 0x100000 bytes /// Initialize the Soc service with a default buffer size of 0x100000 bytes
@ -31,20 +31,28 @@ impl Soc {
/// ///
/// This function will return an error if the `Soc` service is already initialized /// 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> { pub fn init_with_buffer_size(num_bytes: usize) -> crate::Result<Self> {
match SOC_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) { let _service_handler = ServiceHandler::new(
Ok(_) => unsafe { &SOC_ACTIVE,
let soc_mem = memalign(0x1000, num_bytes) as *mut u32; true,
|| {
let r = socInit(soc_mem, num_bytes as u32); 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 { if r < 0 {
free(soc_mem as *mut _); return Err(r.into());
Err(r.into())
} else {
Ok(Self { soc_mem })
} }
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::socExit();
}, },
Err(_) => Err(Error::ServiceAlreadyActive("Soc")), )?;
}
Ok(Self { _service_handler })
} }
/// IP Address of the Nintendo 3DS system. /// IP Address of the Nintendo 3DS system.
@ -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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -72,10 +69,14 @@ mod tests {
#[test] #[test]
fn soc_duplicate() { fn soc_duplicate() {
let _soc = Soc::init().unwrap(); let _soc = Soc::init().unwrap();
let lock = *SOC_ACTIVE.lock().unwrap();
assert_eq!(lock, 1);
drop(_soc);
let lock = *SOC_ACTIVE.lock().unwrap();
match Soc::init() { assert_eq!(lock, 0);
Err(Error::ServiceAlreadyActive("Soc")) => return,
_ => panic!(),
}
} }
} }

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

@ -1,28 +1,41 @@
// TODO: Implement remaining functions // 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] #[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 { impl SslC {
/// Initialize sslc /// Initialize sslc
pub fn init() -> crate::Result<Self> { pub fn init() -> crate::Result<Self> {
match SSLC_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) { let _service_handler = ServiceHandler::new(
Ok(_) => { &SSLC_ACTIVE,
true,
|| {
let r = unsafe { ctru_sys::sslcInit(0) }; let r = unsafe { ctru_sys::sslcInit(0) };
if r < 0 { if r < 0 {
Err(r.into()) return Err(r.into());
} else {
Ok(Self(()))
} }
}
Err(_) => Err(Error::ServiceAlreadyActive("SslC")), 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 /// Fill `buf` with `buf.len()` random bytes
@ -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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -52,9 +57,14 @@ mod tests {
fn sslc_duplicate() { fn sslc_duplicate() {
let _sslc = SslC::init().unwrap(); let _sslc = SslC::init().unwrap();
match SslC::init() { let lock = *SSLC_ACTIVE.lock().unwrap();
Err(Error::ServiceAlreadyActive("SslC")) => return,
_ => panic!(), 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 @@
use std::sync::atomic::{AtomicBool, Ordering}; use std::lazy::SyncLazy;
use std::sync::Mutex;
use crate::error::Error; use crate::services::ServiceHandler;
#[non_exhaustive] #[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 { impl Srv {
pub fn init() -> crate::Result<Self> { pub fn init() -> crate::Result<Self> {
match SRV_ACTIVE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) { let _service_handler = ServiceHandler::new(
Ok(_) => { &SRV_ACTIVE,
true,
|| {
let r = unsafe { ctru_sys::srvInit() }; let r = unsafe { ctru_sys::srvInit() };
if r < 0 { if r < 0 {
Err(r.into()) return Err(r.into());
} else {
Ok(Self(()))
} }
}
Err(_) => Err(Error::ServiceAlreadyActive("Srv")),
}
}
}
impl Drop for Srv { Ok(())
fn drop(&mut self) { },
unsafe { ctru_sys::srvExit() }; || unsafe {
ctru_sys::srvExit();
},
)?;
SRV_ACTIVE.store(false, Ordering::SeqCst); Ok(Self { _service_handler })
} }
} }
@ -39,9 +40,14 @@ mod tests {
fn srv_duplicate() { fn srv_duplicate() {
let _srv = Srv::init().unwrap(); let _srv = Srv::init().unwrap();
match Srv::init() { let lock = *SRV_ACTIVE.lock().unwrap();
Err(Error::ServiceAlreadyActive("Srv")) => return,
_ => panic!(), assert_eq!(lock, 1);
}
drop(_srv);
let lock = *SRV_ACTIVE.lock().unwrap();
assert_eq!(lock, 0);
} }
} }

Loading…
Cancel
Save