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 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 9 deletions(-) (limited to 'build2/test/rule.cxx') 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) -- cgit v1.1