Reputation: 143
This is really weird to me. Have been frustrated for hours. Once a RUN request received, RUNNER will start out a background thread running forever. At anytime, there will be only one running thread (i use mutex). The problem is, for status request, sometimes it returns true, sometimes it returns false.
(done method never been called, since the backend thread will run forever, so there is no way is_running? could be false)
#config/initializers/some_init.rb
RUNNER = Runner.instance
#app/controllers/some_controller.rb
class SomeController < ApplicationController
def run
RUNNER.run {
loop do
Rails.logger.debug "backend thread running #{Time.zone.now}"
sleep(5)
end
}
end
def status
Rails.logger.debug RUNNER.inspect
RUNNER.is_running?
end
end
#lib/runner.rb
require 'singleton'
class Runner
include Singleton
def initialize
@running = false
@mutex = Mutex.new
end
def run(&blk)
@mutex.synchronize do
return if @running #only one job run is allowed at anytime
@running = true
@thr = Thread.new(self) do |r|
blk.call
r.done
end
end
end
def is_running?
@mutex.synchronize{ @running }
end
private
def done
@mutex.synchronize {@running = false}
end
end
Upvotes: 0
Views: 740
Reputation: 211560
Short answer: You can't do this.
The long answer is that Rails consists of one or more processes which serve incoming requests. Starting a thread in one of these processes randomly is not going to make that is_running?
flag become true
in any process other than the one in which the run
method was triggered.
Most Rails hosting environments will create and destroy processes frequently with no guarantee that your process will live longer than the request it is currently processing. This means your thread is likely to get killed unexpectedly.
Rails is strictly a request-response system. If you need a background process, you must create this as a stand-alone script, not something engaged through Rack.
It is possible to create long-running scripts that run inside the Rails environment, for instance, started with rails runner
, but these must be started independently of your application. Normally you use a process supervisor like systemctl
or god
to kick this off and keep it running.
Depending on your use case, a job processing system like delayed job might be a better fit for you. This is great for performing intermittent "background tasks" that don't fit within the web-facing Rails process model.
Upvotes: 4