add barriers - close #53

This commit is contained in:
Josh Holtrop 2019-04-09 22:29:22 -04:00
parent 008fa4844d
commit 9bf4b8fa96
3 changed files with 78 additions and 6 deletions

View File

@ -0,0 +1,24 @@
class ThreadedTestBuilder < Rscons::Builder
def run(options)
if @command
puts "#{@target} finished"
true
else
@command = ["ruby", "-e", %[sleep #{@vars["delay"]}]]
register_command("ThreadedTestBuilder #{@target}", @command)
end
end
end
build do
Environment.new do |env|
env.add_builder(ThreadedTestBuilder)
env.ThreadedTestBuilder("T3", [], "delay" => 3)
env.ThreadedTestBuilder("T2", [], "delay" => 1.0)
env.ThreadedTestBuilder("T1", [], "delay" => 0.5)
env.barrier
env.ThreadedTestBuilder("T6", [], "delay" => 1.5)
env.ThreadedTestBuilder("T5", [], "delay" => 1.0)
env.ThreadedTestBuilder("T4", [], "delay" => 0.5)
end
end

View File

@ -70,7 +70,7 @@ module Rscons
@threads = {} @threads = {}
@registered_build_dependencies = {} @registered_build_dependencies = {}
@side_effects = {} @side_effects = {}
@builder_set = BuilderSet.new(@registered_build_dependencies, @side_effects) @builder_sets = []
@build_targets = {} @build_targets = {}
@user_deps = {} @user_deps = {}
# Hash of builder name (String) => builder class (Class). # Hash of builder name (String) => builder class (Class).
@ -265,12 +265,16 @@ module Rscons
@process_builder_waits = {} @process_builder_waits = {}
@process_builders_to_run = [] @process_builders_to_run = []
begin begin
while @builder_set.size > 0 or @threads.size > 0 or @process_commands_waiting_to_run.size > 0 while @builder_sets.size > 0 or @threads.size > 0 or @process_commands_waiting_to_run.size > 0
process_step process_step
if @builder_sets.size > 0 and @builder_sets.first.empty? and @threads.empty? and @process_commands_waiting_to_run.empty? and @process_builders_to_run.empty?
# Remove empty BuilderSet when all other operations have completed.
@builder_sets.slice!(0)
end
unless @process_failures.empty? unless @process_failures.empty?
# On a build failure, do not start any more builders or commands, # On a build failure, do not start any more builders or commands,
# but let the threads that have already been started complete. # but let the threads that have already been started complete.
@builder_set.clear @builder_sets.clear
@process_commands_waiting_to_run.clear @process_commands_waiting_to_run.clear
end end
end end
@ -290,7 +294,7 @@ module Rscons
# #
# @return [void] # @return [void]
def clear_targets def clear_targets
@builder_set.clear @builder_sets.clear
end end
# Define a build target. # Define a build target.
@ -319,7 +323,10 @@ module Rscons
cache: Cache.instance, cache: Cache.instance,
env: self, env: self,
vars: vars) vars: vars)
@builder_set << builder if @builder_sets.empty?
@builder_sets << build_builder_set
end
@builder_sets.last << builder
@build_targets[target] = builder @build_targets[target] = builder
builder builder
else else
@ -528,8 +535,26 @@ module Rscons
@build_targets[target] @build_targets[target]
end end
# Mark a "barrier" point.
#
# Rscons will wait for all build targets registered before the barrier to
# be built before beginning to build any build targets registered after
# the barrier. In other words, Rscons will not parallelize build operations
# across a barrier.
def barrier
@builder_sets << build_builder_set
end
private private
# Build a BuilderSet.
#
# @return [BuilderSet]
# New {BuilderSet} object.
def build_builder_set
BuilderSet.new(@registered_build_dependencies, @side_effects)
end
# Run a builder and process its return value. # Run a builder and process its return value.
# #
# @param builder [Builder] # @param builder [Builder]
@ -639,7 +664,9 @@ module Rscons
targets_still_building = @threads.reduce([]) do |result, (thread, obj)| targets_still_building = @threads.reduce([]) do |result, (thread, obj)|
result << builder_for_thread(thread).target result << builder_for_thread(thread).target
end end
builder = @builder_set.get_next_builder_to_run(targets_still_building) if @builder_sets.size > 0
builder = @builder_sets[0].get_next_builder_to_run(targets_still_building)
end
if builder if builder
builder.vars = @varset.merge(builder.vars) builder.vars = @varset.merge(builder.vars)

View File

@ -265,6 +265,27 @@ EOF
] ]
end end
it "supports barriers and prevents parallelizing builders across them" do
test_dir "simple"
result = run_rscons(rsconscript: "barrier.rb", rscons_args: %w[-j 3])
expect(result.stderr).to eq ""
slines = lines(result.stdout).select {|line| line =~ /T\d/}
expect(slines).to eq [
"ThreadedTestBuilder T3",
"ThreadedTestBuilder T2",
"ThreadedTestBuilder T1",
"T1 finished",
"T2 finished",
"T3 finished",
"ThreadedTestBuilder T6",
"ThreadedTestBuilder T5",
"ThreadedTestBuilder T4",
"T4 finished",
"T5 finished",
"T6 finished",
]
end
it "expands target and source paths starting with ^/ to be relative to the build root" do it "expands target and source paths starting with ^/ to be relative to the build root" do
test_dir("typical") test_dir("typical")
result = run_rscons(rsconscript: "carat.rb") result = run_rscons(rsconscript: "carat.rb")