Class: Tork::Master

Inherits:
Server show all
Defined in:
lib/tork/master.rb

Constant Summary collapse

MAX_CONCURRENT_WORKERS =

detect the number of CPUs available in the system stackoverflow.com/questions/891537#6420817

[
  'fgrep -c processor /proc/cpuinfo', # Linux
  'sysctl -n hw.ncpu',                # BSD
  'hwprefs cpu_count',                # Darwin 9
  'hwprefs thread_count',             # Darwin 10
].
map {|cmd| `#{cmd} 2>/dev/null`.to_i }.push(1).max

Instance Method Summary collapse

Methods inherited from Server

address, #quit

Constructor Details

#initializeMaster

Returns a new instance of Master.



17
18
19
20
21
22
23
24
# File 'lib/tork/master.rb', line 17

def initialize
  super
  Tork.config :master
  send @clients, [:boot]

  @worker_number_pool = (0 ... MAX_CONCURRENT_WORKERS).to_a
  @command_by_worker_pid = {}
end

Instance Method Details

#loopObject



26
27
28
29
30
# File 'lib/tork/master.rb', line 26

def loop
  super
ensure
  stop :SIGKILL
end

#stop(signal = :SIGTERM) ⇒ Object



79
80
81
82
83
84
# File 'lib/tork/master.rb', line 79

def stop signal=:SIGTERM
  # the reaping threads registered above will reap these killed workers
  Process.kill signal, *@command_by_worker_pid.keys.map {|pid| -pid }
rescue ArgumentError, SystemCallError
  # some workers might have already exited before we sent them the signal
end

#test(test_file, line_numbers) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/tork/master.rb', line 32

def test test_file, line_numbers
  # throttle forking rate to meet the maximum concurrent workers limit
  sleep 1 until @command_by_worker_pid.size <= @worker_number_pool.size

  $tork_test_file = test_file
  $tork_line_numbers = line_numbers
  $tork_log_file = $tork_test_file + '.log'
  $tork_worker_number = @worker_number_pool.shift
  Tork.config :onfork

  worker_pid = fork do
    # make the process title Test::Unit friendly and ps(1) searchable
    $0 = "tork-worker[#{$tork_worker_number}] #{$tork_test_file}"

    # detach worker process from master process' group for kill -pgrp
    Process.setsid

    # detach worker process from master process' standard input stream
    STDIN.reopen IO.pipe.first

    # capture test output in log file because tests are run in parallel
    # which makes it difficult to understand interleaved output thereof
    STDERR.reopen(STDOUT.reopen($tork_log_file, 'w')).sync = true

    Tork.config :worker

    # after loading the user's test file, the at_exit() hook of the user's
    # testing framework will take care of running the tests and reflecting
    # any failures in the worker process' exit status, which will then be
    # handled by the reaping thread registered in the master process (below)
    Kernel.load $tork_test_file if $tork_test_file.end_with? '.rb'
  end

  @command.push $tork_log_file, $tork_worker_number
  @command_by_worker_pid[worker_pid] = @command
  send @clients, @command

  # wait for the worker to finish and report its status to the client
  Thread.new(worker_pid) do |pid| # the reaping thread
    status = Process.wait2(pid).last
    command = @command_by_worker_pid.delete(pid)
    @worker_number_pool.push command.last
    command[0] = status.success? && :pass || :fail
    send @clients, command.push(status.to_i, status.inspect)
  end
end