Skip to content

Commit

Permalink
Use macro for redundant task builder code.
Browse files Browse the repository at this point in the history
  • Loading branch information
zyma98 committed Aug 30, 2024
1 parent 3b94b87 commit f254515
Showing 1 changed file with 140 additions and 180 deletions.
320 changes: 140 additions & 180 deletions src/task/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,133 @@ where
id: Option<u8>,
}

pub struct BreathingTaskBuilder<F, G, H, S, I>
where
F: FnOnce() -> S + Send + Sync + 'static,
G: Fn(&mut S) -> I + Send + Sync + 'static,
H: Fn(&mut S, I) + Send + Sync + 'static,
{
init: Option<F>,
wait: Option<G>,
work: Option<H>,
stack_limit: Option<usize>,
stack_init_size: Option<usize>,
priority: Option<u8>,
id: Option<u8>,
}

macro_rules! define_common_set_methods {
() => {
/// Set a numerical ID for the task.
///
/// The ID is used only for tagging a task and does not have any
/// functional purpose. It might be helpful for diagnosing bugs. The ID
/// need not be unique among tasks.
pub fn set_id(mut self, id: u8) -> Self {
self.id.replace(id);
self
}

/// Set the size limit of the stack in bytes. If the task exceeds the
/// limit, it will be terminated with its stack forcefully unwound to
/// reclaim resources. The task will be restarted if restartable.
pub fn set_stack_limit(mut self, limit: usize) -> Self {
self.stack_limit = Some(limit);
self
}

/// Set the size of the first stacklet in bytes. Only meaningful when
/// dynamic stack extension is enabled. The setting is ignored when
/// dynamic stack extension is disabled.
pub fn set_stack_init_size(mut self, size: usize) -> Self {
self.stack_init_size = Some(size);
self
}

/// Set the priority to a task. If not explicitly set, the task will
/// have the [`DEFAULT_TASK_PRIORITY`](config::DEFAULT_TASK_PRIORITY).
pub fn set_priority(mut self, prio: u8) -> Self {
self.priority.replace(prio);
self
}
};
}

macro_rules! define_task_spawn {
(
$method_name:ident,
$builder_fn:ident
) => {
/// Start the task. A spawned task is always detached. If a panic
/// occurs while running the task, the task's stack will be unwound.
///
/// With [`spawn`](Self::spawn), the task will not be restarted after
/// its resource is reclaimed through unwinding. With
/// [`spawn_restartable`](Self::spawn_restartable), the task will be
/// restarted again from the given entry closure.
pub fn $method_name(self) -> Result<(), TaskBuildError> {
let stack_config = self.parse_stack_config()?;

let entry_closure = self.entry_closure.ok_or(TaskBuildError::NoEntry)?;
let id = self.id.unwrap_or(config::DEFAULT_TASK_ID);
let prio = self.priority.unwrap_or(config::DEFAULT_TASK_PRIORITY);

// Get a quota from the scheduler to ensure that the maximum number of
// tasks has not been reached yet.
let quota = Scheduler::request_task_quota().map_err(|_| TaskBuildError::NoMoreTask)?;

let new_task = Task::$builder_fn(quota, id, entry_closure, stack_config, prio)?;
Scheduler::accept_task(Arc::new(new_task));

Ok(())
}
};
}

macro_rules! define_breathing_task_spawn {
(
$method_name:ident,
$entry_constr_fn:ident,
$builder_fn:ident
) => {
/// Start the task. A spawned task is always detached. If a panic
/// occurs while running the task, the task's stack will be unwound.
///
/// With [`spawn`](Self::spawn), the task will not be restarted after
/// its resource is reclaimed through unwinding. With
/// [`spawn_restartable`](Self::spawn_restartable), the task will be
/// restarted again from the given entry closure.
pub fn $method_name(self) -> Result<(), TaskBuildError> {
let init = self.init.ok_or(TaskBuildError::NoEntry)?;
let wait = self.wait.ok_or(TaskBuildError::NoEntry)?;
let work = self.work.ok_or(TaskBuildError::NoEntry)?;
let id = self.id.unwrap_or(config::DEFAULT_TASK_ID);
let prio = self.priority.unwrap_or(config::DEFAULT_TASK_PRIORITY);

// Get a quota from the scheduler to ensure that the maximum number of
// tasks has not been reached yet.
let quota = Scheduler::request_task_quota().map_err(|_| TaskBuildError::NoMoreTask)?;

let initial = match self.stack_init_size {
Some(initial) => NonZeroUsize::new(initial),
None => None,
};
let limit = match self.stack_limit {
Some(limit) => NonZeroUsize::new(limit),
None => None,
};
let stack_config = StackConfig::Dynamic { initial, limit };

let entry = breathing::$entry_constr_fn(init, wait, work);

let new_task = Task::$builder_fn(quota, id, entry, stack_config, prio)?;
Scheduler::accept_task(Arc::new(new_task));

Ok(())
}
};
}

