123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- /**
- * 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 <hdfspp/locks.h>
- #include <gmock/gmock.h>
- #include <gtest/gtest.h>
- #include <iostream>
- #include <memory>
- #include <mutex>
- #include <thread>
- #include <vector>
- using namespace hdfs;
- // try_lock will always return false, unlock will always throw because it
- // can never be locked.
- class CantLockMutex : public Mutex {
- public:
- void lock() override {
- throw LockFailure("This mutex cannot be locked");
- }
- void unlock() override {
- throw LockFailure("Unlock");
- }
- std::string str() override {
- return "CantLockMutex";
- }
- };
- TEST(UserLockTest, DefaultMutexBasics) {
- Mutex *mtx = LockManager::TEST_get_default_mutex();
- // lock and unlock twice to make sure unlock works
- bool locked = false;
- try {
- mtx->lock();
- locked = true;
- } catch (...) {}
- EXPECT_TRUE(locked);
- mtx->unlock();
- locked = false;
- try {
- mtx->lock();
- locked = true;
- } catch (...) {}
- EXPECT_TRUE(locked);
- mtx->unlock();
- EXPECT_EQ(mtx->str(), "DefaultMutex");
- }
- // Make sure lock manager can only be initialized once unless test reset called
- TEST(UserLockTest, LockManager) {
- std::unique_ptr<CantLockMutex> mtx(new CantLockMutex());
- EXPECT_TRUE(mtx != nullptr);
- // Check the default lock
- Mutex *defaultGssapiMtx = LockManager::getGssapiMutex();
- EXPECT_TRUE(defaultGssapiMtx != nullptr);
- // Try a double init. Should not work
- bool res = LockManager::InitLocks(mtx.get());
- EXPECT_TRUE(res);
- // Check pointer value
- EXPECT_EQ(LockManager::getGssapiMutex(), mtx.get());
- res = LockManager::InitLocks(mtx.get());
- EXPECT_FALSE(res);
- // Make sure test reset still works
- LockManager::TEST_reset_manager();
- res = LockManager::InitLocks(mtx.get());
- EXPECT_TRUE(res);
- LockManager::TEST_reset_manager();
- EXPECT_EQ(LockManager::getGssapiMutex(), defaultGssapiMtx);
- }
- TEST(UserLockTest, CheckCantLockMutex) {
- std::unique_ptr<CantLockMutex> mtx(new CantLockMutex());
- EXPECT_TRUE(mtx != nullptr);
- bool locked = false;
- try {
- mtx->lock();
- } catch (...) {}
- EXPECT_FALSE(locked);
- bool threw_on_unlock = false;
- try {
- mtx->unlock();
- } catch (const LockFailure& e) {
- threw_on_unlock = true;
- }
- EXPECT_TRUE(threw_on_unlock);
- EXPECT_EQ("CantLockMutex", mtx->str());
- }
- TEST(UserLockTest, LockGuardBasics) {
- Mutex *goodMtx = LockManager::TEST_get_default_mutex();
- CantLockMutex badMtx;
- // lock/unlock a few times to increase chances of UB if lock is misused
- for(int i=0;i<10;i++) {
- bool caught_exception = false;
- try {
- LockGuard guard(goodMtx);
- // now have a scoped lock
- } catch (const LockFailure& e) {
- caught_exception = true;
- }
- EXPECT_FALSE(caught_exception);
- }
- // still do a few times, but expect it to blow up each time
- for(int i=0;i<10;i++) {
- bool caught_exception = false;
- try {
- LockGuard guard(&badMtx);
- // now have a scoped lock
- } catch (const LockFailure& e) {
- caught_exception = true;
- }
- EXPECT_TRUE(caught_exception);
- }
- }
- struct Incrementer {
- int64_t& _val;
- int64_t _iters;
- Mutex *_mtx;
- Incrementer(int64_t &val, int64_t iters, Mutex *m)
- : _val(val), _iters(iters), _mtx(m) {}
- void operator()(){
- for(int64_t i=0; i<_iters; i++) {
- LockGuard valguard(_mtx);
- _val += 1;
- }
- }
- };
- struct Decrementer {
- int64_t& _val;
- int64_t _iters;
- Mutex *_mtx;
- Decrementer(int64_t &val, int64_t iters, Mutex *m)
- : _val(val), _iters(iters), _mtx(m) {}
- void operator()(){
- for(int64_t i=0; i<_iters; i++) {
- LockGuard valguard(_mtx);
- _val -= 1;
- }
- }
- };
- TEST(UserLockTest, LockGuardConcurrency) {
- Mutex *mtx = LockManager::TEST_get_default_mutex();
- // Prove that these actually mutate the value
- int64_t test_value = 0;
- Incrementer inc(test_value, 1000, mtx);
- inc();
- EXPECT_EQ(test_value, 1000);
- Decrementer dec(test_value, 1000, mtx);
- dec();
- EXPECT_EQ(test_value, 0);
- std::vector<std::thread> workers;
- std::vector<Incrementer> incrementers;
- std::vector<Decrementer> decrementors;
- const int delta = 1024 * 1024;
- const int threads = 2 * 6;
- EXPECT_EQ(threads % 2, 0);
- // a bunch of threads race to increment and decrement the value
- // if all goes well the operations balance out and the value is unchanged
- for(int i=0; i < threads; i++) {
- if(i%2 == 0) {
- incrementers.emplace_back(test_value, delta, mtx);
- workers.emplace_back(incrementers.back());
- } else {
- decrementors.emplace_back(test_value, delta, mtx);
- workers.emplace_back(decrementors.back());
- }
- }
- // join, everything should balance to 0
- for(std::thread& thread : workers) {
- thread.join();
- }
- EXPECT_EQ(test_value, 0);
- }
- int main(int argc, char *argv[]) {
- // The following line must be executed to initialize Google Mock
- // (and Google Test) before running the tests.
- ::testing::InitGoogleMock(&argc, argv);
- int res = RUN_ALL_TESTS();
- return res;
- }
|