use std::mem::MaybeUninit;
use std::ops::Deref;
use std::ptr;
use std::rc::{Rc, Weak};
use crate::loop_::{AsLoop, LoopRef};
use crate::{error::Error, properties::Properties};
use spa::utils::dict::ReadableDict;
#[derive(Debug, Clone)]
pub struct ThreadLoop {
inner: Rc<ThreadLoopInner>,
}
impl ThreadLoop {
pub unsafe fn new(name: Option<&str>) -> Result<Self, Error> {
super::init();
let inner = ThreadLoopInner::new::<Properties>(name, None)?;
Ok(Self {
inner: Rc::new(inner),
})
}
pub fn with_properties<T: ReadableDict>(
name: Option<&str>,
properties: &T,
) -> Result<Self, Error> {
let inner = ThreadLoopInner::new(name, Some(properties))?;
Ok(Self {
inner: Rc::new(inner),
})
}
pub fn downgrade(&self) -> WeakThreadLoop {
let weak = Rc::downgrade(&self.inner);
WeakThreadLoop { weak }
}
}
impl AsLoop for ThreadLoop {
type Target = ThreadLoopInner;
fn as_loop(&self) -> &Rc<Self::Target> {
&self.inner
}
}
impl std::convert::AsRef<LoopRef> for ThreadLoop {
fn as_ref(&self) -> &LoopRef {
self.deref()
}
}
impl Deref for ThreadLoop {
type Target = ThreadLoopInner;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
pub struct WeakThreadLoop {
weak: Weak<ThreadLoopInner>,
}
impl WeakThreadLoop {
pub fn upgrade(&self) -> Option<ThreadLoop> {
self.weak.upgrade().map(|inner| ThreadLoop { inner })
}
}
pub struct ThreadLoopLockGuard<'a> {
thread_loop: &'a ThreadLoopInner,
}
impl<'a> ThreadLoopLockGuard<'a> {
fn new(thread_loop: &'a ThreadLoopInner) -> Self {
unsafe {
pw_sys::pw_thread_loop_lock(thread_loop.as_ptr());
}
ThreadLoopLockGuard { thread_loop }
}
pub fn unlock(self) {
drop(self);
}
}
impl<'a> Drop for ThreadLoopLockGuard<'a> {
fn drop(&mut self) {
unsafe {
pw_sys::pw_thread_loop_unlock(self.thread_loop.as_ptr());
}
}
}
#[derive(Debug)]
pub struct ThreadLoopInner {
ptr: ptr::NonNull<pw_sys::pw_thread_loop>,
}
impl ThreadLoopInner {
fn new<T: ReadableDict>(name: Option<&str>, properties: Option<&T>) -> Result<Self, Error> {
unsafe {
let props = properties.map_or(ptr::null(), |props| props.get_dict_ptr()) as *mut _;
let l = pw_sys::pw_thread_loop_new(
name.map_or(ptr::null(), |p| p.as_ptr() as *const _),
props,
);
let ptr = ptr::NonNull::new(l).ok_or(Error::CreationFailed)?;
Ok(ThreadLoopInner { ptr })
}
}
fn as_ptr(&self) -> *mut pw_sys::pw_thread_loop {
self.ptr.as_ptr()
}
pub fn lock(&self) -> ThreadLoopLockGuard {
ThreadLoopLockGuard::new(self)
}
pub fn start(&self) {
unsafe {
pw_sys::pw_thread_loop_start(self.as_ptr());
}
}
pub fn stop(&self) {
unsafe {
pw_sys::pw_thread_loop_stop(self.as_ptr());
}
}
pub fn signal(&self, signal: bool) {
unsafe {
pw_sys::pw_thread_loop_signal(self.as_ptr(), signal);
}
}
pub fn wait(&self) {
unsafe {
pw_sys::pw_thread_loop_wait(self.as_ptr());
}
}
pub fn timed_wait(&self, wait_max_sec: std::time::Duration) {
unsafe {
let wait_max_sec: i32 = wait_max_sec
.as_secs()
.try_into()
.expect("Provided timeout does not fit in a i32");
pw_sys::pw_thread_loop_timed_wait(self.as_ptr(), wait_max_sec);
}
}
pub fn get_time(&self, timeout: i64) -> nix::sys::time::TimeSpec {
unsafe {
let mut abstime: MaybeUninit<pw_sys::timespec> = std::mem::MaybeUninit::uninit();
pw_sys::pw_thread_loop_get_time(self.as_ptr(), abstime.as_mut_ptr(), timeout);
let abstime = abstime.assume_init();
nix::sys::time::TimeSpec::new(abstime.tv_sec, abstime.tv_nsec)
}
}
pub fn timed_wait_full(&self, abstime: nix::sys::time::TimeSpec) {
unsafe {
let abstime = pw_sys::timespec {
tv_sec: abstime.tv_sec(),
tv_nsec: abstime.tv_nsec(),
};
pw_sys::pw_thread_loop_timed_wait_full(
self.as_ptr(),
&abstime as *const pw_sys::timespec,
);
}
}
pub fn accept(&self) {
unsafe {
pw_sys::pw_thread_loop_accept(self.as_ptr());
}
}
pub fn in_thread(&self) {
unsafe {
pw_sys::pw_thread_loop_in_thread(self.as_ptr());
}
}
}
impl std::convert::AsRef<LoopRef> for ThreadLoopInner {
fn as_ref(&self) -> &LoopRef {
self.deref()
}
}
impl std::ops::Deref for ThreadLoopInner {
type Target = LoopRef;
fn deref(&self) -> &Self::Target {
unsafe { &*(pw_sys::pw_thread_loop_get_loop(self.ptr.as_ptr()) as *mut LoopRef) }
}
}
impl Drop for ThreadLoopInner {
fn drop(&mut self) {
unsafe { pw_sys::pw_thread_loop_destroy(self.ptr.as_ptr()) }
}
}