aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib/src/tests/portal/handle_manager
diff options
context:
space:
mode:
authorHåvard Pettersen <havardpe@oath.com>2018-10-29 10:04:19 +0000
committerHåvard Pettersen <havardpe@oath.com>2018-11-27 15:11:06 +0000
commitc52bd31c7208d4bab093efb24acb9a3452a0b936 (patch)
tree1fc9c4b9b03dc1fe305e995a41c413da76df493a /vespalib/src/tests/portal/handle_manager
parent95323a3a983e3b87f83ea7eaee990ce6070ed699 (diff)
initial portal code
Diffstat (limited to 'vespalib/src/tests/portal/handle_manager')
-rw-r--r--vespalib/src/tests/portal/handle_manager/CMakeLists.txt8
-rw-r--r--vespalib/src/tests/portal/handle_manager/handle_manager_test.cpp146
2 files changed, 154 insertions, 0 deletions
diff --git a/vespalib/src/tests/portal/handle_manager/CMakeLists.txt b/vespalib/src/tests/portal/handle_manager/CMakeLists.txt
new file mode 100644
index 00000000000..b12b72f9ad6
--- /dev/null
+++ b/vespalib/src/tests/portal/handle_manager/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespalib_handle_manager_test_app TEST
+ SOURCES
+ handle_manager_test.cpp
+ DEPENDS
+ vespalib
+)
+vespa_add_test(NAME vespalib_handle_manager_test_app COMMAND vespalib_handle_manager_test_app)
diff --git a/vespalib/src/tests/portal/handle_manager/handle_manager_test.cpp b/vespalib/src/tests/portal/handle_manager/handle_manager_test.cpp
new file mode 100644
index 00000000000..4ce25aa0a7a
--- /dev/null
+++ b/vespalib/src/tests/portal/handle_manager/handle_manager_test.cpp
@@ -0,0 +1,146 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/portal/handle_manager.h>
+#include <vespa/vespalib/util/gate.h>
+
+#include <thread>
+#include <chrono>
+#include <atomic>
+
+using namespace vespalib;
+using namespace vespalib::portal;
+
+TEST_F("require that handles can be created, locked and destroyed", TimeBomb(60)) {
+ HandleManager manager;
+ uint64_t handle = manager.create();
+ EXPECT_TRUE(handle != manager.null_handle());
+ {
+ HandleGuard guard = manager.lock(handle);
+ EXPECT_TRUE(guard.valid());
+ EXPECT_EQUAL(guard.handle(), handle);
+ }
+ EXPECT_TRUE(manager.destroy(handle));
+ {
+ HandleGuard guard = manager.lock(handle);
+ EXPECT_TRUE(!guard.valid());
+ EXPECT_EQUAL(guard.handle(), manager.null_handle());
+ }
+}
+
+TEST_F("require that multiple guards can be taken for the same handle", TimeBomb(60)) {
+ HandleManager manager;
+ uint64_t handle = manager.create();
+ EXPECT_TRUE(handle != manager.null_handle());
+ {
+ HandleGuard guard1 = manager.lock(handle);
+ HandleGuard guard2 = manager.lock(handle); // <- does not block
+ EXPECT_TRUE(guard1.valid());
+ EXPECT_EQUAL(guard1.handle(), handle);
+ EXPECT_TRUE(guard2.valid());
+ EXPECT_EQUAL(guard2.handle(), handle);
+ }
+ EXPECT_TRUE(manager.destroy(handle));
+}
+
+TEST_F("require that handles are independent", TimeBomb(60)) {
+ HandleManager manager;
+ uint64_t handle1 = manager.create();
+ uint64_t handle2 = manager.create();
+ uint64_t handle3 = manager.create();
+ EXPECT_TRUE(handle1 != manager.null_handle());
+ EXPECT_TRUE(handle2 != manager.null_handle());
+ EXPECT_TRUE(handle3 != manager.null_handle());
+ EXPECT_TRUE(handle1 != handle2);
+ EXPECT_TRUE(handle1 != handle3);
+ EXPECT_TRUE(handle2 != handle3);
+ {
+ HandleGuard guard1 = manager.lock(handle1);
+ HandleGuard guard2 = manager.lock(handle2);
+ EXPECT_TRUE(guard1.valid());
+ EXPECT_EQUAL(guard1.handle(), handle1);
+ EXPECT_TRUE(guard2.valid());
+ EXPECT_EQUAL(guard2.handle(), handle2);
+ EXPECT_TRUE(manager.destroy(handle3)); // <- does not block
+ HandleGuard guard3 = manager.lock(handle3);
+ EXPECT_TRUE(!guard3.valid());
+ EXPECT_EQUAL(guard3.handle(), manager.null_handle());
+ }
+ EXPECT_TRUE(manager.destroy(handle1));
+ EXPECT_TRUE(manager.destroy(handle2));
+ EXPECT_TRUE(!manager.destroy(handle3));
+}
+
+struct Fixture {
+ HandleManager manager;
+ uint64_t handle;
+ Gate gate;
+ std::atomic<size_t> cnt1;
+ std::atomic<size_t> cnt2;
+ Fixture() : manager(), handle(manager.create()), gate(), cnt1(0), cnt2(0) {}
+};
+
+TEST_MT_FF("require that destroy waits for active handle guards", 2, Fixture(), TimeBomb(60)) {
+ if (thread_id == 0) {
+ {
+ auto guard = f1.manager.lock(f1.handle);
+ TEST_BARRIER(); // #1
+ EXPECT_TRUE(!f1.gate.await(20));
+ }
+ EXPECT_TRUE(f1.gate.await(60000));
+ } else {
+ TEST_BARRIER(); // #1
+ EXPECT_TRUE(f1.manager.destroy(f1.handle));
+ f1.gate.countDown();
+ }
+}
+
+TEST_MT_FF("require that destroy disables ability to lock handles", 3, Fixture(), TimeBomb(60)) {
+ if (thread_id == 0) {
+ auto guard = f1.manager.lock(f1.handle);
+ ASSERT_TRUE(guard.valid());
+ TEST_BARRIER(); // #1
+ while (f1.cnt1 == 0) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+ EXPECT_EQUAL(f1.cnt2, 0u);
+ } else if (thread_id == 1) {
+ TEST_BARRIER(); // #1
+ EXPECT_TRUE(f1.manager.destroy(f1.handle));
+ EXPECT_EQUAL(f1.cnt1, 1u);
+ ++f1.cnt2;
+ } else {
+ TEST_BARRIER(); // #1
+ while (f1.cnt1 == 0) {
+ auto guard = f1.manager.lock(f1.handle);
+ if (guard.valid()) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ } else {
+ EXPECT_EQUAL(f1.cnt2, 0u);
+ ++f1.cnt1;
+ }
+ }
+ }
+}
+
+TEST_MT_FF("require that a single destroy call returns true", 10, Fixture(), TimeBomb(60)) {
+ if (thread_id == 0) { // 1 thread here
+ auto guard = f1.manager.lock(f1.handle);
+ ASSERT_TRUE(guard.valid());
+ TEST_BARRIER(); // #1
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ } else { // 'many' threads here
+ TEST_BARRIER(); // #1
+ if (f1.manager.destroy(f1.handle)) {
+ ++f1.cnt1;
+ } else {
+ ++f1.cnt2;
+ }
+ }
+ TEST_BARRIER(); // #2
+ EXPECT_EQUAL(f1.cnt1, 1u);
+ EXPECT_GREATER(num_threads, 5u);
+ EXPECT_EQUAL(f1.cnt2, (num_threads - 2));
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }