autorespawn 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8d577eb9b9d0d736476e22b3431a5a791ed704e6
4
- data.tar.gz: f80a5b60abbe557c1316a9a560be65be4243a43c
3
+ metadata.gz: 7611aabbaa1ad2c8d52ca80fa16ea0c66960dec5
4
+ data.tar.gz: 72812f7b5baf6deefa772122cd8cee4f29f4ff53
5
5
  SHA512:
6
- metadata.gz: 869b489054c370e01612a5b1b7dfed6a0dad02de82e1b00ac8de9b8903cb0d0020b68dccd3e8b0fa0fa1b2aaa1e3793cbfe0d3efe40f154c546fe25502e92a4f
7
- data.tar.gz: 5ffdcebad8ac26a6f37068543de8f36d0e641ccd91d4625a06821cd1d12f0e978a7f079cb4d7508928bf26afd6c0587af8a33468db95792f88fa9e54314eb6e0
6
+ metadata.gz: d94adfafc6192c05751edfb426a4f0fda3077bdc111ccdfdda44374feb4bba8410308bc13a3053e1d4017b3a20462b14b82a69acf9dbe3a67ecacfed67e93962
7
+ data.tar.gz: f466b32d71fdf8a7b14216c85025c20727f14a361331d779c98f6e3a5deb317b8a33e2c9d3ced02c14075add52fd78e58b30d53147dedf71ea96fffa8ea86152
data/.gitignore CHANGED
@@ -8,3 +8,4 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  /vendor/
11
+ .*.sw?
data/autorespawn.gemspec CHANGED
@@ -31,5 +31,6 @@ EOD
31
31
  spec.add_development_dependency "minitest", ">= 5.0", "~> 5.0"
32
32
  spec.add_development_dependency "fakefs", ">= 0.6", "~> 0.6.0"
33
33
  spec.add_development_dependency 'flexmock', ">= 2.0", '~> 2.0'
34
+ spec.add_development_dependency "simplecov", '>= 0.11'
34
35
  spec.add_development_dependency "coveralls"
35
36
  end
data/lib/autorespawn.rb CHANGED
@@ -8,6 +8,7 @@
8
8
  require "autorespawn/slave"
9
9
  require "autorespawn/self"
10
10
  require "autorespawn/manager"
11
+ require 'autorespawn/tracked_file'
11
12
 
12
13
  # Automatically exec's the current program when one of the source file changes
13
14
  #
@@ -25,6 +25,9 @@ class Manager
25
25
  attr_reader :active_slaves
26
26
  # @return [Array<Slave>] list of slaves explicitely queued with {#queue}
27
27
  attr_reader :queued_slaves
28
+ # @return [Hash<Pathname,TrackedFile>] the whole set of files that are
29
+ # tracked by this manager's slaves
30
+ attr_reader :tracked_files
28
31
 
29
32
  # @!group Hooks
30
33
 
@@ -74,6 +77,7 @@ def initialize(name: nil, parallel_level: 1)
74
77
  @workers = Array.new
75
78
  @name = name
76
79
  @seed = ProgramID.for_self
80
+ @tracked_files = Hash.new
77
81
 
78
82
  @self_slave = Self.new(name: name)
79
83
  @workers << self_slave
@@ -121,6 +125,7 @@ def active?(slave)
121
125
  # reporting / tracking
122
126
  def add_slave(*cmdline, name: nil, **spawn_options)
123
127
  slave = Slave.new(*cmdline, name: name, seed: seed, **spawn_options)
128
+ slave.needed!
124
129
  register_slave(slave)
125
130
  slave
126
131
  end
@@ -168,11 +173,20 @@ def collect_finished_slaves
168
173
  def process_finished_slave(pid, status)
169
174
  return if !(slave = active_slaves.delete(pid))
170
175
 
171
- slave.finished(status)
176
+ if slave.finished(status).empty?
177
+ # Do not register the slave if it is already marked as needed?
178
+ slave.each_tracked_file(with_status: true) do |path, mtime, size|
179
+ tracker = (tracked_files[path] ||= TrackedFile.new(path, mtime: mtime, size: size))
180
+ tracker.slaves << slave
181
+ end
182
+ slave.not_needed!
183
+ end
184
+
172
185
  slave.subcommands.each do |name, cmdline, spawn_options|
173
186
  add_slave(*cmdline, name: name, **spawn_options)
174
187
  end
175
188
  seed.merge!(slave.program_id)
189
+
176
190
  run_hook :on_slave_finished, slave
177
191
  slave
178
192
  end
@@ -214,10 +228,25 @@ def run
214
228
  end
215
229
  end
216
230
 
231
+ def trigger_slaves_as_necessary
232
+ tracked_files.delete_if do |path, tracker|
233
+ tracker.slaves.delete_if(&:needed?)
234
+ if tracker.slaves.empty?
235
+ true
236
+ elsif tracker.update
237
+ tracker.slaves.each(&:needed!)
238
+ true
239
+ end
240
+ end
241
+ end
242
+
217
243
  # Wait for children to terminate and spawns them when needed
218
- def poll(autospawn: true)
244
+ def poll(autospawn: true, update_files: true)
219
245
  finished_slaves = collect_finished_slaves
220
246
  new_slaves = Array.new
247
+
248
+ trigger_slaves_as_necessary
249
+
221
250
  while active_slaves.size < parallel_level + 1
222
251
  if slave = queued_slaves.find { |s| !s.running? }
223
252
  queued_slaves.delete(slave)
@@ -228,6 +257,9 @@ def poll(autospawn: true)
228
257
 
229
258
  if slave
230
259
  slave.spawn
260
+ # We manually track the slave's needed flag, just forcefully
261
+ # set it to false at that point
262
+ slave.not_needed!
231
263
  run_hook :__on_slave_start, slave
