Level 1 Basic round robin
Build Scheduler(quantum), a single-CPU round-robin scheduler. Each task needs a certain number of time units of CPU (work >= 1). quantum >= 1 is the length of one time slice.
add(task_id: str, work: int) -> bool: put a new task at the back of the queue. ReturnFalse(and change nothing) if a task with this id is already in the scheduler. An id whose task has finished may be added again.step() -> str | None: the task at the front runs formin(quantum, remaining work)units and the clock moves forward by that much. If the task is now done, it leaves the scheduler; otherwise it goes to the back of the queue. Return the id that ran, orNoneif there is nothing to run (the clock doesn't move).time() -> int: total units run so far (starts at0).finished() -> list[str]: ids in the order they completed.queue() -> list[str]: ids waiting to run, front first. It's for debugging and tests, so it may be O(n).
s = Scheduler(2)
s.add("a", 3); s.add("b", 1); s.add("c", 4)
s.step() # "a" (a has 1 left, goes to the back), time 2
s.step() # "b" (b finishes), time 3
s.queue() # ["c", "a"]
s.step(); s.step(); s.step() # "c", "a", "c"
s.finished() # ["b", "a", "c"]
s.time() # 8
s.step() # None
Each step and add should be O(1): the tests queue 150,000 tasks, so anything that shifts the whole queue on every step is too slow.
Discussion (not tested)
- Is this cooperative or preemptive scheduling? What changes if tasks can yield early?
- How would you plug this into a thread pool, or spread tasks across several worker machines?
Show hint
collections.deque pops from the front and appends to the back in O(1); a plain list's pop(0) is O(n).