From 430e46f7e352b1146f289e82f614d0b68c75aab5 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 15 Mar 2017 18:38:03 +0200 Subject: Implement parallel testscript execution for single target --- build2/test/rule.cxx | 81 ++++++++++++++++++++++++++++++++++++++----- build2/test/script/parser.cxx | 9 ++--- 2 files changed, 77 insertions(+), 13 deletions(-) (limited to 'build2/test') diff --git a/build2/test/rule.cxx b/build2/test/rule.cxx index 0a346d8..33ffb90 100644 --- a/build2/test/rule.cxx +++ b/build2/test/rule.cxx @@ -370,6 +370,7 @@ namespace build2 // which case it should be the only one. // bool one; + size_t count (0); { optional o; for (const target* pt: t.prerequisite_targets) @@ -378,6 +379,8 @@ namespace build2 // if (const testscript* ts = pt->is_a ()) { + count++; + bool r (ts->name == "testscript"); if ((r && o) || (!r && o && *o)) @@ -434,14 +437,23 @@ namespace build2 // bool mk (!one); - // Run all the testscripts. + // Start asynchronous execution of the testscripts. + // + wait_guard wg (target::count_busy (), t.task_count); + + // Result vector. // + using script::scope_state; + + vector result; + result.reserve (count); // Make sure there are no reallocations. + for (const target* pt: t.prerequisite_targets) { if (const testscript* ts = pt->is_a ()) { - // If this is just the testscript, then its id path is empty (and - // it can only be ignored by ignoring the test target, which makes + // If this is just the testscript, then its id path is empty (and it + // can only be ignored by ignoring the test target, which makes // sense since it's the only testscript file). // if (one || test (t, path (ts->name))) @@ -458,16 +470,67 @@ namespace build2 text << "test " << t << " with " << *ts << " on " << tt; } - script::parser p; - script::script s (t, *ts, wd); - p.pre_parse (s); - - script::default_runner r (*this); - p.execute (s, r); + result.push_back (scope_state::unknown); + scope_state& r (result.back ()); + + if (!sched.async (target::count_busy (), + t.task_count, + [this] (scope_state& r, + const target& t, + const testscript& ts, + const dir_path& wd, + const diag_frame* ds) noexcept + { + diag_frame df (ds); + try + { + script::script s (t, ts, wd); + + { + script::parser p; + p.pre_parse (s); + + script::default_runner r (*this); + p.execute (s, r); + } + + r = s.state; + } + catch (const failed&) + { + r = scope_state::failed; + } + }, + ref (r), + cref (t), + cref (*ts), + cref (wd), + diag_frame::stack)) + { + // Executed synchronously. If failed and we were not asked to + // keep going, bail out. + // + if (r == scope_state::failed && !keep_going) + throw failed (); + } } } } + wg.wait (); + + // Re-examine. + // + for (scope_state r: result) + { + switch (r) + { + case scope_state::passed: break; + case scope_state::failed: throw failed (); + case scope_state::unknown: assert (false); + } + } + // Cleanup. // if (!one && !mk) diff --git a/build2/test/script/parser.cxx b/build2/test/script/parser.cxx index fd21a58..9d17304 100644 --- a/build2/test/script/parser.cxx +++ b/build2/test/script/parser.cxx @@ -2832,8 +2832,8 @@ namespace build2 if (!s.empty ()) execute (s, s, r); - - s.state = scope_state::passed; + else + s.state = scope_state::passed; } void parser:: @@ -2972,7 +2972,7 @@ namespace build2 // if (!sched.async (task_count, [] (scope& s, script& scr, runner& r, - const diag_frame* ds) + const diag_frame* ds) noexcept { diag_frame df (ds); @@ -2980,7 +2980,6 @@ namespace build2 { parser p; p.execute (s, scr, r); - s.state = scope_state::passed; } catch (const failed&) { @@ -3024,6 +3023,8 @@ namespace build2 assert (false); runner_->leave (*scope_, scope_->end_loc_); + + scope_->state = scope_state::passed; } void parser:: -- cgit v1.1