232
264
  new_slaves << slave
233
265
  active_slaves[slave.pid] = slave
@@ -128,8 +128,15 @@ def refresh
128
128
  # Enumerate the path of all the files that are being tracked
129
129
  #
130
130
  # @yieldparam [Pathname] path
131
- def each_tracked_file(&block)
132
- files.keys.each(&block)
131
+ def each_tracked_file(with_status: false, &block)
132
+ if with_status
133
+ return enum_for(__method__, with_status: true) if !block_given?
134
+ files.each do |path, info|
135
+ yield(path, info.mtime, info.size)
136
+ end
137
+ else
138
+ files.keys.each(&block)
139
+ end
133
140
  end
134
141
 
135
142
  # Returns a string that can ID this program
@@ -7,7 +7,7 @@ def initialize(*args, **options)
7
7
  @pid = Process.pid
8
8
  end
9
9
 
10
- def needed?; false end
10
+ def needed?(*); false end
11
11
  def needed!; end
12
12
  def spawn
13
13
  pid
@@ -61,6 +61,11 @@ def inspect
61
61
  "#<Autorespawn::Slave #{object_id.to_s(16)} #{cmdline.join(" ")}>"
62
62
  end
63
63
 
64
+ # Enumerate the files that are tracked for {#needed?}
65
+ def each_tracked_file(with_status: false, &block)
66
+ @program_id.each_tracked_file(with_status: with_status, &block)
67
+ end
68
+
64
69
  def to_s; inspect end
65
70
 
66
71
  # Register files on the program ID
@@ -85,7 +90,7 @@ def spawn
85
90
  SLAVE_RESULT_ENV => result_w.fileno.to_s)
86
91
 
87
92
  program_id.refresh
88
- @needed = false
93
+ @needed = nil
89
94
  pid = Kernel.spawn(env, *cmdline, initial_r => initial_r, result_w => result_w, **spawn_options)
90
95
  initial_r.close
91
96
  result_w.close
@@ -113,7 +118,11 @@ def spawn
113
118
  # Whether this slave would need to be spawned, either because it has
114
119
  # never be, or because the program ID changed
115
120
  def needed?
116
- !running? && (@needed || program_id.changed?)
121
+ if running? then false
122
+ elsif !@needed.nil?
123
+ @needed
124
+ else program_id.changed?
125
+ end
117
126
  end
118
127
 
119
128
  # Marks this slave for execution
@@ -127,11 +136,18 @@ def needed!
127
136
  @needed = true
128
137
  end
129
138
 
130
- # Resets {#needed?} to false
139
+ # Forces {#needed?} to return false
140
+ #
141
+ # Call {#needed_auto} to revert back to determining if the slave is
142
+ # needed or not using the tracked files
131
143
  def not_needed!
132
144
  @needed = false
133
145
  end
134
146
 
147
+ def needed_auto
148
+ @needed = nil
149
+ end
150
+
135
151
  # Whether the slave is running
136
152
  def running?
137
153
  pid && !status
@@ -177,6 +193,9 @@ def success?
177
193
  # Announce that the slave already finished, with the given exit status
178
194
  #
179
195
  # @param [Process::Status] the exit status
196
+ # @return [Array<Pathname>] a set of files that either changed or got
197
+ # added since the call to {#spawn}. If not empty, the slave calls
198
+ # {#needed!} by itself to force a re-execution
180
199
  def finished(status)
181
200
  @status = status
182
201
  read_queued_result
@@ -188,8 +207,8 @@ def finished(status)
188
207
  file_list = Array.new
189
208
  @success = false
190
209
  end
191
- modified = program_id.register_files(file_list)
192
210
  @program_id = program_id.slice(file_list)
211
+ modified = program_id.register_files(file_list)
193
212
  if !modified.empty?
194
213
  needed!
195
214
  end
@@ -0,0 +1,25 @@
1
+ class Autorespawn
2
+ class TrackedFile
3
+ attr_reader :path, :mtime, :size, :slaves
4
+
5
+ def initialize(path, mtime: nil, size: nil)
6
+ @path = path
7
+ @mtime = mtime
8
+ @size = size
9
+ @slaves = Array.new
10
+ end
11
+
12
+ def update
13
+ return true if !path.exist?
14
+ return true if !mtime
15
+
16
+ stat = path.stat
17
+ if stat.mtime != mtime || stat.size != size
18
+ @mtime = stat.mtime
19
+ @size = stat.size
20
+ true
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -1,3 +1,3 @@
1
1
  class Autorespawn
2
- VERSION = "0.4.1"
2
+ VERSION = "0.5.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: autorespawn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sylvain Joyeux
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-12-22 00:00:00.000000000 Z
11
+ date: 2015-12-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hooks
@@ -112,6 +112,20 @@ dependencies:
112
112
  - - "~>"
113
113
  - !ruby/object:Gem::Version
114
114
  version: '2.0'
115
+ - !ruby/object:Gem::Dependency
116
+ name: simplecov
117
+ requirement: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0.11'
122
+ type: :development
123
+ prerelease: false
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0.11'
115
129
  - !ruby/object:Gem::Dependency
116
130
  name: coveralls
117
131
  requirement: !ruby/object:Gem::Requirement
@@ -151,6 +165,7 @@ files:
151
165
  - lib/autorespawn/program_id.rb
152
166
  - lib/autorespawn/self.rb
153
167
  - lib/autorespawn/slave.rb
168
+ - lib/autorespawn/tracked_file.rb
154
169
  - lib/autorespawn/version.rb
155
170
  - lib/autorespawn/watch.rb
156
171
  homepage: https://github.com/doudou/autorespawn