class Isomorfeus::Speednode::Runtime::VM

Attributes

responder[R]

Public Class Methods

exit_node(socket, socket_dir, socket_path, pid) click to toggle source
# File lib/isomorfeus/speednode/runtime/vm.rb, line 65
def self.exit_node(socket, socket_dir, socket_path, pid)
  VMCommand.new(socket, "exit", [0]).execute
  socket.close
  File.unlink(socket_path) if File.exist?(socket_path)
  Dir.rmdir(socket_dir) if socket_dir && Dir.exist?(socket_dir)
  if Gem.win_platform?
    # SIGINT or SIGKILL are unreliable on Windows, try native taskkill first
    unless system("taskkill /f /t /pid #{pid} >NUL 2>NUL")
      Process.kill('KILL', pid) rescue nil
    end
  else
    Process.kill('KILL', pid) rescue nil
  end
rescue
  nil
end
finalize(socket, socket_dir, socket_path, pid) click to toggle source
# File lib/isomorfeus/speednode/runtime/vm.rb, line 58
def self.finalize(socket, socket_dir, socket_path, pid)
  proc do 
    Isomorfeus::Speednode::Runtime.responders[socket_path].kill if Isomorfeus::Speednode::Runtime.responders[socket_path]
    exit_node(socket, socket_dir, socket_path, pid)
  end
end
new(options) click to toggle source
# File lib/isomorfeus/speednode/runtime/vm.rb, line 46
def initialize(options)
  @mutex = Mutex.new
  @socket_path = nil
  @options = options
  @started = false
  @socket = nil
end

Public Instance Methods

attach(context, func) click to toggle source
# File lib/isomorfeus/speednode/runtime/vm.rb, line 98
def attach(context, func)
  create_responder(context) unless responder
  command("attach", {'context' => context, 'func' => func }) 
end
create(context, source, options) click to toggle source
# File lib/isomorfeus/speednode/runtime/vm.rb, line 90
def create(context, source, options)
  command("create", {'context' => context, 'source' => source, 'options' => options})
end
createp(context, source, options) click to toggle source
# File lib/isomorfeus/speednode/runtime/vm.rb, line 94
def createp(context, source, options)
  command("createp", {'context' => context, 'source' => source, 'options' => options})
end
delete_context(context) click to toggle source
# File lib/isomorfeus/speednode/runtime/vm.rb, line 103
def delete_context(context)
  command("deleteContext", context)
end
eval(context, source) click to toggle source
# File lib/isomorfeus/speednode/runtime/vm.rb, line 82
def eval(context, source)
  command("eval", {'context' => context, 'source' => source})
end
exec(context, source) click to toggle source
# File lib/isomorfeus/speednode/runtime/vm.rb, line 86
def exec(context, source)
  command("exec", {'context' => context, 'source' => source})
end
start() click to toggle source

def context_options(context)

command('ctxo', {'context' => context })

end

# File lib/isomorfeus/speednode/runtime/vm.rb, line 111
def start
  @mutex.synchronize do
    start_without_synchronization
  end
end
started?() click to toggle source
# File lib/isomorfeus/speednode/runtime/vm.rb, line 54
def started?
  @started
end

Private Instance Methods

command(cmd, *arguments) click to toggle source
# File lib/isomorfeus/speednode/runtime/vm.rb, line 195
def command(cmd, *arguments)
  @mutex.synchronize do
    start_without_synchronization unless @started
    VMCommand.new(@socket, cmd, arguments).execute
  end
end
create_responder(context) click to toggle source
# File lib/isomorfeus/speednode/runtime/vm.rb, line 169
def create_responder(context)
  start unless @started
  run_block = Proc.new do |request|
    args = ::Oj.load(request.chop!, mode: :strict)
    req_context = args[0]
    method = args[1]
    method_args = args[2]
    begin
      result = Isomorfeus::Speednode::Runtime.attached_procs[req_context][method].call(*method_args)
      ::Oj.dump(['ok', result], mode: :strict)
    rescue Exception => err
      ::Oj.dump(['err', err.class.to_s, [err.message].concat(err.backtrace)], mode: :strict)
    end
  end
  responder_path = @socket_path + '_responder'
  @responder = Thread.new do
                            if ExecJS.windows?
                              Isomorfeus::Speednode::AttachPipe.new(responder_path, run_block).run
                            else
                              Isomorfeus::Speednode::AttachSocket.new(responder_path, run_block).run
                            end
                          end
  Isomorfeus::Speednode::Runtime.responders[@socket_path] = @responder_thread
  @responder.run
end
start_without_synchronization() click to toggle source
# File lib/isomorfeus/speednode/runtime/vm.rb, line 119
def start_without_synchronization
  return if @started
  if ExecJS.windows?
    @socket_dir = nil
    @socket_path = SecureRandom.uuid
  else
    @socket_dir = Dir.mktmpdir("isomorfeus-speednode-")
    @socket_path = File.join(@socket_dir, "socket")
  end
  @pid = Process.spawn({"SOCKET_PATH" => @socket_path}, @options[:binary], @options[:source_maps], @options[:runner_path])

  retries = 20

  if ExecJS.windows?
    timeout_or_connected = false
    begin
      retries -= 1 
      begin
        @socket = Win32::Pipe::Client.new(@socket_path, Win32::Pipe::ACCESS_DUPLEX)
      rescue
        sleep 0.1
        raise "Unable to start nodejs process in time" if retries == 0
        next
      end
      timeout_or_connected = true
    end until timeout_or_connected
  else
    while !File.exist?(@socket_path)
      sleep 0.1
      retries -= 1
      raise "Unable to start nodejs process in time" if retries == 0
    end

    @socket = UNIXSocket.new(@socket_path)
  end

  @started = true

  at_exit do
    begin
      self.class.exit_node(@socket, @socket_dir, @socket_path, @pid) unless @socket.closed?
    rescue
      # do nothing
    end
  end

  ObjectSpace.define_finalizer(self, self.class.finalize(@socket, @socket_dir, @socket_path, @pid))
  Kernel.at_exit { self.class.finalize(@socket, @socket_dir, @socket_path, @pid).call }
end