1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2/* 3 * Copyright 2017 Couchbase, Inc 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18#include <atomic> 19#include <chrono> 20#include <thread> 21#include <vector> 22 23#include <gtest/gtest.h> 24 25#include <phosphor/chunk_lock.h> 26 27class ChunkLockTest : public testing::Test { 28protected: 29 phosphor::ChunkLock lock{phosphor::non_trivial_constructor}; 30}; 31 32TEST_F(ChunkLockTest, basic) { 33 lock.slave().lock(); 34 lock.slave().unlock(); 35 36 lock.master().lock(); 37 EXPECT_FALSE(lock.slave().try_lock()); 38 lock.master().unlock(); 39 EXPECT_TRUE(lock.slave().try_lock()); 40 lock.slave().unlock(); 41} 42 43class ThreadedChunkLockTest : public ChunkLockTest { 44protected: 45 ThreadedChunkLockTest() : step(0) {} 46 47 virtual ~ThreadedChunkLockTest() { 48 for (auto& thread : threads) { 49 thread.join(); 50 } 51 } 52 53 std::vector<std::thread> threads; 54 std::atomic<int> step; 55}; 56 57// These tests are more or less imported from the sentinel test. 58// They're not great but do a slight sanity check that TSan 59// might pick up on. 60 61TEST_F(ThreadedChunkLockTest, SlaveSlave) { 62 threads.emplace_back([this]() { 63 lock.slave().lock(); 64 ++step; 65 // Need to sleep to ensure the other thread has 66 // started spinning to acquire the lock 67 std::this_thread::sleep_for(std::chrono::milliseconds(50)); 68 lock.slave().unlock(); 69 }); 70 71 threads.emplace_back([this]() { 72 while (step.load() != 1) { 73 } 74 lock.slave().lock(); 75 lock.slave().unlock(); 76 }); 77} 78 79TEST_F(ThreadedChunkLockTest, SlaveMaster) { 80 threads.emplace_back([this]() { 81 lock.slave().lock(); 82 ++step; 83 // Need to sleep to ensure the other thread has 84 // started spinning to acquire the lock 85 std::this_thread::sleep_for(std::chrono::milliseconds(50)); 86 lock.slave().unlock(); 87 }); 88 89 threads.emplace_back([this]() { 90 while (step.load() != 1) { 91 } 92 lock.master().lock(); 93 lock.master().unlock(); 94 }); 95} 96 97TEST_F(ThreadedChunkLockTest, MasterSlave) { 98 threads.emplace_back([this]() { 99 lock.master().lock(); 100 ++step; 101 // Need to sleep to ensure the other thread has 102 // started spinning to acquire the lock 103 std::this_thread::sleep_for(std::chrono::milliseconds(50)); 104 lock.master().unlock(); 105 }); 106 107 threads.emplace_back([this]() { 108 while (step.load() != 1) { 109 } 110 while(!lock.slave().try_lock()) { 111 } 112 lock.slave().unlock(); 113 }); 114} 115