1 /* -*-c++-*- */ 2 /* osgEarth - Geospatial SDK for OpenSceneGraph 3 * Copyright 2019 Pelican Mapping 4 * http://osgearth.org 5 * 6 * osgEarth is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU Lesser General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 16 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 17 * IN THE SOFTWARE. 18 * 19 * You should have received a copy of the GNU Lesser General Public License 20 * along with this program. If not, see <http://www.gnu.org/licenses/> 21 */ 22 23 #include <osgEarth/catch.hpp> 24 #include <osgEarth/ThreadingUtils> 25 26 using namespace osgEarth; 27 28 29 namespace ReadWriteMutexTest 30 { 31 osgEarth::Threading::ReadWriteMutex mutex; 32 bool readLock1 = false; 33 bool readLock2 = false; 34 bool attemptingWriteLock = false; 35 bool writeLock = false; 36 37 38 class Thread1 : public OpenThreads::Thread 39 { 40 public: Thread1()41 Thread1() 42 { 43 44 } 45 run()46 void run() 47 { 48 // Thread one takes the first read lock. 49 mutex.readLock(); 50 readLock1 = true; 51 52 // Wait for the write lock to happen in thread 2 53 while (!attemptingWriteLock) 54 { 55 OpenThreads::Thread::YieldCurrentThread(); 56 } 57 58 // The write lock is being attempted, sleep for awhile to make sure it actually tries to get the write lock. 59 OpenThreads::Thread::microSleep(2e6); 60 61 // Take a second read lock 62 mutex.readLock(); 63 readLock2 = true; 64 65 // Unlock both of our read locks 66 mutex.readUnlock(); 67 mutex.readUnlock(); 68 } 69 }; 70 71 72 73 class Thread2 : public osg::Referenced, public OpenThreads::Thread 74 { 75 public: Thread2()76 Thread2() 77 { 78 79 } 80 run()81 void run() 82 { 83 // Wait for thread1 to grab the read lock. 84 while (!readLock1) 85 { 86 OpenThreads::Thread::YieldCurrentThread(); 87 } 88 89 // Tell the first thread we are attempting a write lock so it can try to grab it's second read lock. 90 attemptingWriteLock = true; 91 92 // Try to get the write lock 93 mutex.writeLock(); 94 writeLock = true; 95 mutex.writeUnlock(); 96 } 97 }; 98 } 99 100 // Disabled temporarily b/c it's breaking the Travis build for some reason. Works fine on regular machines. 101 /* 102 TEST_CASE( "ReadWriteMutex can handle multiple read locks from the same thread while a writer is trying to lock" ) { 103 104 ReadWriteMutexTest::Thread1 thread1; 105 ReadWriteMutexTest::Thread2 thread2; 106 107 // Start both threads 108 thread1.start(); 109 thread2.start(); 110 111 // Wait a couple of seconds for the threads to actually start. 112 OpenThreads::Thread::microSleep(2e6); 113 114 // Let the threads go for up to 5 seconds. If they don't finish in that amount of time they are deadlocked. 115 double maxTimeSeconds = 5.0; 116 double elapsedTime = 0.0; 117 118 osg::Timer_t startTime = osg::Timer::instance()->tick(); 119 while (thread1.isRunning() && thread2.isRunning()) 120 { 121 OpenThreads::Thread::YieldCurrentThread(); 122 elapsedTime = osg::Timer::instance()->delta_s(startTime, osg::Timer::instance()->tick()); 123 if (elapsedTime >= maxTimeSeconds) 124 { 125 OE_NOTICE << "Threads failed to complete in " << elapsedTime << " seconds" << std::endl; 126 break; 127 } 128 } 129 130 OE_NOTICE << "Elapsed time = " << elapsedTime << std::endl; 131 REQUIRE(!thread1.isRunning()); 132 REQUIRE(!thread2.isRunning()); 133 REQUIRE(elapsedTime < maxTimeSeconds); 134 } 135 */