Why I Built a CPU Scheduling Simulator in C++
Schedulix simulates FCFS, SJF, SRTF, Round Robin and Priority scheduling and draws the Gantt chart. Here's why I wrote it in C++17, how one base class carries all five algorithms, what makes the preemptive ones hard, and what C++ cost me on the UI side.
CPU scheduling is one of those Operating Systems topics that is easy to recite and hard to feel. A textbook gives you one table of arrival and burst times, one Gantt chart, and two averages, and you are left to trust that Shortest Remaining Time First really beats Round Robin. I wanted to type in my own processes, run all five classic algorithms on them, and see the timelines and the numbers side by side. That became Schedulix, a desktop simulator written in C++17. This post is about why C++, how the code is laid out, and what turned out to be harder than the textbook made it look.
Why C++ for an operating-systems project
Real schedulers live inside kernels written in C and C++, and I wanted the simulator to feel close to that. In C++ a process is a plain object with integer fields - arrival time, burst time, priority, time remaining - and time is a counter I advance myself. Nothing runs behind my back: no garbage collector pausing, no event loop deciding when my code gets a turn. When the simulation says P2 ran from time 1 to 4, it is because my loop put it there.
It was also the right language for what I was studying at the same time. Schedulix is built around object-oriented design, with an abstract base class and one subclass per algorithm, and C++ makes you be explicit about all of it: virtual functions, who owns each process, what gets copied and what gets shared. The standard library already has the pieces these algorithms are made of - std::queue is a ready queue, std::min_element finds the shortest job or the highest priority, std::sort puts processes in arrival order - so the algorithm code reads close to the pseudocode in the textbook.
And it compiles to a native Windows program. The quickest version is a single .cpp file that builds with one g++ command and runs straight away, with no interpreter, browser or install step.
One base class, five algorithms
Everything shares one abstract Scheduler class. It holds the processes, the Gantt chart (a list of entries saying which process ran from when to when), the clock, and the averages. It declares two pure virtual functions, execute() and getAlgorithmName(), and implements everything else once: adding and removing processes, resetting a run, and calculating the averages. FCFS, SJF, SRTF, RoundRobin and PriorityScheduler each override execute() and nothing more, so adding a sixth algorithm means one new header and one new .cpp file without touching the others.
The metrics are the same for every algorithm. When a process finishes, its turnaround time is completion minus arrival, and its waiting time is turnaround minus burst. Both are recorded at the moment it completes, in the same loop that writes the Gantt chart, so the chart and the results table come from one run and can never disagree.
The hard part: algorithms that interrupt
First Come First Serve and non-preemptive Shortest Job First are straightforward: pick a process, run it to the end, draw one bar. The preemptive algorithms are where the bugs hide, because a process can stop and restart many times and every restart has to land in the right place.
SRTF advances the clock one unit at a time. At each tick it looks at every process that has arrived, picks the one with the least time remaining, and runs it for one unit. A Gantt bar only ends when the running process changes, so a process that keeps winning gets one long bar instead of a row of one-unit slivers, and an interruption shows up as a clean break.
Round Robin uses a std::queue and a time quantum (2 by default). Each turn runs the process at the front for the quantum or its remaining time, whichever is smaller. If it isn't finished, it goes to the back - but only after any processes that arrived during its slice have joined the queue. That ordering is the textbook convention, and getting it wrong changes the whole chart. When nothing has arrived yet, the clock jumps straight to the next arrival instead of ticking through idle time.
Same four processes, five different answers
The repository ships with a sample workload: P1 arrives at time 0 needing 5 units, P2 at 1 needing 3, P3 at 2 needing 8, and P4 at 3 needing 6. Running all five algorithms on it:
- FCFS: average waiting time 5.75, average turnaround 11.25. P4 waits 13 units behind the long P3.
- SJF: 5.25 and 10.75. Once P1 and P2 are done, the shorter P4 goes before P3.
- SRTF: 5.00 and 10.50, the best of the five. P2 interrupts P1 at time 1, and P1 picks up again at 4.
- Priority: 5.25 and 10.75, the same order as SJF here, because P4 also outranks P3.
- Round Robin with a quantum of 2: 9.75 and 15.25, the worst averages - but no process waits more than 5 units for its first turn on the CPU.
What C++ cost me: three front ends
The price of C++ is the user interface. There is no built-in way to draw a window, so Schedulix ended up with three front ends, each a step further than the last. The console version is a single file that runs a comparison and prints the Gantt timeline and a results table. The Windows version talks straight to the Win32 API - a list of processes, a dropdown for the algorithm, Add Process and Run Simulation buttons, and a results box - and links only Windows' own gdi32 and user32, so there is nothing to install. The full version uses SFML, built with CMake, and draws an animated Gantt chart whose bars grow across the timeline, compares the algorithms, keeps a timestamped copy of each run in a history folder, and can save a simulation to a file and load it back.
The other cost is reach. A web version would be one click to try; Schedulix has no live demo, and anyone who wants to run it has to clone and build it. For a project whose whole point was to understand scheduling from the inside, I think that was the right trade, but it is a real one.
What I'd do differently
The Process class includes an SFML colour, which means the scheduling core depends on a graphics library it never draws with, and the single-file console version ends up re-implementing FCFS and SJF with its own small struct instead of reusing the classes. The colour belongs in the renderer, keyed by process id. With that one change the core would build without SFML, and every front end could share the same five algorithms.
I would also add a small test file of textbook workloads with their known averages and run it on every build. The preemptive algorithms in particular have edge cases - a process that finishes before the next one arrives, two processes with the same remaining time - that are easy to get wrong and invisible in the one sample everyone runs.
Frequently asked questions
- Which CPU scheduling algorithm is the best?
- It depends on what you measure. On the Schedulix sample workload, Shortest Remaining Time First gives the lowest average waiting and turnaround times, while Round Robin gives the worst averages but gets every process onto the CPU quickly, which is why time-sharing systems favour it.
- Why write a CPU scheduling simulator in C++ instead of Python or JavaScript?
- C++ keeps the simulation close to how a real scheduler works: plain process objects, a clock the code advances itself, and nothing running behind its back. It also suits an object-oriented design with one class per algorithm. The trade-off is the interface: a web version would be easier to share, while a C++ GUI takes Win32 or a library like SFML.
Schedulix taught me more about scheduling than the chapter did, mostly through the parts that went wrong: a Gantt bar in the wrong place, an average that didn't match the hand calculation. Writing it in C++ meant every one of those was my own loop's fault, which is exactly what made them worth finding. The code is on GitHub, and the case study on this site has the rest of the design.
