use std::{fmt, path::PathBuf};
use crate::{
parse::{parse, parse_01_bool},
v1::{self, cgroup::CgroupHelper, Cgroup, CgroupPath},
Error, ErrorKind, Result,
};
#[derive(Debug)]
pub struct Subsystem {
path: CgroupPath,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Resources {
pub state: Option<State>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum State {
Thawed,
Freezing,
Frozen,
}
impl_cgroup! {
Subsystem, Freezer,
fn apply(&mut self, resources: &v1::Resources) -> Result<()> {
match resources.freezer.state {
Some(State::Frozen) => self.freeze(),
Some(State::Thawed) => self.thaw(),
Some(State::Freezing) => Err(Error::new(ErrorKind::InvalidArgument)),
None => Ok(())
}
}
}
macro_rules! _gen_setter {
($desc: literal, $setter: ident, $val: expr) => {
with_doc! { concat!(
$desc, " tasks in this cgroup by writing to `freezer.state` file.\n\n",
gen_doc!(see),
gen_doc!(err_write; "freezer.state"),
gen_doc!(eg_write; freezer, $setter)),
pub fn $setter(&mut self) -> Result<()> {
self.write_file("freezer.state", $val)
}
}
};
}
impl Subsystem {
gen_getter!(
freezer,
"the current state of this cgroup",
state: link,
State,
parse
);
gen_getter!(
freezer,
"whether this cgroup itself is frozen or in processes of being frozen,",
self_freezing,
bool,
parse_01_bool
);
gen_getter!(
freezer,
"whether any parent cgroups of this cgroup is frozen or in processes of being frozen,",
parent_freezing,
bool,
parse_01_bool
);
_gen_setter!("Freezes", freeze, State::Frozen);
_gen_setter!("Thaws, i.e. un-freezes", thaw, State::Thawed);
}
impl Into<v1::Resources> for Resources {
fn into(self) -> v1::Resources {
v1::Resources {
freezer: self,
..v1::Resources::default()
}
}
}
impl std::str::FromStr for State {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"THAWED" => Ok(Self::Thawed),
"FREEZING" => Ok(Self::Freezing),
"FROZEN" => Ok(Self::Frozen),
_ => {
bail_parse!();
}
}
}
}
impl fmt::Display for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Thawed => write!(f, "THAWED"),
Self::Freezing => write!(f, "FREEZING"),
Self::Frozen => write!(f, "FROZEN"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use v1::SubsystemKind;
#[test]
fn test_subsystem_create_file_exists() -> Result<()> {
gen_subsystem_test!(Freezer, ["state", "self_freezing", "parent_freezing"])
}
#[test]
fn test_subsystem_apply() -> Result<()> {
gen_subsystem_test!(
Freezer,
Resources {
state: Some(State::Frozen),
},
(state, State::Frozen),
)
}
#[test]
fn test_subsystem_state_freeze_thaw() -> Result<()> {
let mut cgroup =
Subsystem::new(CgroupPath::new(SubsystemKind::Freezer, gen_cgroup_name!()));
cgroup.create()?;
assert_eq!(cgroup.state()?, State::Thawed);
cgroup.freeze()?;
assert_eq!(cgroup.state()?, State::Frozen);
cgroup.thaw()?;
assert_eq!(cgroup.state()?, State::Thawed);
cgroup.delete()
}
#[test]
fn test_subsystem_self_freezing_freeze_thaw() -> Result<()> {
let mut cgroup =
Subsystem::new(CgroupPath::new(SubsystemKind::Freezer, gen_cgroup_name!()));
cgroup.create()?;
assert!(!cgroup.self_freezing()?);
cgroup.freeze()?;
assert!(cgroup.self_freezing()?);
cgroup.thaw()?;
assert!(!cgroup.self_freezing()?);
cgroup.delete()
}
#[test]
fn test_subsystem_parent_freezing_freeze_thaw() -> Result<()> {
let name = gen_cgroup_name!();
let mut child = Subsystem::new(CgroupPath::new(SubsystemKind::Freezer, name.join("child")));
let mut parent = Subsystem::new(CgroupPath::new(SubsystemKind::Freezer, name));
parent.create()?;
child.create()?;
assert!(!parent.parent_freezing()?);
assert!(!child.parent_freezing()?);
parent.freeze()?;
assert!(child.parent_freezing()?);
parent.thaw()?;
assert!(!child.parent_freezing()?);
child.delete()?;
parent.delete()
}
}