class OpalWebpackLoader::CompileServer

Constants

SIGNALS
TIMEOUT

Attributes

cache[R]

Public Class Methods

new(options) click to toggle source
# File lib/opal-webpack-loader/compile_server.rb, line 38
def initialize(options)
  @read_pipe, @write_pipe = IO.pipe
  @workers = {}
  @signal_queue = []
  @socket_path = options[:socket_path]
  @options = options
end
stop(socket_path, do_exit = true) click to toggle source
# File lib/opal-webpack-loader/compile_server.rb, line 21
def self.stop(socket_path, do_exit = true)
  if File.exist?(socket_path)
    dont_unlink_socket_on_exit
    begin
      s = UNIXSocket.new(socket_path)
      s.send("command:stop\x04", 0)
      s.close
    rescue
      # socket cant be reached so owlcs is already dead, delete socket
      unlink_socket_on_exit
    end
    exit(0) if do_exit
  end
end

Public Instance Methods

start(number_of_workers = 4, compiler_options) click to toggle source
# File lib/opal-webpack-loader/compile_server.rb, line 46
def start(number_of_workers = 4, compiler_options)
  $PROGRAM_NAME = 'owl compile server'
  @number_of_workers = number_of_workers
  @server_pid = Process.pid
  $stderr.sync = $stdout.sync = true
  @socket = UNIXServer.new(@socket_path)
  spawn_workers(compiler_options)
  SIGNALS.each { |sig| trap_deferred(sig) }
  trap('CHLD') { @write_pipe.write_nonblock('.') }

  loop do
    reap_workers
    mode = @signal_queue.shift
    case mode
    when nil
      kill_runaway_workers
      spawn_workers(compiler_options)
    when 'QUIT', 'TERM', 'INT'
      @workers.each_pair do |pid, _worker|
        Process.kill('TERM', pid)
      end
      break
    end
    reap_workers
    ready = IO.select([@read_pipe], nil, nil, 1) || next
    ready.first && ready.first.first || next
    @read_pipe.read_nonblock(1)
    OpalWebpackLoader::CompileServer.unlink_socket_on_exit
  end
end

Private Instance Methods

init_worker(worker) click to toggle source
# File lib/opal-webpack-loader/compile_server.rb, line 111
def init_worker(worker)
  @write_pipe.close
  @read_pipe.close
  @workers.each_pair { |_, w| w.tempfile.close }
  worker.start
end
kill_runaway_workers() click to toggle source
# File lib/opal-webpack-loader/compile_server.rb, line 102
def kill_runaway_workers
  now = Time.now
  @workers.each_pair do |pid, worker|
    (now - worker.tempfile.ctime) <= TIMEOUT && next
    $stderr.puts "worker #{worker.number} (PID:#{pid}) has timed out"
    kill_worker('KILL', pid)
  end
end
kill_worker(signal, pid) click to toggle source
# File lib/opal-webpack-loader/compile_server.rb, line 97
def kill_worker(signal, pid)
  Process.kill(signal, pid)
rescue Errno::ESRCH
end
reap_worker(pid, status) click to toggle source
# File lib/opal-webpack-loader/compile_server.rb, line 87
def reap_worker(pid, status)
  worker = @workers.delete(pid)
  begin
    worker.tempfile.close
  rescue
    nil
  end
  puts "OpalWebpackLoader::CompileServer: Reaped worker #{worker.number} (PID:#{pid}) status: #{status.exitstatus}"
end
reap_workers() click to toggle source
# File lib/opal-webpack-loader/compile_server.rb, line 79
def reap_workers
  loop do
    pid, status = Process.waitpid2(-1, Process::WNOHANG) || break
    reap_worker(pid, status)
  end
rescue Errno::ECHILD
end
spawn_workers(compiler_options) click to toggle source
# File lib/opal-webpack-loader/compile_server.rb, line 118
def spawn_workers(compiler_options)
  worker_number = -1
  until (worker_number += 1) == @number_of_workers
    @workers.value?(worker_number) && next
    tempfile = Tempfile.new('')
    tempfile.unlink
    tempfile.sync = true
    worker = OpalWebpackLoader::CompileWorker.new(@server_pid, @socket, tempfile, worker_number, compiler_options, @options)
    pid = fork { init_worker(worker) }
    @workers[pid] = worker
  end
end
trap_deferred(signal) click to toggle source
# File lib/opal-webpack-loader/compile_server.rb, line 131
def trap_deferred(signal)
  trap(signal) do |_|
    @signal_queue << signal
    @write_pipe.write_nonblock('.')
  end
end