// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include #include #include #include class Test : public vespalib::TestApp { public: int Main() override; private: template void testSwap(T initial); template void testSwapStress(T v, int numThreads); }; template class Stress : public FastOS_Runnable { private: void Run(FastOS_ThreadInterface * ti, void * arg) override; void stressSwap(T & value); public: Stress(T * value) : _value(value), _successCount(0), _failedCount(0) { } void wait() { _wait.Lock(); _wait.Unlock(); } FastOS_Mutex _wait; T * _value; size_t _successCount; size_t _failedCount; }; TEST_APPHOOK(Test); template void Test::testSwap(T initial) { T value(initial); ASSERT_TRUE(vespalib::Atomic::cmpSwap(&value, initial+1, initial)); ASSERT_TRUE(value == initial+1); ASSERT_TRUE(!vespalib::Atomic::cmpSwap(&value, initial+2, initial)); ASSERT_TRUE(value == initial+1); } template void Test::testSwapStress(T v, int numThreads) { T old(v); std::vector *> contexts; std::vector threads; FastOS_ThreadPool threadPool(512*1024); for(int i=0; i < numThreads; i++) { contexts.push_back(new Stress(&v)); } for(size_t i = 0; i < contexts.size(); i++) { threads.push_back(threadPool.NewThread(contexts[i])); } FastOS_Thread::Sleep(1000); size_t succesCount(0); size_t failedCount(0); for(size_t i = 0; i < contexts.size(); i++) { Stress * s = contexts[i]; s->wait(); succesCount += s->_successCount; failedCount += s->_failedCount; } ASSERT_TRUE(v == 0); ASSERT_TRUE(old == succesCount); fprintf(stderr, "%ld threads counting down from %" PRIu64 " had %ld succesfull and %ld unsuccessful attempts\n", contexts.size(), uint64_t(old), succesCount, failedCount); for(size_t i = 0; i < contexts.size(); i++) { delete contexts[i]; } } template void Stress::Run(FastOS_ThreadInterface *, void *) { _wait.Lock(); stressSwap(*_value); _wait.Unlock(); } template void Stress::stressSwap(T & value) { for (T old = value; old > 0; old = value) { if (vespalib::Atomic::cmpSwap(&value, old-1, old)) { _successCount++; } else { _failedCount++; } } } int Test::Main() { TEST_INIT("atomic"); { std::atomic uint32V; ASSERT_TRUE(uint32V.is_lock_free()); } { std::atomic uint64V; ASSERT_TRUE(uint64V.is_lock_free()); } { std::atomic taggedPtr; ASSERT_EQUAL(16u, sizeof(vespamalloc::TaggedPtr)); #if __GNUC__ < 7 ASSERT_TRUE(taggedPtr.is_lock_free()); #else ASSERT_FALSE(taggedPtr.is_lock_free()); #endif } testSwap(6); testSwap(7); testSwap(uint32_t(-6)); testSwap(uint32_t(-7)); testSwap(6); testSwap(7); testSwap(uint64_t(-6)); testSwap(uint64_t(-7)); testSwapStress(0x1000000, 4); testSwapStress(0x1000000, 4); TEST_DONE(); }