aboutsummaryrefslogtreecommitdiffstats
path: root/eval/src/tests/eval/compile_cache/compile_cache_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'eval/src/tests/eval/compile_cache/compile_cache_test.cpp')
-rw-r--r--eval/src/tests/eval/compile_cache/compile_cache_test.cpp80
1 files changed, 60 insertions, 20 deletions
diff --git a/eval/src/tests/eval/compile_cache/compile_cache_test.cpp b/eval/src/tests/eval/compile_cache/compile_cache_test.cpp
index 1de56e605c9..a0dad889d9a 100644
--- a/eval/src/tests/eval/compile_cache/compile_cache_test.cpp
+++ b/eval/src/tests/eval/compile_cache/compile_cache_test.cpp
@@ -5,12 +5,16 @@
#include <vespa/eval/eval/test/eval_spec.h>
#include <vespa/vespalib/util/time.h>
#include <vespa/vespalib/util/threadstackexecutor.h>
+#include <vespa/vespalib/util/blockingthreadstackexecutor.h>
+#include <vespa/vespalib/util/stringfmt.h>
#include <thread>
#include <set>
using namespace vespalib;
using namespace vespalib::eval;
+using vespalib::make_string_short::fmt;
+
struct MyExecutor : public Executor {
std::vector<Executor::Task::UP> tasks;
Executor::Task::UP execute(Executor::Task::UP task) override {
@@ -157,7 +161,7 @@ TEST("require that cache usage works") {
}
TEST("require that async cache usage works") {
- ThreadStackExecutor executor(8, 256*1024);
+ auto executor = std::make_shared<ThreadStackExecutor>(8, 256*1024);
auto binding = CompileCache::bind(executor);
CompileCache::Token::UP token_a = CompileCache::compile(*Function::parse("x+y"), PassParams::SEPARATE);
EXPECT_EQUAL(5.0, token_a->get().get_function<2>()(2.0, 3.0));
@@ -166,7 +170,6 @@ TEST("require that async cache usage works") {
CompileCache::Token::UP token_c = CompileCache::compile(*Function::parse("x+y"), PassParams::SEPARATE);
EXPECT_EQUAL(5.0, token_c->get().get_function<2>()(2.0, 3.0));
EXPECT_EQUAL(CompileCache::num_cached(), 2u);
- executor.sync(); // wait for compile threads to drop all compile cache tokens
token_a.reset();
TEST_DO(verify_cache(2, 2));
token_b.reset();
@@ -176,24 +179,24 @@ TEST("require that async cache usage works") {
}
TEST("require that compile tasks are run in the most recently bound executor") {
- MyExecutor exe1;
- MyExecutor exe2;
+ auto exe1 = std::make_shared<MyExecutor>();
+ auto exe2 = std::make_shared<MyExecutor>();
auto token0 = CompileCache::compile(*Function::parse("a+b"), PassParams::SEPARATE);
EXPECT_EQUAL(CompileCache::num_bound(), 0u);
- EXPECT_EQUAL(exe1.tasks.size(), 0u);
- EXPECT_EQUAL(exe2.tasks.size(), 0u);
+ EXPECT_EQUAL(exe1->tasks.size(), 0u);
+ EXPECT_EQUAL(exe2->tasks.size(), 0u);
{
auto bind1 = CompileCache::bind(exe1);
auto token1 = CompileCache::compile(*Function::parse("a-b"), PassParams::SEPARATE);
EXPECT_EQUAL(CompileCache::num_bound(), 1u);
- EXPECT_EQUAL(exe1.tasks.size(), 1u);
- EXPECT_EQUAL(exe2.tasks.size(), 0u);
+ EXPECT_EQUAL(exe1->tasks.size(), 1u);
+ EXPECT_EQUAL(exe2->tasks.size(), 0u);
{
auto bind2 = CompileCache::bind(exe2);
auto token2 = CompileCache::compile(*Function::parse("a*b"), PassParams::SEPARATE);
EXPECT_EQUAL(CompileCache::num_bound(), 2u);
- EXPECT_EQUAL(exe1.tasks.size(), 1u);
- EXPECT_EQUAL(exe2.tasks.size(), 1u);
+ EXPECT_EQUAL(exe1->tasks.size(), 1u);
+ EXPECT_EQUAL(exe2->tasks.size(), 1u);
}
EXPECT_EQUAL(CompileCache::num_bound(), 1u);
}
@@ -201,9 +204,9 @@ TEST("require that compile tasks are run in the most recently bound executor") {
}
TEST("require that executors may be unbound in any order") {
- MyExecutor exe1;
- MyExecutor exe2;
- MyExecutor exe3;
+ auto exe1 = std::make_shared<MyExecutor>();
+ auto exe2 = std::make_shared<MyExecutor>();
+ auto exe3 = std::make_shared<MyExecutor>();
auto bind1 = CompileCache::bind(exe1);
auto bind2 = CompileCache::bind(exe2);
auto bind3 = CompileCache::bind(exe3);
@@ -213,13 +216,13 @@ TEST("require that executors may be unbound in any order") {
bind3.reset();
EXPECT_EQUAL(CompileCache::num_bound(), 1u);
auto token = CompileCache::compile(*Function::parse("a+b"), PassParams::SEPARATE);
- EXPECT_EQUAL(exe1.tasks.size(), 1u);
- EXPECT_EQUAL(exe2.tasks.size(), 0u);
- EXPECT_EQUAL(exe3.tasks.size(), 0u);
+ EXPECT_EQUAL(exe1->tasks.size(), 1u);
+ EXPECT_EQUAL(exe2->tasks.size(), 0u);
+ EXPECT_EQUAL(exe3->tasks.size(), 0u);
}
TEST("require that the same executor can be bound multiple times") {
- MyExecutor exe1;
+ auto exe1 = std::make_shared<MyExecutor>();
auto bind1 = CompileCache::bind(exe1);
auto bind2 = CompileCache::bind(exe1);
auto bind3 = CompileCache::bind(exe1);
@@ -230,7 +233,7 @@ TEST("require that the same executor can be bound multiple times") {
EXPECT_EQUAL(CompileCache::num_bound(), 1u);
auto token = CompileCache::compile(*Function::parse("a+b"), PassParams::SEPARATE);
EXPECT_EQUAL(CompileCache::num_bound(), 1u);
- EXPECT_EQUAL(exe1.tasks.size(), 1u);
+ EXPECT_EQUAL(exe1->tasks.size(), 1u);
}
struct CompileCheck : test::EvalSpec::EvalTest {
@@ -286,9 +289,9 @@ TEST_F("compile sequentially, then run all conformance tests", test::EvalSpec())
TEST_F("compile concurrently (8 threads), then run all conformance tests", test::EvalSpec()) {
f1.add_all_cases();
- ThreadStackExecutor executor(8, 256*1024);
+ auto executor = std::make_shared<ThreadStackExecutor>(8, 256*1024);
auto binding = CompileCache::bind(executor);
- while (executor.num_idle_workers() < 8) {
+ while (executor->num_idle_workers() < 8) {
std::this_thread::sleep_for(1ms);
}
for (size_t i = 0; i < 2; ++i) {
@@ -305,6 +308,43 @@ TEST_F("compile concurrently (8 threads), then run all conformance tests", test:
}
}
+struct MyCompileTask : public Executor::Task {
+ size_t seed;
+ size_t loop;
+ MyCompileTask(size_t seed_in, size_t loop_in) : seed(seed_in), loop(loop_in) {}
+ void run() override {
+ for (size_t i = 0; i < loop; ++i) {
+ // use custom constant to make a unique function that needs compilation
+ auto token = CompileCache::compile(*Function::parse(fmt("%zu", seed + i)), PassParams::SEPARATE);
+ }
+ }
+};
+
+TEST_MT_FF("require that deadlock is avoided with blocking executor", 8, std::shared_ptr<Executor>(nullptr), TimeBomb(300)) {
+ size_t loop = 16;
+ if (thread_id == 0) {
+ auto t0 = steady_clock::now();
+ f1 = std::make_shared<BlockingThreadStackExecutor>(2, 256*1024, 3);
+ auto binding = CompileCache::bind(f1);
+ TEST_BARRIER(); // #1
+ for (size_t i = 0; i < num_threads; ++i) {
+ f1->execute(std::make_unique<MyCompileTask>(i * loop, loop));
+ }
+ TEST_BARRIER(); // #2
+ auto t1 = steady_clock::now();
+ fprintf(stderr, "deadlock test took %" PRIu64 " ms\n", count_ms(t1 - t0));
+
+ } else {
+ TEST_BARRIER(); // #1
+ size_t seed = (10000 + (thread_id * loop));
+ for (size_t i = 0; i < loop; ++i) {
+ // use custom constant to make a unique function that needs compilation
+ auto token = CompileCache::compile(*Function::parse(fmt("%zu", seed + i)), PassParams::SEPARATE);
+ }
+ TEST_BARRIER(); // #2
+ }
+}
+
//-----------------------------------------------------------------------------
TEST_MAIN() { TEST_RUN_ALL(); }