1 use crate::{Errno, Result};
2
3 #[cfg(any(target_os = "android", target_os = "linux"))]
4 pub use self::sched_linux_like::*;
5
6 #[cfg(any(target_os = "android", target_os = "linux"))]
7 mod sched_linux_like {
8 use crate::errno::Errno;
9 use libc::{self, c_int, c_void};
10 use std::mem;
11 use std::option::Option;
12 use std::os::unix::io::RawFd;
13 use crate::unistd::Pid;
14 use crate::{Error, Result};
15
16 // For some functions taking with a parameter of type CloneFlags,
17 // only a subset of these flags have an effect.
18 libc_bitflags! {
19 pub struct CloneFlags: c_int {
20 CLONE_VM;
21 CLONE_FS;
22 CLONE_FILES;
23 CLONE_SIGHAND;
24 CLONE_PTRACE;
25 CLONE_VFORK;
26 CLONE_PARENT;
27 CLONE_THREAD;
28 CLONE_NEWNS;
29 CLONE_SYSVSEM;
30 CLONE_SETTLS;
31 CLONE_PARENT_SETTID;
32 CLONE_CHILD_CLEARTID;
33 CLONE_DETACHED;
34 CLONE_UNTRACED;
35 CLONE_CHILD_SETTID;
36 CLONE_NEWCGROUP;
37 CLONE_NEWUTS;
38 CLONE_NEWIPC;
39 CLONE_NEWUSER;
40 CLONE_NEWPID;
41 CLONE_NEWNET;
42 CLONE_IO;
43 }
44 }
45
46 pub type CloneCb<'a> = Box<dyn FnMut() -> isize + 'a>;
47
48 /// CpuSet represent a bit-mask of CPUs.
49 /// CpuSets are used by sched_setaffinity and
50 /// sched_getaffinity for example.
51 ///
52 /// This is a wrapper around `libc::cpu_set_t`.
53 #[repr(C)]
54 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
55 pub struct CpuSet {
56 cpu_set: libc::cpu_set_t,
57 }
58
59 impl CpuSet {
60 /// Create a new and empty CpuSet.
new() -> CpuSet61 pub fn new() -> CpuSet {
62 CpuSet {
63 cpu_set: unsafe { mem::zeroed() },
64 }
65 }
66
67 /// Test to see if a CPU is in the CpuSet.
68 /// `field` is the CPU id to test
is_set(&self, field: usize) -> Result<bool>69 pub fn is_set(&self, field: usize) -> Result<bool> {
70 if field >= CpuSet::count() {
71 Err(Error::Sys(Errno::EINVAL))
72 } else {
73 Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) })
74 }
75 }
76
77 /// Add a CPU to CpuSet.
78 /// `field` is the CPU id to add
set(&mut self, field: usize) -> Result<()>79 pub fn set(&mut self, field: usize) -> Result<()> {
80 if field >= CpuSet::count() {
81 Err(Error::Sys(Errno::EINVAL))
82 } else {
83 unsafe { libc::CPU_SET(field, &mut self.cpu_set); }
84 Ok(())
85 }
86 }
87
88 /// Remove a CPU from CpuSet.
89 /// `field` is the CPU id to remove
unset(&mut self, field: usize) -> Result<()>90 pub fn unset(&mut self, field: usize) -> Result<()> {
91 if field >= CpuSet::count() {
92 Err(Error::Sys(Errno::EINVAL))
93 } else {
94 unsafe { libc::CPU_CLR(field, &mut self.cpu_set);}
95 Ok(())
96 }
97 }
98
99 /// Return the maximum number of CPU in CpuSet
count() -> usize100 pub fn count() -> usize {
101 8 * mem::size_of::<libc::cpu_set_t>()
102 }
103 }
104
105 impl Default for CpuSet {
default() -> Self106 fn default() -> Self {
107 Self::new()
108 }
109 }
110
111 /// `sched_setaffinity` set a thread's CPU affinity mask
112 /// ([`sched_setaffinity(2)`](http://man7.org/linux/man-pages/man2/sched_setaffinity.2.html))
113 ///
114 /// `pid` is the thread ID to update.
115 /// If pid is zero, then the calling thread is updated.
116 ///
117 /// The `cpuset` argument specifies the set of CPUs on which the thread
118 /// will be eligible to run.
119 ///
120 /// # Example
121 ///
122 /// Binding the current thread to CPU 0 can be done as follows:
123 ///
124 /// ```rust,no_run
125 /// use nix::sched::{CpuSet, sched_setaffinity};
126 /// use nix::unistd::Pid;
127 ///
128 /// let mut cpu_set = CpuSet::new();
129 /// cpu_set.set(0);
130 /// sched_setaffinity(Pid::from_raw(0), &cpu_set);
131 /// ```
sched_setaffinity(pid: Pid, cpuset: &CpuSet) -> Result<()>132 pub fn sched_setaffinity(pid: Pid, cpuset: &CpuSet) -> Result<()> {
133 let res = unsafe {
134 libc::sched_setaffinity(
135 pid.into(),
136 mem::size_of::<CpuSet>() as libc::size_t,
137 &cpuset.cpu_set,
138 )
139 };
140
141 Errno::result(res).map(drop)
142 }
143
144 /// `sched_getaffinity` get a thread's CPU affinity mask
145 /// ([`sched_getaffinity(2)`](http://man7.org/linux/man-pages/man2/sched_getaffinity.2.html))
146 ///
147 /// `pid` is the thread ID to check.
148 /// If pid is zero, then the calling thread is checked.
149 ///
150 /// Returned `cpuset` is the set of CPUs on which the thread
151 /// is eligible to run.
152 ///
153 /// # Example
154 ///
155 /// Checking if the current thread can run on CPU 0 can be done as follows:
156 ///
157 /// ```rust,no_run
158 /// use nix::sched::sched_getaffinity;
159 /// use nix::unistd::Pid;
160 ///
161 /// let cpu_set = sched_getaffinity(Pid::from_raw(0)).unwrap();
162 /// if cpu_set.is_set(0).unwrap() {
163 /// println!("Current thread can run on CPU 0");
164 /// }
165 /// ```
sched_getaffinity(pid: Pid) -> Result<CpuSet>166 pub fn sched_getaffinity(pid: Pid) -> Result<CpuSet> {
167 let mut cpuset = CpuSet::new();
168 let res = unsafe {
169 libc::sched_getaffinity(
170 pid.into(),
171 mem::size_of::<CpuSet>() as libc::size_t,
172 &mut cpuset.cpu_set,
173 )
174 };
175
176 Errno::result(res).and(Ok(cpuset))
177 }
178
clone( mut cb: CloneCb, stack: &mut [u8], flags: CloneFlags, signal: Option<c_int>, ) -> Result<Pid>179 pub fn clone(
180 mut cb: CloneCb,
181 stack: &mut [u8],
182 flags: CloneFlags,
183 signal: Option<c_int>,
184 ) -> Result<Pid> {
185 extern "C" fn callback(data: *mut CloneCb) -> c_int {
186 let cb: &mut CloneCb = unsafe { &mut *data };
187 (*cb)() as c_int
188 }
189
190 let res = unsafe {
191 let combined = flags.bits() | signal.unwrap_or(0);
192 let ptr = stack.as_mut_ptr().add(stack.len());
193 let ptr_aligned = ptr.sub(ptr as usize % 16);
194 libc::clone(
195 mem::transmute(
196 callback as extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32,
197 ),
198 ptr_aligned as *mut c_void,
199 combined,
200 &mut cb as *mut _ as *mut c_void,
201 )
202 };
203
204 Errno::result(res).map(Pid::from_raw)
205 }
206
unshare(flags: CloneFlags) -> Result<()>207 pub fn unshare(flags: CloneFlags) -> Result<()> {
208 let res = unsafe { libc::unshare(flags.bits()) };
209
210 Errno::result(res).map(drop)
211 }
212
setns(fd: RawFd, nstype: CloneFlags) -> Result<()>213 pub fn setns(fd: RawFd, nstype: CloneFlags) -> Result<()> {
214 let res = unsafe { libc::setns(fd, nstype.bits()) };
215
216 Errno::result(res).map(drop)
217 }
218 }
219
220 /// Explicitly yield the processor to other threads.
221 ///
222 /// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_yield.html)
sched_yield() -> Result<()>223 pub fn sched_yield() -> Result<()> {
224 let res = unsafe { libc::sched_yield() };
225
226 Errno::result(res).map(drop)
227 }
228