1use crate::task::{RoleOverrides, Task};
6use fidl_fuchsia_scheduler::{
7 RoleManagerGetProfileForRoleRequest, RoleManagerMarker, RoleManagerSetRoleRequest,
8 RoleManagerSynchronousProxy, RoleName, RoleTarget, RoleType,
9};
10use fuchsia_component::client::connect_to_protocol_sync;
11use starnix_logging::{impossible_error, log_debug, log_warn, track_stub};
12use starnix_sync::{LockDepMutex, ProfileHandleCacheLock};
13use starnix_task_command::TaskCommand;
14use starnix_uapi::errors::Errno;
15use starnix_uapi::{
16 SCHED_BATCH, SCHED_DEADLINE, SCHED_FIFO, SCHED_IDLE, SCHED_NORMAL, SCHED_RESET_ON_FORK,
17 SCHED_RR, errno, error, sched_param,
18};
19use std::collections::HashMap;
20
21pub struct SchedulerManager {
22 role_manager: Option<RoleManagerSynchronousProxy>,
23 role_overrides: RoleOverrides,
24 profile_handle_cache: LockDepMutex<HashMap<String, zx::Profile>, ProfileHandleCacheLock>,
25}
26
27impl SchedulerManager {
28 pub fn new(role_overrides: RoleOverrides) -> SchedulerManager {
31 let profile_handle_cache = Default::default();
32 let role_manager = fuchsia_runtime::with_thread_self(|thread| {
33 let role_manager = connect_to_protocol_sync::<RoleManagerMarker>().unwrap();
34 if let Err(e) = Self::set_zx_thread_role(
35 &role_manager,
36 thread,
37 SchedulerState::default().role_name(),
38 &profile_handle_cache,
39 ) {
40 log_debug!("Setting thread role failed ({e:?}), will not set thread priority.");
41 None
42 } else {
43 log_debug!("Thread role set successfully, scheduler manager initialized.");
44 Some(role_manager)
45 }
46 });
47
48 SchedulerManager { role_manager, role_overrides, profile_handle_cache }
49 }
50
51 pub(crate) fn empty_for_tests() -> Self {
53 Self {
54 role_manager: None,
55 role_overrides: RoleOverrides::new().build().unwrap(),
56 profile_handle_cache: Default::default(),
57 }
58 }
59
60 #[cfg(test)]
62 pub(crate) fn new_for_tests(
63 role_manager: Option<RoleManagerSynchronousProxy>,
64 role_overrides: RoleOverrides,
65 ) -> Self {
66 Self { role_manager, role_overrides, profile_handle_cache: Default::default() }
67 }
68
69 pub(crate) fn role_name(&self, task: &Task) -> Result<&str, Errno> {
72 let scheduler_state = task.read().scheduler_state.clone();
73 self.role_name_inner(task, &scheduler_state)
74 }
75
76 fn role_name_inner(
77 &self,
78 task: &Task,
79 scheduler_state: &SchedulerState,
80 ) -> Result<&str, Errno> {
81 let thread_group = task.thread_group();
82 let thread_group_state = thread_group.read();
83 if thread_group_state.did_exec {
87 let process_name = if task.tid == task.pid {
88 task.command()
89 } else {
90 task.pid.get_task().map_err(|_| errno!(EINVAL))?.command()
91 };
92 let thread_name = task.command();
93
94 let cpuset_path = task.read().cpuset_path.clone();
95
96 return Ok(self.resolve_role_name(
97 &process_name,
98 &thread_name,
99 &cpuset_path,
100 scheduler_state,
101 ));
102 }
103 Ok(scheduler_state.role_name())
104 }
105
106 pub(crate) fn resolve_role_name(
107 &self,
108 process_name: &TaskCommand,
109 thread_name: &TaskCommand,
110 cgroup_path: &str,
111 scheduler_state: &SchedulerState,
112 ) -> &str {
113 if let Some(name) =
114 self.role_overrides.get_role_name(process_name, thread_name, cgroup_path)
115 {
116 return name;
117 }
118 scheduler_state.role_name()
119 }
120
121 pub(crate) fn set_thread_role(&self, task: &Task, role_name: &str) -> Result<(), Errno> {
123 let Some(role_manager) = self.role_manager.as_ref() else {
124 log_debug!("no role manager for setting role");
125 return Ok(());
126 };
127
128 let zircon_thread = {
129 let running_state = match task.running_state() {
130 Ok(live) => live,
131 Err(_) => {
132 log_debug!(
133 "thread role update requested for task without live state, skipping"
134 );
135 return Ok(());
136 }
137 };
138 let Some(zircon_thread) = running_state.thread.get() else {
139 log_debug!("thread role update requested for task without thread, skipping");
140 return Ok(());
141 };
142 zircon_thread.clone()
143 };
144 Self::set_zx_thread_role(
145 role_manager,
146 &zircon_thread.thread,
147 role_name,
148 &self.profile_handle_cache,
149 )?;
150
151 Ok(())
152 }
153
154 fn set_zx_thread_role(
155 role_manager: &RoleManagerSynchronousProxy,
156 thread: &zx::Thread,
157 role_name: &str,
158 cache: &LockDepMutex<HashMap<String, zx::Profile>, ProfileHandleCacheLock>,
159 ) -> Result<(), Errno> {
160 Self::set_zx_thread_role_with_profile_setter(
161 role_manager,
162 thread,
163 role_name,
164 cache,
165 |profile| thread.set_profile(profile, 0),
166 )
167 }
168
169 fn set_zx_thread_role_with_profile_setter(
170 role_manager: &RoleManagerSynchronousProxy,
171 thread: &zx::Thread,
172 role_name: &str,
173 cache: &LockDepMutex<HashMap<String, zx::Profile>, ProfileHandleCacheLock>,
174 set_profile: impl FnOnce(&zx::Profile) -> Result<(), zx::Status>,
175 ) -> Result<(), Errno> {
176 log_debug!(role_name; "setting thread role");
177
178 {
179 let params = cache.lock();
180 if let Some(profile) = params.get(role_name) {
181 return Self::set_zx_thread_profile(role_name, profile, set_profile);
182 }
183 }
184
185 let request = RoleManagerGetProfileForRoleRequest {
186 role: Some(RoleName { role: role_name.to_string() }),
187 target: Some(RoleType::Task),
188 ..Default::default()
189 };
190 match role_manager.get_profile_for_role(request, zx::MonotonicInstant::INFINITE) {
191 Ok(Ok(response)) => {
192 let Some(profile) = response.profile else {
193 log_warn!("GetRole returned success but no profile handle");
194 return error!(EINVAL);
195 };
196
197 let mut params = cache.lock();
198 let profile = params.entry(role_name.to_string()).or_insert(profile);
199 Self::set_zx_thread_profile(role_name, profile, set_profile)
200 }
201 Ok(Err(e)) => {
202 log_warn!(e:%; "GetRole returned error");
203 Self::set_zx_thread_role_legacy(role_manager, thread, role_name)
204 }
205 Err(e) => {
206 log_warn!(e:%; "GetRole FIDL call failed");
207 Self::set_zx_thread_role_legacy(role_manager, thread, role_name)
208 }
209 }
210 }
211
212 fn set_zx_thread_profile(
222 role_name: &str,
223 profile: &zx::Profile,
224 set_profile: impl FnOnce(&zx::Profile) -> Result<(), zx::Status>,
225 ) -> Result<(), Errno> {
226 match set_profile(profile) {
227 Ok(()) => Ok(()),
228 Err(zx::Status::BAD_STATE) => {
232 log_debug!(
233 role_name;
234 "thread role update requested for dead or dying thread, skipping"
235 );
236 Ok(())
237 }
238 Err(e) => {
239 log_warn!(role_name, e:%; "Failed to set thread profile from handle");
240 error!(EINVAL)
241 }
242 }
243 }
244
245 fn set_zx_thread_role_legacy(
246 role_manager: &RoleManagerSynchronousProxy,
247 thread: &zx::Thread,
248 role_name: &str,
249 ) -> Result<(), Errno> {
250 let thread = thread.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(impossible_error)?;
251 let request = RoleManagerSetRoleRequest {
252 target: Some(RoleTarget::Thread(thread)),
253 role: Some(RoleName { role: role_name.to_string() }),
254 ..Default::default()
255 };
256 let _ = role_manager.set_role(request, zx::MonotonicInstant::INFINITE).map_err(|err| {
257 log_warn!(err:%; "Unable to set thread role.");
258 errno!(EINVAL)
259 })?;
260 Ok(())
261 }
262}
263
264#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
269pub struct NormalPriority {
270 value: u8,
273}
274
275impl NormalPriority {
276 const MIN_VALUE: u8 = 1;
277 const DEFAULT_VALUE: u8 = 20;
278 const MAX_VALUE: u8 = 40;
279
280 pub(crate) fn from_setpriority_syscall(user_nice: i32) -> Self {
287 Self {
288 value: (Self::DEFAULT_VALUE as i32)
289 .saturating_sub(user_nice)
290 .clamp(Self::MIN_VALUE as i32, Self::MAX_VALUE as i32) as u8,
291 }
292 }
293
294 pub fn as_nice(&self) -> i8 {
297 (Self::DEFAULT_VALUE as i8) - (self.value as i8)
298 }
299
300 pub(crate) fn raw_priority(&self) -> u8 {
303 self.value
304 }
305
306 pub(crate) fn exceeds(&self, limit: u64) -> bool {
308 limit < (self.value as u64)
309 }
310}
311
312impl std::default::Default for NormalPriority {
313 fn default() -> Self {
314 Self { value: Self::DEFAULT_VALUE }
315 }
316}
317
318#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
322pub(crate) struct RealtimePriority {
323 value: u8,
327}
328
329impl RealtimePriority {
330 const NON_REAL_TIME_VALUE: u8 = 0;
331 const MIN_VALUE: u8 = 1;
332 const MAX_VALUE: u8 = 99;
333
334 const NON_REAL_TIME: RealtimePriority = RealtimePriority { value: Self::NON_REAL_TIME_VALUE };
335
336 pub(crate) fn exceeds(&self, limit: u64) -> bool {
337 limit < (self.value as u64)
338 }
339}
340
341#[derive(Clone, Copy, Debug, Eq, PartialEq)]
343pub(crate) enum SchedulingPolicy {
344 Normal,
345 Batch,
346 Idle,
347 Fifo,
348 RoundRobin,
349}
350
351impl SchedulingPolicy {
352 fn realtime_priority_min(&self) -> u8 {
353 match self {
354 Self::Normal | Self::Batch | Self::Idle => RealtimePriority::NON_REAL_TIME_VALUE,
355 Self::Fifo | Self::RoundRobin => RealtimePriority::MIN_VALUE,
356 }
357 }
358
359 fn realtime_priority_max(&self) -> u8 {
360 match self {
361 Self::Normal | Self::Batch | Self::Idle => RealtimePriority::NON_REAL_TIME_VALUE,
362 Self::Fifo | Self::RoundRobin => RealtimePriority::MAX_VALUE,
363 }
364 }
365
366 pub(crate) fn realtime_priority_from(&self, priority: i32) -> Result<RealtimePriority, Errno> {
367 let priority = u8::try_from(priority).map_err(|_| errno!(EINVAL))?;
368 if priority < self.realtime_priority_min() || priority > self.realtime_priority_max() {
369 return error!(EINVAL);
370 }
371 Ok(RealtimePriority { value: priority })
372 }
373}
374
375impl TryFrom<u32> for SchedulingPolicy {
376 type Error = Errno;
377
378 fn try_from(value: u32) -> Result<Self, Errno> {
379 Ok(match value {
380 SCHED_NORMAL => Self::Normal,
381 SCHED_BATCH => Self::Batch,
382 SCHED_IDLE => Self::Idle,
383 SCHED_FIFO => Self::Fifo,
384 SCHED_RR => Self::RoundRobin,
385 _ => {
386 return error!(EINVAL);
387 }
388 })
389 }
390}
391
392#[derive(Clone, Debug, Eq, PartialEq)]
393pub struct SchedulerState {
394 pub(crate) policy: SchedulingPolicy,
395 pub(crate) normal_priority: NormalPriority,
399 pub(crate) realtime_priority: RealtimePriority,
402 pub(crate) reset_on_fork: bool,
403 pub(crate) applied_role_name: Option<String>,
405}
406
407impl SchedulerState {
408 pub(crate) fn is_default(&self) -> bool {
409 let default = Self::default();
410 self.policy == default.policy
411 && self.normal_priority == default.normal_priority
412 && self.realtime_priority == default.realtime_priority
413 && self.reset_on_fork == default.reset_on_fork
414 && self.applied_role_name.as_deref().is_none_or(|role| role == default.role_name())
415 }
416
417 pub(crate) fn fork(&self) -> Self {
418 if self.reset_on_fork {
419 let (policy, normal_priority, realtime_priority) = if self.is_realtime() {
420 (
426 SchedulingPolicy::Normal,
427 NormalPriority::default(),
428 RealtimePriority::NON_REAL_TIME,
429 )
430 } else {
431 (
436 self.policy,
437 std::cmp::min(self.normal_priority, NormalPriority::default()),
438 RealtimePriority::NON_REAL_TIME,
439 )
440 };
441 Self {
442 policy,
443 normal_priority,
444 realtime_priority,
445 reset_on_fork: false,
447 applied_role_name: None,
448 }
449 } else {
450 Self { applied_role_name: None, ..*self }
451 }
452 }
453
454 pub(crate) fn policy_for_sched_getscheduler(&self) -> u32 {
460 let mut base = match self.policy {
461 SchedulingPolicy::Normal => SCHED_NORMAL,
462 SchedulingPolicy::Batch => SCHED_BATCH,
463 SchedulingPolicy::Idle => SCHED_IDLE,
464 SchedulingPolicy::Fifo => SCHED_FIFO,
465 SchedulingPolicy::RoundRobin => SCHED_RR,
466 };
467 if self.reset_on_fork {
468 base |= SCHED_RESET_ON_FORK;
469 }
470 base
471 }
472
473 pub(crate) fn get_sched_param(&self) -> sched_param {
478 sched_param {
479 sched_priority: (if self.is_realtime() {
480 self.realtime_priority.value
481 } else {
482 RealtimePriority::NON_REAL_TIME_VALUE
483 }) as i32,
484 }
485 }
486
487 pub fn normal_priority(&self) -> NormalPriority {
488 self.normal_priority
489 }
490
491 fn is_realtime(&self) -> bool {
492 match self.policy {
493 SchedulingPolicy::Normal | SchedulingPolicy::Batch | SchedulingPolicy::Idle => false,
494 SchedulingPolicy::Fifo | SchedulingPolicy::RoundRobin => true,
495 }
496 }
497
498 fn role_name(&self) -> &'static str {
511 match self.policy {
512 SchedulingPolicy::Idle => FAIR_PRIORITY_ROLE_NAMES[0],
515
516 SchedulingPolicy::Normal => {
519 FAIR_PRIORITY_ROLE_NAMES[(self.normal_priority.value as usize / 2) + 6]
520 }
521 SchedulingPolicy::Batch => {
522 track_stub!(TODO("https://fxbug.dev/308055542"), "SCHED_BATCH hinting");
523 FAIR_PRIORITY_ROLE_NAMES[(self.normal_priority.value as usize / 2) + 6]
524 }
525
526 SchedulingPolicy::Fifo | SchedulingPolicy::RoundRobin => REALTIME_ROLE_NAME,
532 }
533 }
534}
535
536impl std::default::Default for SchedulerState {
537 fn default() -> Self {
538 Self {
539 policy: SchedulingPolicy::Normal,
540 normal_priority: NormalPriority::default(),
541 realtime_priority: RealtimePriority::NON_REAL_TIME,
542 reset_on_fork: false,
543 applied_role_name: None,
544 }
545 }
546}
547
548pub(crate) fn min_priority_for_sched_policy(policy: u32) -> Result<u8, Errno> {
549 Ok(match policy {
550 SCHED_DEADLINE => RealtimePriority::NON_REAL_TIME_VALUE,
551 _ => SchedulingPolicy::try_from(policy)?.realtime_priority_min(),
552 })
553}
554
555pub(crate) fn max_priority_for_sched_policy(policy: u32) -> Result<u8, Errno> {
556 Ok(match policy {
557 SCHED_DEADLINE => RealtimePriority::NON_REAL_TIME_VALUE,
558 _ => SchedulingPolicy::try_from(policy)?.realtime_priority_max(),
559 })
560}
561
562const FAIR_PRIORITY_ROLE_NAMES: [&str; 32] = [
566 "fuchsia.starnix.fair.0",
567 "fuchsia.starnix.fair.1",
568 "fuchsia.starnix.fair.2",
569 "fuchsia.starnix.fair.3",
570 "fuchsia.starnix.fair.4",
571 "fuchsia.starnix.fair.5",
572 "fuchsia.starnix.fair.6",
573 "fuchsia.starnix.fair.7",
574 "fuchsia.starnix.fair.8",
575 "fuchsia.starnix.fair.9",
576 "fuchsia.starnix.fair.10",
577 "fuchsia.starnix.fair.11",
578 "fuchsia.starnix.fair.12",
579 "fuchsia.starnix.fair.13",
580 "fuchsia.starnix.fair.14",
581 "fuchsia.starnix.fair.15",
582 "fuchsia.starnix.fair.16",
583 "fuchsia.starnix.fair.17",
584 "fuchsia.starnix.fair.18",
585 "fuchsia.starnix.fair.19",
586 "fuchsia.starnix.fair.20",
587 "fuchsia.starnix.fair.21",
588 "fuchsia.starnix.fair.22",
589 "fuchsia.starnix.fair.23",
590 "fuchsia.starnix.fair.24",
591 "fuchsia.starnix.fair.25",
592 "fuchsia.starnix.fair.26",
593 "fuchsia.starnix.fair.27",
594 "fuchsia.starnix.fair.28",
595 "fuchsia.starnix.fair.29",
596 "fuchsia.starnix.fair.30",
597 "fuchsia.starnix.fair.31",
598];
599const REALTIME_ROLE_NAME: &str = "fuchsia.starnix.realtime";
600#[cfg(test)]
603mod tests {
604 use super::*;
605
606 #[fuchsia::test]
607 fn default_role_name() {
608 assert_eq!(SchedulerState::default().role_name(), "fuchsia.starnix.fair.16");
609 }
610
611 #[fuchsia::test]
612 fn scheduler_state_is_default() {
613 assert!(SchedulerState::default().is_default());
614 assert!(
615 SchedulerState {
616 applied_role_name: Some("fuchsia.starnix.fair.16".to_string()),
617 ..SchedulerState::default()
618 }
619 .is_default()
620 );
621 assert!(
622 !SchedulerState {
623 applied_role_name: Some("overridden_role".to_string()),
624 ..SchedulerState::default()
625 }
626 .is_default()
627 );
628 assert!(
629 !SchedulerState { policy: SchedulingPolicy::Idle, ..SchedulerState::default() }
630 .is_default()
631 );
632 assert!(
633 !SchedulerState {
634 normal_priority: NormalPriority { value: 10 },
635 ..SchedulerState::default()
636 }
637 .is_default()
638 );
639 assert!(!SchedulerState { reset_on_fork: true, ..SchedulerState::default() }.is_default());
640 }
641
642 #[fuchsia::test]
643 fn scheduler_state_fork_clears_applied_role_name() {
644 let state = SchedulerState {
645 policy: SchedulingPolicy::Fifo,
646 normal_priority: NormalPriority { value: 30 },
647 realtime_priority: RealtimePriority { value: 50 },
648 reset_on_fork: false,
649 applied_role_name: Some("fuchsia.starnix.realtime".to_string()),
650 };
651 assert_eq!(
652 state.fork(),
653 SchedulerState {
654 policy: SchedulingPolicy::Fifo,
655 normal_priority: NormalPriority { value: 30 },
656 realtime_priority: RealtimePriority { value: 50 },
657 reset_on_fork: false,
658 applied_role_name: None,
659 }
660 );
661
662 let reset_realtime_state = SchedulerState { reset_on_fork: true, ..state };
663 assert_eq!(
664 reset_realtime_state.fork(),
665 SchedulerState {
666 policy: SchedulingPolicy::Normal,
667 normal_priority: NormalPriority::default(),
668 realtime_priority: RealtimePriority::NON_REAL_TIME,
669 reset_on_fork: false,
670 applied_role_name: None,
671 }
672 );
673
674 let reset_normal_state = SchedulerState {
675 policy: SchedulingPolicy::Normal,
676 normal_priority: NormalPriority { value: 30 },
677 realtime_priority: RealtimePriority::NON_REAL_TIME,
678 reset_on_fork: true,
679 applied_role_name: Some("fuchsia.starnix.fair.21".to_string()),
680 };
681 assert_eq!(
682 reset_normal_state.fork(),
683 SchedulerState {
684 policy: SchedulingPolicy::Normal,
685 normal_priority: NormalPriority::default(),
686 realtime_priority: RealtimePriority::NON_REAL_TIME,
687 reset_on_fork: false,
688 applied_role_name: None,
689 }
690 );
691 }
692
693 #[fuchsia::test]
694 fn normal_with_non_default_nice_role_name() {
695 assert_eq!(
696 SchedulerState {
697 policy: SchedulingPolicy::Normal,
698 normal_priority: NormalPriority { value: 10 },
699 realtime_priority: RealtimePriority::NON_REAL_TIME,
700 reset_on_fork: false,
701 applied_role_name: None,
702 }
703 .role_name(),
704 "fuchsia.starnix.fair.11"
705 );
706 assert_eq!(
707 SchedulerState {
708 policy: SchedulingPolicy::Normal,
709 normal_priority: NormalPriority { value: 27 },
710 realtime_priority: RealtimePriority::NON_REAL_TIME,
711 reset_on_fork: false,
712 applied_role_name: None,
713 }
714 .role_name(),
715 "fuchsia.starnix.fair.19"
716 );
717 }
718
719 #[fuchsia::test]
720 fn fifo_role_name() {
721 assert_eq!(
722 SchedulerState {
723 policy: SchedulingPolicy::Fifo,
724 normal_priority: NormalPriority::default(),
725 realtime_priority: RealtimePriority { value: 1 },
726 reset_on_fork: false,
727 applied_role_name: None,
728 }
729 .role_name(),
730 "fuchsia.starnix.realtime",
731 );
732 assert_eq!(
733 SchedulerState {
734 policy: SchedulingPolicy::Fifo,
735 normal_priority: NormalPriority::default(),
736 realtime_priority: RealtimePriority { value: 2 },
737 reset_on_fork: false,
738 applied_role_name: None,
739 }
740 .role_name(),
741 "fuchsia.starnix.realtime",
742 );
743 assert_eq!(
744 SchedulerState {
745 policy: SchedulingPolicy::Fifo,
746 normal_priority: NormalPriority::default(),
747 realtime_priority: RealtimePriority { value: 99 },
748 reset_on_fork: false,
749 applied_role_name: None,
750 }
751 .role_name(),
752 "fuchsia.starnix.realtime",
753 );
754 }
755
756 #[fuchsia::test]
757 fn idle_role_name() {
758 assert_eq!(
759 SchedulerState {
760 policy: SchedulingPolicy::Idle,
761 normal_priority: NormalPriority { value: 1 },
762 realtime_priority: RealtimePriority::NON_REAL_TIME,
763 reset_on_fork: false,
764 applied_role_name: None,
765 }
766 .role_name(),
767 "fuchsia.starnix.fair.0"
768 );
769 assert_eq!(
770 SchedulerState {
771 policy: SchedulingPolicy::Idle,
772 normal_priority: NormalPriority::default(),
773 realtime_priority: RealtimePriority::NON_REAL_TIME,
774 reset_on_fork: false,
775 applied_role_name: None,
776 }
777 .role_name(),
778 "fuchsia.starnix.fair.0"
779 );
780 assert_eq!(
781 SchedulerState {
782 policy: SchedulingPolicy::Idle,
783 normal_priority: NormalPriority { value: 40 },
784 realtime_priority: RealtimePriority::NON_REAL_TIME,
785 reset_on_fork: false,
786 applied_role_name: None,
787 }
788 .role_name(),
789 "fuchsia.starnix.fair.0"
790 );
791 }
792
793 #[fuchsia::test]
794 async fn role_overrides_non_realtime() {
795 crate::testing::spawn_kernel_and_run_sync(|current_task| {
796 let mut builder = RoleOverrides::new();
797 builder.add("my_task", "my_task", None, "overridden_role");
798 let overrides = builder.build().unwrap();
799 let manager = SchedulerManager {
800 role_manager: None,
801 role_overrides: overrides,
802 profile_handle_cache: Default::default(),
803 };
804
805 current_task.thread_group().write().did_exec = true;
807 current_task.set_command_name(starnix_task_command::TaskCommand::new(b"my_task"));
808
809 let mut state = SchedulerState::default();
810 state.policy = SchedulingPolicy::Normal;
811
812 let role = manager.role_name_inner(current_task, &state).expect("role_name");
813 assert_eq!(role, "overridden_role");
814 })
815 .await;
816 }
817
818 fn invalid_profile_for_test() -> zx::Profile {
825 let (client, _server) = zx::Socket::create_stream();
826 zx::Profile::from(zx::NullableHandle::from(client))
827 }
828
829 #[fuchsia::test]
830 fn set_zx_thread_role_cached_profile_ok() {
831 let (role_manager, server_end) = fidl::endpoints::create_sync_proxy::<RoleManagerMarker>();
832 drop(server_end);
834
835 let cache = LockDepMutex::new(HashMap::from([(
836 "fuchsia.starnix.fair.16".to_string(),
837 invalid_profile_for_test(),
838 )]));
839
840 fuchsia_runtime::with_thread_self(|thread| {
841 let mut called = false;
842 let result = SchedulerManager::set_zx_thread_role_with_profile_setter(
843 &role_manager,
844 thread,
845 "fuchsia.starnix.fair.16",
846 &cache,
847 |_| {
848 called = true;
849 Ok(())
850 },
851 );
852 assert_eq!(result, Ok(()));
853 assert!(called);
854 });
855 }
856
857 #[fuchsia::test]
858 fn set_zx_thread_role_cached_profile_bad_state_is_ignored() {
859 let (role_manager, server_end) = fidl::endpoints::create_sync_proxy::<RoleManagerMarker>();
860 drop(server_end);
862
863 let cache = LockDepMutex::new(HashMap::from([(
864 "fuchsia.starnix.fair.16".to_string(),
865 invalid_profile_for_test(),
866 )]));
867
868 fuchsia_runtime::with_thread_self(|thread| {
869 let result = SchedulerManager::set_zx_thread_role_with_profile_setter(
870 &role_manager,
871 thread,
872 "fuchsia.starnix.fair.16",
873 &cache,
874 |_| Err(zx::Status::BAD_STATE),
875 );
876 assert_eq!(result, Ok(()));
877 });
878 }
879
880 #[fuchsia::test]
881 fn set_zx_thread_role_cached_invalid_profile_does_not_call_role_manager() {
882 let (role_manager, server_end) = fidl::endpoints::create_sync_proxy::<RoleManagerMarker>();
883 let server_channel = server_end.into_channel();
884 let watcher = std::thread::spawn(move || {
885 let signals = server_channel
886 .wait_one(
887 zx::Signals::CHANNEL_READABLE | zx::Signals::CHANNEL_PEER_CLOSED,
888 zx::MonotonicInstant::INFINITE,
889 )
890 .to_result()
891 .expect("wait_one");
892 signals.contains(zx::Signals::CHANNEL_READABLE)
893 });
894
895 let cache = LockDepMutex::new(HashMap::from([(
896 "fuchsia.starnix.fair.16".to_string(),
897 invalid_profile_for_test(),
898 )]));
899
900 fuchsia_runtime::with_thread_self(|thread| {
901 let result = SchedulerManager::set_zx_thread_role(
902 &role_manager,
903 thread,
904 "fuchsia.starnix.fair.16",
905 &cache,
906 );
907 assert_eq!(result, error!(EINVAL));
908 });
909
910 drop(role_manager);
911 let received_fidl_request = watcher.join().expect("watcher thread");
912 assert!(!received_fidl_request);
913 }
914
915 #[fuchsia::test]
916 fn set_zx_thread_profile_handles_outcomes() {
917 let profile = invalid_profile_for_test();
918 assert_eq!(
919 SchedulerManager::set_zx_thread_profile(
920 "fuchsia.starnix.fair.16",
921 &profile,
922 |_| Ok(())
923 ),
924 Ok(())
925 );
926 assert_eq!(
927 SchedulerManager::set_zx_thread_profile("fuchsia.starnix.fair.16", &profile, |_| Err(
928 zx::Status::BAD_STATE
929 )),
930 Ok(())
931 );
932 assert_eq!(
933 SchedulerManager::set_zx_thread_profile("fuchsia.starnix.fair.16", &profile, |_| Err(
934 zx::Status::WRONG_TYPE
935 )),
936 error!(EINVAL)
937 );
938 }
939}