aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2017-03-15 18:38:03 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2017-03-15 18:38:03 +0200
commit430e46f7e352b1146f289e82f614d0b68c75aab5 (patch)
tree8ede58e3ada80b63ce15a3e3e93e4850d60986f7
parent925d2d0eabe40517254380c5a12baa338b099d3e (diff)
Implement parallel testscript execution for single target
-rw-r--r--build2/scheduler2
-rw-r--r--build2/test/rule.cxx81
-rw-r--r--build2/test/script/parser.cxx9
3 files changed, 78 insertions, 14 deletions
diff --git a/build2/scheduler b/build2/scheduler
index a8f81cd..86a48df 100644
--- a/build2/scheduler
+++ b/build2/scheduler
@@ -399,7 +399,7 @@ namespace build2
//
struct task_data
{
- std::aligned_storage<sizeof (void*) * 7>::type data;
+ std::aligned_storage<sizeof (void*) * 8>::type data;
void (*thunk) (scheduler&, lock&, void*);
};
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<bool> o;
for (const target* pt: t.prerequisite_targets)
@@ -378,6 +379,8 @@ namespace build2
//
if (const testscript* ts = pt->is_a<testscript> ())
{
+ 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<scope_state> result;
+ result.reserve (count); // Make sure there are no reallocations.
+
for (const target* pt: t.prerequisite_targets)
{
if (const testscript* ts = pt->is_a<testscript> ())
{
- // 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::