Class: QuackConcurrency::UninterruptibleSleeper
- Inherits:
-
Object
- Object
- QuackConcurrency::UninterruptibleSleeper
- Defined in:
- lib/quack_concurrency/uninterruptible_sleeper.rb
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(thread) ⇒ UninterruptibleSleeper
constructor
A new instance of UninterruptibleSleeper.
- #run_thread ⇒ Object
- #sleep_thread(duration) ⇒ Object
- #stop_thread(timeout: nil) ⇒ Object
Constructor Details
#initialize(thread) ⇒ UninterruptibleSleeper
Returns a new instance of UninterruptibleSleeper.
8 9 10 11 12 13 14 15 |
# File 'lib/quack_concurrency/uninterruptible_sleeper.rb', line 8 def initialize(thread) raise ArgumentError, "'thread' must be a Thread" unless thread.is_a?(Thread) @thread = thread @state = :running @mutex = ::Mutex.new @stop_called = false @run_called = false end |
Class Method Details
.for_current ⇒ Object
4 5 6 |
# File 'lib/quack_concurrency/uninterruptible_sleeper.rb', line 4 def self.for_current new(Thread.current) end |
Instance Method Details
#run_thread ⇒ Object
17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/quack_concurrency/uninterruptible_sleeper.rb', line 17 def run_thread @mutex.synchronize do raise '#run_thread has already been called once' if @run_called @run_called = true return if @state == :running Thread.pass until @state = :running || @thread.status == 'sleep' @state = :running @thread.run end nil end |
#sleep_thread(duration) ⇒ Object
29 30 31 32 33 |
# File 'lib/quack_concurrency/uninterruptible_sleeper.rb', line 29 def sleep_thread(duration) start_time = Time.now stop_thread(timeout: duration) time_elapsed = Time.now - start_time end |
#stop_thread(timeout: nil) ⇒ Object
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 |
# File 'lib/quack_concurrency/uninterruptible_sleeper.rb', line 35 def stop_thread(timeout: nil) raise 'can only stop current Thread' unless Thread.current == @thread raise "'timeout' argument must be nil or a Numeric" if timeout != nil && !timeout.is_a?(Numeric) raise '#stop_thread has already been called once' if @stop_called @stop_called = true target_end_time = Time.now + timeout if timeout @mutex.synchronize do return if @run_called @state = :sleeping @mutex.unlock loop do if timeout time_left = target_end_time - Time.now Kernel.sleep(time_left) if time_left > 0 else Thread.stop end break if @state == :running || Time.now >= target_time end @state = :running # we relock the mutex ensure #run_thread has finshed before #stop_thread # if Thread#run is called by another part of the code at the same time as # #run_thread is being called, we dont want the call to #run_thread # to call Thread#run on a Thread has already resumed and stopped again @mutex.lock end nil end |