/rust/registry/src/index.crates.io-6f17d22bba15001f/nix-0.26.2/src/sched.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! Execution scheduling |
2 | | //! |
3 | | //! See Also |
4 | | //! [sched.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html) |
5 | | use crate::{Errno, Result}; |
6 | | |
7 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
8 | | pub use self::sched_linux_like::*; |
9 | | |
10 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
11 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
12 | | mod sched_linux_like { |
13 | | use crate::errno::Errno; |
14 | | use crate::unistd::Pid; |
15 | | use crate::Result; |
16 | | use libc::{self, c_int, c_void}; |
17 | | use std::mem; |
18 | | use std::option::Option; |
19 | | use std::os::unix::io::RawFd; |
20 | | |
21 | | // For some functions taking with a parameter of type CloneFlags, |
22 | | // only a subset of these flags have an effect. |
23 | | libc_bitflags! { |
24 | | /// Options for use with [`clone`] |
25 | | pub struct CloneFlags: c_int { |
26 | | /// The calling process and the child process run in the same |
27 | | /// memory space. |
28 | | CLONE_VM; |
29 | | /// The caller and the child process share the same filesystem |
30 | | /// information. |
31 | | CLONE_FS; |
32 | | /// The calling process and the child process share the same file |
33 | | /// descriptor table. |
34 | | CLONE_FILES; |
35 | | /// The calling process and the child process share the same table |
36 | | /// of signal handlers. |
37 | | CLONE_SIGHAND; |
38 | | /// If the calling process is being traced, then trace the child |
39 | | /// also. |
40 | | CLONE_PTRACE; |
41 | | /// The execution of the calling process is suspended until the |
42 | | /// child releases its virtual memory resources via a call to |
43 | | /// execve(2) or _exit(2) (as with vfork(2)). |
44 | | CLONE_VFORK; |
45 | | /// The parent of the new child (as returned by getppid(2)) |
46 | | /// will be the same as that of the calling process. |
47 | | CLONE_PARENT; |
48 | | /// The child is placed in the same thread group as the calling |
49 | | /// process. |
50 | | CLONE_THREAD; |
51 | | /// The cloned child is started in a new mount namespace. |
52 | | CLONE_NEWNS; |
53 | | /// The child and the calling process share a single list of System |
54 | | /// V semaphore adjustment values |
55 | | CLONE_SYSVSEM; |
56 | | // Not supported by Nix due to lack of varargs support in Rust FFI |
57 | | // CLONE_SETTLS; |
58 | | // Not supported by Nix due to lack of varargs support in Rust FFI |
59 | | // CLONE_PARENT_SETTID; |
60 | | // Not supported by Nix due to lack of varargs support in Rust FFI |
61 | | // CLONE_CHILD_CLEARTID; |
62 | | /// Unused since Linux 2.6.2 |
63 | | #[deprecated(since = "0.23.0", note = "Deprecated by Linux 2.6.2")] |
64 | | CLONE_DETACHED; |
65 | | /// A tracing process cannot force `CLONE_PTRACE` on this child |
66 | | /// process. |
67 | | CLONE_UNTRACED; |
68 | | // Not supported by Nix due to lack of varargs support in Rust FFI |
69 | | // CLONE_CHILD_SETTID; |
70 | | /// Create the process in a new cgroup namespace. |
71 | | CLONE_NEWCGROUP; |
72 | | /// Create the process in a new UTS namespace. |
73 | | CLONE_NEWUTS; |
74 | | /// Create the process in a new IPC namespace. |
75 | | CLONE_NEWIPC; |
76 | | /// Create the process in a new user namespace. |
77 | | CLONE_NEWUSER; |
78 | | /// Create the process in a new PID namespace. |
79 | | CLONE_NEWPID; |
80 | | /// Create the process in a new network namespace. |
81 | | CLONE_NEWNET; |
82 | | /// The new process shares an I/O context with the calling process. |
83 | | CLONE_IO; |
84 | | } |
85 | | } |
86 | | |
87 | | /// Type for the function executed by [`clone`]. |
88 | | pub type CloneCb<'a> = Box<dyn FnMut() -> isize + 'a>; |
89 | | |
90 | | /// `clone` create a child process |
91 | | /// ([`clone(2)`](https://man7.org/linux/man-pages/man2/clone.2.html)) |
92 | | /// |
93 | | /// `stack` is a reference to an array which will hold the stack of the new |
94 | | /// process. Unlike when calling `clone(2)` from C, the provided stack |
95 | | /// address need not be the highest address of the region. Nix will take |
96 | | /// care of that requirement. The user only needs to provide a reference to |
97 | | /// a normally allocated buffer. |
98 | 0 | pub fn clone( |
99 | 0 | mut cb: CloneCb, |
100 | 0 | stack: &mut [u8], |
101 | 0 | flags: CloneFlags, |
102 | 0 | signal: Option<c_int>, |
103 | 0 | ) -> Result<Pid> { |
104 | 0 | extern "C" fn callback(data: *mut CloneCb) -> c_int { |
105 | 0 | let cb: &mut CloneCb = unsafe { &mut *data }; |
106 | 0 | (*cb)() as c_int |
107 | 0 | } |
108 | 0 |
|
109 | 0 | let res = unsafe { |
110 | 0 | let combined = flags.bits() | signal.unwrap_or(0); |
111 | 0 | let ptr = stack.as_mut_ptr().add(stack.len()); |
112 | 0 | let ptr_aligned = ptr.sub(ptr as usize % 16); |
113 | 0 | libc::clone( |
114 | 0 | mem::transmute( |
115 | 0 | callback |
116 | 0 | as extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32, |
117 | 0 | ), |
118 | 0 | ptr_aligned as *mut c_void, |
119 | 0 | combined, |
120 | 0 | &mut cb as *mut _ as *mut c_void, |
121 | 0 | ) |
122 | 0 | }; |
123 | 0 |
|
124 | 0 | Errno::result(res).map(Pid::from_raw) |
125 | 0 | } |
126 | | |
127 | | /// disassociate parts of the process execution context |
128 | | /// |
129 | | /// See also [unshare(2)](https://man7.org/linux/man-pages/man2/unshare.2.html) |
130 | 0 | pub fn unshare(flags: CloneFlags) -> Result<()> { |
131 | 0 | let res = unsafe { libc::unshare(flags.bits()) }; |
132 | 0 |
|
133 | 0 | Errno::result(res).map(drop) |
134 | 0 | } |
135 | | |
136 | | /// reassociate thread with a namespace |
137 | | /// |
138 | | /// See also [setns(2)](https://man7.org/linux/man-pages/man2/setns.2.html) |
139 | 0 | pub fn setns(fd: RawFd, nstype: CloneFlags) -> Result<()> { |
140 | 0 | let res = unsafe { libc::setns(fd, nstype.bits()) }; |
141 | 0 |
|
142 | 0 | Errno::result(res).map(drop) |
143 | 0 | } |
144 | | } |
145 | | |
146 | | #[cfg(any( |
147 | | target_os = "android", |
148 | | target_os = "dragonfly", |
149 | | target_os = "freebsd", |
150 | | target_os = "linux" |
151 | | ))] |
152 | | pub use self::sched_affinity::*; |
153 | | |
154 | | #[cfg(any( |
155 | | target_os = "android", |
156 | | target_os = "dragonfly", |
157 | | target_os = "freebsd", |
158 | | target_os = "linux" |
159 | | ))] |
160 | | mod sched_affinity { |
161 | | use crate::errno::Errno; |
162 | | use crate::unistd::Pid; |
163 | | use crate::Result; |
164 | | use std::mem; |
165 | | |
166 | | /// CpuSet represent a bit-mask of CPUs. |
167 | | /// CpuSets are used by sched_setaffinity and |
168 | | /// sched_getaffinity for example. |
169 | | /// |
170 | | /// This is a wrapper around `libc::cpu_set_t`. |
171 | | #[repr(transparent)] |
172 | 0 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
173 | | pub struct CpuSet { |
174 | | #[cfg(not(target_os = "freebsd"))] |
175 | | cpu_set: libc::cpu_set_t, |
176 | | #[cfg(target_os = "freebsd")] |
177 | | cpu_set: libc::cpuset_t, |
178 | | } |
179 | | |
180 | | impl CpuSet { |
181 | | /// Create a new and empty CpuSet. |
182 | 0 | pub fn new() -> CpuSet { |
183 | 0 | CpuSet { |
184 | 0 | cpu_set: unsafe { mem::zeroed() }, |
185 | 0 | } |
186 | 0 | } |
187 | | |
188 | | /// Test to see if a CPU is in the CpuSet. |
189 | | /// `field` is the CPU id to test |
190 | 0 | pub fn is_set(&self, field: usize) -> Result<bool> { |
191 | 0 | if field >= CpuSet::count() { |
192 | 0 | Err(Errno::EINVAL) |
193 | | } else { |
194 | 0 | Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) }) |
195 | | } |
196 | 0 | } |
197 | | |
198 | | /// Add a CPU to CpuSet. |
199 | | /// `field` is the CPU id to add |
200 | 0 | pub fn set(&mut self, field: usize) -> Result<()> { |
201 | 0 | if field >= CpuSet::count() { |
202 | 0 | Err(Errno::EINVAL) |
203 | | } else { |
204 | 0 | unsafe { |
205 | 0 | libc::CPU_SET(field, &mut self.cpu_set); |
206 | 0 | } |
207 | 0 | Ok(()) |
208 | | } |
209 | 0 | } |
210 | | |
211 | | /// Remove a CPU from CpuSet. |
212 | | /// `field` is the CPU id to remove |
213 | 0 | pub fn unset(&mut self, field: usize) -> Result<()> { |
214 | 0 | if field >= CpuSet::count() { |
215 | 0 | Err(Errno::EINVAL) |
216 | | } else { |
217 | 0 | unsafe { |
218 | 0 | libc::CPU_CLR(field, &mut self.cpu_set); |
219 | 0 | } |
220 | 0 | Ok(()) |
221 | | } |
222 | 0 | } |
223 | | |
224 | | /// Return the maximum number of CPU in CpuSet |
225 | 0 | pub const fn count() -> usize { |
226 | 0 | #[cfg(not(target_os = "freebsd"))] |
227 | 0 | let bytes = mem::size_of::<libc::cpu_set_t>(); |
228 | 0 | #[cfg(target_os = "freebsd")] |
229 | 0 | let bytes = mem::size_of::<libc::cpuset_t>(); |
230 | 0 |
|
231 | 0 | 8 * bytes |
232 | 0 | } |
233 | | } |
234 | | |
235 | | impl Default for CpuSet { |
236 | 0 | fn default() -> Self { |
237 | 0 | Self::new() |
238 | 0 | } |
239 | | } |
240 | | |
241 | | /// `sched_setaffinity` set a thread's CPU affinity mask |
242 | | /// ([`sched_setaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_setaffinity.2.html)) |
243 | | /// |
244 | | /// `pid` is the thread ID to update. |
245 | | /// If pid is zero, then the calling thread is updated. |
246 | | /// |
247 | | /// The `cpuset` argument specifies the set of CPUs on which the thread |
248 | | /// will be eligible to run. |
249 | | /// |
250 | | /// # Example |
251 | | /// |
252 | | /// Binding the current thread to CPU 0 can be done as follows: |
253 | | /// |
254 | | /// ```rust,no_run |
255 | | /// use nix::sched::{CpuSet, sched_setaffinity}; |
256 | | /// use nix::unistd::Pid; |
257 | | /// |
258 | | /// let mut cpu_set = CpuSet::new(); |
259 | | /// cpu_set.set(0).unwrap(); |
260 | | /// sched_setaffinity(Pid::from_raw(0), &cpu_set).unwrap(); |
261 | | /// ``` |
262 | 0 | pub fn sched_setaffinity(pid: Pid, cpuset: &CpuSet) -> Result<()> { |
263 | 0 | let res = unsafe { |
264 | 0 | libc::sched_setaffinity( |
265 | 0 | pid.into(), |
266 | 0 | mem::size_of::<CpuSet>() as libc::size_t, |
267 | 0 | &cpuset.cpu_set, |
268 | 0 | ) |
269 | 0 | }; |
270 | 0 |
|
271 | 0 | Errno::result(res).map(drop) |
272 | 0 | } |
273 | | |
274 | | /// `sched_getaffinity` get a thread's CPU affinity mask |
275 | | /// ([`sched_getaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_getaffinity.2.html)) |
276 | | /// |
277 | | /// `pid` is the thread ID to check. |
278 | | /// If pid is zero, then the calling thread is checked. |
279 | | /// |
280 | | /// Returned `cpuset` is the set of CPUs on which the thread |
281 | | /// is eligible to run. |
282 | | /// |
283 | | /// # Example |
284 | | /// |
285 | | /// Checking if the current thread can run on CPU 0 can be done as follows: |
286 | | /// |
287 | | /// ```rust,no_run |
288 | | /// use nix::sched::sched_getaffinity; |
289 | | /// use nix::unistd::Pid; |
290 | | /// |
291 | | /// let cpu_set = sched_getaffinity(Pid::from_raw(0)).unwrap(); |
292 | | /// if cpu_set.is_set(0).unwrap() { |
293 | | /// println!("Current thread can run on CPU 0"); |
294 | | /// } |
295 | | /// ``` |
296 | 0 | pub fn sched_getaffinity(pid: Pid) -> Result<CpuSet> { |
297 | 0 | let mut cpuset = CpuSet::new(); |
298 | 0 | let res = unsafe { |
299 | 0 | libc::sched_getaffinity( |
300 | 0 | pid.into(), |
301 | 0 | mem::size_of::<CpuSet>() as libc::size_t, |
302 | 0 | &mut cpuset.cpu_set, |
303 | 0 | ) |
304 | 0 | }; |
305 | 0 |
|
306 | 0 | Errno::result(res).and(Ok(cpuset)) |
307 | 0 | } |
308 | | |
309 | | /// Determines the CPU on which the calling thread is running. |
310 | 0 | pub fn sched_getcpu() -> Result<usize> { |
311 | 0 | let res = unsafe { libc::sched_getcpu() }; |
312 | 0 |
|
313 | 0 | Errno::result(res).map(|int| int as usize) |
314 | 0 | } |
315 | | } |
316 | | |
317 | | /// Explicitly yield the processor to other threads. |
318 | | /// |
319 | | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_yield.html) |
320 | 0 | pub fn sched_yield() -> Result<()> { |
321 | 0 | let res = unsafe { libc::sched_yield() }; |
322 | 0 |
|
323 | 0 | Errno::result(res).map(drop) |
324 | 0 | } |