Path: blob/master/runtime/gc_tests/rwlocktests/gc_rwlocktest.cpp
6000 views
/*******************************************************************************1* Copyright (c) 2001, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/21#include "CuTest.h"22#include "j9.h"23#include "omrutil.h"24#include "thread_api.h"2526#include "LightweightNonReentrantRWLock.hpp"2728#define MILLI_TIMEOUT 1000029#define NANO_TIMEOUT 03031#define STEP_MILLI_TIMEOUT 60000032#define STEP_NANO_TIMEOUT 0333435extern J9PortLibrary *sharedPortLibrary;3637MM_LightweightNonReentrantRWLock g_lock;38BOOLEAN g_isInitialized = FALSE;3940/* structure used to pass info to concurrent threads for some tests */41typedef struct SupportThreadInfo {42MM_LightweightNonReentrantRWLock* lock;43omrthread_monitor_t synchronization;44omrthread_entrypoint_t* functionsToRun;45UDATA numberFunctions;46volatile UDATA readCounter;47volatile UDATA writeCounter;48volatile BOOLEAN done;49}SupportThreadInfo ;5051/* forward declarations */52void freeSupportThreadInfo(SupportThreadInfo* info);5354/**55* This method is called to run the set of steps that will be run on a thread56* @param info SupportThreadInfo structure that contains the information for the steps and other parameters57* that are used58*/59static IDATA J9THREAD_PROC60runRequest(SupportThreadInfo* info)61{62IDATA result = 0;63UDATA i =0;6465omrthread_monitor_enter(info->synchronization);66omrthread_monitor_exit(info->synchronization);67for (i=0;i<info->numberFunctions;i++){68result = info->functionsToRun[i]((void*)info);69omrthread_monitor_enter(info->synchronization);70omrthread_monitor_notify(info->synchronization);71if (info->done == TRUE){72omrthread_exit(info->synchronization);73break;74}75omrthread_monitor_wait_interruptable(info->synchronization,STEP_MILLI_TIMEOUT,STEP_NANO_TIMEOUT);76omrthread_monitor_exit(info->synchronization);77}7879return result;80}8182/**83* This method is called to create a SupportThreadInfo for a test. It will populate the monitor and84* lock being used and zero out the counter85*86* @param functionsToRun an array of functions pointers. Each function will be run one in sequence synchronized87* using the monitor within the SupportThreadInfo88* @param numberFunctions the number of functions in the functionsToRun array89* @returns a pointer to the newly created SupportThreadInfo90*/91SupportThreadInfo*92createSupportThreadInfo(omrthread_entrypoint_t* functionsToRun, UDATA numberFunctions){93PORT_ACCESS_FROM_PORT(sharedPortLibrary);94SupportThreadInfo* info = (SupportThreadInfo*) j9mem_allocate_memory(sizeof(SupportThreadInfo), OMRMEM_CATEGORY_THREADS);95info->readCounter = 0;96info->writeCounter = 0;97info->functionsToRun = functionsToRun;98info->numberFunctions = numberFunctions;99info->done = FALSE;100info->lock = &g_lock;101if (!g_isInitialized) {102info->lock->initialize(128);103g_isInitialized = TRUE;104}105106omrthread_monitor_init_with_name(&info->synchronization, 0, "supportThreadAInfo monitor");107return info;108}109110/**111* This method free the internal structures and memory for a SupportThreadInfo112* @param info the SupportThreadInfo instance to be freed113*/114void115freeSupportThreadInfo(SupportThreadInfo* info){116PORT_ACCESS_FROM_PORT(sharedPortLibrary);117if (info->synchronization != NULL){118omrthread_monitor_destroy(info->synchronization);119}120121if (g_isInitialized) {122info->lock->tearDown();123g_isInitialized = FALSE;124}125j9mem_free_memory(info);126}127128/**129* This method is called to push the concurrent thread to run the next function130*/131void132triggerNextStepWithStatus(SupportThreadInfo* info, BOOLEAN done){133omrthread_monitor_enter(info->synchronization);134info->done = done;135omrthread_monitor_notify(info->synchronization);136omrthread_monitor_wait_interruptable(info->synchronization,MILLI_TIMEOUT,NANO_TIMEOUT);137omrthread_monitor_exit(info->synchronization);138}139140/**141* This method is called to push the concurrent thread to run the next function142*/143void144triggerNextStep(SupportThreadInfo* info){145triggerNextStepWithStatus(info,FALSE);146}147148/**149* This method is called to push the concurrent thread to run the next function150* and tell the thread that the test is done151*/152void153triggerNextStepDone(SupportThreadInfo* info){154triggerNextStepWithStatus(info,TRUE);155}156157/**158* This method starts a concurrent thread and runs the functions specified in the159* SupportThreadInfo passed in. It only returns once the first function has run160*161* @param info SupportThreadInfo structure containing the functions and lock for the tests162* @returns 0 on success163*/164IDATA165startConcurrentThread(SupportThreadInfo* info){166omrthread_t newThread = NULL;167168omrthread_monitor_enter(info->synchronization);169createThreadWithCategory(170&newThread,1710, /* default stack size */172J9THREAD_PRIORITY_NORMAL,1730, /* start immediately */174(omrthread_entrypoint_t) runRequest,175(void*) info,176J9THREAD_CATEGORY_SYSTEM_GC_THREAD);177178omrthread_monitor_wait_interruptable(info->synchronization,MILLI_TIMEOUT,NANO_TIMEOUT);179omrthread_monitor_exit(info->synchronization);180181return 0;182}183184/***********************************************185* Functions that can be used as steps in concurrent threads186* for a test187************************************************/188/**189* This step enters the lock in the SupportThreadInfo for read190* @param info the SupportThreadInfo which can be used by the step191*/192static IDATA J9THREAD_PROC193enter_rwlock_read(SupportThreadInfo* info)194{195info->lock->enterRead();196info->readCounter++;197return 0;198}199200/**201* This step exits the lock in the SupportThreadInfo for read202* @param info the SupportThreadInfo which can be used by the step203*/204static IDATA J9THREAD_PROC205exit_rwlock_read(SupportThreadInfo* info)206{207info->lock->exitRead();208info->readCounter--;209return 0;210}211212/**213* This step enters the lock in the SupportThreadInfo for write214* @param info the SupportThreadInfo which can be used by the step215*/216static IDATA J9THREAD_PROC217enter_rwlock_write(SupportThreadInfo* info)218{219info->lock->enterWrite();220info->writeCounter++;221return 0;222}223224/**225* This step exits the lock in the SupportThreadInfo for write226* @param info the SupportThreadInfo which can be used by the step227*/228static IDATA229J9THREAD_PROC exit_rwlock_write(SupportThreadInfo* info)230{231info->lock->exitWrite();232info->writeCounter--;233return 0;234}235236237238/**239* validate that we can initialize a LightweightNonReentrantRWLock successfully240*/241void242Test_RWLock_InitializeTest(CuTest *tc)243{244IDATA result;245MM_LightweightNonReentrantRWLock lock;246/* initialize */247result = lock.initialize(128);248CuAssertTrue(tc, 0 == result);249/* clean up */250result = lock.tearDown();251CuAssertTrue(tc, 0 == result);252}253254/**255* Validate that we can enter/exit a LightweightNonReentrantRWLock for read256*/257void258Test_RWLock_RWReadEnterExitTest(CuTest *tc)259{260IDATA result;261MM_LightweightNonReentrantRWLock lock;262/* initialize */263result = lock.initialize(128);264CuAssertTrue(tc, 0 == result);265/* enterRead */266result = lock.enterRead();267CuAssertTrue(tc, 0 == result);268/* exitRead */269result = lock.exitRead();270CuAssertTrue(tc, 0 == result);271/* clean up */272result = lock.tearDown();273CuAssertTrue(tc, 0 == result);274}275276/**277* Validate that we can enter/exit a LightweightNonReentrantRWLock for write278*/279void280Test_RWLock_RWWriteEnterExitTest(CuTest *tc)281{282IDATA result;283MM_LightweightNonReentrantRWLock lock;284/* initialize */285result = lock.initialize(128);286CuAssertTrue(tc, 0 == result);287/* enterWrite */288result = lock.enterWrite();289CuAssertTrue(tc, 0 == result);290/* exitWrite */291result = lock.exitWrite();292CuAssertTrue(tc, 0 == result);293/* clean up */294result = lock.tearDown();295CuAssertTrue(tc, 0 == result);296}297298/**299* Validate threads can shared the LightweightNonReentrantRWLock for read300*/301void302Test_RWLock_multipleReadersTest(CuTest *tc)303{304305/* Start concurrent thread acquired the LightweightNonReentrantRWLock for read */306SupportThreadInfo* info;307omrthread_entrypoint_t functionsToRun[2];308functionsToRun[0] = (omrthread_entrypoint_t) &enter_rwlock_read;309functionsToRun[1] = (omrthread_entrypoint_t) &exit_rwlock_read;310info = createSupportThreadInfo(functionsToRun, 2);311startConcurrentThread(info);312313/* Validate that we can acquire the lock for read as well */314CuAssertTrue(tc, 1 == info->readCounter);315enter_rwlock_read(info);316CuAssertTrue(tc, 2 == info->readCounter);317exit_rwlock_read(info);318CuAssertTrue(tc, 1 == info->readCounter);319320/* ok we were not blocked by the other thread holding the LightweightNonReentrantRWLock for read321* so ask it to release the lock322*/323triggerNextStepDone(info);324CuAssertTrue(tc, 0 == info->readCounter);325freeSupportThreadInfo(info);326}327328/**329* Validates the following330*331* readers are excludes while another thread holds the LightweightNonReentrantRWLock for write332* once writer exits, reader can enter333*/334void335Test_RWLock_readersExcludedTest(CuTest *tc)336{337SupportThreadInfo* info;338omrthread_entrypoint_t functionsToRun[2];339functionsToRun[0] = (omrthread_entrypoint_t) &enter_rwlock_read;340functionsToRun[1] = (omrthread_entrypoint_t) &exit_rwlock_read;341info = createSupportThreadInfo(functionsToRun, 2);342/* first enter the lock for write */343CuAssertTrue(tc, 0 == info->readCounter);344enter_rwlock_write(info);345CuAssertTrue(tc, 1 == info->writeCounter);346347/* start the concurrent thread that will try to enter for read and348* check that it is blocked349*/350startConcurrentThread(info);351CuAssertTrue(tc, 0 == info->readCounter);352353/* now release the lock and validate that the thread enters it */354omrthread_monitor_enter(info->synchronization);355exit_rwlock_write(info);356CuAssertTrue(tc, 0 == info->writeCounter);357omrthread_monitor_wait_interruptable(info->synchronization,MILLI_TIMEOUT,NANO_TIMEOUT);358omrthread_monitor_exit(info->synchronization);359CuAssertTrue(tc, 1 == info->readCounter);360/* done now so ask thread to release and clean up */361triggerNextStepDone(info);362CuAssertTrue(tc, 0 == info->readCounter);363freeSupportThreadInfo(info);364365}366367/**368* validates the following369*370* writer is excluded while another thread holds the LightweightNonReentrantRWLock for read371* once reader exits writer can enter372*/373void374Test_RWLock_writersExcludedTest(CuTest *tc)375{376SupportThreadInfo* info;377SupportThreadInfo* infoReader;378omrthread_entrypoint_t functionsToRun[2];379omrthread_entrypoint_t functionsToRunReader[2];380functionsToRun[0] = (omrthread_entrypoint_t) &enter_rwlock_write;381functionsToRun[1] = (omrthread_entrypoint_t) &exit_rwlock_write;382functionsToRunReader[0] = (omrthread_entrypoint_t) &enter_rwlock_read;383functionsToRunReader[1] = (omrthread_entrypoint_t) &exit_rwlock_read;384info = createSupportThreadInfo(functionsToRun, 2);385infoReader = createSupportThreadInfo(functionsToRunReader, 2);386387/* start the thread that will enter for read */388startConcurrentThread(infoReader);389CuAssertTrue(tc, 1 == infoReader->readCounter);390391/* start the concurrent thread that will try to enter for write and392* check that it is blocked393*/394startConcurrentThread(info);395CuAssertTrue(tc, 0 == info->writeCounter);396397/* now ask the thread that as entered for read to exit we know that for RT398* it cannot complete until the writer exist but it will have released the lock399*/400omrthread_monitor_enter(info->synchronization);401triggerNextStepDone(infoReader);402omrthread_monitor_wait_interruptable(info->synchronization,MILLI_TIMEOUT,NANO_TIMEOUT);403omrthread_monitor_exit(info->synchronization);404CuAssertTrue(tc, 1 == info->writeCounter);405406/* now ask the thread that should now be entered write to exit */407omrthread_monitor_enter(infoReader->synchronization);408triggerNextStepDone(info);409omrthread_monitor_wait_interruptable(infoReader->synchronization,MILLI_TIMEOUT,NANO_TIMEOUT);410omrthread_monitor_exit(infoReader->synchronization);411CuAssertTrue(tc, 0 == info->writeCounter);412CuAssertTrue(tc, 0 == infoReader->readCounter);413414/* done now clean up */415freeSupportThreadInfo(info);416freeSupportThreadInfo(infoReader);417}418419/**420* validates the following421*422* writer is excluded while another thread holds the lock for write423* once writer exits second writer can enter424*/425void426Test_RWLock_writerExcludesWriterTest(CuTest *tc)427{428SupportThreadInfo* info;429omrthread_entrypoint_t functionsToRun[2];430functionsToRun[0] = (omrthread_entrypoint_t) &enter_rwlock_write;431functionsToRun[1] = (omrthread_entrypoint_t) &exit_rwlock_write;432info = createSupportThreadInfo(functionsToRun, 2);433/* first enter the lock for write */434CuAssertTrue(tc, 0 == info->writeCounter);435info->lock->enterWrite();436437/* start the concurrent thread that will try to enter for write and438* check that it is blocked439*/440startConcurrentThread(info);441CuAssertTrue(tc, 0 == info->writeCounter);442443/* now release the lock and validate that the thread enters it */444omrthread_monitor_enter(info->synchronization);445info->lock->exitWrite();446omrthread_monitor_wait_interruptable(info->synchronization,MILLI_TIMEOUT,NANO_TIMEOUT);447omrthread_monitor_exit(info->synchronization);448CuAssertTrue(tc, 1 == info->writeCounter);449450/* done now so ask thread to release and clean up */451triggerNextStepDone(info);452freeSupportThreadInfo(info);453}454455/* validates the following456*457* the new reader will be excluded if the writer is pending (waiting on another reader)458* 2nd reader to enter the lock after all of writer threads exit459*/460void461Test_RWLock_waitingWriterExcludes2ndReader(CuTest *tc)462{463SupportThreadInfo* info;464SupportThreadInfo* infoReader;465SupportThreadInfo* infoReader2;466omrthread_entrypoint_t functionsToRun[2];467omrthread_entrypoint_t functionsToRunReader[2];468469/* set up the steps for the 2 concurrent threads */470functionsToRun[0] = (omrthread_entrypoint_t) &enter_rwlock_write;471functionsToRun[1] = (omrthread_entrypoint_t) &exit_rwlock_write;472functionsToRunReader[0] = (omrthread_entrypoint_t) &enter_rwlock_read;473functionsToRunReader[1] = (omrthread_entrypoint_t) &exit_rwlock_read;474475info = createSupportThreadInfo(functionsToRun, 2);476infoReader = createSupportThreadInfo(functionsToRunReader, 2);477infoReader2 = createSupportThreadInfo(functionsToRunReader, 2);478479/* SupportThreadInfo structures use the same lock (g_lock) */480481/* start the first concurrent thread that will enter for read */482startConcurrentThread(infoReader2);483CuAssertTrue(tc, 1 == infoReader2->readCounter);484485/* start the concurrent thread that will try to enter for write and486* check that it is blocked487*/488startConcurrentThread(info);489CuAssertTrue(tc, 0 == info->writeCounter);490491/* start the second concurrent thread that will try to enter for read and492* check that it is blocked493*/494startConcurrentThread(infoReader);495CuAssertTrue(tc, 0 == info->readCounter);496497/* now ask the first reader to exit so that the write can enter */498omrthread_monitor_enter(info->synchronization);499triggerNextStep(infoReader2);500omrthread_monitor_wait_interruptable(info->synchronization,MILLI_TIMEOUT,NANO_TIMEOUT);501omrthread_monitor_exit(info->synchronization);502CuAssertTrue(tc, 1 == info->writeCounter);503504/* now let the writer exit so the second reader can enter */505CuAssertTrue(tc, 0 == infoReader->readCounter);506omrthread_monitor_enter(infoReader->synchronization);507triggerNextStepDone(info);508omrthread_monitor_wait_interruptable(infoReader->synchronization,MILLI_TIMEOUT,NANO_TIMEOUT);509omrthread_monitor_exit(infoReader->synchronization);510CuAssertTrue(tc, 1 == infoReader->readCounter);511CuAssertTrue(tc, 0 == info->writeCounter);512513/* ok now let second reader exit */514triggerNextStepDone(infoReader);515CuAssertTrue(tc, 0 == infoReader->readCounter);516517/* now let the threads clean up. */518freeSupportThreadInfo(info);519freeSupportThreadInfo(infoReader);520freeSupportThreadInfo(infoReader2);521}522/**523* validates the following524*525* readers are excluded while another thread holds the lock for write526* once writer exits, all readers wake up and can enter527*/528void529Test_RWLock_AllReadersProceedTest(CuTest *tc)530{531SupportThreadInfo* infoReader1;532SupportThreadInfo* infoReader2;533omrthread_entrypoint_t functionsToRunReader1[2];534omrthread_entrypoint_t functionsToRunReader2[2];535536/* set up the steps for the 2 concurrent threads */537functionsToRunReader1[0] = (omrthread_entrypoint_t) &enter_rwlock_read;538functionsToRunReader1[1] = (omrthread_entrypoint_t) &exit_rwlock_read;539functionsToRunReader2[0] = (omrthread_entrypoint_t) &enter_rwlock_read;540functionsToRunReader2[1] = (omrthread_entrypoint_t) &exit_rwlock_read;541542infoReader1 = createSupportThreadInfo(functionsToRunReader1, 2);543infoReader2 = createSupportThreadInfo(functionsToRunReader2, 2);544545/* two SupportThreadInfo structures use the same lock(g_lock) */546547/* first enter the lock for write */548CuAssertTrue(tc, 0 == infoReader1->readCounter);549CuAssertTrue(tc, 0 == infoReader2->readCounter);550infoReader1->lock->enterWrite();551/* start the concurrent thread that will try to enter for read and552* check that it is blocked553*/554startConcurrentThread(infoReader1);555CuAssertTrue(tc, 0 == infoReader1->readCounter);556557/* start the concurrent thread that will try to enter for read and558* check that it is blocked559*/560startConcurrentThread(infoReader2);561CuAssertTrue(tc, 0 == infoReader2->readCounter);562563/* now release the lock and validate that the second readers still excludes the writer */564omrthread_monitor_enter(infoReader2->synchronization);565omrthread_monitor_enter(infoReader1->synchronization);566567infoReader1->lock->exitWrite();568569/* now validate that the readers have entered the lock*/570omrthread_monitor_wait_interruptable(infoReader1->synchronization,MILLI_TIMEOUT,NANO_TIMEOUT);571omrthread_monitor_exit(infoReader1->synchronization);572omrthread_monitor_wait_interruptable(infoReader2->synchronization,MILLI_TIMEOUT,NANO_TIMEOUT);573omrthread_monitor_exit(infoReader2->synchronization);574575CuAssertTrue(tc, 1 == infoReader1->readCounter);576CuAssertTrue(tc, 1 == infoReader2->readCounter);577578/* ok now let the readers exit */579triggerNextStepDone(infoReader1);580CuAssertTrue(tc, 0 == infoReader1->readCounter);581triggerNextStepDone(infoReader2);582CuAssertTrue(tc, 0 == infoReader2->readCounter);583584/* now let the threads clean up. */585freeSupportThreadInfo(infoReader1);586freeSupportThreadInfo(infoReader2);587}588589CuSuite590*GetRWLockTestSuite()591{592CuSuite *suite = CuSuiteNew();593SUITE_ADD_TEST(suite, Test_RWLock_InitializeTest);594SUITE_ADD_TEST(suite, Test_RWLock_RWReadEnterExitTest);595SUITE_ADD_TEST(suite, Test_RWLock_RWWriteEnterExitTest);596SUITE_ADD_TEST(suite, Test_RWLock_multipleReadersTest);597SUITE_ADD_TEST(suite, Test_RWLock_readersExcludedTest);598SUITE_ADD_TEST(suite, Test_RWLock_writersExcludedTest);599SUITE_ADD_TEST(suite, Test_RWLock_waitingWriterExcludes2ndReader);600SUITE_ADD_TEST(suite, Test_RWLock_writerExcludesWriterTest);601SUITE_ADD_TEST(suite, Test_RWLock_AllReadersProceedTest);602return suite;603}604605606607