nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: Filename : OVR_Lockless.cpp nuclear@0: Content : Test logic for lock-less classes nuclear@0: Created : December 27, 2013 nuclear@0: Authors : Michael Antonov nuclear@0: nuclear@0: Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. nuclear@0: nuclear@0: Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); nuclear@0: you may not use the Oculus VR Rift SDK except in compliance with the License, nuclear@0: which is provided at the time of installation or download, or which nuclear@0: otherwise accompanies this software in either electronic or hard copy form. nuclear@0: nuclear@0: You may obtain a copy of the License at nuclear@0: nuclear@0: http://www.oculusvr.com/licenses/LICENSE-3.2 nuclear@0: nuclear@0: Unless required by applicable law or agreed to in writing, the Oculus VR SDK nuclear@0: distributed under the License is distributed on an "AS IS" BASIS, nuclear@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. nuclear@0: See the License for the specific language governing permissions and nuclear@0: limitations under the License. nuclear@0: nuclear@0: *************************************************************************************/ nuclear@0: nuclear@0: #include "OVR_Lockless.h" nuclear@0: nuclear@0: #ifdef OVR_LOCKLESS_TEST nuclear@0: nuclear@0: #include "OVR_Threads.h" nuclear@0: #include "OVR_Timer.h" nuclear@0: #include "OVR_Log.h" nuclear@0: nuclear@0: namespace OVR { namespace LocklessTest { nuclear@0: nuclear@0: nuclear@0: const int TestIterations = 10000000; nuclear@0: nuclear@0: // Use volatile dummies to force compiler to do spinning. nuclear@0: volatile int Dummy1; nuclear@0: int Unused1[32]; nuclear@0: volatile int Dummy2; nuclear@0: int Unused2[32]; nuclear@0: volatile int Dummy3; nuclear@0: int Unused3[32]; nuclear@0: nuclear@0: nuclear@0: // Data block out of 20 consecutive integers, should be internally consistent. nuclear@0: struct TestData nuclear@0: { nuclear@0: enum { ItemCount = 20 }; nuclear@0: nuclear@0: int Data[ItemCount]; nuclear@0: nuclear@0: nuclear@0: void Set(int val) nuclear@0: { nuclear@0: for (int i=0; i TestDataUpdater; nuclear@0: nuclear@0: // Use this lock to verify that testing algorithm is otherwise correct... nuclear@0: Lock TestLock; nuclear@0: nuclear@0: nuclear@0: //------------------------------------------------------------------------------------- nuclear@0: nuclear@0: // Consumer thread reads values from TestDataUpdater and nuclear@0: // ensures that each one is internally consistent. nuclear@0: nuclear@0: class Consumer : public Thread nuclear@0: { nuclear@0: virtual int Run() nuclear@0: { nuclear@0: LogText("LocklessTest::Consumer::Run started.\n"); nuclear@0: nuclear@0: while (!FirstItemWritten) nuclear@0: { nuclear@0: // spin until producer wrote first value... nuclear@0: } nuclear@0: nuclear@0: TestData d; nuclear@0: int oldValue = 0; nuclear@0: int newValue; nuclear@0: nuclear@0: do nuclear@0: { nuclear@0: { nuclear@0: //Lock::Locker scope(&TestLock); nuclear@0: d = TestDataUpdater.GetState(); nuclear@0: } nuclear@0: nuclear@0: newValue = d.ReadAndCheckConsistency(oldValue); nuclear@0: nuclear@0: // Values should increase or stay the same! nuclear@0: if (newValue < oldValue) nuclear@0: { nuclear@0: LogText("LocklessTest Fail - %d after %d; delta = %d\n", nuclear@0: newValue, oldValue, newValue - oldValue); nuclear@0: // OVR_ASSERT(0); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: if (oldValue != newValue) nuclear@0: { nuclear@0: oldValue = newValue; nuclear@0: nuclear@0: if (oldValue % (TestIterations/30) == 0) nuclear@0: { nuclear@0: LogText("LocklessTest::Consumer - %5.2f%% done\n", nuclear@0: 100.0f * (float)oldValue/(float)TestIterations); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Spin a while nuclear@0: for (int j = 0; j< 300; j++) nuclear@0: { nuclear@0: Dummy3 = j; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: } while (oldValue < (TestIterations * 99 / 100)); nuclear@0: nuclear@0: LogText("LocklessTest::Consumer::Run exiting.\n"); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //------------------------------------------------------------------------------------- nuclear@0: nuclear@0: class Producer : public Thread nuclear@0: { nuclear@0: nuclear@0: virtual int Run() nuclear@0: { nuclear@0: LogText("LocklessTest::Producer::Run started.\n"); nuclear@0: nuclear@0: for (int testVal = 0; testVal < TestIterations; testVal++) nuclear@0: { nuclear@0: TestData d; nuclear@0: d.Set(testVal); nuclear@0: nuclear@0: { nuclear@0: //Lock::Locker scope(&TestLock); nuclear@0: TestDataUpdater.SetState(d); nuclear@0: } nuclear@0: nuclear@0: FirstItemWritten = true; nuclear@0: nuclear@0: // Spin a bit nuclear@0: for(int j = 0; j < 1000; j++) nuclear@0: { nuclear@0: Dummy2 = j; nuclear@0: } nuclear@0: nuclear@0: if (testVal % (TestIterations/30) == 0) nuclear@0: { nuclear@0: LogText("LocklessTest::Producer - %5.2f%% done\n", nuclear@0: 100.0f * (float)testVal/(float)TestIterations); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: LogText("LocklessTest::Producer::Run exiting.\n"); nuclear@0: return 0; nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: } // namespace LocklessTest nuclear@0: nuclear@0: nuclear@0: nuclear@0: void StartLocklessTest() nuclear@0: { nuclear@0: // These threads will release themselves once done nuclear@0: Ptr producerThread = *new LocklessTest::Producer; nuclear@0: Ptr consumerThread = *new LocklessTest::Consumer; nuclear@0: nuclear@0: producerThread->Start(); nuclear@0: consumerThread->Start(); nuclear@0: nuclear@0: while (!producerThread->IsFinished() && consumerThread->IsFinished()) nuclear@0: { nuclear@0: Thread::MSleep(500); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: } // namespace OVR nuclear@0: nuclear@0: #endif // OVR_LOCKLESS_TEST