diff --git a/parc/CMakeLists.txt b/parc/CMakeLists.txt index 237ea12..79af72e 100644 --- a/parc/CMakeLists.txt +++ b/parc/CMakeLists.txt @@ -231,6 +231,7 @@ set(LIBPARC_CONCURRENT_HEADER_FILES concurrent/parc_RingBuffer.h concurrent/parc_RingBuffer_1x1.h concurrent/parc_RingBuffer_NxM.h + concurrent/parc_RWLock.h concurrent/parc_ScheduledTask.h concurrent/parc_ScheduledThreadPool.h concurrent/parc_Synchronizer.h @@ -251,6 +252,7 @@ set(LIBPARC_CONCURRENT_SOURCE_FILES concurrent/parc_RingBuffer.c concurrent/parc_RingBuffer_1x1.c concurrent/parc_RingBuffer_NxM.c + concurrent/parc_RWLock.c concurrent/parc_ScheduledTask.c concurrent/parc_ScheduledThreadPool.c concurrent/parc_Synchronizer.c diff --git a/parc/concurrent/parc_RWLock.c b/parc/concurrent/parc_RWLock.c new file mode 100644 index 0000000..a828289 --- /dev/null +++ b/parc/concurrent/parc_RWLock.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2015, Xerox Corporation (Xerox) and Palo Alto Research Center, Inc (PARC) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL XEROX OR PARC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ################################################################################ + * # + * # PATENT NOTICE + * # + * # This software is distributed under the BSD 2-clause License (see LICENSE + * # file). This BSD License does not make any patent claims and as such, does + * # not act as a patent grant. The purpose of this section is for each contributor + * # to define their intentions with respect to intellectual property. + * # + * # Each contributor to this source code is encouraged to state their patent + * # claims and licensing mechanisms for any contributions made. At the end of + * # this section contributors may each make their own statements. Contributor's + * # claims and grants only apply to the pieces (source code, programs, text, + * # media, etc) that they have contributed directly to this software. + * # + * # There is no guarantee that this section is complete, up to date or accurate. It + * # is up to the contributors to maintain their portion of this section and up to + * # the user of the software to verify any claims herein. + * # + * # Do not remove this header notification. The contents of this section must be + * # present in all distributions of the software. You may only modify your own + * # intellectual property statements. Please provide contact information. + * + * - Palo Alto Research Center, Inc + * This software distribution does not grant any rights to patents owned by Palo + * Alto Research Center, Inc (PARC). Rights to these patents are available via + * various mechanisms. As of January 2016 PARC has committed to FRAND licensing any + * intellectual property used by its contributions to this software. You may + * contact PARC at cipo@parc.com for more information or visit http://www.ccnx.org + */ +/** + * @author <#Greg Rutz #>, Cable Television Laboratories, Inc. (CableLabs) + * @copyright (c) 2016, Cable Television Laboratories, Inc. All rights reserved. + */ +#include + +#include +#include + +#include +#include + +#include + +struct PARCRWLock { + pthread_rwlock_t lock; +}; + +static void +_parcRWLock_Finalize(PARCRWLock **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCRWLock pointer."); + + parcRWLock_OptionalAssertValid(*instancePtr); + + /* cleanup the instance fields here */ + pthread_rwlock_destroy(&(*instancePtr)->lock); +} + +parcObject_ImplementAcquire(parcRWLock, PARCRWLock); + +parcObject_ImplementRelease(parcRWLock, PARCRWLock); + +parcObject_ExtendPARCObject(PARCRWLock, _parcRWLock_Finalize, NULL, NULL, NULL, NULL, NULL, NULL); + +void +parcRWLock_AssertValid(const PARCRWLock *instance) +{ + assertTrue(parcRWLock_IsValid(instance), + "PARCRWLock is not valid."); +} + +PARCRWLock * +parcRWLock_Create(void) +{ + PARCRWLock *result = parcObject_CreateInstance(PARCRWLock); + + pthread_rwlock_init(&result->lock, NULL); + + return result; +} + +bool +parcRWLock_IsValid(const PARCRWLock *instance) +{ + bool result = false; + + if (instance != NULL) { + result = true; + } + + return result; +} + +bool +parcRWLock_ReadLock(PARCRWLock *lock) +{ + bool result = false; + + parcRWLock_OptionalAssertValid(lock); + + int error = pthread_rwlock_rdlock(&lock->lock); + + if (error == 0) { + result = true; + errno = 0; + } else { + errno = error; + } + + return result; +} + +bool +parcRWLock_WriteLock(PARCRWLock *lock) +{ + bool result = false; + + parcRWLock_OptionalAssertValid(lock); + + int error = pthread_rwlock_wrlock(&lock->lock); + + if (error == 0) { + result = true; + errno = 0; + } else { + errno = error; + } + + return result; +} + +bool +parcRWLock_TryReadLock(PARCRWLock *lock) +{ + bool result = false; + + parcRWLock_OptionalAssertValid(lock); + + int error = pthread_rwlock_tryrdlock(&lock->lock); + + if (error == 0) { + result = true; + errno = 0; + } else { + errno = error; + } + + return result; +} + +bool +parcRWLock_TryWriteLock(PARCRWLock *lock) +{ + bool result = false; + + parcRWLock_OptionalAssertValid(lock); + + int error = pthread_rwlock_trywrlock(&lock->lock); + + if (error == 0) { + result = true; + errno = 0; + } else { + errno = error; + } + + return result; +} + +bool +parcRWLock_Unlock(PARCRWLock *lock) +{ + bool result = false; + + parcRWLock_OptionalAssertValid(lock); + + int error = pthread_rwlock_unlock(&lock->lock); + + if (error == 0) { + result = true; + errno = 0; + } else { + errno = error; + } + + return result; +} diff --git a/parc/concurrent/parc_RWLock.h b/parc/concurrent/parc_RWLock.h new file mode 100644 index 0000000..8566205 --- /dev/null +++ b/parc/concurrent/parc_RWLock.h @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2015, Xerox Corporation (Xerox) and Palo Alto Research Center, Inc (PARC) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL XEROX OR PARC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ################################################################################ + * # + * # PATENT NOTICE + * # + * # This software is distributed under the BSD 2-clause License (see LICENSE + * # file). This BSD License does not make any patent claims and as such, does + * # not act as a patent grant. The purpose of this section is for each contributor + * # to define their intentions with respect to intellectual property. + * # + * # Each contributor to this source code is encouraged to state their patent + * # claims and licensing mechanisms for any contributions made. At the end of + * # this section contributors may each make their own statements. Contributor's + * # claims and grants only apply to the pieces (source code, programs, text, + * # media, etc) that they have contributed directly to this software. + * # + * # There is no guarantee that this section is complete, up to date or accurate. It + * # is up to the contributors to maintain their portion of this section and up to + * # the user of the software to verify any claims herein. + * # + * # Do not remove this header notification. The contents of this section must be + * # present in all distributions of the software. You may only modify your own + * # intellectual property statements. Please provide contact information. + * + * - Palo Alto Research Center, Inc + * This software distribution does not grant any rights to patents owned by Palo + * Alto Research Center, Inc (PARC). Rights to these patents are available via + * various mechanisms. As of January 2016 PARC has committed to FRAND licensing any + * intellectual property used by its contributions to this software. You may + * contact PARC at cipo@parc.com for more information or visit http://www.ccnx.org + */ +/** + * @file parc_RWLock.h + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + * @author <#Greg Rutz #>, Cable Television Laboratories, Inc. (CableLabs) + * @copyright (c) 2016, Cable Television Laboratories, Inc. All rights reserved. + */ +#ifndef PARCLibrary_parc_RWLock +#define PARCLibrary_parc_RWLock +#include + +#include +#include + +struct PARCRWLock; +typedef struct PARCRWLock PARCRWLock; + +/** + * Increase the number of references to a `PARCRWLock` instance. + * + * Note that new `PARCRWLock` is not created, + * only that the given `PARCRWLock` reference count is incremented. + * Discard the reference by invoking `parcRWLock_Release`. + * + * @param [in] instance A pointer to a valid PARCRWLock instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCRWLock *a = parcRWLock_Create(); + * + * PARCRWLock *b = parcRWLock_Acquire(); + * + * parcRWLock_Release(&a); + * parcRWLock_Release(&b); + * } + * @endcode + */ +PARCRWLock *parcRWLock_Acquire(const PARCRWLock *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcRWLock_OptionalAssertValid(_instance_) +#else +# define parcRWLock_OptionalAssertValid(_instance_) parcRWLock_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCRWLock` instance is valid. + * + * @param [in] instance A pointer to a valid PARCRWLock instance. + * + * Example: + * @code + * { + * PARCRWLock *a = parcRWLock_Create(); + * + * parcRWLock_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcRWLock_Release(&b); + * } + * @endcode + */ +void parcRWLock_AssertValid(const PARCRWLock *instance); + +/** + * Create an instance of PARCRWLock + * + * <#Paragraphs Of Explanation#> + * + * @return non-NULL A pointer to a valid PARCRWLock instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCRWLock *a = parcRWLock_Create(); + * + * parcRWLock_Release(&a); + * } + * @endcode + */ +PARCRWLock *parcRWLock_Create(void); + +/** + * Compares @p instance with @p other for order. + * + * Returns a negative integer, zero, or a positive integer as @p instance + * is less than, equal to, or greater than @p other. + * + * @param [in] instance A pointer to a valid PARCRWLock instance. + * @param [in] other A pointer to a valid PARCRWLock instance. + * + * @return <0 Instance is less than @p other. + * @return 0 Instance a and instance b compare the same. + * @return >0 Instance a is greater than instance b. + * + * Example: + * @code + * { + * PARCRWLock *a = parcRWLock_Create(); + * PARCRWLock *b = parcRWLock_Create(); + * + * if (parcRWLock_Compare(a, b) == 0) { + * printf("Instances are equal.\n"); + * } + * + * parcRWLock_Release(&a); + * parcRWLock_Release(&b); + * } + * @endcode + * + * @see parcRWLock_Equals + */ +int parcRWLock_Compare(const PARCRWLock *instance, const PARCRWLock *other); + +/** + * Determine if an instance of `PARCRWLock` is valid. + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] instance A pointer to a valid PARCRWLock instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCRWLock *a = parcRWLock_Create(); + * + * if (parcRWLock_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcRWLock_Release(&a); + * } + * @endcode + * + */ +bool parcRWLock_IsValid(const PARCRWLock *instance); + +/** + * Release a previously acquired reference to the given `PARCRWLock` instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] instancePtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * PARCRWLock *a = parcRWLock_Create(); + * + * parcRWLock_Release(&a); + * } + * @endcode + */ +void parcRWLock_Release(PARCRWLock **instancePtr); + +/** + * Obtain the READ lock on the given `PARCRWLock` instance. + * + * If the WRITE lock is already held by another thread, this function will block. + * If the READ lock is already held by another thread, this function will increment the READ + * lock counter and continue. + * If the READ lock is already held by the current thread, this function will increment the + * read lock counter and continue + * + * Implementors should avoid deadlock by not attempting to acquire the READ lock with a + * thread that already owns the WRITE lock. + * + * @param [in] lock A pointer to a valid `PARCRWLock` instance. + * + * @return true The READ lock was obtained successfully. + * @return false The WRITE lock is already held by the current thread, the maximum READ lock counter has + * been reached, or the `PARCRWLock` is invalid. + * + * Example: + * @code + * { + * if (parcRWLock_ReadLock(lock)) { + * + * } + * } + * @endcode + */ +bool parcRWLock_ReadLock(PARCRWLock *lock); + +/** + * Obtain the lock on the given `PARCRWLock` instance. + * + * If the WRITE lock or READ lock is already held by another thread, this function will block. + * If the lock is already held by the current thread, this function will return `false`. + * + * @param [in] lock A pointer to a valid `PARCRWLock` instance. + * + * @return true The lock was obtained successfully. + * @return false The READ lock or WRITE lock is already held by the current thread, or the + * `PARCRWLock` is invalid. + * + * Example: + * @code + * { + * if (parcRWLock_WriteLock(lock)) { + * + * } + * } + * @endcode + */ +bool parcRWLock_WriteLock(PARCRWLock *lock); + +/** + * Try to obtain the READ lock on the given `PARCRWLock` instance. + * + * Once the lock is obtained, the caller must release the lock via `parcRWLock_Unlock`. + * + * @param [in] lock A pointer to a valid `PARCRWLock` instance. + * + * @return true A `PARCRWLock` READ lock was obtained + * @return false A `PARCRWLock` READ lock was not unlocked. + * + * Example: + * @code + * { + * while (parcRWLock_TryReadLock(object)) + * ; + * } + * @endcode + */ +bool parcRWLock_TryReadLock(PARCRWLock *lock); + +/** + * Try to obtain the WRITE lock on the given `PARCObject` instance. + * + * Once the lock is obtained, the caller must release the lock via `parcRWLock_Unlock`. + * + * @param [in] lock A pointer to a valid `PARCRWLock` instance. + * + * @return true The `PARCRWLock` WRITE lock was obtained. + * @return false The `PARCRWLock` WRITE lock was not obtained + * + * Example: + * @code + * { + * while (parcRWLock_TryWriteLock(object)) + * ; + * } + * @endcode + */ +bool parcRWLock_TryWriteLock(PARCRWLock *lock); + +/** + * Try to unlock the READ or WRITE lock on the given PARCObject instance. + * + * @param [in] lock A pointer to a valid PARCRWLock instance. + * + * @return true The PARCRWLock READ or WRITE lock was released by this thread. Note this may + * not leave the PARCRWLock unlocked, since other threads may also hold a READ lock. + * @return false The PARCRWLock READ or WRITE lock was not released, either because the + * PARCRWLock object was not initialized or because the calling thread did not hold a lock on + * this object. + * + * Example: + * @code + * { + * parcRWLock_Unlock(object); + * } + * @endcode + */ +bool parcRWLock_Unlock(PARCRWLock *lock); + +#endif diff --git a/parc/concurrent/test/CMakeLists.txt b/parc/concurrent/test/CMakeLists.txt index 092d020..5c3c1da 100644 --- a/parc/concurrent/test/CMakeLists.txt +++ b/parc/concurrent/test/CMakeLists.txt @@ -8,6 +8,7 @@ set(TestsExpectedToPass test_parc_Notifier test_parc_RingBuffer_1x1 test_parc_RingBuffer_NxM + test_parc_RWLock test_parc_ScheduledTask test_parc_ScheduledThreadPool test_parc_Synchronizer diff --git a/parc/concurrent/test/test_parc_RWLock.c b/parc/concurrent/test/test_parc_RWLock.c new file mode 100644 index 0000000..0c48ec0 --- /dev/null +++ b/parc/concurrent/test/test_parc_RWLock.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2015, Xerox Corporation (Xerox) and Palo Alto Research Center, Inc (PARC) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL XEROX OR PARC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ################################################################################ + * # + * # PATENT NOTICE + * # + * # This software is distributed under the BSD 2-clause License (see LICENSE + * # file). This BSD License does not make any patent claims and as such, does + * # not act as a patent grant. The purpose of this section is for each contributor + * # to define their intentions with respect to intellectual property. + * # + * # Each contributor to this source code is encouraged to state their patent + * # claims and licensing mechanisms for any contributions made. At the end of + * # this section contributors may each make their own statements. Contributor's + * # claims and grants only apply to the pieces (source code, programs, text, + * # media, etc) that they have contributed directly to this software. + * # + * # There is no guarantee that this section is complete, up to date or accurate. It + * # is up to the contributors to maintain their portion of this section and up to + * # the user of the software to verify any claims herein. + * # + * # Do not remove this header notification. The contents of this section must be + * # present in all distributions of the software. You may only modify your own + * # intellectual property statements. Please provide contact information. + * + * - Palo Alto Research Center, Inc + * This software distribution does not grant any rights to patents owned by Palo + * Alto Research Center, Inc (PARC). Rights to these patents are available via + * various mechanisms. As of January 2016 PARC has committed to FRAND licensing any + * intellectual property used by its contributions to this software. You may + * contact PARC at cipo@parc.com for more information or visit http://www.ccnx.org + */ +/** + * @author <#Glenn Scott #>, Palo Alto Research Center (Xerox PARC) + * @copyright (c) 2015, Xerox Corporation (Xerox) and Palo Alto Research Center, Inc (PARC). All rights reserved. + */ +#include "../parc_RWLock.c" + +#include + +#include +#include +#include +#include +#include + +#include +#include + +LONGBOW_TEST_RUNNER(parc_RWLock) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(CreateAcquireRelease); + LONGBOW_RUN_TEST_FIXTURE(Locking); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_RWLock) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_RWLock) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(CreateAcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, CreateRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(CreateAcquireRelease) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(CreateAcquireRelease) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, CreateRelease) +{ + PARCRWLock *instance = parcRWLock_Create(); + assertNotNull(instance, "Expected non-null result from parcRWLock_Create()."); + + parcObjectTesting_AssertAcquire(instance); + + parcObjectTesting_AssertAcquireReleaseContract(parcRWLock_Acquire, instance); + + parcRWLock_Release(&instance); + assertNull(instance, "Expected null result from parcRWLock_Release()."); +} + +LONGBOW_TEST_FIXTURE(Locking) +{ + LONGBOW_RUN_TEST_CASE(Locking, parcRWLock_TryReadLock_Unlock); + LONGBOW_RUN_TEST_CASE(Locking, parcRWLock_TryWriteLock_Unlock); + LONGBOW_RUN_TEST_CASE(Locking, parcRWLock_TryReadLock_AlreadyReadLocked); + LONGBOW_RUN_TEST_CASE(Locking, parcRWLock_TryReadLock_AlreadyWriteLocked); + LONGBOW_RUN_TEST_CASE(Locking, parcRWLock_TryWriteLock_AlreadyReadLocked); + LONGBOW_RUN_TEST_CASE(Locking, parcRWLock_TryWriteLock_AlreadyWriteLocked); + LONGBOW_RUN_TEST_CASE(Locking, parcRWLock_ReadLock_Unlock); + LONGBOW_RUN_TEST_CASE(Locking, parcRWLock_WriteLock_Unlock); +} + +LONGBOW_TEST_FIXTURE_SETUP(Locking) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Locking) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Locking, parcRWLock_TryReadLock_Unlock) +{ + PARCRWLock *lock = parcRWLock_Create(); + + bool actual = parcRWLock_TryReadLock(lock); + + assertTrue(actual, "Expected parcObject_TryReadLock to succeed."); + + actual = parcRWLock_Unlock(lock); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); + + parcRWLock_Release(&lock); +} + +LONGBOW_TEST_CASE(Locking, parcRWLock_TryWriteLock_Unlock) +{ + PARCRWLock *lock = parcRWLock_Create(); + + bool actual = parcRWLock_TryWriteLock(lock); + + assertTrue(actual, "Expected parcObject_TryWriteLock to succeed."); + + actual = parcRWLock_Unlock(lock); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); + + parcRWLock_Release(&lock); +} + +LONGBOW_TEST_CASE(Locking, parcRWLock_ReadLock_Unlock) +{ + PARCRWLock *lock = parcRWLock_Create(); + + bool actual = parcRWLock_ReadLock(lock); + + assertTrue(actual, "Expected parcObject_ReadLock to succeed."); + + actual = parcRWLock_Unlock(lock); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); + + parcRWLock_Release((PARCRWLock **) &lock); +} + +LONGBOW_TEST_CASE(Locking, parcRWLock_WriteLock_Unlock) +{ + PARCRWLock *lock = parcRWLock_Create(); + + bool actual = parcRWLock_WriteLock(lock); + + assertTrue(actual, "Expected parcObject_WriteLock to succeed."); + + actual = parcRWLock_Unlock(lock); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); + + parcRWLock_Release((PARCRWLock **) &lock); +} + +LONGBOW_TEST_CASE(Locking, parcRWLock_TryReadLock_AlreadyReadLocked) +{ + PARCRWLock *lock = parcRWLock_Create(); + + bool actual = parcRWLock_TryReadLock(lock); + + assertTrue(actual, "Expected parcObject_TryReadLock to succeed."); + + actual = parcRWLock_TryReadLock(lock); + + assertTrue(actual, "Expected parcObject_TryReadLock to succeed when already read locked."); + + actual = parcRWLock_Unlock(lock); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); + + actual = parcRWLock_Unlock(lock); + assertTrue(actual, "Expected parcObject_Unlock to succeed second time."); + + parcRWLock_Release((PARCRWLock **) &lock); +} + +LONGBOW_TEST_CASE(Locking, parcRWLock_TryReadLock_AlreadyWriteLocked) +{ + PARCRWLock *lock = parcRWLock_Create(); + + bool actual = parcRWLock_WriteLock(lock); + + assertTrue(actual, "Expected parcObject_WriteLock to succeed."); + + actual = parcRWLock_TryReadLock(lock); + + assertFalse(actual, "Expected parcObject_TryReadLock to fail when already write locked."); + + actual = parcRWLock_Unlock(lock); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); + + parcRWLock_Release((PARCRWLock **) &lock); +} + +LONGBOW_TEST_CASE(Locking, parcRWLock_TryWriteLock_AlreadyReadLocked) +{ + PARCRWLock *lock = parcRWLock_Create(); + + bool actual = parcRWLock_ReadLock(lock); + + assertTrue(actual, "Expected parcObject_ReadLock to succeed."); + + actual = parcRWLock_TryWriteLock(lock); + + assertFalse(actual, "Expected parcObject_TryWriteLock to fail when already read locked."); + + actual = parcRWLock_Unlock(lock); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); + + parcRWLock_Release((PARCRWLock **) &lock); +} + +LONGBOW_TEST_CASE(Locking, parcRWLock_TryWriteLock_AlreadyWriteLocked) +{ + PARCRWLock *lock = parcRWLock_Create(); + + bool actual = parcRWLock_WriteLock(lock); + + assertTrue(actual, "Expected parcObject_WriteLock to succeed."); + + actual = parcRWLock_TryWriteLock(lock); + + assertFalse(actual, "Expected parcObject_TryWriteLock to fail when already write locked."); + + actual = parcRWLock_Unlock(lock); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); + + parcRWLock_Release((PARCRWLock **) &lock); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_RWLock); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +}