Class: Autorespawn::Manager

Inherits:
Object
  • Object
show all
Includes:
Hooks, Hooks::InstanceHooks
Defined in:
lib/autorespawn/manager.rb

Overview

Manager of a bunch of autorespawn slaves

Instance Attribute Summary collapse

Hooks collapse

Instance Method Summary collapse

Methods included from Hooks

included

Constructor Details

#initialize(name: nil, parallel_level: 1) ⇒ Manager

Returns a new instance of Manager.



72
73
74
75
76
77
78
79
80
81
82
# File 'lib/autorespawn/manager.rb', line 72

def initialize(name: nil, parallel_level: 1)
    @parallel_level = parallel_level
    @workers   = Array.new
    @name = name
    @seed = ProgramID.for_self

    @self_slave = Self.new(name: name)
    @workers << self_slave
    @queued_slaves = Array.new
    @active_slaves = Hash[self_slave.pid => self_slave]
end

Instance Attribute Details

#active_slavesHash<Slave> (readonly)

Returns list of active slaves.

Returns:

  • (Hash<Slave>)

    list of active slaves



25
26
27
# File 'lib/autorespawn/manager.rb', line 25

def active_slaves
  @active_slaves
end

#nameObject (readonly)

Returns an object that is used to identify the manager itself.

Returns:

  • (Object)

    an object that is used to identify the manager itself



14
15
16
# File 'lib/autorespawn/manager.rb', line 14

def name
  @name
end

#parallel_levelInteger

Returns the number of processes allowed to work in parallel.

Returns:

  • (Integer)

    the number of processes allowed to work in parallel



20
21
22
# File 'lib/autorespawn/manager.rb', line 20

def parallel_level
  @parallel_level
end

#queued_slavesArray<Slave> (readonly)

Returns list of slaves explicitely queued with #queue.

Returns:

  • (Array<Slave>)

    list of slaves explicitely queued with #queue



27
28
29
# File 'lib/autorespawn/manager.rb', line 27

def queued_slaves
  @queued_slaves
end

#seedProgramID (readonly)

Returns a seed object that is passed to new slaves to represent the currently known state of file, to avoid unnecessary respawning.

Returns:

  • (ProgramID)

    a seed object that is passed to new slaves to represent the currently known state of file, to avoid unnecessary respawning

See Also:

  • add_seed_file


12
13
14
# File 'lib/autorespawn/manager.rb', line 12

def seed
  @seed
end

#self_slaveSelf (readonly)

Returns an object that has the same API than [Slave] to represent the manager’s process itself. It is always included in #workers and #active_slaves.

Returns:

  • (Self)

    an object that has the same API than [Slave] to represent the manager’s process itself. It is always included in #workers and #active_slaves



18
19
20
# File 'lib/autorespawn/manager.rb', line 18

def self_slave
  @self_slave
end

#workersArray<Slave> (readonly)

Returns declared worker processes, as a hash from the PID to a Slave object.

Returns:

  • (Array<Slave>)

    declared worker processes, as a hash from the PID to a Slave object



23
24
25
# File 'lib/autorespawn/manager.rb', line 23

def workers
  @workers
end

Instance Method Details

#active?(slave) ⇒ Boolean

Tests whether this slave is currently active on self

Returns:

  • (Boolean)


113
114
115
# File 'lib/autorespawn/manager.rb', line 113

def active?(slave)
    active_slaves[slave.pid] == slave
end

#add_slave(*cmdline, name: nil, **spawn_options) ⇒ Object

Spawns a worker, i.e. a program that will perform the intended work and report the program state

Parameters:

  • name (Object) (defaults to: nil)

    an arbitrary object that can be used for reporting / tracking



122
123
124
125
126
# File 'lib/autorespawn/manager.rb', line 122

def add_slave(*cmdline, name: nil, **spawn_options)
    slave = Slave.new(*cmdline, name: name, seed: seed, **spawn_options)
    register_slave(slave)
    slave
end

#clearObject

Kill and remove all workers from this manager

See Also:



195
196
197
198
199
200
201
202
# File 'lib/autorespawn/manager.rb', line 195

def clear
    kill
    workers.dup.each do |w|
        if w != self_slave
            remove_slave(w)
        end
    end
end

#collect_finished_slavesArray<Slave>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Collect information about the finished slaves

Returns:

  • (Array<Slave>)

    the slaves that finished



158
159
160
161
162
163
164
165
166
# File 'lib/autorespawn/manager.rb', line 158

def collect_finished_slaves
    finished_slaves = Array.new
    while finished_child = Process.waitpid2(-1, Process::WNOHANG)
        finished_slaves << process_finished_slave(*finished_child)
    end
    finished_slaves
rescue Errno::ECHILD
    finished_slaves
end

#has_active_slaves?Boolean

Tests whether this manager has some slaves that are active

Returns:

  • (Boolean)


108
109
110
# File 'lib/autorespawn/manager.rb', line 108

def has_active_slaves?
    active_slaves.size != 1
