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.



70
71
72
73
74
75
76
77
78
79
# File 'lib/autorespawn/manager.rb', line 70

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

    @self_slave = Self.new(name: name)
    @workers << self_slave
    @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 (readonly)

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

#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)


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

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



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

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:



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

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

#collect_finished_slaves(wait: false) ⇒ Array<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



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/autorespawn/manager.rb', line 134

def collect_finished_slaves(wait: false)
    finished_slaves = Array.new
    waitpid_options =
        if wait then []
        else [Process::WNOHANG]
        end

    while finished_child = Process.waitpid2(-1, *waitpid_options)
        pid, status = *finished_child
        if slave = active_slaves.delete(pid)
            finished_slaves << slave
            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
        end
    end
    finished_slaves
rescue Errno::ECHILD
    Array.new
end

#include?(slave) ⇒ Boolean

Tests whether this slave is registered as a worker on self

Returns:

  • (Boolean)


89
90
91
# File 'lib/autorespawn/manager.rb', line 89

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

#killObject

Kill all active slaves

See Also:



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

def kill
    active_slaves.each { |s| s.kill(join: false) }
    while active_slaves != [self_slave]
        collect_finished_slaves(wait: true)
    end
end

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

Hook called when a slave finishes

Yield Parameters:



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

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



33
34
35
36
37
38
# File 'lib/autorespawn/manager.rb', line 33

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:



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

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



46
47
48
49
50
51
# File 'lib/autorespawn/manager.rb', line 46

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

#pollObject

Wait for children to terminate and spawns them when needed



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/autorespawn/manager.rb', line 194

def poll
    finished_slaves = collect_finished_slaves
    new_slaves = Array.new
    while active_slaves.size < parallel_level
        if slave_i = workers.index { |s| s.needs_spawn? }
            slave = workers.delete_at(slave_i)
            @workers = workers[slave_i..-1] + workers[0, slave_i] + [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

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

Add files to #seed

(see ProgramID#register_files)



84
85
86
# File 'lib/autorespawn/manager.rb', line 84

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



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

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



112
113
114
115
116
117
118
# File 'lib/autorespawn/manager.rb', line 112

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



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

def run
    while true
        poll
        sleep 1
    end

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