diff options
author | Matt Raybuck <mraybuc@us.ibm.com> | 2018-10-24 07:59:26 -0500 |
---|---|---|
committer | Daniel M. Crowell <dcrowell@us.ibm.com> | 2018-11-12 09:07:12 -0600 |
commit | 2e77a7fb14331b3d734aad3d500732dd51dcb32e (patch) | |
tree | 9fad75729721906f9001d86f0d479bb0052fd6e6 /src/usr | |
parent | 87adeec286402eb648f14d274382fb8b84351467 (diff) | |
download | talos-hostboot-2e77a7fb14331b3d734aad3d500732dd51dcb32e.tar.gz talos-hostboot-2e77a7fb14331b3d734aad3d500732dd51dcb32e.zip |
Attribute support for recursive mutexes
Added a new recursive mutex attribute, new test cases, and initializers
for recursive mutex attributes.
Change-Id: I49e6bc4fc2fd84e5c46e5d4c5ec125270347bde5
RTC: 196793
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/67939
Reviewed-by: Nicholas E. Bofferding <bofferdn@us.ibm.com>
Reviewed-by: Ilya Smirnov <ismirno@us.ibm.com>
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src/usr')
-rw-r--r-- | src/usr/targeting/common/targetservice.C | 15 | ||||
-rwxr-xr-x | src/usr/targeting/common/xmltohb/attribute_types_hb.xml | 12 | ||||
-rw-r--r-- | src/usr/targeting/common/xmltohb/target_types_hb.xml | 3 | ||||
-rwxr-xr-x | src/usr/targeting/common/xmltohb/xmltohb.pl | 89 | ||||
-rwxr-xr-x | src/usr/targeting/targetservicestart.C | 47 | ||||
-rw-r--r-- | src/usr/targeting/test/testtargeting.H | 176 | ||||
-rw-r--r-- | src/usr/testcore/lib/makefile | 2 | ||||
-rw-r--r-- | src/usr/testcore/lib/synctest.H | 15 |
8 files changed, 307 insertions, 52 deletions
diff --git a/src/usr/targeting/common/targetservice.C b/src/usr/targeting/common/targetservice.C index c7f002f87..e3c3dde92 100644 --- a/src/usr/targeting/common/targetservice.C +++ b/src/usr/targeting/common/targetservice.C @@ -1793,14 +1793,23 @@ uint32_t TargetService::resetMutexAttributes(const Target* i_pTarget) for ( uint32_t l_attrIndex = 0; l_attrIndex < l_attrCount; l_attrIndex++) { const ATTRIBUTE_ID l_attrId = l_pAttrIds[l_attrIndex]; - for( const auto mutexId : hbMutexAttrIds) + for( const auto mutex : hbMutexAttrIds) { - if(l_attrId == mutexId) + if(l_attrId == mutex.id) { mutex_t* l_mutex; if(i_pTarget->_tryGetHbMutexAttr(l_attrId, l_mutex)) { - mutex_init(l_mutex); +#ifdef __HOSTBOOT_MODULE + if (mutex.isRecursive) + { + recursive_mutex_init(l_mutex); + } + else +#endif + { + mutex_init(l_mutex); + } l_numberMutexAttrsReset++; } else diff --git a/src/usr/targeting/common/xmltohb/attribute_types_hb.xml b/src/usr/targeting/common/xmltohb/attribute_types_hb.xml index 5f88bbce1..1c574ebaa 100755 --- a/src/usr/targeting/common/xmltohb/attribute_types_hb.xml +++ b/src/usr/targeting/common/xmltohb/attribute_types_hb.xml @@ -330,6 +330,18 @@ </attribute> <attribute> + <id>HB_RECURSIVE_MUTEX_TEST_LOCK</id> + <description>Host boot recursive mutex for testing</description> + <simpleType> + <hbrecursivemutex/> + </simpleType> + <persistency>volatile-zeroed</persistency> + <readable/> + <writeable/> + <hbOnly/> + </attribute> + + <attribute> <id>HB_RSV_MEM_NEXT_SECTION</id> <description> The next HB reserved memory section available to assign diff --git a/src/usr/targeting/common/xmltohb/target_types_hb.xml b/src/usr/targeting/common/xmltohb/target_types_hb.xml index 7ac29598f..eaf0ca0b6 100644 --- a/src/usr/targeting/common/xmltohb/target_types_hb.xml +++ b/src/usr/targeting/common/xmltohb/target_types_hb.xml @@ -260,6 +260,9 @@ <id>HB_MUTEX_TEST_LOCK</id> </attribute> <attribute> + <id>HB_RECURSIVE_MUTEX_TEST_LOCK</id> + </attribute> + <attribute> <id>HB_RSV_MEM_NEXT_SECTION</id> </attribute> <attribute> diff --git a/src/usr/targeting/common/xmltohb/xmltohb.pl b/src/usr/targeting/common/xmltohb/xmltohb.pl index e92bf598e..29727c403 100755 --- a/src/usr/targeting/common/xmltohb/xmltohb.pl +++ b/src/usr/targeting/common/xmltohb/xmltohb.pl @@ -2330,7 +2330,7 @@ namespace TARGETING * Array defining all attribute ids found that are of type hbMutex. * This file is autogenerated and should not be altered. */ -const uint32_t hbMutexAttrIds[] = { +const struct {uint32_t id; bool isRecursive;} hbMutexAttrIds[] = { VERBATIM my @mutexAttrIds; @@ -2340,22 +2340,30 @@ VERBATIM #check if hbmutex tag is present #check that attr is readable/writeable if( (exists $attribute->{simpleType}) - && (exists $attribute->{simpleType}->{hbmutex}) + && (exists $attribute->{simpleType}->{hbmutex} + || exists $attribute->{simpleType}->{hbrecursivemutex}) && (exists $attribute->{readable}) && (exists $attribute->{writeable})) { - push @mutexAttrIds, $attribute->{id}; + my $recursiveType = "false"; + if (exists $attribute->{simpleType}->{hbrecursivemutex}) + { + $recursiveType = "true"; + } + + push @mutexAttrIds, ([ $attribute->{id}, $recursiveType ]); } } # variables that can be used for writing the enums to the file my $attrId; my $hexVal; + my $recursiveVal; # Format below intentionally > 80 chars for clarity format ATTRMUTEXFORMAT = - @>>>>>>>>>> - $hexVal ."," + @>>>>>>>>>>>>>>>>>>>> + "{ ". $hexVal .", ". $recursiveVal ." }," . select($outFile); $~ = 'ATTRMUTEXFORMAT'; @@ -2368,7 +2376,9 @@ VERBATIM $attrId = $enumerator->{name}; foreach my $mutexAttrId (@mutexAttrIds) { - if( $mutexAttrId eq $attrId ) + $recursiveVal = $mutexAttrId->[1]; + + if( $mutexAttrId->[0] eq $attrId ) { write; last; @@ -2494,7 +2504,8 @@ sub writeTraitFileTraits { # Mark the attribute as being a host boot mutex or non-host boot mutex if( (exists $attribute->{simpleType}) - && (exists $attribute->{simpleType}->{hbmutex}) ) + && (exists $attribute->{simpleType}->{hbmutex} + || exists $attribute->{simpleType}->{hbrecursivemutex}) ) { $traits .= " hbMutex,"; } @@ -2751,6 +2762,7 @@ sub writeAttrErrlCFile { !(exists $attribute->{writeable}) || # read-only attributes (exists $attribute->{simpleType} && ( (exists $attribute->{simpleType}->{hbmutex}) || + (exists $attribute->{simpleType}->{hbrecrusivemutex}) || (exists $attribute->{simpleType}->{fspmutex}))) # mutex attributes ) { print $outFile " case (ATTR_",$attribute->{id},"): { break; }\n"; @@ -2923,6 +2935,7 @@ sub writeAttrErrlCFile { !(exists $attribute->{writeable}) || # read-only attributes (exists $attribute->{simpleType} && ( (exists $attribute->{simpleType}->{hbmutex}) || + (exists $attribute->{simpleType}->{hbrecursivemutex}) || (exists $attribute->{simpleType}->{fspmutex}))) # mutex attributes ) { next; @@ -3040,6 +3053,7 @@ sub writeAttrErrlHFile { !(exists $attribute->{writeable}) || # read-only attributes (exists $attribute->{simpleType} && ( (exists $attribute->{simpleType}->{hbmutex}) || + (exists $attribute->{simpleType}->{hbrecursivemutex}) || (exists $attribute->{simpleType}->{fspmutex}))) # mutex attributes ) { print $outFile " //not readable\n"; @@ -3069,7 +3083,7 @@ sub writeAttrErrlHFile { } } # makes no sense to dump mutex attributes, so skipping - elsif(exists $attribute->{simpleType} && (exists $attribute->{simpleType}->{hbmutex}) ) { + elsif(exists $attribute->{simpleType} && (exists $attribute->{simpleType}->{hbmutex}) || (exists $attribute->{simpleType}->{hbrecursivemutex}) ) { print $outFile " //Mutex attributes - skipping\n"; } # makes no sense to dump fsp mutex attributes, so skipping @@ -4880,6 +4894,7 @@ sub simpleTypeProperties { $typesHoH{"uint64_t"} = { supportsArray => 1, canBeHex => 1, complexTypeSupport => 1, typeName => "uint64_t" , bytes => 8, bits => 64, default => \&defaultZero , alignment => 1, specialPolicies =>\&null, packfmt =>\&pack8byte}; $typesHoH{"enumeration"} = { supportsArray => 1, canBeHex => 1, complexTypeSupport => 0, typeName => "XMLTOHB_USE_PARENT_ATTR_ID" , bytes => 0, bits => 0 , default => \&defaultEnum , alignment => 1, specialPolicies =>\&null, packfmt => "packEnumeration"}; $typesHoH{"hbmutex"} = { supportsArray => 1, canBeHex => 1, complexTypeSupport => 0, typeName => "mutex_t*" , bytes => 24, bits => 192, default => \&defaultZero , alignment => 8, specialPolicies =>\&enforceHbMutex, packfmt =>\&packMutex}; + $typesHoH{"hbrecursivemutex"} = { supportsArray => 1, canBeHex => 1, complexTypeSupport => 0, typeName => "mutex_t*" , bytes => 24, bits => 192, default => \&defaultZero , alignment => 8, specialPolicies =>\&enforceHbMutex, packfmt =>\&packMutex}; $typesHoH{"Target_t"} = { supportsArray => 0, canBeHex => 1, complexTypeSupport => 0, typeName => "TARGETING::Target*" , bytes => 8, bits => 64, default => \&defaultZero , alignment => 8, specialPolicies =>\&null, packfmt =>\&pack8byte}; $typesHoH{"fspmutex"} = { supportsArray => 1, canBeHex => 1, complexTypeSupport => 0, typeName => "util::Mutex*" , bytes => 8, bits => 64, default => \&defaultZero , alignment => 8, specialPolicies =>\&enforceFspMutex, packfmt =>\&pack8byte}; @@ -5038,33 +5053,45 @@ sub mergeComplexAttributeFields { # the two fields. my $mergedFields = dclone $currentAttrFields; - # Iterate over the fields of $newField and look for their corresponding id - # in $currentAttrFields. All the fields in $newField should exist in - # $currentAttrFields, if not, then there is a problem - foreach my $newField (@{$newAttrFields->{default}->{field}}) + # If the merged field's (an alias for the current attribute fields) + # hash is empty, then just assign it the new attribute field's value - + # no merge necessary + if ($mergedFields->{default} == 0) { - my $foundField = 0; - - # Iterate over $mergedFields (really $currentAttrFields) looking - # for the $newField of $newAttrFields - foreach my $currentField (@{$mergedFields->{default}->{field}}) + $mergedFields->{default} = $newAttrFields->{default}; + } + # Only proceed if both attribute pairs have values to merge + elsif ($newAttrFields->{default} != 0) + { + # Iterate over the fields of $newField and look for their corresponding + # id in $currentAttrFields. All the fields in $newField should exist in + # $currentAttrFields, if not, then there is a problem + foreach my $newField (@{$newAttrFields->{default}->{field}}) { - # Found the field in question - if ($currentField->{id} eq $newField->{id}) + my $foundField = 0; + + # Iterate over $mergedFields (really $currentAttrFields) looking + # for the $newField of $newAttrFields + foreach my $currentField (@{$mergedFields->{default}->{field}}) { - # Merge in the new value from $newField - $currentField->{value} = $newField->{value}; - $foundField = 1; - last; - } - } # end foreach my $currentField ... + # Found the field in question + if ($currentField->{id} eq $newField->{id}) + { + # Merge in the new value from $newField + $currentField->{value} = $newField->{value}; + $foundField = 1; + last; + } + } # end foreach my $currentField ... - # A field was not found ... halt execution - if ($foundField == 0) - { - croak("Field $newField is not supported.") - } - } # end foreach my $newField ... + # A field was not found ... halt execution + if ($foundField == 0) + { + croak("Field $newField is not supported.") + } + } # end foreach my $newField ... + } + # else new attribute fields is empty - nothing to merge return $mergedFields; } diff --git a/src/usr/targeting/targetservicestart.C b/src/usr/targeting/targetservicestart.C index 20dce1e48..795bd4072 100755 --- a/src/usr/targeting/targetservicestart.C +++ b/src/usr/targeting/targetservicestart.C @@ -221,29 +221,40 @@ static void initTargeting(errlHndl_t& io_pError) } } - initializeAttributes(l_targetService, l_isMpipl, l_isIstepMode, l_scratch); - //Ensure all mutex attributes are reset on MPIPL + initializeAttributes(l_targetService, l_isMpipl, l_isIstepMode, + l_scratch); + + + uint32_t l_peerTargetsAdjusted = 0; + uint32_t l_numberMutexAttrsReset = 0; + if(l_isMpipl) { - // updatePeerTargets will write to read-only attribute pages - // to get around vmm fails we need to allow writes to readonly - // memory for the duration of this loop (unlocking is done above) - uint32_t l_peerTargetsAdjusted = 0; - uint32_t l_numberMutexAttrsReset = 0; + + for( auto targ_iter = l_targetService.begin(); targ_iter != l_targetService.end(); targ_iter++) { const Target* l_pTarget = *targ_iter; - // Check if there any mutex attributes we need to reset on this target - l_numberMutexAttrsReset += l_targetService.resetMutexAttributes(l_pTarget); + + // Ensure all mutex attributes are setup correctly for MPIPL + // Check if there any mutex attributes + // we need to reset on this target + l_numberMutexAttrsReset += + l_targetService.resetMutexAttributes(l_pTarget); // Update any peer target addresses if necessary + // updatePeerTargets will write to read-only attribute pages. + // To get around vmm fails we need to allow writes to readonly + // memory for the duration of this loop (unlocking is done + // above). if(l_targetService.updatePeerTarget(l_pTarget)) { l_peerTargetsAdjusted++; } } + // Now that the loop is complete we can re-apply // the read only permissions to the read only attr pages l_targetService.modifyReadOnlyPagePermissions(false); @@ -252,6 +263,24 @@ static void initTargeting(errlHndl_t& io_pError) TARG_INF("Number of mutex attributes reset: %d", l_numberMutexAttrsReset); } + else + { + + for( auto targ_iter = l_targetService.begin(); + targ_iter != l_targetService.end(); + targ_iter++) + { + const Target* l_pTarget = *targ_iter; + + // Ensure all mutex attributes are setup correctly for IPL + // Recursive Mutexes need to be setup since they are defaulted + // to all zero. + // Check if there any mutex attributes + // we need to reset on this target + l_targetService.resetMutexAttributes(l_pTarget); + } + } + checkProcessorTargeting(l_targetService); diff --git a/src/usr/targeting/test/testtargeting.H b/src/usr/targeting/test/testtargeting.H index c87d599e9..16d2fc99d 100644 --- a/src/usr/targeting/test/testtargeting.H +++ b/src/usr/targeting/test/testtargeting.H @@ -90,6 +90,63 @@ void* funcTestMutex(void* i_pData) return NULL; } +/** + * @brief Function which attempts to recursively access a critical section with + * a recursive mutex. + * + * @param[in] i_pData Pointer to mutex pointer/value pointer structure + * + * @return nullptr + */ +void* funcTestRecursiveMutex(void* i_pData) +{ + MutexTestData_t* l_pData = static_cast<MutexTestData_t*>(i_pData); + + // The parent thread holds the lock initially. So the child thread will be + // blocked when it attempts to access the critical section. + TS_INFO("Child Thread: Attempting to access the Critical Section"); + recursive_mutex_lock(l_pData->pMutex); + + TS_INFO("Child Thread: Entering the Critical Section"); + if (*(l_pData->pVar) > 0) + { + *(l_pData->pVar) = 0; + funcTestRecursiveMutex(l_pData); + } + + TS_INFO("Child Thread: Leaving the Critical Section"); + recursive_mutex_unlock(l_pData->pMutex); + return nullptr; +} + +/** + * @brief Function that serves as an entry point to the recursive function + * it calls. This is so that the barrier_wait calls work without the + * recursion causing a deadlock. + * + * @param[in] i_pData Pointer to mutex pointer/value pointer structure + * + * @return nullptr + */ +void* funcTestRecursiveMutexEntry(void* i_pData) +{ + MutexTestData_t* l_pData = static_cast<MutexTestData_t*>(i_pData); + + // Signal to the parent thread that the child thread is about to access the + // lock which is held by it. That way the main thread can verify that + // this thread hasn't started recursion yet. + barrier_wait(l_pData->pBarrier); + + funcTestRecursiveMutex(l_pData); + + // Wake the parent thread so that it can verify that this thread accessed + // the protected value which proves that the child thread got + // the lock recursively without a deadlock occuring. + barrier_wait(l_pData->pBarrier); + + return nullptr; +} + class TargetingTestSuite : public CxxTest::TestSuite { public: @@ -97,6 +154,122 @@ class TargetingTestSuite : public CxxTest::TestSuite /** * @brief Test Hostboot specific mutex attribute support */ + void testHbRecursiveMutexAttr() + { + TS_TRACE(ENTER_MRK "testHbRecursiveMutexAttr" ); + + using namespace TARGETING; + + do { + + // Get a reference to the target service + TargetService& l_targetService = targetService(); + + // Get the system target containing the test mutex + TARGETING::Target* l_pTarget = nullptr; + (void) l_targetService.getTopLevelTarget(l_pTarget); + if (l_pTarget == nullptr) + { + TS_FAIL("Top level target handle is NULL"); + break; + } + + // Get the mutex attribute (actually a mutex_t* which points to + // a mutex) + HB_RECURSIVE_MUTEX_TEST_LOCK_ATTR l_pLock = + l_pTarget->getHbMutexAttr + <TARGETING::ATTR_HB_RECURSIVE_MUTEX_TEST_LOCK>(); + + // Verify the recursive mutex was initialized correctly + mutex_t* l_recursiveMutex = reinterpret_cast<mutex_t*>(l_pLock); + + if ((l_recursiveMutex->iv_val != 0) + || (l_recursiveMutex->iv_ownerLockCount != 0) + || (l_recursiveMutex->iv_owner != 0) + || (l_recursiveMutex->iv_recursive != true)) + { + TS_FAIL("Mutex attribute must be initialized as recursive."); + break; + } + + // Try to get the attribute, and ensure it's the same + HB_RECURSIVE_MUTEX_TEST_LOCK_ATTR l_pLockTry = nullptr; + if(l_pTarget->tryGetHbMutexAttr + <TARGETING::ATTR_HB_RECURSIVE_MUTEX_TEST_LOCK>(l_pLockTry)) + { + if(l_pLockTry != l_pLock) + { + TS_FAIL("Mutex attributes should match, but dont. " + "l_pLockTry = %p, l_pLock = %p",l_pLockTry, + l_pLock); + break; + } + } + else + { + TS_FAIL("Mutex attribute tryGet failed, even though it exists"); + break; + } + + // Create a structure holding pointers to + // the mutex and a protected value + volatile uint32_t l_var = 1; + TS_INFO("Parent Thread: Acquiring recursive lock"); + (void)recursive_mutex_lock(l_pLock); + barrier_t l_barrier; + (void)barrier_init(&l_barrier, 2); + MutexTestData_t l_mutexTestData = { l_pLock, &l_barrier, &l_var }; + + // Spawn off a function which tries to write the protected value to + // something unexpected. If the mutex is working, the for loop will + // always poll the expected value. + TS_INFO("Parent Thread: Creating Child thread"); + task_create(funcTestRecursiveMutexEntry, + static_cast<void*>(&l_mutexTestData)); + + // Guarantee the child process runs and blocks on the mutex prior to + // modifying the protected value. isync to ensure the processor doesn't + // speculatively perform the comparison prior to the sleep completing + TS_INFO("Parent Thread: Waiting for Child to attempt to grab the lock"); + barrier_wait(&l_barrier); + nanosleep(0,TEN_CTX_SWITCHES_NS); isync(); + + if(l_var != 1) + { + TS_FAIL("Protected value must be 1, was %d instead",l_var); + break; + } + + // Now unlock the mutex, allowing the other thread to overwrite the + // protected value; which should happen within 100,000 reads of the + // var. This will confirm the other thread was actively trying to + // write the controlled value + TS_INFO( + "Parent Thread: Releasing lock to Child to test recursive mutex."); + (void)recursive_mutex_unlock(l_pLock); + + // Guarantee the child process acquires the mutex and modifies the + // protected value. + TS_INFO("Parent Thread: Waiting for Child to modify the value"); + barrier_wait(&l_barrier); + + if(l_var != 0) + { + TS_FAIL("Protected value must now be 0, was %d instead",l_var); + break; + } + + TS_INFO("Parent Thread: Child completed successfully."); + barrier_destroy(&l_barrier); + + } while(0); + + TS_TRACE(EXIT_MRK "testHbRecursiveMutexAttr"); + } + + /** + * @brief Test Hostboot specific mutex attribute support + */ void testHbMutexAttr() { TS_TRACE(ENTER_MRK "testHbMutexAttr" ); @@ -149,7 +322,8 @@ class TargetingTestSuite : public CxxTest::TestSuite break; } - // Create a structue holding pointers to the mutex and a protected value + // Create a structure holding pointers to + // the mutex and a protected value volatile uint32_t l_var = 0; (void)mutex_lock(l_pLock); barrier_t l_barrier; diff --git a/src/usr/testcore/lib/makefile b/src/usr/testcore/lib/makefile index 9bf180042..ce80dc0df 100644 --- a/src/usr/testcore/lib/makefile +++ b/src/usr/testcore/lib/makefile @@ -28,7 +28,7 @@ MODULE = testsyslib #@TODO-RTC:151185-Turn enable all test cases #TESTS = *.H TESTS = stltest.H -#TESTS += synctest.H +TESTS += synctest.H SUBDIRS += runtime.d diff --git a/src/usr/testcore/lib/synctest.H b/src/usr/testcore/lib/synctest.H index 2662f020d..ea71dac53 100644 --- a/src/usr/testcore/lib/synctest.H +++ b/src/usr/testcore/lib/synctest.H @@ -50,10 +50,11 @@ class SyncTest: public CxxTest::TestSuite // deadlocks occur. // Case 1: Single thread locks recursive mutex three times in a row. { - recursive_mutex_init(&mutex); + mutex_t recursive_mutex; + recursive_mutex_init(&recursive_mutex); l_status = TASK_STATUS_EXITED_CLEAN; - tid_t l_task1 = task_create(entry_func, this); + tid_t l_task1 = task_create(entry_func, &recursive_mutex); if ((l_task1 != task_wait_tid(l_task1, &l_status, nullptr)) || (l_status == TASK_STATUS_EXITED_CLEAN)) @@ -66,11 +67,12 @@ class SyncTest: public CxxTest::TestSuite // grab it (hangs) and then the first thread grabs it again // without issue. { - recursive_mutex_init(&mutex); + mutex_t recursive_mutex; + recursive_mutex_init(&recursive_mutex); l_status = TASK_STATUS_EXITED_CLEAN; - tid_t l_task1 = task_create(entry_func, this); - tid_t l_task2 = task_create(entry_func, this); + tid_t l_task1 = task_create(entry_func, &recursive_mutex); + tid_t l_task2 = task_create(entry_func, &recursive_mutex); if ((l_task2 != task_wait_tid(l_task2, &l_status, nullptr)) || (l_status == TASK_STATUS_EXITED_CLEAN)) @@ -205,8 +207,7 @@ class SyncTest: public CxxTest::TestSuite static void* entry_func(void* i_p) { - SyncTest* my = (SyncTest*) i_p; - mutex_t* myMutex = &(my->mutex); + mutex_t* myMutex = (mutex_t*) i_p; // Call the recursive function. recursive_func(myMutex, 2); |