From 14f8245dd8f416bc6b59a4654fc3e230f78426ce Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 17 Mar 2014 17:07:42 -0700 Subject: [PATCH] adding threading and read write lock tests --- tests/threads/CMakeLists.txt | 36 ++++++++++++ tests/threads/src/SampleReadThread.cpp | 65 ++++++++++++++++++++++ tests/threads/src/SampleReadThread.h | 31 +++++++++++ tests/threads/src/SampleWriteThread.cpp | 47 ++++++++++++++++ tests/threads/src/SampleWriteThread.h | 30 ++++++++++ tests/threads/src/SharedResource.cpp | 38 +++++++++++++ tests/threads/src/SharedResource.h | 40 ++++++++++++++ tests/threads/src/ThreadsTests.cpp | 73 +++++++++++++++++++++++++ tests/threads/src/ThreadsTests.h | 16 ++++++ tests/threads/src/main.cpp | 11 ++++ 10 files changed, 387 insertions(+) create mode 100644 tests/threads/CMakeLists.txt create mode 100644 tests/threads/src/SampleReadThread.cpp create mode 100644 tests/threads/src/SampleReadThread.h create mode 100644 tests/threads/src/SampleWriteThread.cpp create mode 100644 tests/threads/src/SampleWriteThread.h create mode 100644 tests/threads/src/SharedResource.cpp create mode 100644 tests/threads/src/SharedResource.h create mode 100644 tests/threads/src/ThreadsTests.cpp create mode 100644 tests/threads/src/ThreadsTests.h create mode 100644 tests/threads/src/main.cpp diff --git a/tests/threads/CMakeLists.txt b/tests/threads/CMakeLists.txt new file mode 100644 index 0000000000..f12a124b0c --- /dev/null +++ b/tests/threads/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 2.8) + +if (WIN32) + cmake_policy (SET CMP0020 NEW) +endif (WIN32) + +set(TARGET_NAME threads-tests) + +set(ROOT_DIR ../..) +set(MACRO_DIR ${ROOT_DIR}/cmake/macros) + +# setup for find modules +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") + +find_package(Qt5Core REQUIRED) + +include(${MACRO_DIR}/SetupHifiProject.cmake) +setup_hifi_project(${TARGET_NAME} TRUE) + +include(${MACRO_DIR}/AutoMTC.cmake) +auto_mtc(${TARGET_NAME} ${ROOT_DIR}) + +qt5_use_modules(${TARGET_NAME} Core) + +#include glm +include(${MACRO_DIR}/IncludeGLM.cmake) +include_glm(${TARGET_NAME} ${ROOT_DIR}) + +# link in the shared libraries +include(${MACRO_DIR}/LinkHifiLibrary.cmake) +link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) + +IF (WIN32) + #target_link_libraries(${TARGET_NAME} Winmm Ws2_32) +ENDIF(WIN32) + diff --git a/tests/threads/src/SampleReadThread.cpp b/tests/threads/src/SampleReadThread.cpp new file mode 100644 index 0000000000..129c3f098b --- /dev/null +++ b/tests/threads/src/SampleReadThread.cpp @@ -0,0 +1,65 @@ +// +// SampleReadThread.cpp +// interface +// +// Created by Brad Hefta-Gaub on 2014.03.17 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded interface thread for hiding and showing voxels in the local tree. +// + +#include + +#include + +#include "SharedResource.h" +#include "SampleReadThread.h" + +SampleReadThread::SampleReadThread(SharedResource* theResource, int id) : + _theResource(theResource), + _myID(id) { +} + +bool SampleReadThread::process() { + const quint64 FRAME_RATE = 60; + const quint64 USECS_PER_FRAME = USECS_PER_SECOND / FRAME_RATE; // every 60fps + + quint64 start = usecTimestampNow(); + _theResource->lockForRead(); + quint64 end = usecTimestampNow(); + quint64 elapsed = end - start; + + int theValue = _theResource->getValue(); + + bool wantDebugging = true; + if (wantDebugging) { + qDebug() << "SampleReadThread::process()... _myID=" << _myID << "lockForRead() took" << elapsed << "usecs" << + " _theResource->getValue()=" << theValue; + } + + quint64 startWork = usecTimestampNow(); + { + const int LOTS_OF_OPERATIONS = 100000; + for(int i = 0; i < LOTS_OF_OPERATIONS; i++) { + float x = rand(); + float y = rand(); + float z = x * y; + } + } + quint64 endWork = usecTimestampNow(); + quint64 elapsedWork = endWork - startWork; + qDebug() << "SampleReadThread::process()... _myID=" << _myID << " work took" << elapsedWork << "usecs"; + + _theResource->unlock(); + + + if (isStillRunning()) { + if (elapsed < USECS_PER_FRAME) { + quint64 sleepFor = USECS_PER_FRAME - elapsed; + usleep(sleepFor); + } else { + usleep(5); // sleep at least a little + } + } + return isStillRunning(); // keep running till they terminate us +} diff --git a/tests/threads/src/SampleReadThread.h b/tests/threads/src/SampleReadThread.h new file mode 100644 index 0000000000..a9a25ac7a1 --- /dev/null +++ b/tests/threads/src/SampleReadThread.h @@ -0,0 +1,31 @@ +// +// SampleReadThread.h +// tests +// +// Created by Brad Hefta-Gaub on 2014.03.17 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__SampleReadThread__ +#define __interface__SampleReadThread__ + +#include + +class SharedResource; + +/// Generalized threaded processor for handling received inbound packets. +class SampleReadThread : public GenericThread { + Q_OBJECT +public: + SampleReadThread(SharedResource* theResource, int id); + +protected: + /// Implements generic processing behavior for this thread. + virtual bool process(); + +private: + SharedResource* _theResource; + int _myID; +}; + +#endif // __interface__SampleReadThread__ diff --git a/tests/threads/src/SampleWriteThread.cpp b/tests/threads/src/SampleWriteThread.cpp new file mode 100644 index 0000000000..0fbe68904d --- /dev/null +++ b/tests/threads/src/SampleWriteThread.cpp @@ -0,0 +1,47 @@ +// +// SampleWriteThread.cpp +// interface +// +// Created by Brad Hefta-Gaub on 2014.03.17 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded interface thread for hiding and showing voxels in the local tree. +// + +#include + +#include + +#include "SharedResource.h" +#include "SampleWriteThread.h" + +SampleWriteThread::SampleWriteThread(SharedResource* theResource) : + _theResource(theResource) { +} + +bool SampleWriteThread::process() { + const quint64 USECS_PER_OPERATION = USECS_PER_SECOND * 1; // once every second. + + quint64 start = usecTimestampNow(); + _theResource->lockForWrite(); + quint64 end = usecTimestampNow(); + quint64 elapsed = end - start; + + int theValue = _theResource->incrementValue(); + + bool wantDebugging = false; + if (wantDebugging) { + qDebug() << "SampleWriteThread::process()... lockForWrite() took" << elapsed << "usecs" << + " _theResource->incrementValue()=" << theValue; + } + _theResource->unlock(); + + + if (isStillRunning()) { + if (elapsed < USECS_PER_OPERATION) { + quint64 sleepFor = USECS_PER_OPERATION - elapsed; + usleep(sleepFor); + } + } + return isStillRunning(); // keep running till they terminate us +} diff --git a/tests/threads/src/SampleWriteThread.h b/tests/threads/src/SampleWriteThread.h new file mode 100644 index 0000000000..970b4435a9 --- /dev/null +++ b/tests/threads/src/SampleWriteThread.h @@ -0,0 +1,30 @@ +// +// SampleWriteThread.h +// tests +// +// Created by Brad Hefta-Gaub on 2014.03.17 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__SampleWriteThread__ +#define __interface__SampleWriteThread__ + +#include + +class SharedResource; + +/// Generalized threaded processor for handling received inbound packets. +class SampleWriteThread : public GenericThread { + Q_OBJECT +public: + SampleWriteThread(SharedResource* theResource); + +protected: + /// Implements generic processing behavior for this thread. + virtual bool process(); + +private: + SharedResource* _theResource; +}; + +#endif // __interface__SampleWriteThread__ diff --git a/tests/threads/src/SharedResource.cpp b/tests/threads/src/SharedResource.cpp new file mode 100644 index 0000000000..c397e6a7db --- /dev/null +++ b/tests/threads/src/SharedResource.cpp @@ -0,0 +1,38 @@ +// +// SharedResource.cpp +// tests +// +// Created by Brad Hefta-Gaub on 2014.03.17 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include + +#include "SharedResource.h" + +const int MOVING_AVERAGE_SAMPLES = 10000; + +SharedResource::SharedResource() : + _value(0), + _lockAverage(MOVING_AVERAGE_SAMPLES), + _lockForReadAverage(MOVING_AVERAGE_SAMPLES), + _lockForWriteAverage(MOVING_AVERAGE_SAMPLES) +{ +}; + +void SharedResource::lockForRead() { + quint64 start = usecTimestampNow(); + _lock.lockForRead(); + quint64 end = usecTimestampNow(); + quint64 elapsed = end - start; + _lockForReadAverage.updateAverage(elapsed); + _lockAverage.updateAverage(elapsed); +} +void SharedResource::lockForWrite() { + quint64 start = usecTimestampNow(); + _lock.lockForWrite(); + quint64 end = usecTimestampNow(); + quint64 elapsed = end - start; + _lockForWriteAverage.updateAverage(elapsed); + _lockAverage.updateAverage(elapsed); +} diff --git a/tests/threads/src/SharedResource.h b/tests/threads/src/SharedResource.h new file mode 100644 index 0000000000..1426471f16 --- /dev/null +++ b/tests/threads/src/SharedResource.h @@ -0,0 +1,40 @@ +// +// SharedResource.h +// tests +// +// Created by Brad Hefta-Gaub on 2014.03.17 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__SharedResource__ +#define __interface__SharedResource__ + +#include + +#include + +/// Generalized threaded processor for handling received inbound packets. +class SharedResource { +public: + SharedResource(); + + void lockForRead(); + void lockForWrite(); + void unlock() { _lock.unlock(); } + + int getValue() const { return _value; } + int incrementValue() { return ++_value; } + + float getAverageLockTime() const { return _lockAverage.getAverage(); } + float getAverageLockForReadTime() const { return _lockForReadAverage.getAverage(); } + float getAverageLockForWriteTime() const { return _lockForWriteAverage.getAverage(); } + +private: + QReadWriteLock _lock; + int _value; + SimpleMovingAverage _lockAverage; + SimpleMovingAverage _lockForReadAverage; + SimpleMovingAverage _lockForWriteAverage; +}; + +#endif // __interface__SharedResource__ diff --git a/tests/threads/src/ThreadsTests.cpp b/tests/threads/src/ThreadsTests.cpp new file mode 100644 index 0000000000..15b1c66e63 --- /dev/null +++ b/tests/threads/src/ThreadsTests.cpp @@ -0,0 +1,73 @@ +// +// ThreadsTests.cpp +// thread-tests +// +// Created by Brad Hefta-Gaub on 2014.03.17 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#include +#include + +#include +#include + +#include "SampleReadThread.h" +#include "SampleWriteThread.h" +#include "SharedResource.h" +#include "ThreadsTests.h" + + +void ThreadsTests::runAllTests() { + qDebug() << "START ThreadsTests::runAllTests()"; + + const quint64 TOTAL_RUN_TIME = USECS_PER_SECOND * 10; // run for 2 minutes + const quint64 MAIN_THREAD_SLEEP = USECS_PER_SECOND * 0.25; // main thread checks in every 1/4 second + quint64 start = usecTimestampNow(); + quint64 now = usecTimestampNow(); + + SharedResource resource; + + const int NUMBER_OF_READERS = 125; // + 3 = 128, Max number of QThreads on the mac. + QVector readThreads; + for(int i = 0; i < NUMBER_OF_READERS; i++) { + SampleReadThread* newReader = new SampleReadThread(&resource, i); + newReader->initialize(); + readThreads.append(newReader); + } + + SampleWriteThread write(&resource); + write.initialize(); + + while(now - start < TOTAL_RUN_TIME) { + float elapsed = (float)(now - start) / (float)USECS_PER_SECOND; + qDebug() << "Main thread still running... elapsed:" << elapsed << "seconds"; + qDebug() << " average Read Lock:" << resource.getAverageLockForReadTime() << "usecs"; + qDebug() << " average Write Lock:" << resource.getAverageLockForWriteTime() << "usecs"; + qDebug() << " average Lock:" << resource.getAverageLockTime() << "usecs"; + qDebug() << " resource.value:" << resource.getValue(); + + + usleep(MAIN_THREAD_SLEEP); + now = usecTimestampNow(); + } + write.terminate(); + + foreach(SampleReadThread* readThread, readThreads) { + readThread->terminate(); + delete readThread; + } + readThreads.clear(); + + qDebug() << "DONE running..."; + qDebug() << " average Read Lock:" << resource.getAverageLockForReadTime() << "usecs"; + qDebug() << " average Write Lock:" << resource.getAverageLockForWriteTime() << "usecs"; + qDebug() << " average Lock:" << resource.getAverageLockTime() << "usecs"; + qDebug() << " resource.value:" << resource.getValue(); + + qDebug() << "END ThreadsTests::runAllTests()"; + + qDebug() << "_POSIX_THREAD_THREADS_MAX" << _POSIX_THREAD_THREADS_MAX; + qDebug() << "QThread::idealThreadCount()" << QThread::idealThreadCount(); + +} diff --git a/tests/threads/src/ThreadsTests.h b/tests/threads/src/ThreadsTests.h new file mode 100644 index 0000000000..29bd9aebd4 --- /dev/null +++ b/tests/threads/src/ThreadsTests.h @@ -0,0 +1,16 @@ +// +// ThreadsTests.h +// threads-tests +// +// Created by Brad Hefta-Gaub on 2014.03.17 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __tests__ThreadsTests__ +#define __tests__ThreadsTests__ + +namespace ThreadsTests { + void runAllTests(); +} + +#endif // __tests__ThreadsTests__ diff --git a/tests/threads/src/main.cpp b/tests/threads/src/main.cpp new file mode 100644 index 0000000000..16f3ea0900 --- /dev/null +++ b/tests/threads/src/main.cpp @@ -0,0 +1,11 @@ +// +// main.cpp +// threads-tests +// + +#include "ThreadsTests.h" + +int main(int argc, char** argv) { + ThreadsTests::runAllTests(); + return 0; +}