pipewire/thread_loop/
mod.rs

1// Copyright The pipewire-rs Contributors.
2// SPDX-License-Identifier: MIT
3
4use std::mem::MaybeUninit;
5
6use crate::loop_::Loop;
7
8mod box_;
9pub use box_::*;
10mod rc;
11pub use rc::*;
12
13/// A wrapper around the pipewire threaded loop interface. ThreadLoops are a higher level
14/// of abstraction around the loop interface. A ThreadLoop can be used to spawn a new thread
15/// that runs the wrapped loop.
16#[repr(transparent)]
17pub struct ThreadLoop(pw_sys::pw_thread_loop);
18
19impl ThreadLoop {
20    pub fn as_raw(&self) -> &pw_sys::pw_thread_loop {
21        &self.0
22    }
23
24    pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_thread_loop {
25        std::ptr::addr_of!(self.0).cast_mut()
26    }
27
28    pub fn loop_(&self) -> &Loop {
29        unsafe {
30            let pw_loop = pw_sys::pw_thread_loop_get_loop(self.as_raw_ptr());
31            // FIXME: Make sure pw_loop is not null
32            &*(pw_loop.cast::<Loop>())
33        }
34    }
35
36    /// Lock the Loop
37    ///
38    /// This ensures that the loop thread will not access objects associated
39    /// with the loop while the lock is held, `lock()` can be used multiple times
40    /// from the same thread.
41    ///
42    /// The lock needs to be held whenever you call any PipeWire function that
43    /// uses an object associated with this loop. Make sure to not hold
44    /// on to the lock more than necessary though, as the threaded loop stops
45    /// while the lock is held.
46    pub fn lock(&self) -> ThreadLoopLockGuard<'_> {
47        ThreadLoopLockGuard::new(self)
48    }
49
50    /// Start the ThreadLoop
51    pub fn start(&self) {
52        unsafe {
53            pw_sys::pw_thread_loop_start(self.as_raw_ptr());
54        }
55    }
56
57    /// Stop the ThreadLoop
58    ///
59    /// Stopping the ThreadLoop must be called without the lock
60    pub fn stop(&self) {
61        unsafe {
62            pw_sys::pw_thread_loop_stop(self.as_raw_ptr());
63        }
64    }
65
66    /// Signal all threads waiting with [`wait()`](`Self::wait`)
67    pub fn signal(&self, signal: bool) {
68        unsafe {
69            pw_sys::pw_thread_loop_signal(self.as_raw_ptr(), signal);
70        }
71    }
72
73    /// Release the lock and wait
74    ///
75    /// Release the lock and wait until some thread calls [`signal()`](`Self::signal`)
76    pub fn wait(&self) {
77        unsafe {
78            pw_sys::pw_thread_loop_wait(self.as_raw_ptr());
79        }
80    }
81
82    /// Release the lock and wait a maximum of `wait_max_sec` seconds
83    /// until some thread calls [`signal()`](`Self::signal`) or time out
84    pub fn timed_wait(&self, wait_max_sec: std::time::Duration) {
85        unsafe {
86            let wait_max_sec: i32 = wait_max_sec
87                .as_secs()
88                .try_into()
89                .expect("Provided timeout does not fit in a i32");
90            pw_sys::pw_thread_loop_timed_wait(self.as_raw_ptr(), wait_max_sec);
91        }
92    }
93
94    /// Get a timespec suitable for [`timed_wait_full()`](`Self::timed_wait_full`)
95    pub fn get_time(&self, timeout: i64) -> nix::sys::time::TimeSpec {
96        unsafe {
97            let mut abstime: MaybeUninit<pw_sys::timespec> = std::mem::MaybeUninit::uninit();
98            pw_sys::pw_thread_loop_get_time(self.as_raw_ptr(), abstime.as_mut_ptr(), timeout);
99            let abstime = abstime.assume_init();
100            nix::sys::time::TimeSpec::new(abstime.tv_sec, abstime.tv_nsec)
101        }
102    }
103
104    /// Release the lock and wait up to abs seconds until some
105    /// thread calls [`signal()`](`Self::signal`). Use [`get_time()`](`Self::get_time`)
106    /// to get a suitable timespec
107    pub fn timed_wait_full(&self, abstime: nix::sys::time::TimeSpec) {
108        unsafe {
109            let mut abstime = pw_sys::timespec {
110                tv_sec: abstime.tv_sec(),
111                tv_nsec: abstime.tv_nsec(),
112            };
113            pw_sys::pw_thread_loop_timed_wait_full(
114                self.as_raw_ptr(),
115                &mut abstime as *mut pw_sys::timespec,
116            );
117        }
118    }
119
120    /// Signal all threads executing [`signal()`](`Self::signal`) with `wait_for_accept`
121    pub fn accept(&self) {
122        unsafe {
123            pw_sys::pw_thread_loop_accept(self.as_raw_ptr());
124        }
125    }
126
127    /// Check if inside the thread
128    pub fn in_thread(&self) {
129        unsafe {
130            pw_sys::pw_thread_loop_in_thread(self.as_raw_ptr());
131        }
132    }
133}
134
135pub struct ThreadLoopLockGuard<'a> {
136    thread_loop: &'a ThreadLoop,
137}
138
139impl<'a> ThreadLoopLockGuard<'a> {
140    fn new(thread_loop: &'a ThreadLoop) -> Self {
141        unsafe {
142            pw_sys::pw_thread_loop_lock(thread_loop.as_raw_ptr());
143        }
144        ThreadLoopLockGuard { thread_loop }
145    }
146
147    /// Unlock the loop
148    ///
149    /// Equivalent to dropping the lock guard.
150    pub fn unlock(self) {
151        drop(self);
152    }
153}
154
155impl<'a> Drop for ThreadLoopLockGuard<'a> {
156    fn drop(&mut self) {
157        unsafe {
158            pw_sys::pw_thread_loop_unlock(self.thread_loop.as_raw_ptr());
159        }
160    }
161}