From: Pyry Haulos Date: Thu, 9 Apr 2015 23:01:58 +0000 (-0700) Subject: Add de::SpinBarrier X-Git-Tag: upstream/0.1.0~1778 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3f4cf9ed9550eeab35386a9cebedb1b6e882fcac;p=platform%2Fupstream%2FVK-GL-CTS.git Add de::SpinBarrier SpinBarrier provides convenient cross-thread barriers. Change-Id: I70eac2ed07b2c123d9709ecf5bbe284f35771204 --- diff --git a/Android.mk b/Android.mk index 2115e14..9ace5f1 100644 --- a/Android.mk +++ b/Android.mk @@ -98,6 +98,7 @@ LOCAL_SRC_FILES := \ framework/delibs/decpp/deSemaphore.cpp \ framework/delibs/decpp/deSharedPtr.cpp \ framework/delibs/decpp/deSocket.cpp \ + framework/delibs/decpp/deSpinBarrier.cpp \ framework/delibs/decpp/deSTLUtil.cpp \ framework/delibs/decpp/deStringUtil.cpp \ framework/delibs/decpp/deThread.cpp \ diff --git a/framework/delibs/decpp/CMakeLists.txt b/framework/delibs/decpp/CMakeLists.txt index f5120ef..974c385 100644 --- a/framework/delibs/decpp/CMakeLists.txt +++ b/framework/delibs/decpp/CMakeLists.txt @@ -55,6 +55,8 @@ set(DECPP_SRCS deThreadSafeRingBuffer.hpp deUniquePtr.cpp deUniquePtr.hpp + deSpinBarrier.cpp + deSpinBarrier.hpp ) add_library(decpp STATIC ${DECPP_SRCS}) diff --git a/framework/delibs/decpp/deSpinBarrier.cpp b/framework/delibs/decpp/deSpinBarrier.cpp new file mode 100644 index 0000000..150bbc2 --- /dev/null +++ b/framework/delibs/decpp/deSpinBarrier.cpp @@ -0,0 +1,196 @@ +/*------------------------------------------------------------------------- + * drawElements C++ Base Library + * ----------------------------- + * + * Copyright 2015 The Android Open Source Project + * + * Licensed 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. + * + *//*! + * \file + * \brief Cross-thread barrier. + *//*--------------------------------------------------------------------*/ + +#include "deSpinBarrier.hpp" +#include "deThread.hpp" +#include "deRandom.hpp" +#include "deInt32.h" + +#include + +namespace de +{ + +SpinBarrier::SpinBarrier (deInt32 numThreads) + : m_numThreads (numThreads) + , m_numEntered (0) + , m_numLeaving (0) +{ + DE_ASSERT(numThreads > 0); +} + +SpinBarrier::~SpinBarrier (void) +{ + DE_ASSERT(m_numEntered == 0 && m_numLeaving == 0); +} + +void SpinBarrier::sync (WaitMode mode) +{ + DE_ASSERT(mode == WAIT_MODE_YIELD || mode == WAIT_MODE_BUSY); + + deMemoryReadWriteFence(); + + if (m_numLeaving > 0) + { + for (;;) + { + if (m_numLeaving == 0) + break; + + if (mode == WAIT_MODE_YIELD) + deYield(); + } + } + + if (deAtomicIncrement32(&m_numEntered) == m_numThreads) + { + m_numLeaving = m_numThreads; + deMemoryReadWriteFence(); + m_numEntered = 0; + } + else + { + for (;;) + { + if (m_numEntered == 0) + break; + + if (mode == WAIT_MODE_YIELD) + deYield(); + } + } + + deAtomicDecrement32(&m_numLeaving); + deMemoryReadWriteFence(); +} + +namespace +{ + +void singleThreadTest (SpinBarrier::WaitMode mode) +{ + SpinBarrier barrier(1); + + barrier.sync(mode); + barrier.sync(mode); + barrier.sync(mode); +} + +class TestThread : public de::Thread +{ +public: + TestThread (SpinBarrier& barrier, volatile deInt32* sharedVar, int numThreads, int threadNdx, bool busyOk) + : m_barrier (barrier) + , m_sharedVar (sharedVar) + , m_numThreads (numThreads) + , m_threadNdx (threadNdx) + , m_busyOk (busyOk) + { + } + + void run (void) + { + const int numIters = 10000; + de::Random rnd (deInt32Hash(m_numThreads) ^ deInt32Hash(m_threadNdx)); + + for (int iterNdx = 0; iterNdx < numIters; iterNdx++) + { + // Phase 1: count up + deAtomicIncrement32(m_sharedVar); + + // Verify + m_barrier.sync(getWaitMode(rnd)); + + DE_TEST_ASSERT(*m_sharedVar == m_numThreads); + + m_barrier.sync(getWaitMode(rnd)); + + // Phase 2: count down + deAtomicDecrement32(m_sharedVar); + + // Verify + m_barrier.sync(getWaitMode(rnd)); + + DE_TEST_ASSERT(*m_sharedVar == 0); + + m_barrier.sync(getWaitMode(rnd)); + } + } + +private: + SpinBarrier& m_barrier; + volatile deInt32* m_sharedVar; + int m_numThreads; + int m_threadNdx; + bool m_busyOk; + + SpinBarrier::WaitMode getWaitMode (de::Random& rnd) + { + if (m_busyOk && rnd.getBool()) + return SpinBarrier::WAIT_MODE_BUSY; + else + return SpinBarrier::WAIT_MODE_YIELD; + } +}; + +void multiThreadTest (int numThreads) +{ + SpinBarrier barrier (numThreads); + volatile deInt32 sharedVar = 0; + std::vector threads (numThreads, static_cast(DE_NULL)); + + // Going over logical cores with busy-waiting will cause priority inversion and make tests take + // excessive amount of time. Use busy waiting only when number of threads is at most one per + // core. + const bool busyOk = (deUint32)numThreads <= deGetNumAvailableLogicalCores(); + + for (int ndx = 0; ndx < numThreads; ndx++) + { + threads[ndx] = new TestThread(barrier, &sharedVar, numThreads, ndx, busyOk); + DE_TEST_ASSERT(threads[ndx]); + threads[ndx]->start(); + } + + for (int ndx = 0; ndx < numThreads; ndx++) + { + threads[ndx]->join(); + delete threads[ndx]; + } + + DE_TEST_ASSERT(sharedVar == 0); +} + +} // namespace + +void SpinBarrier_selfTest (void) +{ + singleThreadTest(SpinBarrier::WAIT_MODE_YIELD); + singleThreadTest(SpinBarrier::WAIT_MODE_BUSY); + multiThreadTest(1); + multiThreadTest(2); + multiThreadTest(4); + multiThreadTest(8); + multiThreadTest(16); +} + +} // de diff --git a/framework/delibs/decpp/deSpinBarrier.hpp b/framework/delibs/decpp/deSpinBarrier.hpp new file mode 100644 index 0000000..6679f8d --- /dev/null +++ b/framework/delibs/decpp/deSpinBarrier.hpp @@ -0,0 +1,68 @@ +#ifndef _DESPINBARRIER_HPP +#define _DESPINBARRIER_HPP +/*------------------------------------------------------------------------- + * drawElements C++ Base Library + * ----------------------------- + * + * Copyright 2015 The Android Open Source Project + * + * Licensed 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. + * + *//*! + * \file + * \brief Cross-thread barrier. + *//*--------------------------------------------------------------------*/ + +#include "deDefs.hpp" +#include "deAtomic.h" + +namespace de +{ + +/*--------------------------------------------------------------------*//*! + * \brief Cross-thread barrier + * + * SpinBarrier provides barrier implementation that uses spin loop for + * waiting for other threads. Threads may choose to wait in tight loop + * (WAIT_MODE_BUSY) or yield between iterations (WAIT_MODE_YIELD). + *//*--------------------------------------------------------------------*/ +class SpinBarrier +{ +public: + enum WaitMode + { + WAIT_MODE_BUSY = 0, + WAIT_MODE_YIELD, + + WAIT_MODE_LAST + }; + + SpinBarrier (deInt32 numThreads); + ~SpinBarrier (void); + + void sync (WaitMode mode); + +private: + SpinBarrier (const SpinBarrier&); + SpinBarrier operator= (const SpinBarrier&); + + const deInt32 m_numThreads; + volatile deInt32 m_numEntered; + volatile deInt32 m_numLeaving; +}; + +void SpinBarrier_selfTest (void); + +} // de + +#endif // _DESPINBARRIER_HPP diff --git a/modules/internal/ditDelibsTests.cpp b/modules/internal/ditDelibsTests.cpp index bdda4ff..df47341 100644 --- a/modules/internal/ditDelibsTests.cpp +++ b/modules/internal/ditDelibsTests.cpp @@ -52,6 +52,7 @@ #include "deCommandLine.hpp" #include "deArrayBuffer.hpp" #include "deStringUtil.hpp" +#include "deSpinBarrier.hpp" namespace dit { @@ -159,6 +160,7 @@ public: addChild(new SelfCheckCase(m_testCtx, "commandline", "de::cmdline::selfTest()", de::cmdline::selfTest)); addChild(new SelfCheckCase(m_testCtx, "array_buffer", "de::ArrayBuffer_selfTest()", de::ArrayBuffer_selfTest)); addChild(new SelfCheckCase(m_testCtx, "string_util", "de::StringUtil_selfTest()", de::StringUtil_selfTest)); + addChild(new SelfCheckCase(m_testCtx, "spin_barrier", "de::SpinBarrier_selfTest()", de::SpinBarrier_selfTest)); } };