/// Build a new task with the task builder.
///
/// # Example
Expand All @@ -52,6 +179,9 @@ impl<F> TaskBuilder<F>
where
F: FnOnce() + Send + 'static,
{
define_common_set_methods!();
define_task_spawn!(spawn, build);

const fn new() -> Self {
Self {
entry_closure: None,
Expand All @@ -63,39 +193,13 @@ where
}
}

/// Set a numerical ID for the task.
///
/// The ID is used only for tagging a task and does not have any functional
/// purpose. It might be helpful for diagnosing bugs. The ID need not be
/// unique among tasks.
pub fn set_id(mut self, id: u8) -> Self {
self.id.replace(id);
self
}

/// Set the entry closure for the task. This is the entry point where the
/// task will start to run.
pub fn set_entry(mut self, closure: F) -> Self {
self.entry_closure.replace(closure);
self
}

/// Set the size limit of the stack in bytes. If the task exceeds the
/// limit, it will be terminated with its stack forcefully unwound to
/// reclaim resources. The task will be restarted if restartable.
pub fn set_stack_limit(mut self, limit: usize) -> Self {
self.stack_limit = Some(limit);
self
}

/// Set the size of the first stacklet in bytes. Only meaningful when
/// dynamic stack extension is enabled. The setting is ignored when dynamic
/// stack extension is disabled.
pub fn set_stack_init_size(mut self, size: usize) -> Self {
self.stack_init_size = Some(size);
self
}

/// Disable dynamic stack extension for the task.
///
/// When dynamic stack extension is disabled, must set a stack size limit
Expand All @@ -111,32 +215,6 @@ where
self
}

/// Set the priority to a task. If not explicitly set, the task will have
/// the [`DEFAULT_TASK_PRIORITY`](config::DEFAULT_TASK_PRIORITY).
pub fn set_priority(mut self, prio: u8) -> Self {
self.priority.replace(prio);
self
}

/// Start the task. A spawned task is always detached. If a panic occurs
/// while running the task, the task's stack will be unwound.
pub fn spawn(self) -> Result<(), TaskBuildError> {
let stack_config = self.parse_stack_config()?;

let entry_closure = self.entry_closure.ok_or(TaskBuildError::NoEntry)?;
let id = self.id.unwrap_or(config::DEFAULT_TASK_ID);
let prio = self.priority.unwrap_or(config::DEFAULT_TASK_PRIORITY);

// Get a quota from the scheduler to ensure that the maximum number of
// tasks has not been reached yet.
let quota = Scheduler::request_task_quota().map_err(|_| TaskBuildError::NoMoreTask)?;

let new_task = Task::build(quota, id, entry_closure, stack_config, prio)?;
Scheduler::accept_task(Arc::new(new_task));

Ok(())
}

/// Check the configuration of the stack and generate a [`StackConfig`]
/// instance representing a valid configuration.
fn parse_stack_config(&self) -> Result<StackConfig, TaskBuildError> {
Expand Down Expand Up @@ -165,26 +243,8 @@ impl<F> TaskBuilder<F>
where
F: FnOnce() + Send + Sync + Clone + 'static,
{
/// Start the task. A spawned task is always detached. If a panic occurs
/// while running the task, the task's stack will be unwound and a new
/// instance will be automatically created.
#[cfg(feature = "unwind")]
pub fn spawn_restartable(self) -> Result<(), TaskBuildError> {
let stack_config = self.parse_stack_config()?;

let entry_closure = self.entry_closure.ok_or(TaskBuildError::NoEntry)?;
let id = self.id.unwrap_or(config::DEFAULT_TASK_ID);
let prio = self.priority.unwrap_or(config::DEFAULT_TASK_PRIORITY);

// Get a quota from the scheduler to ensure that the maximum number of
// tasks has not been reached yet.
let quota = Scheduler::request_task_quota().map_err(|_| TaskBuildError::NoMoreTask)?;

let new_task = Task::build_restartable(quota, id, entry_closure, stack_config, prio)?;
Scheduler::accept_task(Arc::new(new_task));

Ok(())
}
define_task_spawn!(spawn_restartable, build_restartable);
}

/// Start a new task from a previously failed task.
Expand All @@ -208,27 +268,15 @@ where
BreathingTaskBuilder::new()
}

pub struct BreathingTaskBuilder<F, G, H, S, I>
where
F: FnOnce() -> S + Send + Sync + 'static,
G: Fn(&mut S) -> I + Send + Sync + 'static,
H: Fn(&mut S, I) + Send + Sync + 'static,
{
init: Option<F>,
wait: Option<G>,
work: Option<H>,
stack_limit: Option<usize>,
stack_init_size: Option<usize>,
priority: Option<u8>,
id: Option<u8>,
}

