Class: Clockwork::Manager

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

Direct Known Subclasses

DatabaseEvents::Manager

Defined Under Namespace

Classes: NoHandlerDefined

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeManager

Returns a new instance of Manager.



7
8
9
10
11
12
13
14
15
# File 'lib/clockwork/manager.rb', line 7

def initialize
  @events = []
  @callbacks = {}
  @config = default_configuration
  @handler = nil
  @mutex = Mutex.new
  @condvar = ConditionVariable.new
  @finish = false
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



5
6
7
# File 'lib/clockwork/manager.rb', line 5

def config
  @config
end

Instance Method Details

#configure {|config| ... } ⇒ Object

Yields:



21
22
23
24
25
26
# File 'lib/clockwork/manager.rb', line 21

def configure
  yield(config)
  if config[:sleep_timeout] < 1
    config[:logger].warn 'sleep_timeout must be >= 1 second'
  end
end

#default_configurationObject



28
29
30
# File 'lib/clockwork/manager.rb', line 28

def default_configuration
  { :sleep_timeout => 1, :logger => Logger.new(STDOUT), :thread => false, :max_threads => 10 }
end

#error_handler(&block) ⇒ Object



38
39
40
41
# File 'lib/clockwork/manager.rb', line 38

def error_handler(&block)
  @error_handler = block if block_given?
  @error_handler
end

#every(period, job = 'unnamed', options = {}, &block) ⇒ Object



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

def every(period, job='unnamed', options={}, &block)
  if job.is_a?(Hash) and options.empty?
    options = job
    job = "unnamed"
  end
  if options[:at].respond_to?(:each)
    every_with_multiple_times(period, job, options, &block)
  else
    register(period, job, block, options)
  end
end

#fire_callbacks(event, *args) ⇒ Object



60
61
62
# File 'lib/clockwork/manager.rb', line 60

def fire_callbacks(event, *args)
  @callbacks[event].nil? || @callbacks[event].all? { |h| h.call(*args) }
end

#graceful_shutdownObject



102
103
104
105
106
107
# File 'lib/clockwork/manager.rb', line 102

def graceful_shutdown
  logger.info 'Gracefully shutting down'
  stop_tick_loop
  wait_tick_loop_finishes
  exit(0)
end

#handle_error(e) ⇒ Object



153
154
155
# File 'lib/clockwork/manager.rb', line 153

def handle_error(e)
  error_handler.call(e) if error_handler
end

#handle_signal(sig) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/clockwork/manager.rb', line 83

def handle_signal(sig)
  logger.debug "Got #{sig} signal"
  case sig
  when 'INT'
    shutdown
  when 'TERM'
    # Heroku sends TERM signal, and waits 10 seconds before exit
    graceful_shutdown
  when 'HUP'
    graceful_shutdown
  end
end

#handler(&block) ⇒ Object

Raises:



32
33
34
35
36
# File 'lib/clockwork/manager.rb', line 32

def handler(&block)
  @handler = block if block_given?
  raise NoHandlerDefined unless @handler
  @handler
end

#log(msg) ⇒ Object



157
158
159
# File 'lib/clockwork/manager.rb', line 157

def log(msg)
  config[:logger].info(msg)
end

#log_error(e) ⇒ Object



149
150
151
# File 'lib/clockwork/manager.rb', line 149

def log_error(e)
  config[:logger].error(e)
end

#loggerObject



145
146
147
# File 'lib/clockwork/manager.rb', line 145

def logger
  config[:logger]
end

#on(event, options = {}, &block) ⇒ Object



43
44
45
46
# File 'lib/clockwork/manager.rb', line 43

def on(event, options={}, &block)
  raise "Unsupported callback #{event}" unless [:before_tick, :after_tick, :before_run, :after_run].include?(event.to_sym)
  (@callbacks[event.to_sym]||=[]) << block
end

#runObject



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/clockwork/manager.rb', line 64

def run
  log "Starting clock for #{@events.size} events: [ #{@events.map(&:to_s).join(' ')} ]"

  sig_read, sig_write = IO.pipe

  (%w[INT TERM HUP] & Signal.list.keys).each do |sig|
    trap sig do
      sig_write.puts(sig)
    end
  end

  run_tick_loop

  while io = IO.select([sig_read])
    sig = io.first[0].gets.chomp
    handle_signal(sig)
  end
end

#run_tick_loopObject



119
120
121
122
123
124
125
126
127
128
129
# File 'lib/clockwork/manager.rb', line 119

def run_tick_loop
  Thread.new do
    @mutex.synchronize do
      until @finish
        tick
        interval = config[:sleep_timeout] - Time.now.subsec + 0.001
        @condvar.wait(@mutex, interval) if interval > 0
      end
    end
  end
end

#shutdownObject



96
97
98
99
100
# File 'lib/clockwork/manager.rb', line 96

def shutdown
  logger.info 'Shutting down'
  stop_tick_loop
  exit(0)
end

#stop_tick_loopObject



109
110
111
# File 'lib/clockwork/manager.rb', line 109

def stop_tick_loop
  @finish = true
end

#thread_available?Boolean

Returns:

  • (Boolean)


17
18
19
# File 'lib/clockwork/manager.rb', line 17

def thread_available?
  Thread.list.select { |t| t['creator'] == self }.count < config[:max_threads]
end

#tick(t = Time.now) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/clockwork/manager.rb', line 131

def tick(t=Time.now)
  if (fire_callbacks(:before_tick))
    events = events_to_run(t)
    events.each do |event|
      if (fire_callbacks(:before_run, event, t))
        event.run(t)
        fire_callbacks(:after_run, event, t)
      end
    end
  end
  fire_callbacks(:after_tick)
  events
end

#wait_tick_loop_finishesObject



113
114
115
116
117
# File 'lib/clockwork/manager.rb', line 113

def wait_tick_loop_finishes
  @mutex.synchronize do # wait by synchronize
    @condvar.signal
  end
end