bunny bunny bunny bunny bunny

Writing a Concurrent "Task" Scheduler in Rust - Part 1.5

Date: April 22, 2026

In the previous article, I describe the requirements for my final project and share my testing code. In this section, I address some amendments made to the project guidelines.

As noted at the end of the previous article, the requirements for the project slightly shifted since writing. The amendment is as follows:

  • Different kinds of tasks now have a resource load, which is a specified as 35% for CPU tasks and 10% for IO tasks.
  • There must be at most 8 workers in the worker pool (I keep this generalized and simply limit the test to 8 workers).
  • The collective resource usage across all workers must not surprass 100%, e.g., 2 workers for CPU and 3 workers for IO tasks is valid, but delegating 6 workers to IO instead is not (as the total usage would be (2x) 35% + (6x) 10% = 130%).
  • Tasks musts be sent 20ms apart (so I guess I'm rewriting the arrival time and task dispatcher code?)

For what it's worth, this is a very uniform distribution.

  • Both IO and CPU tasks will take 200ms

The lack of a variable system load is meant to simplify the project by foregoing heuristic analysis of worker threads. Though, I would like to explore this after the project is finished.

Thus, until additional amendments are made to the runtime requirements, this is the new testing code:

use rand::rngs::StdRng;
use rand::SeedableRng;
use rand::prelude::*;
use rand::distr::weighted::WeightedIndex;

use std::time::{Duration, SystemTime};

use crate::td::task::{Task, TaskKind};
use crate::td::pool::WorkerPool;

pub mod td;

fn main() {
    let seed: u64 = 123456;
    let mut rng = StdRng::seed_from_u64(seed);

    let mut tasks = vec![];
    let tasks_total = 500;

    let arrival_time_0 = SystemTime::now() + Duration::new(5, 0);
    let dist_taskkind = WeightedIndex::new(&[3, 7]).unwrap();
    let mut arrival_time_offset_20ms = 0;

    for id in 1..=tasks_total {
        let kind = vec![TaskKind::CPU, TaskKind::IO][dist_taskkind.sample(&mut rng)].clone();

        let duration = Duration::from_millis(200);

        let arrival_time = arrival_time_0 + Duration::from_millis(20 * arrival_time_offset_20ms);
        arrival_time_offset_20ms += 1;

        tasks.push(Task{
            id,
            arrival_time,
            kind,
            duration,
        });
    }

    // no need to sort anymore 
    tasks.reverse();

    let pool = WorkerPool::new(8);

    println!("Worker pool opened. Waiting for tasks...");

    while !tasks.is_empty() {
        if let Some(task) = tasks.last() && task.arrival_time <= SystemTime::now() {
            println!("Dispatching task {:?}, which will take about {:.3?}s...", task.id, task.duration.as_secs_f32());

            pool.execute_task(task.clone());

            tasks.pop();
        }
    }
}

« Previous Post