X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ftest%2FSpinLockTest.cpp;h=7c263c377ed55ae2e0ee83723faa9aaa6a1ae9c1;hb=512aa17590988db15d6f303528a45dfdcb9503de;hp=323050e70e46d2840ff3c02639a5f33bea938896;hpb=4494e275ffe1c2bd4a4e59550c325fccbafed139;p=folly.git diff --git a/folly/test/SpinLockTest.cpp b/folly/test/SpinLockTest.cpp index 323050e7..7c263c37 100644 --- a/folly/test/SpinLockTest.cpp +++ b/folly/test/SpinLockTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2014 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,104 +15,153 @@ */ #include -#include +#include + #include -using folly::SpinLock; -using folly::SpinLockGuard; +#include +#include + +using folly::SpinLockGuardImpl; namespace { +template struct LockedVal { int ar[1024]; - SpinLock lock; + LOCK lock; LockedVal() { memset(ar, 0, sizeof ar); } }; -LockedVal v; -void splock_test() { +template +void spinlockTestThread(LockedVal* v) { const int max = 1000; - unsigned int seed = (uintptr_t)pthread_self(); + auto rng = folly::ThreadLocalPRNG(); for (int i = 0; i < max; i++) { - asm("pause"); - SpinLockGuard g(v.lock); + folly::asm_pause(); + SpinLockGuardImpl g(v->lock); - int first = v.ar[0]; - for (size_t i = 1; i < sizeof v.ar / sizeof i; ++i) { - EXPECT_EQ(first, v.ar[i]); + int first = v->ar[0]; + for (size_t j = 1; j < sizeof v->ar / sizeof j; ++j) { + EXPECT_EQ(first, v->ar[j]); } - int byte = rand_r(&seed); - memset(v.ar, char(byte), sizeof v.ar); + int byte = folly::Random::rand32(rng); + memset(v->ar, char(byte), sizeof v->ar); } } +template struct TryLockState { - SpinLock lock1; - SpinLock lock2; + LOCK lock1; + LOCK lock2; bool locked{false}; uint64_t obtained{0}; uint64_t failed{0}; }; -TryLockState tryState; -void trylock_test() { - const int max = 1000; +template +void trylockTestThread(TryLockState* state, size_t count) { while (true) { - asm("pause"); - SpinLockGuard g(tryState.lock1); - if (tryState.obtained >= max) { + folly::asm_pause(); + SpinLockGuardImpl g(state->lock1); + if (state->obtained >= count) { break; } - bool ret = tryState.lock2.trylock(); - EXPECT_NE(tryState.locked, ret); + bool ret = state->lock2.trylock(); + EXPECT_NE(state->locked, ret); if (ret) { // We got lock2. - ++tryState.obtained; - tryState.locked = true; - - // Release lock1 and let other threads try to obtain lock2 - tryState.lock1.unlock(); - asm("pause"); - tryState.lock1.lock(); - - tryState.locked = false; - tryState.lock2.unlock(); + ++state->obtained; + state->locked = true; + + // Release lock1 and wait until at least one other thread fails to + // obtain the lock2 before continuing. + auto oldFailed = state->failed; + while (state->failed == oldFailed && state->obtained < count) { + state->lock1.unlock(); + folly::asm_pause(); + state->lock1.lock(); + } + + state->locked = false; + state->lock2.unlock(); } else { - ++tryState.failed; + ++state->failed; } } } -} // unnamed namespace - -TEST(SpinLock, Correctness) { +template +void correctnessTest() { int nthrs = sysconf(_SC_NPROCESSORS_ONLN) * 2; std::vector threads; + LockedVal v; for (int i = 0; i < nthrs; ++i) { - threads.push_back(std::thread(splock_test)); + threads.push_back(std::thread(spinlockTestThread, &v)); } for (auto& t : threads) { t.join(); } } -TEST(SpinLock, TryLock) { - int nthrs = sysconf(_SC_NPROCESSORS_ONLN) * 2; +template +void trylockTest() { + int nthrs = sysconf(_SC_NPROCESSORS_ONLN) + 4; std::vector threads; + TryLockState state; + size_t count = 100; for (int i = 0; i < nthrs; ++i) { - threads.push_back(std::thread(trylock_test)); + threads.push_back(std::thread(trylockTestThread, &state, count)); } for (auto& t : threads) { t.join(); } - EXPECT_EQ(1000, tryState.obtained); - EXPECT_GT(tryState.failed, 0); - LOG(INFO) << "failed count: " << tryState.failed; + EXPECT_EQ(count, state.obtained); + // Each time the code obtains lock2 it waits for another thread to fail + // to acquire it. The only time this might not happen is on the very last + // loop when no other threads are left. + EXPECT_GE(state.failed + 1, state.obtained); +} + +} // unnamed namespace + +#if __x86_64__ +TEST(SpinLock, MslCorrectness) { + correctnessTest(); +} +TEST(SpinLock, MslTryLock) { + trylockTest(); +} +#endif + +#if __APPLE__ +TEST(SpinLock, AppleCorrectness) { + correctnessTest(); +} +TEST(SpinLock, AppleTryLock) { + trylockTest(); +} +#endif + +#if FOLLY_HAVE_PTHREAD_SPINLOCK_T +TEST(SpinLock, PthreadCorrectness) { + correctnessTest(); +} +TEST(SpinLock, PthreadTryLock) { + trylockTest(); +} +#endif + +TEST(SpinLock, MutexCorrectness) { + correctnessTest(); +} +TEST(SpinLock, MutexTryLock) { + trylockTest(); }