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) -> rustix::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            rustix::time::Timespec {
101                tv_sec: abstime.tv_sec,
102                tv_nsec: abstime.tv_nsec,
103            }
104        }
105    }
106
107    /// Release the lock and wait up to abs seconds until some
108    /// thread calls [`signal()`](`Self::signal`). Use [`get_time()`](`Self::get_time`)
109    /// to get a suitable timespec
110    pub fn timed_wait_full(&self, abstime: rustix::time::Timespec) {
111        unsafe {
112            // Some 32-bit systems e.g. musl add padding fields for 64-bit time compatibility
113            let mut timespec = std::mem::MaybeUninit::<pw_sys::timespec>::zeroed().assume_init();
114
115            timespec.tv_sec = abstime.tv_sec;
116            timespec.tv_nsec = abstime.tv_nsec;
117
118            pw_sys::pw_thread_loop_timed_wait_full(
119                self.as_raw_ptr(),
120                &mut timespec as *mut pw_sys::timespec,
121            );
122        }
123    }
124
125    /// Signal all threads executing [`signal()`](`Self::signal`) with `wait_for_accept`
126    pub fn accept(&self) {
127        unsafe {
128            pw_sys::pw_thread_loop_accept(self.as_raw_ptr());
129        }
130    }
131
132    /// Check if inside the thread
133    pub fn in_thread(&self) {
134        unsafe {
135            pw_sys::pw_thread_loop_in_thread(self.as_raw_ptr());
136        }
137    }
138}
139
140pub struct ThreadLoopLockGuard<'a> {
141    thread_loop: &'a ThreadLoop,
142}
143
144impl<'a> ThreadLoopLockGuard<'a> {
145    fn new(thread_loop: &'a ThreadLoop) -> Self {
146        unsafe {
147            pw_sys::pw_thread_loop_lock(thread_loop.as_raw_ptr());
148        }
149        ThreadLoopLockGuard { thread_loop }
150    }
151
152    /// Unlock the loop
153    ///
154    /// Equivalent to dropping the lock guard.
155    pub fn unlock(self) {
156        drop(self);
157    }
158}
159
160impl<'a> Drop for ThreadLoopLockGuard<'a> {
161    fn drop(&mut self) {
162        unsafe {
163            pw_sys::pw_thread_loop_unlock(self.thread_loop.as_raw_ptr());
164        }
165    }
166}