Class: Always

Inherits:
Object
  • Object
show all
Defined in:
lib/always.rb

Overview

Always.

In order to start five threads performing the same piece of code over and over again, with a 60-seconds pause between cycles, do this:

require 'always'
a = Always.new(5)
a.start(60) do
  puts 'Hello, world!'
end

Then, in order to stop them all together:

a.stop

It’s possible to get a quick summary of the thread pool, by calling to_s. The result will be a “T/C/E” string, where T is the total number of currently running threads, C is the total number of all cycles so far, and E is the total number of all errors seen so far.

Author

Yegor Bugayenko ([email protected])

Copyright

Copyright © 2024-2025 Yegor Bugayenko

License

MIT

Constant Summary collapse

VERSION =

The version of the framework.

'0.1.0'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(total, max_backtraces: 32, name: "always-#{SecureRandom.hex(4)}") ⇒ Always

Constructor.

Parameters:

  • total (Integer)

    The number of threads to run

  • max_backtraces (Integer) (defaults to: 32)

    How many backtraces to keep in memory?



48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/always.rb', line 48

def initialize(total, max_backtraces: 32, name: "always-#{SecureRandom.hex(4)}")
  raise "The number of threads (#{total}) must be positive" unless total.positive?

  @total = total
  @on_error = nil
  @name = name
  @threads = []
  @backtraces = []
  @cycles = Concurrent::Atom.new(0)
  @errors = Concurrent::Atom.new(0)
  @max_backtraces = max_backtraces
end

Instance Attribute Details

#backtracesArray<Exception> (readonly)

Get the array of most recent exception backtraces.

Examples:

a = Always.new(5, max_backtraces: 10)
a.start { raise 'Oops' }
sleep 1
puts a.backtraces.size # => number of exceptions caught

Returns:

  • (Array<Exception>)

    The array of exceptions caught



40
41
42
# File 'lib/always.rb', line 40

def backtraces
  @backtraces
end

Instance Method Details

#on_error(&block) ⇒ Always

What to do when an exception occurs?

Call it like this (the e provided is the exception and i is the number of the thread where it occurred):

a = Always.new(5)
a.on_error do |e, i|
  puts e.message
end

If the block that you provided will also throw an error, it will simply be ignored (not logged anywhere, just ignored!)

Returns:



74
75
76
77
# File 'lib/always.rb', line 74

def on_error(&block)
  @on_error = block
  self
end

#start(pause = 0) ⇒ Object

Start them all and let them run forever (until the stop method is called).

Parameters:

  • pause (Integer) (defaults to: 0)

    The delay between cycles, in seconds



81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/always.rb', line 81

def start(pause = 0, &)
  raise 'It is running now, call .stop() first' unless @threads.empty?

  (0..@total - 1).each do |i|
    t =
      Thread.new do
        body(pause, &)
      end
    t.name = "#{@name}-#{i + 1}"
    @threads[i] = t
  end
end

#stopObject

Stop them all.



95
96
97
98
99
100
101
102
103
104
105
# File 'lib/always.rb', line 95

def stop
  raise 'It is not running now, call .start() first' if @threads.empty?

  @threads.delete_if do |t|
    t.kill
    sleep(0.001) while t.alive?
    true
  end
  @cycles.swap { |_| 0 }
  @errors.swap { |_| 0 }
end

#to_sString

Represent its internal state as a string.

Examples:

a = Always.new(5)
a.start(60) { puts 'Working...' }
puts a.to_s # => "5/42/3"

Returns:

  • (String)

    Something like “4/230/23”, where 4 is the number of running threads, 230 is the number of successful loops, and 23 is the number of failures occurred so far.



115
116
117
# File 'lib/always.rb', line 115

def to_s
  "#{@threads.size}/#{@cycles.value}/#{@errors.value}"
end