Skip to main content

ProcessPool

Struct ProcessPool 

Source
pub struct ProcessPool<T: Task> { /* private fields */ }
Expand description

A pool of worker processes for concurrent task execution.

ProcessPool manages multiple worker processes, distributing tasks across them using round-robin scheduling. Each worker runs in its own isolated process with automatic crash recovery.

§Example

use std::num::NonZeroUsize;
use tarnish::{Task, ProcessPool};

#[derive(Default)]
struct HeavyComputation;

impl Task for HeavyComputation {
    type Input = Vec<u8>;
    type Output = u64;
    type Error = String;

    fn run(&mut self, input: Vec<u8>) -> Result<u64, String> {
        // Expensive computation here
        Ok(input.iter().map(|&x| x as u64).sum())
    }
}

tarnish::main::<HeavyComputation>(|| {
    let size = NonZeroUsize::new(4).unwrap();
    let mut pool = ProcessPool::<HeavyComputation>::new(size)
        .expect("Failed to create pool");

    // Process 100 items across 4 workers
    for i in 0..100 {
        let result = pool.call(vec![i; 1000]);
        println!("Result {}: {:?}", i, result);
    }
});

Implementations§

Source§

impl<T: Task> ProcessPool<T>

Source

pub fn new(size: NonZeroUsize) -> Result<Self>

Create a new process pool with the specified number of workers.

Each worker is a separate process that will be spawned immediately. If any worker fails to spawn, an error is returned and no pool is created.

§Arguments
  • size - The number of worker processes to spawn (must be non-zero).
§Errors

Returns an error if any worker process fails to spawn.

§Example
use std::num::NonZeroUsize;
use tarnish::ProcessPool;

let size = NonZeroUsize::new(4).unwrap();
let pool = ProcessPool::<MyTask>::new(size)?;
Examples found in repository?
examples/pool.rs (line 39)
34fn main() {
35    tarnish::main::<HeavyComputation>(|| {
36        eprintln!("Creating pool with 4 workers...");
37
38        let size = NonZeroUsize::new(4).unwrap();
39        let mut pool = match ProcessPool::<HeavyComputation>::new(size) {
40            Ok(p) => p,
41            Err(e) => {
42                eprintln!("Failed to create pool: {e}");
43                return;
44            }
45        };
46
47        let pool_size = pool.size();
48        eprintln!("Pool size: {pool_size}\n");
49
50        // Process 12 tasks across 4 workers
51        // Each worker should get ~3 tasks due to round-robin
52        eprintln!("Processing 12 tasks across {pool_size} workers:\n");
53
54        for i in 1_usize..=12 {
55            eprintln!("[Parent] Submitting task {i}");
56
57            // Use saturating_mul to avoid arithmetic overflow
58            let input = i.saturating_mul(100);
59
60            match pool.call(input) {
61                Ok(result) => eprintln!("[Parent] Task {i} result: {result}\n"),
62                Err(e) => eprintln!("[Parent] Task {i} failed: {e}\n"),
63            }
64        }
65
66        eprintln!("All tasks completed!");
67    });
68}
Source

pub fn call(&mut self, input: T::Input) -> Result<T::Output>

Execute a task on the next available worker.

This method uses round-robin scheduling to distribute work across workers. The call blocks until the worker returns a result. If the worker crashes, it will be automatically restarted.

§Errors

Returns an error if:

  • The worker process crashes and cannot be restarted
  • Communication with the worker fails
  • The worker returns a task error
§Example
let size = NonZeroUsize::new(4).unwrap();
let mut pool = ProcessPool::<MyTask>::new(size)?;
let result = pool.call("hello".to_string())?;
Examples found in repository?
examples/pool.rs (line 60)
34fn main() {
35    tarnish::main::<HeavyComputation>(|| {
36        eprintln!("Creating pool with 4 workers...");
37
38        let size = NonZeroUsize::new(4).unwrap();
39        let mut pool = match ProcessPool::<HeavyComputation>::new(size) {
40            Ok(p) => p,
41            Err(e) => {
42                eprintln!("Failed to create pool: {e}");
43                return;
44            }
45        };
46
47        let pool_size = pool.size();
48        eprintln!("Pool size: {pool_size}\n");
49
50        // Process 12 tasks across 4 workers
51        // Each worker should get ~3 tasks due to round-robin
52        eprintln!("Processing 12 tasks across {pool_size} workers:\n");
53
54        for i in 1_usize..=12 {
55            eprintln!("[Parent] Submitting task {i}");
56
57            // Use saturating_mul to avoid arithmetic overflow
58            let input = i.saturating_mul(100);
59
60            match pool.call(input) {
61                Ok(result) => eprintln!("[Parent] Task {i} result: {result}\n"),
62                Err(e) => eprintln!("[Parent] Task {i} failed: {e}\n"),
63            }
64        }
65
66        eprintln!("All tasks completed!");
67    });
68}
Source

pub const fn size(&self) -> usize

Returns the number of workers in the pool.

§Example
let size = NonZeroUsize::new(4).unwrap();
let pool = ProcessPool::<MyTask>::new(size)?;
assert_eq!(pool.size(), 4);
Examples found in repository?
examples/pool.rs (line 47)
34fn main() {
35    tarnish::main::<HeavyComputation>(|| {
36        eprintln!("Creating pool with 4 workers...");
37
38        let size = NonZeroUsize::new(4).unwrap();
39        let mut pool = match ProcessPool::<HeavyComputation>::new(size) {
40            Ok(p) => p,
41            Err(e) => {
42                eprintln!("Failed to create pool: {e}");
43                return;
44            }
45        };
46
47        let pool_size = pool.size();
48        eprintln!("Pool size: {pool_size}\n");
49
50        // Process 12 tasks across 4 workers
51        // Each worker should get ~3 tasks due to round-robin
52        eprintln!("Processing 12 tasks across {pool_size} workers:\n");
53
54        for i in 1_usize..=12 {
55            eprintln!("[Parent] Submitting task {i}");
56
57            // Use saturating_mul to avoid arithmetic overflow
58            let input = i.saturating_mul(100);
59
60            match pool.call(input) {
61                Ok(result) => eprintln!("[Parent] Task {i} result: {result}\n"),
62                Err(e) => eprintln!("[Parent] Task {i} failed: {e}\n"),
63            }
64        }
65
66        eprintln!("All tasks completed!");
67    });
68}

Auto Trait Implementations§

§

impl<T> !Freeze for ProcessPool<T>

§

impl<T> RefUnwindSafe for ProcessPool<T>

§

impl<T> Send for ProcessPool<T>
where Vec<Process<T>>: Send,

§

impl<T> Sync for ProcessPool<T>
where Vec<Process<T>>: Sync,

§

impl<T> Unpin for ProcessPool<T>
where Vec<Process<T>>: Unpin,

§

impl<T> UnsafeUnpin for ProcessPool<T>
where Vec<Process<T>>: UnsafeUnpin,

§

impl<T> UnwindSafe for ProcessPool<T>
where Vec<Process<T>>: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.