end

#has_slaves?Boolean

Check whether this manager has slaves

Returns:

  • (Boolean)


92
93
94
95
# File 'lib/autorespawn/manager.rb', line 92

def has_slaves?
    # There's always a worker for self
    workers.size != 1
end

#include?(slave) ⇒ Boolean

Tests whether this slave is registered as a worker on self

Returns:

  • (Boolean)


103
104
105
# File 'lib/autorespawn/manager.rb', line 103

def include?(slave)
    workers.include?(slave)
end

#killObject

Kill all active slaves

See Also:



183
184
185
186
187
188
189
190
# File 'lib/autorespawn/manager.rb', line 183

def kill
    active_slaves.each_value { |s| s.kill(join: false) }
    while has_active_slaves?
        finished_child = Process.waitpid2(-1)
        process_finished_slave(*finished_child)
    end
rescue Errno::ECHILD
end

#on_slave_finished {|the| ... } ⇒ Object

Hook called when a slave finishes

Yield Parameters:



61
# File 'lib/autorespawn/manager.rb', line 61

define_hooks :on_slave_finished

#on_slave_new(&block) {|the| ... } ⇒ Object

Register a callback for when a new slave is added by #add_slave

Parameters:

  • block (#call)

    the callback

Yield Parameters:

  • the (Slave)

    new slave



35
36
37
38
39
40
# File 'lib/autorespawn/manager.rb', line 35

def on_slave_new(&block)
    __on_slave_new(&block)
    workers.each do |w|
        block.call(w)
    end
end

#on_slave_removed {|the| ... } ⇒ Object

Hook called when a slave has been removed from this manager

Yield Parameters:



68
# File 'lib/autorespawn/manager.rb', line 68

define_hooks :on_slave_removed

#on_slave_start(&block) {|the| ... } ⇒ Object

Register a callback that should be called when a new slave has been spawned by #poll

Parameters:

  • block (#call)

    the callback

Yield Parameters:

  • the (Slave)

    newly started slave



48
49
50
51
52
53
# File 'lib/autorespawn/manager.rb', line 48

def on_slave_start(&block)
    __on_slave_start(&block)
    active_slaves.each_value do |w|
        block.call(w)
    end
end

#poll(autospawn: true) ⇒ Object

Wait for children to terminate and spawns them when needed



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/autorespawn/manager.rb', line 218

def poll(autospawn: true)
    finished_slaves = collect_finished_slaves
    new_slaves = Array.new
    while active_slaves.size < parallel_level + 1
        if slave = queued_slaves.find { |s| !s.running? }
            queued_slaves.delete(slave)
        elsif autospawn && (slave_i = workers.index { |s| s.needed? })
            slave = workers.delete_at(slave_i)
            @workers = workers[slave_i..-1] + workers[0, slave_i] + [slave]
        end

        if slave
            slave.spawn
            run_hook :__on_slave_start, slave
            new_slaves << slave
            active_slaves[slave.pid] = slave
        else
            break
        end
    end
    return new_slaves, finished_slaves
end

#process_finished_slave(pid, status) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
# File 'lib/autorespawn/manager.rb', line 168

def process_finished_slave(pid, status)
    return if !(slave = active_slaves.delete(pid))

    slave.finished(status)
    slave.subcommands.each do |name, cmdline, spawn_options|
        add_slave(*cmdline, name: name, **spawn_options)
    end
    seed.merge!(slave.program_id)
    run_hook :on_slave_finished, slave
    slave
end

#queue(slave) ⇒ Object

Queue a slave for execution



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

def queue(slave)
    queued_slaves << slave
end

#register_seed_files(files, search_patch = seed.ruby_load_path, ignore_not_found: true) ⇒ Object

Add files to #seed

(see ProgramID#register_files)



87
88
89
# File 'lib/autorespawn/manager.rb', line 87

def register_seed_files(files, search_patch = seed.ruby_load_path, ignore_not_found: true)
    seed.register_files(files, search_path, ignore_not_found)
end

#register_slave(slave) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Registers a slave



142
143
144
145
146
# File 'lib/autorespawn/manager.rb', line 142

def register_slave(slave)
    workers << slave
    run_hook :__on_slave_new, slave
    slave
end

#remove_slave(slave) ⇒ Object

Remove a worker from this manager

Raises:

  • (ArgumentError)

    if the slave is still running



131
132
133
134
135
136
137
# File 'lib/autorespawn/manager.rb', line 131

def remove_slave(slave)
    if active?(slave)
        raise ArgumentError, "#{slave} is still running"
    end
    workers.delete(slave)
    run_hook :on_slave_removed, slave
end

#runObject



204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/autorespawn/manager.rb', line 204

def run
    while true
        poll
        sleep 1
    end

rescue Interrupt
ensure
    active_slaves.values.each do |slave|
        slave.kill
    end
end

#slave_countObject

The number of slaves registered



98
99
100
# File 'lib/autorespawn/manager.rb', line 98

def slave_count
    workers.size - 1
end