I've been writing some software to test connectivity to our server software. I started writing the code in Ruby primarily because it's easy to write. It also provides another language implementation of libraries to use against the server verifying that nothing written is specific to our particular language/setup.
One of the tests we needed was a testing a bunch of different connections to the server. To test the absolute max thru-put of the system JMeter was used. However, a test that was more of a simulation of users connecting was needed. Ruby has threads that are non-native and are only scheduled by the Ruby process. These aren't necessarily optimal, but may have been good enough.
So, what I decided to do was use Ruby's native system fork ability and spawn processes that would communicate with the server. The original code worked fine. However, I needed the ability to do process control in a more generic way that made it easy to spawn any simulation that I needed. To do this I ended up modifying the code to allow the passing of functions/methods to the process controller.
def process_control(num_process_to_spawn, param_for_func) ... func = nil num_process_to_spawn.times { |count| func = yield func pid = fork { if func.call(param_for_func) = true #Exit after child func is finished. Process.exit(true) else #Don't exit we're the parent Process.exit(false) end } .... end
The above code calls forks and executes in the child the function call that is passed in via the yield. An example of how to use this is below.
<pre> def function_count_num_times(num_times) num_times.times { |count| puts "Count: #{count}" } end #Spawn 400 processes and pass 10 to the called function process_control(400, 10) { |func| func = method(:function_count_num_times) }
If you need to pass a method of an object it's pretty easy and you can setup the appropriate data for the method to work with ahead of time.
class TestControl attr_reader :internal_count def initialize() @internal_count = 0 end def output(num_times) num_times.times { |count| puts "count: #{count}" } end end #be careful of garbage collection test_control_objects = Array.new process_control(400, 10) { |func| ctrl_obj = TestControl.new test_control_objects.push(ctrl_obj) func = ctrl_obj.method(:output) }