From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- sal/qa/osl/mutex/osl_Mutex.cxx | 905 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 905 insertions(+) create mode 100644 sal/qa/osl/mutex/osl_Mutex.cxx (limited to 'sal/qa/osl/mutex/osl_Mutex.cxx') diff --git a/sal/qa/osl/mutex/osl_Mutex.cxx b/sal/qa/osl/mutex/osl_Mutex.cxx new file mode 100644 index 000000000..3424bca07 --- /dev/null +++ b/sal/qa/osl/mutex/osl_Mutex.cxx @@ -0,0 +1,905 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you 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 . + */ + +// include files + +#include +#include +#include +#include +#include +#include "osl_Mutex_Const.h" + +using namespace osl; + +/** pause nSec seconds helper function. +*/ +namespace ThreadHelper +{ + static void thread_sleep_tenth_sec(sal_uInt32 _nTenthSec) + { + osl::Thread::wait(std::chrono::milliseconds(_nTenthSec * 100)); + } + static void thread_sleep( sal_uInt32 _nSec ) + { + /// print statement in thread process must use fflush() to force display. + // t_print("# wait %d seconds. ", _nSec ); + fflush(stdout); + + thread_sleep_tenth_sec( _nSec * 10 ); + // printf("# done\n" ); + } +} + +// Beginning of the test cases for osl_Mutex class + +namespace { + +/** mutually exclusive data +*/ +struct resource { + sal_Int32 data1; + sal_Int32 data2; + Mutex lock; +}; + +/** IncreaseThread provide data. +*/ +class IncreaseThread : public Thread +{ +public: + explicit IncreaseThread( struct resource *pData ): pResource( pData ) { } + + virtual ~IncreaseThread( ) override + { + CPPUNIT_ASSERT_MESSAGE( "#IncreaseThread does not shutdown properly.\n", !isRunning( ) ); + } +protected: + struct resource *pResource; + + void SAL_CALL run( ) override + { + pResource->lock.acquire( ); + for( sal_Int8 i = 0; i < 3; i++ ) + { + pResource->data1++; + yield( ); //yield() give CPU time to other thread, other thread if not block, they will change the data; + } + if ( pResource->data2 == 0 ) + pResource->data2 = ( pResource->data1 > 0 ? pResource->data1 : 0 - pResource->data1 ); + pResource->lock.release(); + } +}; + +/** DecreaseThread consume data. +*/ +class DecreaseThread : public Thread +{ +public: + explicit DecreaseThread( struct resource *pData ): pResource( pData ) { } + + virtual ~DecreaseThread( ) override + { + CPPUNIT_ASSERT_MESSAGE( "#DecreaseThread does not shutdown properly.\n", !isRunning( ) ); + } +protected: + struct resource *pResource; + + void SAL_CALL run( ) override + { + pResource->lock.acquire( ); + for( sal_Int8 i = 0; i < 3; i++ ) + { + pResource->data1--; + yield( ); //yield() give CPU time to other thread, other thread if not block, they will change the data; + } + if ( pResource->data2 == 0 ) + pResource->data2 = ( pResource->data1 > 0 ? pResource->data1 : 0 - pResource->data1 ); + pResource->lock.release(); + } +}; + +/** chain structure used in Threads as critical resource +*/ +struct chain { + sal_Int32 buffer[ BUFFER_SIZE ]; + Mutex lock; + sal_Int8 pos; +}; + +/** PutThread write to the chain structure in a mutex manner. +*/ +class PutThread : public Thread +{ +public: + //get the struct pointer to write data to buffer + explicit PutThread( struct chain* pData ): pChain( pData ) { } + + virtual ~PutThread( ) override + { + CPPUNIT_ASSERT_MESSAGE( "#PutThread does not shutdown properly.\n", !isRunning( ) ); + } +protected: + struct chain* pChain; + + void SAL_CALL run( ) override + { + //block here if the mutex has been acquired + pChain->lock.acquire( ); + + //current position in buffer to write + sal_Int8 nPos = pChain->pos; + oslThreadIdentifier oId = getIdentifier( ); + //write data + sal_Int8 i; + for ( i = 0; i < 5; i++ ) + { + pChain->buffer[ nPos + i ] = oId; + yield( ); + } + //revise the position + pChain->pos = nPos + i; + + //finish writing, release the mutex + pChain->lock.release(); + } +}; + +/** thread for testing Mutex acquire. + */ +class HoldThread : public Thread +{ +public: + //get the Mutex pointer to operate + explicit HoldThread( Mutex* pMutex ): pMyMutex( pMutex ) { } + + virtual ~HoldThread( ) override + { + CPPUNIT_ASSERT_MESSAGE( "#HoldThread does not shutdown properly.\n", !isRunning( ) ); + } +protected: + Mutex* pMyMutex; + + void SAL_CALL run() override + { + // block here if the mutex has been acquired + pMyMutex->acquire( ); + printf("# Mutex acquired. \n" ); + pMyMutex->release( ); + } +}; + +class WaitThread : public Thread +{ +public: + //get the Mutex pointer to operate + explicit WaitThread( Mutex* pMutex ): pMyMutex( pMutex ) { } + + virtual ~WaitThread( ) override + { + CPPUNIT_ASSERT_MESSAGE( "#WaitThread does not shutdown properly.\n", !isRunning( ) ); + } +protected: + Mutex* pMyMutex; + + void SAL_CALL run( ) override + { + // block here if the mutex has been acquired + pMyMutex->acquire( ); + ThreadHelper::thread_sleep_tenth_sec( 2 ); + pMyMutex->release( ); + } +}; + +/** thread for testing getGlobalMutex. + */ +class GlobalMutexThread : public Thread +{ +public: + //get the Mutex pointer to operate + GlobalMutexThread( ){ } + + virtual ~GlobalMutexThread( ) override + { + CPPUNIT_ASSERT_MESSAGE( "#GlobalMutexThread does not shutdown properly.\n", !isRunning( ) ); + } +protected: + void SAL_CALL run( ) override + { + // block here if the mutex has been acquired + Mutex* pGlobalMutex; + pGlobalMutex = Mutex::getGlobalMutex( ); + pGlobalMutex->acquire( ); + printf("# Global Mutex acquired. \n" ); + pGlobalMutex->release( ); + } +}; + +} + +namespace osl_Mutex +{ + + /** Test of the osl::Mutex::constructor + */ + class ctor : public CppUnit::TestFixture + { + public: + // initialise your test code values here. + struct chain m_Data; + struct resource m_Res; + + void setUp( ) override + { + for ( sal_Int8 i=0; i < BUFFER_SIZE; i++ ) + m_Data.buffer[i] = 0; + m_Data.pos = 0; + + m_Res.data1 = 0; + m_Res.data2 = 0; + } + + /** Create two threads to write data to the same buffer, use Mutex to assure + during one thread write data five times, the other thread should not begin writing. + the two threads wrote two different data: their thread ID, so we can check the data + in buffer to know the order of the two threads writing + */ + void ctor_001() + { + PutThread myThread1( &m_Data ); + PutThread myThread2( &m_Data ); + + myThread1.create( ); + myThread2.create( ); + + //wait until the two threads terminate + myThread1.join( ); + myThread2.join( ); + + bool bRes = false; + + // every 5 data should the same + // LLA: this is not a good check, it's too fix + if (m_Data.buffer[0] == m_Data.buffer[1] && + m_Data.buffer[1] == m_Data.buffer[2] && + m_Data.buffer[2] == m_Data.buffer[3] && + m_Data.buffer[3] == m_Data.buffer[4] && + m_Data.buffer[5] == m_Data.buffer[6] && + m_Data.buffer[6] == m_Data.buffer[7] && + m_Data.buffer[7] == m_Data.buffer[8] && + m_Data.buffer[8] == m_Data.buffer[9]) + bRes = true; + + /*for (sal_Int8 i=0; iacquire(); + + GlobalMutexThread myThread; + myThread.create(); + + ThreadHelper::thread_sleep_tenth_sec(1); + bool bRes1 = myThread.isRunning(); + + pGlobalMutex->release(); + ThreadHelper::thread_sleep_tenth_sec(1); + // after release mutex, myThread stops blocking and will terminate immediately + bool bRes2 = myThread.isRunning(); + + CPPUNIT_ASSERT_MESSAGE("Global Mutex works", bRes1); + CPPUNIT_ASSERT_MESSAGE("Global Mutex works", !bRes2); + } + + void getGlobalMutex_002( ) + { + bool bRes; + + Mutex *pGlobalMutex; + pGlobalMutex = Mutex::getGlobalMutex( ); + pGlobalMutex->acquire( ); + { + Mutex *pGlobalMutex1; + pGlobalMutex1 = Mutex::getGlobalMutex( ); + bRes = pGlobalMutex1->release( ); + } + + CPPUNIT_ASSERT_MESSAGE( "Global Mutex works: if the code between {} get the different mutex as the former one, it will return false when release.", + bRes ); + } + + CPPUNIT_TEST_SUITE(getGlobalMutex); + CPPUNIT_TEST(getGlobalMutex_001); + CPPUNIT_TEST(getGlobalMutex_002); + CPPUNIT_TEST_SUITE_END(); + }; // class getGlobalMutex + +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Mutex::ctor, "osl_Mutex"); +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Mutex::acquire, "osl_Mutex"); +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Mutex::tryToAcquire, "osl_Mutex"); +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Mutex::release, "osl_Mutex"); +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Mutex::getGlobalMutex, "osl_Mutex"); +} // namespace osl_Mutex + +// Beginning of the test cases for osl_Guard class + +namespace { + +class GuardThread : public Thread +{ +public: + //get the Mutex pointer to operate + explicit GuardThread( Mutex* pMutex ): pMyMutex( pMutex ) { } + + virtual ~GuardThread( ) override + { + CPPUNIT_ASSERT_MESSAGE( "#GuardThread does not shutdown properly.\n", !isRunning( ) ); + } +protected: + Mutex* pMyMutex; + + void SAL_CALL run( ) override + { + // block here if the mutex has been acquired + MutexGuard aGuard( pMyMutex ); + ThreadHelper::thread_sleep_tenth_sec( 2 ); + } +}; + +} + +namespace osl_Guard +{ + class ctor : public CppUnit::TestFixture + { + public: + // insert your test code here. + void ctor_001() + { + Mutex aMutex; + GuardThread myThread(&aMutex); + myThread.create(); + + ThreadHelper::thread_sleep_tenth_sec(1); + bool bRes = aMutex.tryToAcquire(); + // after 1 second, the mutex has been guarded, and the child thread should be running + bool bRes1 = myThread.isRunning(); + + myThread.join(); + bool bRes2 = aMutex.tryToAcquire(); + + CPPUNIT_ASSERT_MESSAGE("GuardThread constructor", + !bRes); + CPPUNIT_ASSERT_MESSAGE("GuardThread constructor", + bRes1); + CPPUNIT_ASSERT_MESSAGE("GuardThread constructor", + bRes2); + } + + void ctor_002( ) + { + Mutex aMutex; + + /// use reference constructor here + MutexGuard myGuard( aMutex ); + + /// the GuardThread will block here when it is initialised. + GuardThread myThread( &aMutex ); + myThread.create( ); + + /// is it still blocking? + ThreadHelper::thread_sleep_tenth_sec( 2 ); + bool bRes = myThread.isRunning( ); + + /// oh, release him. + aMutex.release( ); + myThread.join( ); + + CPPUNIT_ASSERT_MESSAGE("GuardThread constructor: reference initialization, acquire the mutex before running the thread, then check if it is blocking.", + bRes); + } + + CPPUNIT_TEST_SUITE(ctor); + CPPUNIT_TEST(ctor_001); + CPPUNIT_TEST(ctor_002); + CPPUNIT_TEST_SUITE_END(); + }; // class ctor + +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Guard::ctor, "osl_Guard"); +} // namespace osl_Guard + +// Beginning of the test cases for osl_ClearableGuard class + +namespace { + +/** Thread for test ClearableGuard + */ +class ClearGuardThread : public Thread +{ +public: + //get the Mutex pointer to operate + explicit ClearGuardThread( Mutex* pMutex ): pMyMutex( pMutex ) {} + + virtual ~ClearGuardThread( ) override + { + CPPUNIT_ASSERT_MESSAGE( "#ClearGuardThread does not shutdown properly.\n", !isRunning( ) ); + } +protected: + Mutex* pMyMutex; + + void SAL_CALL run( ) override + { + // acquire the mutex + // printf("# ClearGuardThread" ); + ClearableMutexGuard aGuard( pMyMutex ); + ThreadHelper::thread_sleep( 5 ); + + // release the mutex + aGuard.clear( ); + ThreadHelper::thread_sleep( 2 ); + } +}; + +} + +namespace osl_ClearableGuard +{ + + class ctor : public CppUnit::TestFixture + { + public: + void ctor_001() + { + Mutex aMutex; + + /// now, the aMutex has been guarded. + ClearableMutexGuard myMutexGuard( &aMutex ); + + /// it will return sal_False if the aMutex has not been Guarded. + bool bRes = aMutex.release( ); + + CPPUNIT_ASSERT_MESSAGE("ClearableMutexGuard constructor, test the acquire operation when initialized.", + bRes); + } + + void ctor_002( ) + { + Mutex aMutex; + + /// now, the aMutex has been guarded, this time, we use reference constructor. + ClearableMutexGuard myMutexGuard( aMutex ); + + /// it will return sal_False if the aMutex has not been Guarded. + bool bRes = aMutex.release( ); + + CPPUNIT_ASSERT_MESSAGE("ClearableMutexGuard constructor, test the acquire operation when initialized, we use reference constructor this time.", + bRes); + } + + CPPUNIT_TEST_SUITE(ctor); + CPPUNIT_TEST(ctor_001); + CPPUNIT_TEST(ctor_002); + CPPUNIT_TEST_SUITE_END(); + }; // class ctor + + class clear : public CppUnit::TestFixture + { + public: + void clear_001() + { + Mutex aMutex; + ClearGuardThread myThread(&aMutex); + myThread.create(); + + TimeValue aTimeVal_befor; + osl_getSystemTime( &aTimeVal_befor ); + // wait 1 second to assure the child thread has begun + ThreadHelper::thread_sleep(1); + + while (true) + { + if (aMutex.tryToAcquire()) + { + break; + } + ThreadHelper::thread_sleep(1); + } + TimeValue aTimeVal_after; + osl_getSystemTime( &aTimeVal_after ); + sal_Int32 nSec = aTimeVal_after.Seconds - aTimeVal_befor.Seconds; + printf("nSec is %" SAL_PRIdINT32 "\n", nSec); + + myThread.join(); + + CPPUNIT_ASSERT_MESSAGE("ClearableGuard method: clear", + nSec < 7); + CPPUNIT_ASSERT_MESSAGE("ClearableGuard method: clear", + nSec > 1); + } + + void clear_002( ) + { + Mutex aMutex; + + /// now, the aMutex has been guarded. + ClearableMutexGuard myMutexGuard( &aMutex ); + + /// launch the HoldThread, it will be blocked here. + HoldThread myThread( &aMutex ); + myThread.create( ); + + /// is it blocking? + ThreadHelper::thread_sleep_tenth_sec( 4 ); + bool bRes = myThread.isRunning( ); + + /// use clear to release. + myMutexGuard.clear( ); + myThread.join( ); + bool bRes1 = myThread.isRunning( ); + + CPPUNIT_ASSERT_MESSAGE( "ClearableGuard method: clear, control the HoldThread's running status!", + bRes ); + CPPUNIT_ASSERT_MESSAGE( "ClearableGuard method: clear, control the HoldThread's running status!", + !bRes1 ); + } + + CPPUNIT_TEST_SUITE( clear ); + CPPUNIT_TEST( clear_001 ); + CPPUNIT_TEST( clear_002 ); + CPPUNIT_TEST_SUITE_END( ); + }; // class clear + +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( osl_ClearableGuard::ctor, "osl_ClearableGuard" ); +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( osl_ClearableGuard::clear, "osl_ClearableGuard" ); +} // namespace osl_ClearableGuard + +// Beginning of the test cases for osl_ResettableGuard class + +namespace { + +/** Thread for test ResettableGuard + */ +class ResetGuardThread : public Thread +{ +public: + //get the Mutex pointer to operate + explicit ResetGuardThread( Mutex* pMutex ): pMyMutex( pMutex ) {} + + virtual ~ResetGuardThread( ) override + { + CPPUNIT_ASSERT_MESSAGE( "#ResetGuardThread does not shutdown properly.\n", !isRunning( ) ); + } +protected: + Mutex* pMyMutex; + + void SAL_CALL run( ) override + { + // acquire the mutex + printf("# ResettableGuard\n" ); + ResettableMutexGuard aGuard( pMyMutex ); + // release the mutex + aGuard.clear( ); + ThreadHelper::thread_sleep_tenth_sec( 2 ); + } +}; + +} + +namespace osl_ResettableGuard +{ + class ctor : public CppUnit::TestFixture + { + public: + void ctor_001() + { + Mutex aMutex; + + /// now, the aMutex has been guarded. + ResettableMutexGuard myMutexGuard( &aMutex ); + + /// it will return sal_False if the aMutex has not been Guarded. + bool bRes = aMutex.release( ); + + CPPUNIT_ASSERT_MESSAGE("ResettableMutexGuard constructor, test the acquire operation when initialized.", + bRes); + + aMutex.acquire(); + } + + void ctor_002( ) + { + Mutex aMutex; + + /// now, the aMutex has been guarded, this time, we use reference constructor. + ResettableMutexGuard myMutexGuard( aMutex ); + + /// it will return sal_False if the aMutex has not been Guarded. + bool bRes = aMutex.release( ); + + CPPUNIT_ASSERT_MESSAGE( "ResettableMutexGuard constructor, test the acquire operation when initialized, we use reference constructor this time.", + bRes); + + aMutex.acquire(); + } + + CPPUNIT_TEST_SUITE(ctor); + CPPUNIT_TEST(ctor_001); + CPPUNIT_TEST(ctor_002); + CPPUNIT_TEST_SUITE_END(); + }; // class ctor + + class reset : public CppUnit::TestFixture + { + public: + void reset_001( ) + { + Mutex aMutex; + ResetGuardThread myThread( &aMutex ); + ResettableMutexGuard myMutexGuard( aMutex ); + myThread.create( ); + + /// is it running? and clear done? + bool bRes = myThread.isRunning( ); + myMutexGuard.clear( ); + ThreadHelper::thread_sleep_tenth_sec( 1 ); + + /// if reset is not success, the release will return sal_False + myMutexGuard.reset( ); + bool bRes1 = aMutex.release( ); + myThread.join( ); + + CPPUNIT_ASSERT_MESSAGE( "ResettableMutexGuard method: reset", + bRes ); + CPPUNIT_ASSERT_MESSAGE( "ResettableMutexGuard method: reset", + bRes1 ); + + aMutex.acquire(); + } + +#ifdef LINUX + void reset_002( ) + { + Mutex aMutex; + ResettableMutexGuard myMutexGuard( &aMutex ); + + /// shouldn't release after clear; + myMutexGuard.clear( ); + aMutex.acquire(); + bool bRes = aMutex.release( ); + + /// can release after reset. + myMutexGuard.reset( ); + bool bRes1 = aMutex.release( ); + + CPPUNIT_ASSERT_MESSAGE( "ResettableMutexGuard method: reset, release after clear and reset, on Solaris, the mutex can be release without acquire, so it can not passed on (SOLARIS), but not the reason for reset_002", + bRes ); + CPPUNIT_ASSERT_MESSAGE( "ResettableMutexGuard method: reset, release after clear and reset, on Solaris, the mutex can be release without acquire, so it can not passed on (SOLARIS), but not the reason for reset_002", + bRes1 ); + + aMutex.acquire(); + } +#endif + + CPPUNIT_TEST_SUITE(reset); + CPPUNIT_TEST(reset_001); +#ifdef LINUX + CPPUNIT_TEST(reset_002); +#endif + CPPUNIT_TEST_SUITE_END(); + }; // class reset + +CPPUNIT_TEST_SUITE_REGISTRATION(osl_ResettableGuard::ctor); +CPPUNIT_TEST_SUITE_REGISTRATION(osl_ResettableGuard::reset); +} // namespace osl_ResettableGuard + +// The following sets variables for GNU EMACS +// Local Variables: +// tab-width:4 +// End: + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3