PageRenderTime 265ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/cache/ruby/2.7.0/gems/spring-2.1.0/lib/spring/application_manager.rb

https://gitlab.com/gitnyasha/zimcreative
Ruby | 140 lines | 112 code | 20 blank | 8 comment | 8 complexity | 4b52e34f2a7e5b96ac4f2f6871c3194d MD5 | raw file
  1. module Spring
  2. class ApplicationManager
  3. attr_reader :pid, :child, :app_env, :spring_env, :status
  4. def initialize(app_env, spring_env)
  5. @app_env = app_env
  6. @spring_env = spring_env
  7. @mutex = Mutex.new
  8. @state = :running
  9. @pid = nil
  10. end
  11. def log(message)
  12. spring_env.log "[application_manager:#{app_env}] #{message}"
  13. end
  14. # We're not using @mutex.synchronize to avoid the weird "<internal:prelude>:10"
  15. # line which messes with backtraces in e.g. rspec
  16. def synchronize
  17. @mutex.lock
  18. yield
  19. ensure
  20. @mutex.unlock
  21. end
  22. def start
  23. start_child
  24. end
  25. def restart
  26. return if @state == :stopping
  27. start_child(true)
  28. end
  29. def alive?
  30. @pid
  31. end
  32. def with_child
  33. synchronize do
  34. if alive?
  35. begin
  36. yield
  37. rescue Errno::ECONNRESET, Errno::EPIPE
  38. # The child has died but has not been collected by the wait thread yet,
  39. # so start a new child and try again.
  40. log "child dead; starting"
  41. start
  42. yield
  43. end
  44. else
  45. log "child not running; starting"
  46. start
  47. yield
  48. end
  49. end
  50. end
  51. # Returns the pid of the process running the command, or nil if the application process died.
  52. def run(client)
  53. with_child do
  54. child.send_io client
  55. child.gets or raise Errno::EPIPE
  56. end
  57. pid = child.gets.to_i
  58. unless pid.zero?
  59. log "got worker pid #{pid}"
  60. pid
  61. end
  62. rescue Errno::ECONNRESET, Errno::EPIPE => e
  63. log "#{e} while reading from child; returning no pid"
  64. nil
  65. ensure
  66. client.close
  67. end
  68. def stop
  69. log "stopping"
  70. @state = :stopping
  71. if pid
  72. Process.kill('TERM', pid)
  73. Process.wait(pid)
  74. end
  75. rescue Errno::ESRCH, Errno::ECHILD
  76. # Don't care
  77. end
  78. private
  79. def start_child(preload = false)
  80. @child, child_socket = UNIXSocket.pair
  81. Bundler.with_original_env do
  82. @pid = Process.spawn(
  83. {
  84. "RAILS_ENV" => app_env,
  85. "RACK_ENV" => app_env,
  86. "SPRING_ORIGINAL_ENV" => JSON.dump(Spring::ORIGINAL_ENV),
  87. "SPRING_PRELOAD" => preload ? "1" : "0"
  88. },
  89. "ruby",
  90. "-I", File.expand_path("../..", $LOADED_FEATURES.grep(/bundler\/setup\.rb$/).first),
  91. "-I", File.expand_path("../..", __FILE__),
  92. "-e", "require 'spring/application/boot'",
  93. 3 => child_socket,
  94. 4 => spring_env.log_file,
  95. )
  96. end
  97. start_wait_thread(pid, child) if child.gets
  98. child_socket.close
  99. end
  100. def start_wait_thread(pid, child)
  101. Process.detach(pid)
  102. Spring.failsafe_thread {
  103. # The recv can raise an ECONNRESET, killing the thread, but that's ok
  104. # as if it does we're no longer interested in the child
  105. loop do
  106. IO.select([child])
  107. break if child.recv(1, Socket::MSG_PEEK).empty?
  108. sleep 0.01
  109. end
  110. log "child #{pid} shutdown"
  111. synchronize {
  112. if @pid == pid
  113. @pid = nil
  114. restart
  115. end
  116. }
  117. }
  118. end
  119. end
  120. end