Intro to Forking inside Ruby

Intro to forking inside Ruby

In digging through some old scripts, I stumbled upon a CPU load testing script I had gotten from an old colleague written in Perl using threading.

cpu_load.pl

#!/usr/bin/perl

use strict;
use warnings;

use threads;
my $NUM_THREADS = 8;

sub do_work {
    while() {
        my $a = rand + rand;
    }
}

for(1 .. $NUM_THREADS) {
    my $thr = threads->create(\&do_work);
}

for my $thr (threads->list()) {
    $thr->join();
}

exit;

As you can see, it was written using threads, and I was wondering how I could port this to Ruby. As GIL still is part of ruby (Global Interpreter Lock), using threads was not an option for a pure implementation of the perl script in ruby as CPU bound processes would not allow concurrency.

Since threads are not usable in this scenario, using Kernel#fork similar to Unix#fork, which allows a parent process to spawn multiple child processes and in this case, allowing concurrency. See below:

cpu_load.rb

#! /usr/bin/env ruby

# set equal to number of CPU cores
NUMBER_OF_THREADS = 4

def load_test
  while(true) do
    rand(0..10000) + rand(0..10000)
  end
end

(1..NUMBER_OF_THREADS).each do |p|
  fork do
    puts "PID -> #{Process.pid}"
    load_test
  end
end

# Process.waitall waits for all children to return an exit status
Process.waitall

example output

$ ruby cpu_load.rb 
PID -> 81815
PID -> 81816
PID -> 81817
PID -> 81818

In this example, the parent PID is the script itself and with an Enumerable#each loop, forks itself into child processes which then run the same arbitrary math in order to max out the CPU. By calling Process.waitall, the script will not complete and it will not leave orphaned processes running. Currently killing the ruby script via Ctrl-C will exit with interrupts and it will not exit cleanly ( v2 maybe? :) )

Sources:

Gists for Perl and Ruby scripts

Global Interpreter Lock intro