| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| **/ |
| |
| #include "transaction/DeadLockDetector.hpp" |
| |
| #include <atomic> |
| #include <memory> |
| #include <vector> |
| |
| #include "transaction/AccessMode.hpp" |
| #include "transaction/DirectedGraph.hpp" |
| #include "transaction/LockTable.hpp" |
| #include "transaction/ResourceId.hpp" |
| #include "transaction/Transaction.hpp" |
| |
| #include "gtest/gtest.h" |
| |
| namespace quickstep { |
| namespace transaction { |
| |
| class DeadLockDetectorTest : public ::testing::Test { |
| protected: |
| DeadLockDetectorTest() |
| : lock_table_(std::make_unique<LockTable>()), |
| status_(DeadLockDetectorStatus::kDone) { |
| } |
| |
| std::unique_ptr<LockTable> lock_table_; |
| std::atomic<DeadLockDetectorStatus> status_; |
| std::vector<DirectedGraph::node_id> victims_; |
| }; |
| |
| TEST_F(DeadLockDetectorTest, SimpleCycle) { |
| const transaction_id transaction_one(1), transaction_two(2); |
| const ResourceId resource_one(1, 2), resource_two(4, 5); |
| |
| const AccessMode x_lock_mode(AccessMode::XLockMode()); |
| |
| // Produce a conflicting schedule. |
| // Transaction 1 will acquire X lock on resource 1. |
| lock_table_->putLock(transaction_one, |
| resource_one, |
| x_lock_mode); |
| |
| // Transaction 2 will acquire X lock on resource 2. |
| lock_table_->putLock(transaction_two, |
| resource_two, |
| x_lock_mode); |
| |
| // Transaction 1 will try to acquire X lock on resource 2, |
| // but it will fail since Transaction 2 has already acquired |
| // X lock on resource 2. |
| lock_table_->putLock(transaction_one, |
| resource_two, |
| x_lock_mode); |
| |
| // Transaction 2 will try to acquire X lock on resource 1, |
| // but it will fail since Transaction 1 has already acquired |
| // X lock on resource 2. |
| lock_table_->putLock(transaction_two, |
| resource_one, |
| x_lock_mode); |
| |
| // Run deadlock detector. |
| DeadLockDetector deadlock_detector(lock_table_.get(), &status_, &victims_); |
| status_.store(DeadLockDetectorStatus::kNotReady); |
| |
| deadlock_detector.start(); |
| |
| // Signal deadlock detector. |
| while (status_.load() == DeadLockDetectorStatus::kNotReady) { |
| } |
| |
| status_.store(DeadLockDetectorStatus::kQuit); |
| deadlock_detector.join(); |
| |
| // Victim size must be 1. |
| ASSERT_EQ(1u, victims_.size()); |
| } |
| |
| } // namespace transaction |
| } // namespace quickstep |