impl<F, G, H, S, I> BreathingTaskBuilder<F, G, H, S, I>
where
F: FnOnce() -> S + Send + Sync + 'static,
G: Fn(&mut S) -> I + Send + Sync + 'static,
H: Fn(&mut S, I) + Send + Sync + 'static,
{
define_common_set_methods!();
define_breathing_task_spawn!(spawn, construct_breathing_task_entry, build);

const fn new() -> Self {
Self {
init: None,
Expand All @@ -241,16 +289,6 @@ where
}
}

/// Set a numerical ID for the task.
///
/// The ID is used only for tagging a task and does not have any functional
/// purpose. It might be helpful for diagnosing bugs. The ID need not be
/// unique among tasks.
pub fn set_id(mut self, id: u8) -> Self {
self.id.replace(id);
self
}

pub fn set_init(mut self, init: F) -> Self {
self.init.replace(init);
self
Expand All @@ -265,60 +303,6 @@ where
self.work.replace(work);
self
}

/// Set the size limit of the stack in bytes. If the task exceeds the
/// limit, it will be terminated with its stack forcefully unwound to
/// reclaim resources. The task will be restarted if restartable.
pub fn set_stack_limit(mut self, limit: usize) -> Self {
self.stack_limit = Some(limit);
self
}

/// Set the size of the first stacklet in bytes. Only meaningful when
/// dynamic stack extension is enabled. The setting is ignored when dynamic
/// stack extension is disabled.
pub fn set_stack_init_size(mut self, size: usize) -> Self {
self.stack_init_size = Some(size);
self
}

/// Set the priority to a task. If not explicitly set, the task will have
/// the [`DEFAULT_TASK_PRIORITY`](config::DEFAULT_TASK_PRIORITY).
pub fn set_priority(mut self, prio: u8) -> Self {
self.priority.replace(prio);
self
}

/// Start the task. A spawned task is always detached. If a panic occurs
/// while running the task, the task's stack will be unwound.
pub fn spawn(self) -> Result<(), TaskBuildError> {
let init = self.init.ok_or(TaskBuildError::NoEntry)?;
let wait = self.wait.ok_or(TaskBuildError::NoEntry)?;
let work = self.work.ok_or(TaskBuildError::NoEntry)?;
let id = self.id.unwrap_or(config::DEFAULT_TASK_ID);
let prio = self.priority.unwrap_or(config::DEFAULT_TASK_PRIORITY);

// Get a quota from the scheduler to ensure that the maximum number of
// tasks has not been reached yet.
let quota = Scheduler::request_task_quota().map_err(|_| TaskBuildError::NoMoreTask)?;

let initial = match self.stack_init_size {
Some(initial) => NonZeroUsize::new(initial),
None => None,
};
let limit = match self.stack_limit {
Some(limit) => NonZeroUsize::new(limit),
None => None,
};
let stack_config = StackConfig::Dynamic { initial, limit };

let entry = breathing::construct_breathing_task_entry(init, wait, work);

let new_task = Task::build(quota, id, entry, stack_config, prio)?;
Scheduler::accept_task(Arc::new(new_task));

Ok(())
}
}

impl<F, G, H, S, I> BreathingTaskBuilder<F, G, H, S, I>
Expand All @@ -327,34 +311,10 @@ where
G: Fn(&mut S) -> I + Clone + Send + Sync + 'static,
H: Fn(&mut S, I) + Clone + Send + Sync + 'static,
{
/// Start the task. A spawned task is always detached. If a panic occurs
/// while running the task, the task's stack will be unwound.
pub fn spawn_restartable(self) -> Result<(), TaskBuildError> {
let init = self.init.ok_or(TaskBuildError::NoEntry)?;
let wait = self.wait.ok_or(TaskBuildError::NoEntry)?;
let work = self.work.ok_or(TaskBuildError::NoEntry)?;
let id = self.id.unwrap_or(config::DEFAULT_TASK_ID);
let prio = self.priority.unwrap_or(config::DEFAULT_TASK_PRIORITY);

// Get a quota from the scheduler to ensure that the maximum number of
// tasks has not been reached yet.
let quota = Scheduler::request_task_quota().map_err(|_| TaskBuildError::NoMoreTask)?;

let initial = match self.stack_init_size {
Some(initial) => NonZeroUsize::new(initial),
None => None,
};
let limit = match self.stack_limit {
Some(limit) => NonZeroUsize::new(limit),
None => None,
};
let stack_config = StackConfig::Dynamic { initial, limit };

let entry = breathing::construct_restartable_breathing_task_entry(init, wait, work);

let new_task = Task::build_restartable(quota, id, entry, stack_config, prio)?;
Scheduler::accept_task(Arc::new(new_task));

Ok(())
}
#[cfg(feature = "unwind")]
define_breathing_task_spawn!(
spawn_restartable,
construct_restartable_breathing_task_entry,
build_restartable
);
}

0 comments on commit f254515

Please sign in to comment.