Skip to content

Commit a416248

Browse files
committed
Extract platform-specific QoS code into module
1 parent bb02ae7 commit a416248

File tree

1 file changed

+94
-79
lines changed

1 file changed

+94
-79
lines changed

crates/stdx/src/thread.rs

Lines changed: 94 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -207,105 +207,120 @@ pub enum QoSClass {
207207
UserInteractive,
208208
}
209209

210-
#[cfg(target_vendor = "apple")]
211-
pub const IS_QOS_AVAILABLE: bool = true;
210+
pub const IS_QOS_AVAILABLE: bool = imp::IS_QOS_AVAILABLE;
212211

213-
#[cfg(not(target_vendor = "apple"))]
214-
pub const IS_QOS_AVAILABLE: bool = false;
212+
pub fn set_current_thread_qos_class(class: QoSClass) {
213+
imp::set_current_thread_qos_class(class)
214+
}
215+
216+
pub fn get_current_thread_qos_class() -> Option<QoSClass> {
217+
imp::get_current_thread_qos_class()
218+
}
215219

216220
// All Apple platforms use XNU as their kernel
217221
// and thus have the concept of QoS.
218222
#[cfg(target_vendor = "apple")]
219-
pub fn set_current_thread_qos_class(class: QoSClass) {
220-
let c = match class {
221-
QoSClass::UserInteractive => libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE,
222-
QoSClass::UserInitiated => libc::qos_class_t::QOS_CLASS_USER_INITIATED,
223-
QoSClass::Utility => libc::qos_class_t::QOS_CLASS_UTILITY,
224-
QoSClass::Background => libc::qos_class_t::QOS_CLASS_BACKGROUND,
225-
};
223+
mod imp {
224+
use super::QoSClass;
226225

227-
let code = unsafe { libc::pthread_set_qos_class_self_np(c, 0) };
226+
pub(super) const IS_QOS_AVAILABLE: bool = true;
228227

229-
if code == 0 {
230-
return;
231-
}
228+
pub(super) fn set_current_thread_qos_class(class: QoSClass) {
229+
let c = match class {
230+
QoSClass::UserInteractive => libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE,
231+
QoSClass::UserInitiated => libc::qos_class_t::QOS_CLASS_USER_INITIATED,
232+
QoSClass::Utility => libc::qos_class_t::QOS_CLASS_UTILITY,
233+
QoSClass::Background => libc::qos_class_t::QOS_CLASS_BACKGROUND,
234+
};
232235

233-
let errno = unsafe { *libc::__error() };
236+
let code = unsafe { libc::pthread_set_qos_class_self_np(c, 0) };
234237

235-
match errno {
236-
libc::EPERM => {
237-
// This thread has been excluded from the QoS system
238-
// due to a previous call to a function such as `pthread_setschedparam`
239-
// which is incompatible with QoS.
240-
//
241-
// Panic instead of returning an error
242-
// to maintain the invariant that we only use QoS APIs.
243-
panic!("tried to set QoS of thread which has opted out of QoS (os error {errno})")
238+
if code == 0 {
239+
return;
240+
}
241+
242+
let errno = unsafe { *libc::__error() };
243+
244+
match errno {
245+
libc::EPERM => {
246+
// This thread has been excluded from the QoS system
247+
// due to a previous call to a function such as `pthread_setschedparam`
248+
// which is incompatible with QoS.
249+
//
250+
// Panic instead of returning an error
251+
// to maintain the invariant that we only use QoS APIs.
252+
panic!("tried to set QoS of thread which has opted out of QoS (os error {errno})")
253+
}
254+
255+
libc::EINVAL => {
256+
// This is returned if we pass something other than a qos_class_t
257+
// to `pthread_set_qos_class_self_np`.
258+
//
259+
// This is impossible, so again panic.
260+
unreachable!(
261+
"invalid qos_class_t value was passed to pthread_set_qos_class_self_np"
262+
)
263+
}
264+
265+
_ => {
266+
// `pthread_set_qos_class_self_np`’s documentation
267+
// does not mention any other errors.
268+
unreachable!("`pthread_set_qos_class_self_np` returned unexpected error {errno}")
269+
}
244270
}
271+
}
245272

246-
libc::EINVAL => {
247-
// This is returned if we pass something other than a qos_class_t
248-
// to `pthread_set_qos_class_self_np`.
273+
pub(super) fn get_current_thread_qos_class() -> Option<QoSClass> {
274+
let current_thread = unsafe { libc::pthread_self() };
275+
let mut qos_class_raw = libc::qos_class_t::QOS_CLASS_UNSPECIFIED;
276+
let code = unsafe {
277+
libc::pthread_get_qos_class_np(current_thread, &mut qos_class_raw, std::ptr::null_mut())
278+
};
279+
280+
if code != 0 {
281+
// `pthread_get_qos_class_np`’s documentation states that
282+
// an error value is placed into errno if the return code is not zero.
283+
// However, it never states what errors are possible.
284+
// Inspecting the source[0] shows that, as of this writing, it always returns zero.
285+
//
286+
// Whatever errors the function could report in future are likely to be
287+
// ones which we cannot handle anyway
249288
//
250-
// This is impossible, so again panic.
251-
unreachable!("invalid qos_class_t value was passed to pthread_set_qos_class_self_np")
289+
// 0: https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/src/qos.c#L171-L177
290+
let errno = unsafe { *libc::__error() };
291+
unreachable!("`pthread_get_qos_class_np` failed unexpectedly (os error {errno})");
252292
}
253293

254-
_ => {
255-
// `pthread_set_qos_class_self_np`’s documentation
256-
// does not mention any other errors.
257-
unreachable!("`pthread_set_qos_class_self_np` returned unexpected error {errno}")
294+
match qos_class_raw {
295+
libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE => Some(QoSClass::UserInteractive),
296+
libc::qos_class_t::QOS_CLASS_USER_INITIATED => Some(QoSClass::UserInitiated),
297+
libc::qos_class_t::QOS_CLASS_DEFAULT => None, // QoS has never been set
298+
libc::qos_class_t::QOS_CLASS_UTILITY => Some(QoSClass::Utility),
299+
libc::qos_class_t::QOS_CLASS_BACKGROUND => Some(QoSClass::Background),
300+
301+
libc::qos_class_t::QOS_CLASS_UNSPECIFIED => {
302+
// Using manual scheduling APIs causes threads to “opt out” of QoS.
303+
// At this point they become incompatible with QoS,
304+
// and as such have the “unspecified” QoS class.
305+
//
306+
// Panic instead of returning an error
307+
// to maintain the invariant that we only use QoS APIs.
308+
panic!("tried to get QoS of thread which has opted out of QoS")
309+
}
258310
}
259311
}
260312
}
261313

314+
// FIXME: Windows has QoS APIs, we should use them!
262315
#[cfg(not(target_vendor = "apple"))]
263-
pub fn set_current_thread_qos_class(class: QoSClass) {
264-
// FIXME: Windows has QoS APIs, we should use them!
265-
}
316+
mod imp {
317+
use super::QoSClass;
266318

267-
#[cfg(target_vendor = "apple")]
268-
pub fn get_current_thread_qos_class() -> Option<QoSClass> {
269-
let current_thread = unsafe { libc::pthread_self() };
270-
let mut qos_class_raw = libc::qos_class_t::QOS_CLASS_UNSPECIFIED;
271-
let code = unsafe {
272-
libc::pthread_get_qos_class_np(current_thread, &mut qos_class_raw, std::ptr::null_mut())
273-
};
274-
275-
if code != 0 {
276-
// `pthread_get_qos_class_np`’s documentation states that
277-
// an error value is placed into errno if the return code is not zero.
278-
// However, it never states what errors are possible.
279-
// Inspecting the source[0] shows that, as of this writing, it always returns zero.
280-
//
281-
// Whatever errors the function could report in future are likely to be
282-
// ones which we cannot handle anyway
283-
//
284-
// 0: https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/src/qos.c#L171-L177
285-
let errno = unsafe { *libc::__error() };
286-
unreachable!("`pthread_get_qos_class_np` failed unexpectedly (os error {errno})");
287-
}
319+
pub(super) const IS_QOS_AVAILABLE: bool = false;
288320

289-
match qos_class_raw {
290-
libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE => Some(QoSClass::UserInteractive),
291-
libc::qos_class_t::QOS_CLASS_USER_INITIATED => Some(QoSClass::UserInitiated),
292-
libc::qos_class_t::QOS_CLASS_DEFAULT => None, // QoS has never been set
293-
libc::qos_class_t::QOS_CLASS_UTILITY => Some(QoSClass::Utility),
294-
libc::qos_class_t::QOS_CLASS_BACKGROUND => Some(QoSClass::Background),
295-
296-
libc::qos_class_t::QOS_CLASS_UNSPECIFIED => {
297-
// Using manual scheduling APIs causes threads to “opt out” of QoS.
298-
// At this point they become incompatible with QoS,
299-
// and as such have the “unspecified” QoS class.
300-
//
301-
// Panic instead of returning an error
302-
// to maintain the invariant that we only use QoS APIs.
303-
panic!("tried to get QoS of thread which has opted out of QoS")
304-
}
305-
}
306-
}
321+
pub(super) fn set_current_thread_qos_class(_: QoSClass) {}
307322

308-
#[cfg(not(target_vendor = "apple"))]
309-
pub fn get_current_thread_qos_class() -> Option<QoSClass> {
310-
None
323+
pub(super) fn get_current_thread_qos_class() -> Option<QoSClass> {
324+
None
325+
}
311326
}

0 commit comments

Comments
 (0)