diff options
Diffstat (limited to 'libparc/parc/concurrent')
52 files changed, 13726 insertions, 0 deletions
diff --git a/libparc/parc/concurrent/.gitignore b/libparc/parc/concurrent/.gitignore new file mode 100644 index 00000000..e6ed7cc3 --- /dev/null +++ b/libparc/parc/concurrent/.gitignore @@ -0,0 +1,5 @@ +parcConcurrent_About.c +parcConcurrent_About.h + +test/test_parc_RingBuffer_1x1 +test/test_parc_RingBuffer_NxM diff --git a/libparc/parc/concurrent/parc_AtomicUint16.c b/libparc/parc/concurrent/parc_AtomicUint16.c new file mode 100755 index 00000000..24c9f922 --- /dev/null +++ b/libparc/parc/concurrent/parc_AtomicUint16.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/concurrent/parc_AtomicUint16.h> + +struct PARCAtomicUint16 { + uint16_t value; +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_t mutex; +#endif +}; + +static void +_parcAtomicUint16_Finalize(PARCAtomicUint16 **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCAtomicUint16 pointer."); + + parcAtomicUint16_OptionalAssertValid((*instancePtr)); + + /* cleanup the instance fields here */ +} + +parcObject_ImplementAcquire(parcAtomicUint16, PARCAtomicUint16); + +parcObject_ImplementRelease(parcAtomicUint16, PARCAtomicUint16); + +parcObject_ExtendPARCObject(PARCAtomicUint16, _parcAtomicUint16_Finalize, parcAtomicUint16_Copy, NULL, parcAtomicUint16_Equals, parcAtomicUint16_Compare, parcAtomicUint16_HashCode, NULL); + + +void +parcAtomicUint16_AssertValid(const PARCAtomicUint16 *instance) +{ + assertTrue(parcAtomicUint16_IsValid(instance), + "PARCAtomicUint16 is not valid."); +} + +PARCAtomicUint16 * +parcAtomicUint16_Create(uint16_t value) +{ + PARCAtomicUint16 *result = parcObject_CreateAndClearInstance(PARCAtomicUint16); + +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_init(&result->mutex, NULL); + result->value = value; +#else + *result = value; +#endif + + return result; +} + +int +parcAtomicUint16_Compare(const PARCAtomicUint16 *instance, const PARCAtomicUint16 *other) +{ + int16_t comparison = parcAtomicUint16_GetValue(instance) - parcAtomicUint16_GetValue(other); + + int result = 0; + if (comparison < 0) { + result = -1; + } else if (comparison > 0) { + result = 1; + } + + return result; +} + +PARCAtomicUint16 * +parcAtomicUint16_Copy(const PARCAtomicUint16 *original) +{ + PARCAtomicUint16 *result = parcAtomicUint16_Create(parcAtomicUint16_GetValue(original)); + + return result; +} + +bool +parcAtomicUint16_Equals(const PARCAtomicUint16 *x, const PARCAtomicUint16 *y) +{ + bool result = false; + + result = parcAtomicUint16_GetValue(x) == parcAtomicUint16_GetValue(y); + + return result; +} + +PARCHashCode +parcAtomicUint16_HashCode(const PARCAtomicUint16 *instance) +{ + PARCHashCode result = (PARCHashCode) parcAtomicUint16_GetValue(instance); + + return result; +} + +bool +parcAtomicUint16_IsValid(const PARCAtomicUint16 *instance) +{ + bool result = false; + + if (instance != NULL) { + result = true; + } + + return result; +} + +uint16_t +parcAtomicUint16_GetValue(const PARCAtomicUint16 *instance) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + return instance->value; +#else + return *instance; +#endif +} + +uint16_t +parcAtomicUint16_AddImpl(PARCAtomicUint16 *value, uint16_t addend) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_lock(&value->mutex); + value->value += addend; + uint16_t result = value->value; + pthread_mutex_unlock(&value->mutex); + return result; +#else + return __sync_add_and_fetch(value, addend); +#endif +} + +uint16_t +parcAtomicUint16_SubtractImpl(PARCAtomicUint16 *value, uint16_t subtrahend) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_lock(&value->mutex); + value->value -= subtrahend; + uint16_t result = value->value; + pthread_mutex_unlock(&value->mutex); + return result; +#else + return __sync_sub_and_fetch(value, subtrahend); +#endif +} + +bool +parcAtomicUint16_CompareAndSwapImpl(PARCAtomicUint16 *value, uint16_t predicate, uint16_t newValue) +{ + bool result = false; +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_lock(&value->mutex); + if (value->value == predicate) { + value->value = newValue; + result = true; + } + pthread_mutex_unlock(&value->mutex); + return result; +#else + result = __sync_bool_compare_and_swap(value, predicate, newValue); +#endif + return result; +} diff --git a/libparc/parc/concurrent/parc_AtomicUint16.h b/libparc/parc/concurrent/parc_AtomicUint16.h new file mode 100755 index 00000000..ff6a4fc3 --- /dev/null +++ b/libparc/parc/concurrent/parc_AtomicUint16.h @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_AtomicUint16.h + * @ingroup threading + * @brief An atomically updated 16-bit unsigned integer. + * + * <#Detailed Description#> + * + */ +#ifndef PARCLibrary_parc_AtomicUint16 +#define PARCLibrary_parc_AtomicUint16 +#include <stdbool.h> +#include <stdint.h> + +#include <parc/algol/parc_JSON.h> + +#ifdef PARCLibrary_DISABLE_ATOMICS +#include <pthread.h> +struct PARCAtomicUint16; +typedef struct PARCAtomicUint16 PARCAtomicUint16; +#else +typedef uint16_t PARCAtomicUint16; +#endif + + +PARCAtomicUint16 *parcAtomicInteger_CreateUint16(uint16_t value); + +uint16_t parcAtomicUint16_AddImpl(PARCAtomicUint16 *value, uint16_t addend); + +uint16_t parcAtomicUint16_SubtractImpl(PARCAtomicUint16 *value, uint16_t subtrahend); + +bool parcAtomicUint16_CompareAndSwapImpl(PARCAtomicUint16 *value, uint16_t predicate, uint16_t newValue); + +#ifdef PARCLibrary_DISABLE_ATOMICS + +#define parcAtomicUint16_Add parcAtomicUint16_AddImpl + +#define parcAtomicUint16_Subtract parcAtomicUint16_SubtractImpl + +#define parcAtomicUint16_CompareAndSwap parcAtomicUint16_CompareAndSwapImpl + +#else + +#define parcAtomicUint16_Add(_atomic_uint16_, _addend_) \ + __sync_add_and_fetch(_atomic_uint16_, _addend_) + +#define parcAtomicUint16_Subtract(_atomic_uint16_, _subtrahend_) \ + __sync_sub_and_fetch(_atomic_uint16_, _subtrahend_) + +#define parcAtomicUint16_CompareAndSwap(_atomic_uint16_, _predicate_, _newValue_) \ + __sync_bool_compare_and_swap(_atomic_uint16_, _predicate_, _newValue_) +#endif + +#define parcAtomicUint16_Increment(_atomic_uint16_) parcAtomicUint16_Add(_atomic_uint16_, 1) +#define parcAtomicUint16_Decrement(_atomic_uint16_) parcAtomicUint16_Subtract(_atomic_uint16_, 1) + +/** + * Increase the number of references to a `PARCAtomicUint16` instance. + * + * Note that new `PARCAtomicUint16` is not created, + * only that the given `PARCAtomicUint16` reference count is incremented. + * Discard the reference by invoking `parcAtomicUint16_Release`. + * + * @param [in] instance A pointer to a valid PARCAtomicUint16 instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCAtomicUint16 *a = parcAtomicUint16_Create(); + * + * PARCAtomicUint16 *b = parcAtomicUint16_Acquire(); + * + * parcAtomicUint16_Release(&a); + * parcAtomicUint16_Release(&b); + * } + * @endcode + */ +PARCAtomicUint16 *parcAtomicUint16_Acquire(const PARCAtomicUint16 *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcAtomicUint16_OptionalAssertValid(_instance_) +#else +# define parcAtomicUint16_OptionalAssertValid(_instance_) parcAtomicUint16_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCAtomicUint16` instance is valid. + * + * @param [in] instance A pointer to a valid PARCAtomicUint16 instance. + * + * Example: + * @code + * { + * PARCAtomicUint16 *a = parcAtomicUint16_Create(); + * + * parcAtomicUint16_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcAtomicUint16_Release(&b); + * } + * @endcode + */ +void parcAtomicUint16_AssertValid(const PARCAtomicUint16 *instance); + +/** + * Create an instance of PARCAtomicUint16 + * + * <#Paragraphs Of Explanation#> + * + * @return non-NULL A pointer to a valid PARCAtomicUint16 instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCAtomicUint16 *a = parcAtomicUint16_Create(); + * + * parcAtomicUint16_Release(&b); + * } + * @endcode + */ +PARCAtomicUint16 *parcAtomicUint16_Create(uint16_t); + +/** + * 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 PARCAtomicUint16 instance. + * @param [in] other A pointer to a valid PARCAtomicUint16 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 + * { + * PARCAtomicUint16 *a = parcAtomicUint16_Create(); + * PARCAtomicUint16 *b = parcAtomicUint16_Create(); + * + * if (parcAtomicUint16_Compare(a, b) == 0) { + * printf("Instances are equal.\n"); + * } + * + * parcAtomicUint16_Release(&a); + * parcAtomicUint16_Release(&b); + * } + * @endcode + * + * @see parcAtomicUint16_Equals + */ +int parcAtomicUint16_Compare(const PARCAtomicUint16 *instance, const PARCAtomicUint16 *other); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A pointer to a valid PARCAtomicUint16 instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCAtomicUint16` instance. + * + * Example: + * @code + * { + * PARCAtomicUint16 *a = parcAtomicUint16_Create(); + * + * PARCAtomicUint16 *copy = parcAtomicUint16_Copy(&b); + * + * parcAtomicUint16_Release(&b); + * parcAtomicUint16_Release(©); + * } + * @endcode + */ +PARCAtomicUint16 *parcAtomicUint16_Copy(const PARCAtomicUint16 *original); + +/** + * Determine if two `PARCAtomicUint16` instances are equal. + * + * The following equivalence relations on non-null `PARCAtomicUint16` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcAtomicUint16_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcAtomicUint16_Equals(x, y)` must return true if and only if + * `parcAtomicUint16_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcAtomicUint16_Equals(x, y)` returns true and + * `parcAtomicUint16_Equals(y, z)` returns true, + * then `parcAtomicUint16_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcAtomicUint16_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcAtomicUint16_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCAtomicUint16 instance. + * @param [in] y A pointer to a valid PARCAtomicUint16 instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCAtomicUint16 *a = parcAtomicUint16_Create(); + * PARCAtomicUint16 *b = parcAtomicUint16_Create(); + * + * if (parcAtomicUint16_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcAtomicUint16_Release(&a); + * parcAtomicUint16_Release(&b); + * } + * @endcode + * @see parcAtomicUint16_HashCode + */ +bool parcAtomicUint16_Equals(const PARCAtomicUint16 *x, const PARCAtomicUint16 *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcAtomicUint16_Equals} method, + * then calling the {@link parcAtomicUint16_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcAtomicUint16_Equals} function, + * then calling the `parcAtomicUint16_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCAtomicUint16 instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCAtomicUint16 *a = parcAtomicUint16_Create(); + * + * PARCHashCode hashValue = parcAtomicUint16_HashCode(buffer); + * parcAtomicUint16_Release(&a); + * } + * @endcode + */ +PARCHashCode parcAtomicUint16_HashCode(const PARCAtomicUint16 *instance); + +/** + * Determine if an instance of `PARCAtomicUint16` 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 PARCAtomicUint16 instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCAtomicUint16 *a = parcAtomicUint16_Create(); + * + * if (parcAtomicUint16_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcAtomicUint16_Release(&b); + * } + * @endcode + */ +bool parcAtomicUint16_IsValid(const PARCAtomicUint16 *instance); + +/** + * Release a previously acquired reference to the given `PARCAtomicUint16` 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 + * { + * PARCAtomicUint16 *a = parcAtomicUint16_Create(); + * + * parcAtomicUint16_Release(&a); + * } + * @endcode + */ +void parcAtomicUint16_Release(PARCAtomicUint16 **instancePtr); + +/** + * Get the current value of the given `PARCAtomicUint16` instance. + * + * @param [in] instance A pointer to a valid `PARCAtomicUint16` instance. + * + * @return the current value of the given `PARCAtomicUint16` instance. + * + * Example: + * @code + * { + * PARCAtomicUint16 *instance = parcAtomicUint16_Create(7); + * + * uint16_t value = parcAtomicUint16_GetValue(instance); + * + * parcAtomicUint16_Release(&instance); + * } + * @endcode + */ +uint16_t parcAtomicUint16_GetValue(const PARCAtomicUint16 *instance); +#endif diff --git a/libparc/parc/concurrent/parc_AtomicUint32.c b/libparc/parc/concurrent/parc_AtomicUint32.c new file mode 100755 index 00000000..32ed7a6a --- /dev/null +++ b/libparc/parc/concurrent/parc_AtomicUint32.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/concurrent/parc_AtomicUint32.h> + +struct PARCAtomicUint32 { + uint32_t value; +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_t mutex; +#endif +}; + +static void +_parcAtomicUint32_Finalize(PARCAtomicUint32 **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCAtomicUint32 pointer."); + + parcAtomicUint32_OptionalAssertValid((*instancePtr)); + + /* cleanup the instance fields here */ +} + +parcObject_ImplementAcquire(parcAtomicUint32, PARCAtomicUint32); + +parcObject_ImplementRelease(parcAtomicUint32, PARCAtomicUint32); + +parcObject_ExtendPARCObject(PARCAtomicUint32, _parcAtomicUint32_Finalize, parcAtomicUint32_Copy, NULL, parcAtomicUint32_Equals, parcAtomicUint32_Compare, parcAtomicUint32_HashCode, NULL); + + +void +parcAtomicUint32_AssertValid(const PARCAtomicUint32 *instance) +{ + assertTrue(parcAtomicUint32_IsValid(instance), + "PARCAtomicUint32 is not valid."); +} + +PARCAtomicUint32 * +parcAtomicUint32_Create(uint32_t value) +{ + PARCAtomicUint32 *result = parcObject_CreateAndClearInstance(PARCAtomicUint32); + +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_init(&result->mutex, NULL); + result->value = value; +#else + *result = value; +#endif + + return result; +} + +int +parcAtomicUint32_Compare(const PARCAtomicUint32 *instance, const PARCAtomicUint32 *other) +{ + int32_t comparison = parcAtomicUint32_GetValue(instance) - parcAtomicUint32_GetValue(other); + + int result = 0; + if (comparison < 0) { + result = -1; + } else if (comparison > 0) { + result = 1; + } + + return result; +} + +PARCAtomicUint32 * +parcAtomicUint32_Copy(const PARCAtomicUint32 *original) +{ + PARCAtomicUint32 *result = parcAtomicUint32_Create(parcAtomicUint32_GetValue(original)); + + return result; +} + +bool +parcAtomicUint32_Equals(const PARCAtomicUint32 *x, const PARCAtomicUint32 *y) +{ + bool result = false; + + result = parcAtomicUint32_GetValue(x) == parcAtomicUint32_GetValue(y); + + return result; +} + +PARCHashCode +parcAtomicUint32_HashCode(const PARCAtomicUint32 *instance) +{ + PARCHashCode result = (PARCHashCode) parcAtomicUint32_GetValue(instance); + + return result; +} + +bool +parcAtomicUint32_IsValid(const PARCAtomicUint32 *instance) +{ + bool result = false; + + if (instance != NULL) { + result = true; + } + + return result; +} + +uint32_t +parcAtomicUint32_GetValue(const PARCAtomicUint32 *instance) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + return instance->value; +#else + return *instance; +#endif +} + +uint32_t +parcAtomicUint32_AddImpl(PARCAtomicUint32 *value, uint32_t addend) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_lock(&value->mutex); + value->value += addend; + uint32_t result = value->value; + pthread_mutex_unlock(&value->mutex); + return result; +#else + return __sync_add_and_fetch(value, addend); +#endif +} + +uint32_t +parcAtomicUint32_SubtractImpl(PARCAtomicUint32 *value, uint32_t subtrahend) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_lock(&value->mutex); + value->value -= subtrahend; + uint32_t result = value->value; + pthread_mutex_unlock(&value->mutex); + return result; +#else + return __sync_sub_and_fetch(value, subtrahend); +#endif +} + +bool +parcAtomicUint32_CompareAndSwapImpl(PARCAtomicUint32 *value, uint32_t predicate, uint32_t newValue) +{ + bool result = false; +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_lock(&value->mutex); + if (value->value == predicate) { + value->value = newValue; + result = true; + } + pthread_mutex_unlock(&value->mutex); + return result; +#else + result = __sync_bool_compare_and_swap(value, predicate, newValue); +#endif + return result; +} diff --git a/libparc/parc/concurrent/parc_AtomicUint32.h b/libparc/parc/concurrent/parc_AtomicUint32.h new file mode 100755 index 00000000..870cbe80 --- /dev/null +++ b/libparc/parc/concurrent/parc_AtomicUint32.h @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_AtomicUint32.h + * @ingroup threading + * @brief An atomically updated 32-bit unsigned integer. + * + * <#Detailed Description#> + * + */ +#ifndef PARCLibrary_parc_AtomicUint32 +#define PARCLibrary_parc_AtomicUint32 +#include <stdbool.h> +#include <stdint.h> + +#include <parc/algol/parc_JSON.h> + +#ifdef PARCLibrary_DISABLE_ATOMICS +#include <pthread.h> +struct PARCAtomicUint32; +typedef struct PARCAtomicUint32 PARCAtomicUint32; +#else +typedef uint32_t PARCAtomicUint32; +#endif + + +PARCAtomicUint32 *parcAtomicInteger_CreateUint32(uint32_t value); + +uint32_t parcAtomicUint32_AddImpl(PARCAtomicUint32 *value, uint32_t addend); + +uint32_t parcAtomicUint32_SubtractImpl(PARCAtomicUint32 *value, uint32_t subtrahend); + +bool parcAtomicUint32_CompareAndSwapImpl(PARCAtomicUint32 *value, uint32_t predicate, uint32_t newValue); + +#ifdef PARCLibrary_DISABLE_ATOMICS + +#define parcAtomicUint32_Add parcAtomicUint32_AddImpl + +#define parcAtomicUint32_Subtract parcAtomicUint32_SubtractImpl + +#define parcAtomicUint32_CompareAndSwap parcAtomicUint32_CompareAndSwapImpl + +#else + +#define parcAtomicUint32_Add(_atomic_uint32_, _addend_) \ + __sync_add_and_fetch(_atomic_uint32_, _addend_) + +#define parcAtomicUint32_Subtract(_atomic_uint32_, _subtrahend_) \ + __sync_sub_and_fetch(_atomic_uint32_, _subtrahend_) + +#define parcAtomicUint32_CompareAndSwap(_atomic_uint32_, _predicate_, _newValue_) \ + __sync_bool_compare_and_swap(_atomic_uint32_, _predicate_, _newValue_) +#endif + +#define parcAtomicUint32_Increment(_atomic_uint32_) parcAtomicUint32_Add(_atomic_uint32_, 1) +#define parcAtomicUint32_Decrement(_atomic_uint32_) parcAtomicUint32_Subtract(_atomic_uint32_, 1) + +/** + * Increase the number of references to a `PARCAtomicUint32` instance. + * + * Note that new `PARCAtomicUint32` is not created, + * only that the given `PARCAtomicUint32` reference count is incremented. + * Discard the reference by invoking `parcAtomicUint32_Release`. + * + * @param [in] instance A pointer to a valid PARCAtomicUint32 instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCAtomicUint32 *a = parcAtomicUint32_Create(); + * + * PARCAtomicUint32 *b = parcAtomicUint32_Acquire(); + * + * parcAtomicUint32_Release(&a); + * parcAtomicUint32_Release(&b); + * } + * @endcode + */ +PARCAtomicUint32 *parcAtomicUint32_Acquire(const PARCAtomicUint32 *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcAtomicUint32_OptionalAssertValid(_instance_) +#else +# define parcAtomicUint32_OptionalAssertValid(_instance_) parcAtomicUint32_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCAtomicUint32` instance is valid. + * + * @param [in] instance A pointer to a valid PARCAtomicUint32 instance. + * + * Example: + * @code + * { + * PARCAtomicUint32 *a = parcAtomicUint32_Create(); + * + * parcAtomicUint32_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcAtomicUint32_Release(&b); + * } + * @endcode + */ +void parcAtomicUint32_AssertValid(const PARCAtomicUint32 *instance); + +/** + * Create an instance of PARCAtomicUint32 + * + * <#Paragraphs Of Explanation#> + * + * @return non-NULL A pointer to a valid PARCAtomicUint32 instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCAtomicUint32 *a = parcAtomicUint32_Create(); + * + * parcAtomicUint32_Release(&b); + * } + * @endcode + */ +PARCAtomicUint32 *parcAtomicUint32_Create(uint32_t); + +/** + * 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 PARCAtomicUint32 instance. + * @param [in] other A pointer to a valid PARCAtomicUint32 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 + * { + * PARCAtomicUint32 *a = parcAtomicUint32_Create(); + * PARCAtomicUint32 *b = parcAtomicUint32_Create(); + * + * if (parcAtomicUint32_Compare(a, b) == 0) { + * printf("Instances are equal.\n"); + * } + * + * parcAtomicUint32_Release(&a); + * parcAtomicUint32_Release(&b); + * } + * @endcode + * + * @see parcAtomicUint32_Equals + */ +int parcAtomicUint32_Compare(const PARCAtomicUint32 *instance, const PARCAtomicUint32 *other); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A pointer to a valid PARCAtomicUint32 instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCAtomicUint32` instance. + * + * Example: + * @code + * { + * PARCAtomicUint32 *a = parcAtomicUint32_Create(); + * + * PARCAtomicUint32 *copy = parcAtomicUint32_Copy(&b); + * + * parcAtomicUint32_Release(&b); + * parcAtomicUint32_Release(©); + * } + * @endcode + */ +PARCAtomicUint32 *parcAtomicUint32_Copy(const PARCAtomicUint32 *original); + +/** + * Determine if two `PARCAtomicUint32` instances are equal. + * + * The following equivalence relations on non-null `PARCAtomicUint32` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcAtomicUint32_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcAtomicUint32_Equals(x, y)` must return true if and only if + * `parcAtomicUint32_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcAtomicUint32_Equals(x, y)` returns true and + * `parcAtomicUint32_Equals(y, z)` returns true, + * then `parcAtomicUint32_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcAtomicUint32_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcAtomicUint32_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCAtomicUint32 instance. + * @param [in] y A pointer to a valid PARCAtomicUint32 instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCAtomicUint32 *a = parcAtomicUint32_Create(); + * PARCAtomicUint32 *b = parcAtomicUint32_Create(); + * + * if (parcAtomicUint32_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcAtomicUint32_Release(&a); + * parcAtomicUint32_Release(&b); + * } + * @endcode + * @see parcAtomicUint32_HashCode + */ +bool parcAtomicUint32_Equals(const PARCAtomicUint32 *x, const PARCAtomicUint32 *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcAtomicUint32_Equals} method, + * then calling the {@link parcAtomicUint32_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcAtomicUint32_Equals} function, + * then calling the `parcAtomicUint32_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCAtomicUint32 instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCAtomicUint32 *a = parcAtomicUint32_Create(); + * + * PARCHashCode hashValue = parcAtomicUint32_HashCode(buffer); + * parcAtomicUint32_Release(&a); + * } + * @endcode + */ +PARCHashCode parcAtomicUint32_HashCode(const PARCAtomicUint32 *instance); + +/** + * Determine if an instance of `PARCAtomicUint32` 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 PARCAtomicUint32 instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCAtomicUint32 *a = parcAtomicUint32_Create(); + * + * if (parcAtomicUint32_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcAtomicUint32_Release(&b); + * } + * @endcode + */ +bool parcAtomicUint32_IsValid(const PARCAtomicUint32 *instance); + +/** + * Release a previously acquired reference to the given `PARCAtomicUint32` 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 + * { + * PARCAtomicUint32 *a = parcAtomicUint32_Create(); + * + * parcAtomicUint32_Release(&a); + * } + * @endcode + */ +void parcAtomicUint32_Release(PARCAtomicUint32 **instancePtr); + +/** + * Get the current value of the given `PARCAtomicUint32` instance. + * + * @param [in] instance A pointer to a valid `PARCAtomicUint32` instance. + * + * @return the current value of the given `PARCAtomicUint32` instance. + * + * Example: + * @code + * { + * PARCAtomicUint32 *instance = parcAtomicUint32_Create(7); + * + * uint32_t value = parcAtomicUint32_GetValue(instance); + * + * parcAtomicUint32_Release(&instance); + * } + * @endcode + */ +uint32_t parcAtomicUint32_GetValue(const PARCAtomicUint32 *instance); +#endif diff --git a/libparc/parc/concurrent/parc_AtomicUint64.c b/libparc/parc/concurrent/parc_AtomicUint64.c new file mode 100755 index 00000000..35d5cf66 --- /dev/null +++ b/libparc/parc/concurrent/parc_AtomicUint64.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/concurrent/parc_AtomicUint64.h> + +struct PARCAtomicUint64 { + uint64_t value; +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_t mutex; +#endif +}; + +static void +_parcAtomicUint64_Finalize(PARCAtomicUint64 **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCAtomicUint64 pointer."); + + parcAtomicUint64_OptionalAssertValid((*instancePtr)); + + /* cleanup the instance fields here */ +} + +parcObject_ImplementAcquire(parcAtomicUint64, PARCAtomicUint64); + +parcObject_ImplementRelease(parcAtomicUint64, PARCAtomicUint64); + +parcObject_ExtendPARCObject(PARCAtomicUint64, _parcAtomicUint64_Finalize, parcAtomicUint64_Copy, NULL, parcAtomicUint64_Equals, parcAtomicUint64_Compare, parcAtomicUint64_HashCode, NULL); + + +void +parcAtomicUint64_AssertValid(const PARCAtomicUint64 *instance) +{ + assertTrue(parcAtomicUint64_IsValid(instance), + "PARCAtomicUint64 is not valid."); +} + +PARCAtomicUint64 * +parcAtomicUint64_Create(uint64_t value) +{ + PARCAtomicUint64 *result = parcObject_CreateAndClearInstance(PARCAtomicUint64); + +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_init(&result->mutex, NULL); + result->value = value; +#else + *result = value; +#endif + + return result; +} + +int +parcAtomicUint64_Compare(const PARCAtomicUint64 *instance, const PARCAtomicUint64 *other) +{ + int64_t comparison = parcAtomicUint64_GetValue(instance) - parcAtomicUint64_GetValue(other); + + int result = 0; + if (comparison < 0) { + result = -1; + } else if (comparison > 0) { + result = 1; + } + + return result; +} + +PARCAtomicUint64 * +parcAtomicUint64_Copy(const PARCAtomicUint64 *original) +{ + PARCAtomicUint64 *result = parcAtomicUint64_Create(parcAtomicUint64_GetValue(original)); + + return result; +} + +bool +parcAtomicUint64_Equals(const PARCAtomicUint64 *x, const PARCAtomicUint64 *y) +{ + bool result = false; + + result = parcAtomicUint64_GetValue(x) == parcAtomicUint64_GetValue(y); + + return result; +} + +PARCHashCode +parcAtomicUint64_HashCode(const PARCAtomicUint64 *instance) +{ + PARCHashCode result = (PARCHashCode) parcAtomicUint64_GetValue(instance); + + return result; +} + +bool +parcAtomicUint64_IsValid(const PARCAtomicUint64 *instance) +{ + bool result = false; + + if (instance != NULL) { + result = true; + } + + return result; +} + +uint64_t +parcAtomicUint64_GetValue(const PARCAtomicUint64 *instance) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + return instance->value; +#else + return *instance; +#endif +} + +uint64_t +parcAtomicUint64_AddImpl(PARCAtomicUint64 *value, uint64_t addend) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_lock(&value->mutex); + value->value += addend; + uint64_t result = value->value; + pthread_mutex_unlock(&value->mutex); + return result; +#else + return __sync_add_and_fetch(value, addend); +#endif +} + +uint64_t +parcAtomicUint64_SubtractImpl(PARCAtomicUint64 *value, uint64_t subtrahend) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_lock(&value->mutex); + value->value -= subtrahend; + uint64_t result = value->value; + pthread_mutex_unlock(&value->mutex); + return result; +#else + return __sync_sub_and_fetch(value, subtrahend); +#endif +} + +bool +parcAtomicUint64_CompareAndSwapImpl(PARCAtomicUint64 *value, uint64_t predicate, uint64_t newValue) +{ + bool result = false; +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_lock(&value->mutex); + if (value->value == predicate) { + value->value = newValue; + result = true; + } + pthread_mutex_unlock(&value->mutex); + return result; +#else + result = __sync_bool_compare_and_swap(value, predicate, newValue); +#endif + return result; +} diff --git a/libparc/parc/concurrent/parc_AtomicUint64.h b/libparc/parc/concurrent/parc_AtomicUint64.h new file mode 100755 index 00000000..a6ac3ec1 --- /dev/null +++ b/libparc/parc/concurrent/parc_AtomicUint64.h @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_AtomicUint64.h + * @ingroup threading + * @brief An atomically updated 64-bit unsigned integer. + * + * <#Detailed Description#> + * + */ +#ifndef PARCLibrary_parc_AtomicUint64 +#define PARCLibrary_parc_AtomicUint64 +#include <stdbool.h> +#include <stdint.h> + +#include <parc/algol/parc_JSON.h> + +#ifdef PARCLibrary_DISABLE_ATOMICS +#include <pthread.h> +struct PARCAtomicUint64; +typedef struct PARCAtomicUint64 PARCAtomicUint64; +#else +typedef uint64_t PARCAtomicUint64; +#endif + + +PARCAtomicUint64 *parcAtomicInteger_CreateUint64(uint64_t value); + +uint64_t parcAtomicUint64_AddImpl(PARCAtomicUint64 *value, uint64_t addend); + +uint64_t parcAtomicUint64_SubtractImpl(PARCAtomicUint64 *value, uint64_t subtrahend); + +bool parcAtomicUint64_CompareAndSwapImpl(PARCAtomicUint64 *value, uint64_t predicate, uint64_t newValue); + +#ifdef PARCLibrary_DISABLE_ATOMICS + +#define parcAtomicUint64_Add parcAtomicUint64_AddImpl + +#define parcAtomicUint64_Subtract parcAtomicUint64_SubtractImpl + +#define parcAtomicUint64_CompareAndSwap parcAtomicUint64_CompareAndSwapImpl + +#else + +#define parcAtomicUint64_Add(_atomic_uint64_, _addend_) \ + __sync_add_and_fetch(_atomic_uint64_, _addend_) + +#define parcAtomicUint64_Subtract(_atomic_uint64_, _subtrahend_) \ + __sync_sub_and_fetch(_atomic_uint64_, _subtrahend_) + +#define parcAtomicUint64_CompareAndSwap(_atomic_uint64_, _predicate_, _newValue_) \ + __sync_bool_compare_and_swap(_atomic_uint64_, _predicate_, _newValue_) +#endif + +#define parcAtomicUint64_Increment(_atomic_uint64_) parcAtomicUint64_Add(_atomic_uint64_, 1) +#define parcAtomicUint64_Decrement(_atomic_uint64_) parcAtomicUint64_Subtract(_atomic_uint64_, 1) + +/** + * Increase the number of references to a `PARCAtomicUint64` instance. + * + * Note that new `PARCAtomicUint64` is not created, + * only that the given `PARCAtomicUint64` reference count is incremented. + * Discard the reference by invoking `parcAtomicUint64_Release`. + * + * @param [in] instance A pointer to a valid PARCAtomicUint64 instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCAtomicUint64 *a = parcAtomicUint64_Create(); + * + * PARCAtomicUint64 *b = parcAtomicUint64_Acquire(); + * + * parcAtomicUint64_Release(&a); + * parcAtomicUint64_Release(&b); + * } + * @endcode + */ +PARCAtomicUint64 *parcAtomicUint64_Acquire(const PARCAtomicUint64 *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcAtomicUint64_OptionalAssertValid(_instance_) +#else +# define parcAtomicUint64_OptionalAssertValid(_instance_) parcAtomicUint64_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCAtomicUint64` instance is valid. + * + * @param [in] instance A pointer to a valid PARCAtomicUint64 instance. + * + * Example: + * @code + * { + * PARCAtomicUint64 *a = parcAtomicUint64_Create(); + * + * parcAtomicUint64_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcAtomicUint64_Release(&b); + * } + * @endcode + */ +void parcAtomicUint64_AssertValid(const PARCAtomicUint64 *instance); + +/** + * Create an instance of PARCAtomicUint64 + * + * <#Paragraphs Of Explanation#> + * + * @return non-NULL A pointer to a valid PARCAtomicUint64 instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCAtomicUint64 *a = parcAtomicUint64_Create(); + * + * parcAtomicUint64_Release(&b); + * } + * @endcode + */ +PARCAtomicUint64 *parcAtomicUint64_Create(uint64_t); + +/** + * 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 PARCAtomicUint64 instance. + * @param [in] other A pointer to a valid PARCAtomicUint64 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 + * { + * PARCAtomicUint64 *a = parcAtomicUint64_Create(); + * PARCAtomicUint64 *b = parcAtomicUint64_Create(); + * + * if (parcAtomicUint64_Compare(a, b) == 0) { + * printf("Instances are equal.\n"); + * } + * + * parcAtomicUint64_Release(&a); + * parcAtomicUint64_Release(&b); + * } + * @endcode + * + * @see parcAtomicUint64_Equals + */ +int parcAtomicUint64_Compare(const PARCAtomicUint64 *instance, const PARCAtomicUint64 *other); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A pointer to a valid PARCAtomicUint64 instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCAtomicUint64` instance. + * + * Example: + * @code + * { + * PARCAtomicUint64 *a = parcAtomicUint64_Create(); + * + * PARCAtomicUint64 *copy = parcAtomicUint64_Copy(&b); + * + * parcAtomicUint64_Release(&b); + * parcAtomicUint64_Release(©); + * } + * @endcode + */ +PARCAtomicUint64 *parcAtomicUint64_Copy(const PARCAtomicUint64 *original); + +/** + * Determine if two `PARCAtomicUint64` instances are equal. + * + * The following equivalence relations on non-null `PARCAtomicUint64` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcAtomicUint64_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcAtomicUint64_Equals(x, y)` must return true if and only if + * `parcAtomicUint64_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcAtomicUint64_Equals(x, y)` returns true and + * `parcAtomicUint64_Equals(y, z)` returns true, + * then `parcAtomicUint64_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcAtomicUint64_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcAtomicUint64_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCAtomicUint64 instance. + * @param [in] y A pointer to a valid PARCAtomicUint64 instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCAtomicUint64 *a = parcAtomicUint64_Create(); + * PARCAtomicUint64 *b = parcAtomicUint64_Create(); + * + * if (parcAtomicUint64_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcAtomicUint64_Release(&a); + * parcAtomicUint64_Release(&b); + * } + * @endcode + * @see parcAtomicUint64_HashCode + */ +bool parcAtomicUint64_Equals(const PARCAtomicUint64 *x, const PARCAtomicUint64 *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcAtomicUint64_Equals} method, + * then calling the {@link parcAtomicUint64_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcAtomicUint64_Equals} function, + * then calling the `parcAtomicUint64_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCAtomicUint64 instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCAtomicUint64 *a = parcAtomicUint64_Create(); + * + * PARCHashCode hashValue = parcAtomicUint64_HashCode(buffer); + * parcAtomicUint64_Release(&a); + * } + * @endcode + */ +PARCHashCode parcAtomicUint64_HashCode(const PARCAtomicUint64 *instance); + +/** + * Determine if an instance of `PARCAtomicUint64` 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 PARCAtomicUint64 instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCAtomicUint64 *a = parcAtomicUint64_Create(); + * + * if (parcAtomicUint64_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcAtomicUint64_Release(&b); + * } + * @endcode + */ +bool parcAtomicUint64_IsValid(const PARCAtomicUint64 *instance); + +/** + * Release a previously acquired reference to the given `PARCAtomicUint64` 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 + * { + * PARCAtomicUint64 *a = parcAtomicUint64_Create(); + * + * parcAtomicUint64_Release(&a); + * } + * @endcode + */ +void parcAtomicUint64_Release(PARCAtomicUint64 **instancePtr); + +/** + * Get the current value of the given `PARCAtomicUint64` instance. + * + * @param [in] instance A pointer to a valid `PARCAtomicUint64` instance. + * + * @return the current value of the given `PARCAtomicUint64` instance. + * + * Example: + * @code + * { + * PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + * + * uint64_t value = parcAtomicUint64_GetValue(instance); + * + * parcAtomicUint64_Release(&instance); + * } + * @endcode + */ +uint64_t parcAtomicUint64_GetValue(const PARCAtomicUint64 *instance); +#endif diff --git a/libparc/parc/concurrent/parc_AtomicUint8.c b/libparc/parc/concurrent/parc_AtomicUint8.c new file mode 100755 index 00000000..59a3d865 --- /dev/null +++ b/libparc/parc/concurrent/parc_AtomicUint8.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/concurrent/parc_AtomicUint8.h> + +struct PARCAtomicUint8 { + uint8_t value; +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_t mutex; +#endif +}; + +static void +_parcAtomicUint8_Finalize(PARCAtomicUint8 **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCAtomicUint8 pointer."); + + parcAtomicUint8_OptionalAssertValid((*instancePtr)); + + /* cleanup the instance fields here */ +} + +parcObject_ImplementAcquire(parcAtomicUint8, PARCAtomicUint8); + +parcObject_ImplementRelease(parcAtomicUint8, PARCAtomicUint8); + +parcObject_ExtendPARCObject(PARCAtomicUint8, _parcAtomicUint8_Finalize, parcAtomicUint8_Copy, NULL, parcAtomicUint8_Equals, parcAtomicUint8_Compare, parcAtomicUint8_HashCode, NULL); + + +void +parcAtomicUint8_AssertValid(const PARCAtomicUint8 *instance) +{ + assertTrue(parcAtomicUint8_IsValid(instance), + "PARCAtomicUint8 is not valid."); +} + +PARCAtomicUint8 * +parcAtomicUint8_Create(uint8_t value) +{ + PARCAtomicUint8 *result = parcObject_CreateAndClearInstance(PARCAtomicUint8); + +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_init(&result->mutex, NULL); + result->value = value; +#else + *result = value; +#endif + + return result; +} + +int +parcAtomicUint8_Compare(const PARCAtomicUint8 *instance, const PARCAtomicUint8 *other) +{ + int8_t comparison = parcAtomicUint8_GetValue(instance) - parcAtomicUint8_GetValue(other); + + int result = 0; + if (comparison < 0) { + result = -1; + } else if (comparison > 0) { + result = 1; + } + + return result; +} + +PARCAtomicUint8 * +parcAtomicUint8_Copy(const PARCAtomicUint8 *original) +{ + PARCAtomicUint8 *result = parcAtomicUint8_Create(parcAtomicUint8_GetValue(original)); + + return result; +} + +bool +parcAtomicUint8_Equals(const PARCAtomicUint8 *x, const PARCAtomicUint8 *y) +{ + bool result = false; + + result = parcAtomicUint8_GetValue(x) == parcAtomicUint8_GetValue(y); + + return result; +} + +PARCHashCode +parcAtomicUint8_HashCode(const PARCAtomicUint8 *instance) +{ + PARCHashCode result = (uint32_t) parcAtomicUint8_GetValue(instance); + + return result; +} + +bool +parcAtomicUint8_IsValid(const PARCAtomicUint8 *instance) +{ + bool result = false; + + if (instance != NULL) { + result = true; + } + + return result; +} + +uint8_t +parcAtomicUint8_GetValue(const PARCAtomicUint8 *instance) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + return instance->value; +#else + return *instance; +#endif +} + +uint8_t +parcAtomicUint8_AddImpl(PARCAtomicUint8 *value, uint8_t addend) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_lock(&value->mutex); + value->value += addend; + uint8_t result = value->value; + pthread_mutex_unlock(&value->mutex); + return result; +#else + return __sync_add_and_fetch(value, addend); +#endif +} + +uint8_t +parcAtomicUint8_SubtractImpl(PARCAtomicUint8 *value, uint8_t subtrahend) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_lock(&value->mutex); + value->value -= subtrahend; + uint8_t result = value->value; + pthread_mutex_unlock(&value->mutex); + return result; +#else + return __sync_sub_and_fetch(value, subtrahend); +#endif +} + +bool +parcAtomicUint8_CompareAndSwapImpl(PARCAtomicUint8 *value, uint8_t predicate, uint8_t newValue) +{ + bool result = false; +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_lock(&value->mutex); + if (value->value == predicate) { + value->value = newValue; + result = true; + } + pthread_mutex_unlock(&value->mutex); + return result; +#else + result = __sync_bool_compare_and_swap(value, predicate, newValue); +#endif + return result; +} diff --git a/libparc/parc/concurrent/parc_AtomicUint8.h b/libparc/parc/concurrent/parc_AtomicUint8.h new file mode 100755 index 00000000..7434b93d --- /dev/null +++ b/libparc/parc/concurrent/parc_AtomicUint8.h @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_AtomicUint8.h + * @ingroup threading + * @brief An atomically updated 8-bit unsigned integer. + * + * <#Detailed Description#> + * + */ +#ifndef PARCLibrary_parc_AtomicUint8 +#define PARCLibrary_parc_AtomicUint8 +#include <stdbool.h> +#include <stdint.h> + +#include <parc/algol/parc_JSON.h> + +#ifdef PARCLibrary_DISABLE_ATOMICS +#include <pthread.h> +struct PARCAtomicUint8; +typedef struct PARCAtomicUint8 PARCAtomicUint8; +#else +typedef uint8_t PARCAtomicUint8; +#endif + + +PARCAtomicUint8 *parcAtomicInteger_CreateUint8(uint8_t value); + +uint8_t parcAtomicUint8_AddImpl(PARCAtomicUint8 *value, uint8_t addend); + +uint8_t parcAtomicUint8_SubtractImpl(PARCAtomicUint8 *value, uint8_t subtrahend); + +bool parcAtomicUint8_CompareAndSwapImpl(PARCAtomicUint8 *value, uint8_t predicate, uint8_t newValue); + +#ifdef PARCLibrary_DISABLE_ATOMICS + +#define parcAtomicUint8_Add parcAtomicUint8_AddImpl + +#define parcAtomicUint8_Subtract parcAtomicUint8_SubtractImpl + +#define parcAtomicUint8_CompareAndSwap parcAtomicUint8_CompareAndSwapImpl + +#else + +#define parcAtomicUint8_Add(_atomic_uint8_, _addend_) \ + __sync_add_and_fetch(_atomic_uint8_, _addend_) + +#define parcAtomicUint8_Subtract(_atomic_uint8_, _subtrahend_) \ + __sync_sub_and_fetch(_atomic_uint8_, _subtrahend_) + +#define parcAtomicUint8_CompareAndSwap(_atomic_uint8_, _predicate_, _newValue_) \ + __sync_bool_compare_and_swap(_atomic_uint8_, _predicate_, _newValue_) +#endif + +#define parcAtomicUint8_Increment(_atomic_uint8_) parcAtomicUint8_Add(_atomic_uint8_, 1) +#define parcAtomicUint8_Decrement(_atomic_uint8_) parcAtomicUint8_Subtract(_atomic_uint8_, 1) + +/** + * Increase the number of references to a `PARCAtomicUint8` instance. + * + * Note that new `PARCAtomicUint8` is not created, + * only that the given `PARCAtomicUint8` reference count is incremented. + * Discard the reference by invoking `parcAtomicUint8_Release`. + * + * @param [in] instance A pointer to a valid PARCAtomicUint8 instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCAtomicUint8 *a = parcAtomicUint8_Create(); + * + * PARCAtomicUint8 *b = parcAtomicUint8_Acquire(); + * + * parcAtomicUint8_Release(&a); + * parcAtomicUint8_Release(&b); + * } + * @endcode + */ +PARCAtomicUint8 *parcAtomicUint8_Acquire(const PARCAtomicUint8 *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcAtomicUint8_OptionalAssertValid(_instance_) +#else +# define parcAtomicUint8_OptionalAssertValid(_instance_) parcAtomicUint8_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCAtomicUint8` instance is valid. + * + * @param [in] instance A pointer to a valid PARCAtomicUint8 instance. + * + * Example: + * @code + * { + * PARCAtomicUint8 *a = parcAtomicUint8_Create(); + * + * parcAtomicUint8_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcAtomicUint8_Release(&b); + * } + * @endcode + */ +void parcAtomicUint8_AssertValid(const PARCAtomicUint8 *instance); + +/** + * Create an instance of PARCAtomicUint8 + * + * <#Paragraphs Of Explanation#> + * + * @return non-NULL A pointer to a valid PARCAtomicUint8 instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCAtomicUint8 *a = parcAtomicUint8_Create(); + * + * parcAtomicUint8_Release(&b); + * } + * @endcode + */ +PARCAtomicUint8 *parcAtomicUint8_Create(uint8_t); + +/** + * 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 PARCAtomicUint8 instance. + * @param [in] other A pointer to a valid PARCAtomicUint8 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 + * { + * PARCAtomicUint8 *a = parcAtomicUint8_Create(); + * PARCAtomicUint8 *b = parcAtomicUint8_Create(); + * + * if (parcAtomicUint8_Compare(a, b) == 0) { + * printf("Instances are equal.\n"); + * } + * + * parcAtomicUint8_Release(&a); + * parcAtomicUint8_Release(&b); + * } + * @endcode + * + * @see parcAtomicUint8_Equals + */ +int parcAtomicUint8_Compare(const PARCAtomicUint8 *instance, const PARCAtomicUint8 *other); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A pointer to a valid PARCAtomicUint8 instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCAtomicUint8` instance. + * + * Example: + * @code + * { + * PARCAtomicUint8 *a = parcAtomicUint8_Create(); + * + * PARCAtomicUint8 *copy = parcAtomicUint8_Copy(&b); + * + * parcAtomicUint8_Release(&b); + * parcAtomicUint8_Release(©); + * } + * @endcode + */ +PARCAtomicUint8 *parcAtomicUint8_Copy(const PARCAtomicUint8 *original); + +/** + * Determine if two `PARCAtomicUint8` instances are equal. + * + * The following equivalence relations on non-null `PARCAtomicUint8` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcAtomicUint8_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcAtomicUint8_Equals(x, y)` must return true if and only if + * `parcAtomicUint8_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcAtomicUint8_Equals(x, y)` returns true and + * `parcAtomicUint8_Equals(y, z)` returns true, + * then `parcAtomicUint8_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcAtomicUint8_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcAtomicUint8_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCAtomicUint8 instance. + * @param [in] y A pointer to a valid PARCAtomicUint8 instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCAtomicUint8 *a = parcAtomicUint8_Create(); + * PARCAtomicUint8 *b = parcAtomicUint8_Create(); + * + * if (parcAtomicUint8_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcAtomicUint8_Release(&a); + * parcAtomicUint8_Release(&b); + * } + * @endcode + * @see parcAtomicUint8_HashCode + */ +bool parcAtomicUint8_Equals(const PARCAtomicUint8 *x, const PARCAtomicUint8 *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcAtomicUint8_Equals} method, + * then calling the {@link parcAtomicUint8_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcAtomicUint8_Equals} function, + * then calling the `parcAtomicUint8_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCAtomicUint8 instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCAtomicUint8 *a = parcAtomicUint8_Create(); + * + * PARCHashCode hashValue = parcAtomicUint8_HashCode(buffer); + * parcAtomicUint8_Release(&a); + * } + * @endcode + */ +PARCHashCode parcAtomicUint8_HashCode(const PARCAtomicUint8 *instance); + +/** + * Determine if an instance of `PARCAtomicUint8` 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 PARCAtomicUint8 instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCAtomicUint8 *a = parcAtomicUint8_Create(); + * + * if (parcAtomicUint8_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcAtomicUint8_Release(&b); + * } + * @endcode + */ +bool parcAtomicUint8_IsValid(const PARCAtomicUint8 *instance); + +/** + * Release a previously acquired reference to the given `PARCAtomicUint8` 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 + * { + * PARCAtomicUint8 *a = parcAtomicUint8_Create(); + * + * parcAtomicUint8_Release(&a); + * } + * @endcode + */ +void parcAtomicUint8_Release(PARCAtomicUint8 **instancePtr); + +/** + * Get the current value of the given `PARCAtomicUint8` instance. + * + * @param [in] instance A pointer to a valid `PARCAtomicUint8` instance. + * + * @return the current value of the given `PARCAtomicUint8` instance. + * + * Example: + * @code + * { + * PARCAtomicUint8 *instance = parcAtomicUint8_Create(7); + * + * uint8_t value = parcAtomicUint8_GetValue(instance); + * + * parcAtomicUint8_Release(&instance); + * } + * @endcode + */ +uint8_t parcAtomicUint8_GetValue(const PARCAtomicUint8 *instance); +#endif diff --git a/libparc/parc/concurrent/parc_FutureTask.c b/libparc/parc/concurrent/parc_FutureTask.c new file mode 100755 index 00000000..0ba2c224 --- /dev/null +++ b/libparc/parc/concurrent/parc_FutureTask.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Execution.h> + +#include <parc/concurrent/parc_FutureTask.h> + +struct PARCFutureTask { + void *(*function)(PARCFutureTask *task, void *parameter); + void *parameter; + void *result; + bool isRunning; + bool isDone; + bool isCancelled; +}; + +static void +_parcFutureTask_Initialise(PARCFutureTask *futureTask) +{ + futureTask->result = NULL; + futureTask->isDone = false; + futureTask->isCancelled = false; + futureTask->isRunning = false; +} + +static bool +_parcFutureTask_Destructor(PARCFutureTask **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCFutureTask pointer."); + PARCFutureTask *task = *instancePtr; + + if (parcObject_IsInstanceOf(task->parameter, &PARCObject_Descriptor)) { + parcObject_Release(&task->parameter); + } + + return true; +} + +parcObject_ImplementAcquire(parcFutureTask, PARCFutureTask); + +parcObject_ImplementRelease(parcFutureTask, PARCFutureTask); + +parcObject_Override(PARCFutureTask, PARCObject, + .isLockable = true, + .destructor = (PARCObjectDestructor *) _parcFutureTask_Destructor, + .copy = (PARCObjectCopy *) parcFutureTask_Copy, + .toString = (PARCObjectToString *) parcFutureTask_ToString, + .equals = (PARCObjectEquals *) parcFutureTask_Equals, + .compare = (PARCObjectCompare *) parcFutureTask_Compare, + .hashCode = (PARCObjectHashCode *) parcFutureTask_HashCode, + .display = (PARCObjectDisplay *) parcFutureTask_Display); + +void +parcFutureTask_AssertValid(const PARCFutureTask *task) +{ + assertTrue(parcFutureTask_IsValid(task), + "PARCFutureTask is not valid."); +} + +PARCFutureTask * +parcFutureTask_Create(void *(*function)(PARCFutureTask *task, void *parameter), void *parameter) +{ + PARCFutureTask *result = parcObject_CreateInstance(PARCFutureTask); + + if (parcObject_IsInstanceOf(parameter, &PARCObject_Descriptor)) { + parameter = parcObject_Acquire(parameter); + } + + if (result != NULL) { + result->function = function; + result->parameter = parameter; + _parcFutureTask_Initialise(result); + } + + return result; +} + +int +parcFutureTask_Compare(const PARCFutureTask *instance, const PARCFutureTask *other) +{ + int result = 0; + + return result; +} + +PARCFutureTask * +parcFutureTask_Copy(const PARCFutureTask *original) +{ + PARCFutureTask *result = parcFutureTask_Create(original->function, original->parameter); + + return result; +} + +void +parcFutureTask_Display(const PARCFutureTask *instance, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCFutureTask@%p {", instance); + /* Call Display() functions for the fields here. */ + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool +parcFutureTask_Equals(const PARCFutureTask *x, const PARCFutureTask *y) +{ + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + if (x->function == y->function) { + if (x->parameter == y->parameter) { + result = true; + } + } + } + + return result; +} + +PARCHashCode +parcFutureTask_HashCode(const PARCFutureTask *instance) +{ + PARCHashCode result = 0; + + return result; +} + +bool +parcFutureTask_IsValid(const PARCFutureTask *instance) +{ + bool result = false; + + if (instance != NULL) { + result = true; + } + + return result; +} + +PARCJSON * +parcFutureTask_ToJSON(const PARCFutureTask *instance) +{ + PARCJSON *result = parcJSON_Create(); + + if (result != NULL) { + } + + return result; +} + +char * +parcFutureTask_ToString(const PARCFutureTask *instance) +{ + char *result = parcMemory_Format("PARCFutureTask@%p\n", instance); + + return result; +} + +bool +parcFutureTask_Cancel(PARCFutureTask *task, bool mayInterruptIfRunning) +{ + bool result = false; + + if (parcObject_Lock(task)) { + if (task->isRunning) { + if (mayInterruptIfRunning) { + printf("Interrupting a running task is not implemented yet.\n"); + } + result = false; + } else { + task->isCancelled = true; + task->isDone = true; + parcObject_Notify(task); + result = true; + } + + parcObject_Unlock(task); + } + + return result; +} + +PARCFutureTaskResult +parcFutureTask_Get(const PARCFutureTask *futureTask, const PARCTimeout *timeout) +{ + PARCFutureTaskResult result; + + result.execution = PARCExecution_Timeout; + result.value = 0; + + if (parcTimeout_IsImmediate(timeout)) { + if (futureTask->isDone) { + result.execution = PARCExecution_OK; + result.value = futureTask->result; + } + } else { + result.execution = PARCExecution_Interrupted; + result.value = 0; + + parcObject_Lock(futureTask); + while (!futureTask->isDone) { + if (parcTimeout_IsNever(timeout)) { + parcObject_Wait(futureTask); + result.execution = PARCExecution_OK; + result.value = futureTask->result; + break; + } else { + if (parcObject_WaitFor(futureTask, parcTimeout_InNanoSeconds(timeout))) { + result.execution = PARCExecution_OK; + result.value = futureTask->result; + break; + } + } + } + parcObject_Unlock(futureTask); + } + + return result; +} + +bool +parcFutureTask_IsCancelled(const PARCFutureTask *task) +{ + return task->isCancelled; +} + +bool +parcFutureTask_IsDone(const PARCFutureTask *task) +{ + return task->isDone; +} + +static void * +_parcFutureTask_Execute(PARCFutureTask *task) +{ + task->isRunning = true; + void *result = task->function(task, task->parameter); + task->isRunning = false; + + return result; +} + +void * +parcFutureTask_Run(PARCFutureTask *task) +{ + if (parcFutureTask_Lock(task)) { + if (!task->isCancelled) { + task->result = _parcFutureTask_Execute(task); + task->isDone = true; + parcFutureTask_Notify(task); + } + parcFutureTask_Unlock(task); + } else { + trapCannotObtainLock("Cannot lock PARCFutureTask"); + } + return task->result; +} + +bool +parcFutureTask_RunAndReset(PARCFutureTask *task) +{ + bool result = false; + + if (parcObject_Lock(task)) { + if (!task->isCancelled) { + _parcFutureTask_Execute(task); + parcFutureTask_Reset(task); + result = true; + } + parcFutureTask_Unlock(task); + } else { + trapCannotObtainLock("Cannot lock PARCFutureTask"); + } + + return result; +} + +void +parcFutureTask_Reset(PARCFutureTask *task) +{ + _parcFutureTask_Initialise(task); +} diff --git a/libparc/parc/concurrent/parc_FutureTask.h b/libparc/parc/concurrent/parc_FutureTask.h new file mode 100755 index 00000000..9a5776c5 --- /dev/null +++ b/libparc/parc/concurrent/parc_FutureTask.h @@ -0,0 +1,657 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_FutureTask.h + * @ingroup threading + * @brief An encapsulated, asynchronous computation. + * + * This type associates a function and a pointer to data and provides the functionality + * to invoke the function supplying the given pointer to data and returning the result. + * + * The operations of invoking the function and collecting its return value may be asynchronous from each other + * in that an attempting to fetch the return value before the function has been invoked + * will cause the calling thread to block until the function has been invoked and run to completion. + * This enables the use of PARCFutureTask in a work queue, or thread pool where tasks are run asynchronously + * from each other and from an originating thread. + * + * Each instance of the type may be cancelled, + * inhibiting a future invocation of the associated function. + * + * Typical use is a one time invocation of the associated function, induced by the `parcFutureTask_Get`, + * but invoking `parcFutureTask_GetAndReset` invokes the associated function and resets the task to the initial state, + * permitting a future call to `parcFutureTask_Get` or `parcFutureTask_GetAndReset` the run the associated function again. + * + */ +#ifndef PARCLibrary_parc_FutureTask +#define PARCLibrary_parc_FutureTask +#include <stdbool.h> +#include <stdint.h> + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_HashCode.h> +#include <parc/concurrent/parc_Timeout.h> +#include <parc/algol/parc_Execution.h> + +struct PARCFutureTask; +typedef struct PARCFutureTask PARCFutureTask; + +typedef struct PARCFutureTaskResult { + void *value; + PARCExecution *execution; +} PARCFutureTaskResult; + +/** + * Increase the number of references to a `PARCFutureTask` instance. + * + * Note that new `PARCFutureTask` is not created, + * only that the given `PARCFutureTask` reference count is incremented. + * Discard the reference by invoking `parcFutureTask_Release`. + * + * @param [in] instance A pointer to a valid PARCFutureTask instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCFutureTask *a = parcFutureTask_Create(); + * + * PARCFutureTask *b = parcFutureTask_Acquire(); + * + * parcFutureTask_Release(&a); + * parcFutureTask_Release(&b); + * } + * @endcode + */ +PARCFutureTask *parcFutureTask_Acquire(const PARCFutureTask *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcFutureTask_OptionalAssertValid(_instance_) +#else +# define parcFutureTask_OptionalAssertValid(_instance_) parcFutureTask_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCFutureTask` instance is valid. + * + * @param [in] instance A pointer to a valid PARCFutureTask instance. + * + * Example: + * @code + * { + * PARCFutureTask *a = parcFutureTask_Create(); + * + * parcFutureTask_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcFutureTask_Release(&b); + * } + * @endcode + */ +void parcFutureTask_AssertValid(const PARCFutureTask *instance); + +/** + * Create an instance of `PARCFutureTask` + * + * If the parameter is an instance of `PARCObject` a reference to the object will + * be created and ultimately released via `parcFutureTask_Release` + * + * @param [in] function A pointer to a function to call. + * @param [in] parameter A pointer that will be passed to the function when invoked. + * + * @return non-NULL A pointer to a valid `PARCFutureTask` instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCFutureTask *a = parcFutureTask_Create(function, parameter); + * + * parcFutureTask_Release(&a); + * } + * @endcode + */ +PARCFutureTask *parcFutureTask_Create(void *(*runnable)(PARCFutureTask *task, void *parameter), void *parameter); + +/** + * 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 PARCFutureTask instance. + * @param [in] other A pointer to a valid PARCFutureTask 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 + * { + * PARCFutureTask *a = parcFutureTask_Create(); + * PARCFutureTask *b = parcFutureTask_Create(); + * + * if (parcFutureTask_Compare(a, b) == 0) { + * printf("Instances are equal.\n"); + * } + * + * parcFutureTask_Release(&a); + * parcFutureTask_Release(&b); + * } + * @endcode + * + * @see parcFutureTask_Equals + */ +int parcFutureTask_Compare(const PARCFutureTask *instance, const PARCFutureTask *other); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A pointer to a valid PARCFutureTask instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCFutureTask` instance. + * + * Example: + * @code + * { + * PARCFutureTask *a = parcFutureTask_Create(); + * + * PARCFutureTask *copy = parcFutureTask_Copy(&b); + * + * parcFutureTask_Release(&b); + * parcFutureTask_Release(©); + * } + * @endcode + */ +PARCFutureTask *parcFutureTask_Copy(const PARCFutureTask *original); + +/** + * Print a human readable representation of the given `PARCFutureTask`. + * + * @param [in] instance A pointer to a valid `PARCFutureTask` instance. + * @param [in] indentation The indentation level to use for printing. + * + * Example: + * @code + * { + * PARCFutureTask *a = parcFutureTask_Create(); + * + * parcFutureTask_Display(a, 0); + * + * parcFutureTask_Release(&a); + * } + * @endcode + */ +void parcFutureTask_Display(const PARCFutureTask *instance, int indentation); + +/** + * Determine if two `PARCFutureTask` instances are equal. + * + * The following equivalence relations on non-null `PARCFutureTask` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcFutureTask_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcFutureTask_Equals(x, y)` must return true if and only if + * `parcFutureTask_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcFutureTask_Equals(x, y)` returns true and + * `parcFutureTask_Equals(y, z)` returns true, + * then `parcFutureTask_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcFutureTask_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcFutureTask_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCFutureTask instance. + * @param [in] y A pointer to a valid PARCFutureTask instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCFutureTask *a = parcFutureTask_Create(); + * PARCFutureTask *b = parcFutureTask_Create(); + * + * if (parcFutureTask_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcFutureTask_Release(&a); + * parcFutureTask_Release(&b); + * } + * @endcode + * @see parcFutureTask_HashCode + */ +bool parcFutureTask_Equals(const PARCFutureTask *x, const PARCFutureTask *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcFutureTask_Equals} method, + * then calling the {@link parcFutureTask_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcFutureTask_Equals} function, + * then calling the `parcFutureTask_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCFutureTask instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCFutureTask *a = parcFutureTask_Create(); + * + * PARCHashCode hashValue = parcFutureTask_HashCode(buffer); + * parcFutureTask_Release(&a); + * } + * @endcode + */ +PARCHashCode parcFutureTask_HashCode(const PARCFutureTask *instance); + +/** + * Determine if an instance of `PARCFutureTask` 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 PARCFutureTask instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCFutureTask *a = parcFutureTask_Create(); + * + * if (parcFutureTask_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcFutureTask_Release(&a); + * } + * @endcode + * + */ +bool parcFutureTask_IsValid(const PARCFutureTask *instance); + +/** + * Release a previously acquired reference to the given `PARCFutureTask` 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 + * { + * PARCFutureTask *a = parcFutureTask_Create(); + * + * parcFutureTask_Release(&a); + * } + * @endcode + */ +void parcFutureTask_Release(PARCFutureTask **instancePtr); + +/** + * Create a `PARCJSON` instance (representation) of the given object. + * + * @param [in] instance A pointer to a valid PARCFutureTask instance. + * + * @return NULL Memory could not be allocated to contain the `PARCJSON` instance. + * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate(). + * + * Example: + * @code + * { + * PARCFutureTask *a = parcFutureTask_Create(); + * + * PARCJSON *json = parcFutureTask_ToJSON(a); + * + * printf("JSON representation: %s\n", parcJSON_ToString(json)); + * parcJSON_Release(&json); + * + * parcFutureTask_Release(&a); + * } + * @endcode + */ +PARCJSON *parcFutureTask_ToJSON(const PARCFutureTask *instance); + +/** + * Produce a null-terminated string representation of the specified `PARCFutureTask`. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to a valid PARCFutureTask instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCFutureTask *a = parcFutureTask_Create(); + * + * char *string = parcFutureTask_ToString(a); + * + * parcFutureTask_Release(&a); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + * + * @see parcFutureTask_Display + */ +char *parcFutureTask_ToString(const PARCFutureTask *instance); + +/** + * Wakes up a single thread that is waiting on this object (see `parcFutureTask_Wait)`. + * If any threads are waiting on this object, one of them is chosen to be awakened. + * The choice is arbitrary and occurs at the discretion of the underlying implementation. + * + * The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. + * The awakened thread will compete in the usual manner with any other threads that might be actively + * competing to synchronize on this object; + * for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object. + * + * @param [in] object A pointer to a valid PARCFutureTask instance. + * + * Example: + * @code + * { + * if (parcFutureTask_Lock(object)) { + * parcFutureTask_Notify(object); + * parcFutureTask_Unlock(object); + * } + * } + * @endcode + */ +parcObject_ImplementNotify(parcFutureTask, PARCFutureTask); + +/** + * Wakes up all threads that are waiting on the given object's lock. + * + * A thread waits on an object by calling one of the wait methods, `parcFutureTask_Wait`, `parcFutureTask_WaitFor`, `parcFutureTask_WaitUntil`. + * The awakened threads will proceed after the current thread relinquishes the lock on the given object. + * The awakened threads will compete in the usual manner with any other threads that might be actively competing + * to synchronize on this object. + * Awakened threads have no priority between them in being the next thread to lock this object. + * + * This method can only be called by a thread that is the owner of this object's lock. + * + * @param [in] object A pointer to a valid `PARCFutureTask` instance. + * + * Example: + * @code + * { + * if (parcFutureTask_Lock(object)) { + * parcFutureTask_NotifyAll(object); + * parcFutureTask_Unlock(object); + * } + * } + * @endcode + */ +parcObject_ImplementNotifyAll(parcFutureTask, PARCFutureTask); + +/** + * Causes the calling thread to wait until either another thread invokes the parcFutureTask_Notify() function on the same object. + * * + * @param [in] object A pointer to a valid `PARCFutureTask` instance. + * + * Example: + * @code + * { + * + * parcFutureTask_Wait(object); + * } + * @endcode + */ +parcObject_ImplementWait(parcFutureTask, PARCFutureTask); + +/** + * Obtain the lock on the given `PARCFutureTask` instance. + * + * If the lock is already held by another thread, this function will block. + * If the lock is aleady held by the current thread, this function will return `false`. + * + * Implementors must avoid deadlock by attempting to lock the object a second time within the same calling thread. + * + * @param [in] object A pointer to a valid `PARCFutureTask` instance. + * + * @return true The lock was obtained successfully. + * @return false The lock is already held by the current thread, or the `PARCFutureTask` is invalid. + * + * Example: + * @code + * { + * if (parcFutureTask_Lock(object)) { + * + * } + * } + * @endcode + */ +parcObject_ImplementLock(parcFutureTask, PARCFutureTask); + +/** + * Try to obtain the advisory lock on the given PARCFutureTask instance. + * + * Once the lock is obtained, the caller must release the lock as soon as possible. + * + * @param [in] object A pointer to a valid PARCFutureTask instance. + * + * @return true The PARCFutureTask is locked. + * @return false The PARCFutureTask is unlocked. + * + * Example: + * @code + * { + * parcFutureTask_TryLock(object); + * } + * @endcode + */ +parcObject_ImplementTryLock(parcFutureTask, PARCFutureTask); + +/** + * Try to unlock the advisory lock on the given `PARCFutureTask` instance. + * + * @param [in] object A pointer to a valid `PARCFutureTask` instance. + * + * @return true The `PARCFutureTask` was locked and now is unlocked. + * @return false The `PARCFutureTask` was not locked and remains unlocked. + * + * Example: + * @code + * { + * parcFutureTask_Unlock(object); + * } + * @endcode + */ +parcObject_ImplementUnlock(parcFutureTask, PARCFutureTask); + +/** + * Determine if the advisory lock on the given `PARCFutureTask` instance is locked. + * + * @param [in] object A pointer to a valid `PARCFutureTask` instance. + * + * @return true The `PARCFutureTask` is locked. + * @return false The `PARCFutureTask` is unlocked. + * Example: + * @code + * { + * if (parcFutureTask_IsLocked(object)) { + * ... + * } + * } + * @endcode + */ +parcObject_ImplementIsLocked(parcFutureTask, PARCFutureTask); + +/** + * Attempt to cancel the execution of this task. + * + * This will return `false` if the task is already done or has already been cancelled. + * Otherswise, if this task has not started when `parcFutureTask_Cancel` is called, this task will never run. + * + * If the task is already running, the boolean `mayInterruptIfRunning` may cause the task to be interrupted, + * otherwise this function will have no effect. + * + * After this function returns, subsequent calls to `parcFutureTask_IsDone` will always return true. + * Subsequent calls to `parcFutureTask_isCancelled` will always return true if this function returned true. + * + * @param [in] object A pointer to a valid `PARCFutureTask` instance. + * + * @return true The task was cancelled. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcFutureTask_Cancel(PARCFutureTask *task, bool mayInterruptIfRunning); + +/** + * Waits if necessary for at most the given time for the computation to complete, and then retrieves its result, if available. + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCFutureTaskResult parcFutureTask_Get(const PARCFutureTask *futureTask, const PARCTimeout *timeout); + +/** + * Returns true if this task was cancelled before it completed normally. + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcFutureTask_IsCancelled(const PARCFutureTask *futureTask); + +/** + * Returns true if this task completed. + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcFutureTask_IsDone(const PARCFutureTask *futureTask); + +/** + * Sets this Future to the result of its computation unless it has been cancelled. + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return The result returned by the task function. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +void *parcFutureTask_Run(PARCFutureTask *futureTask); + +/** + * Executes the computation without setting its result, and then resets this future to initial state, failing to do so if the computation encounters an exception or is cancelled. + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return true The task was executed. + * @retval false The task was not executed because it was previously completed, or it was cancelled. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcFutureTask_RunAndReset(PARCFutureTask *futureTask); + +/** + * Reset the given PARCFutureTask to the intial state, a subsequent ecutes the computation without setting its result, and then resets this future to initial state, failing to do so if the computation encounters an exception or is cancelled. + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return true The task was successfully run + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +void parcFutureTask_Reset(PARCFutureTask *task); +#endif diff --git a/libparc/parc/concurrent/parc_Lock.c b/libparc/parc/concurrent/parc_Lock.c new file mode 100755 index 00000000..d510b5ed --- /dev/null +++ b/libparc/parc/concurrent/parc_Lock.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <pthread.h> +#include <errno.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/concurrent/parc_Lock.h> + +struct PARCLock { + pthread_mutex_t lock; + pthread_mutexattr_t lockAttributes; + pthread_cond_t notification; + bool locked; + + bool notified; +}; + +static void +_parcLock_Finalize(PARCLock **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCLock pointer."); + + parcLock_OptionalAssertValid(*instancePtr); + + /* cleanup the instance fields here */ +} + +parcObject_ImplementAcquire(parcLock, PARCLock); + +parcObject_ImplementRelease(parcLock, PARCLock); + +parcObject_ExtendPARCObject(PARCLock, _parcLock_Finalize, NULL, parcLock_ToString, NULL, NULL, NULL, NULL); + +void +parcLock_AssertValid(const PARCLock *instance) +{ + assertTrue(parcLock_IsValid(instance), + "PARCLock is not valid."); +} + +PARCLock * +parcLock_Create(void) +{ + PARCLock *result = parcObject_CreateInstance(PARCLock); + + pthread_mutexattr_init(&result->lockAttributes); + pthread_mutexattr_settype(&result->lockAttributes, PTHREAD_MUTEX_ERRORCHECK); + + pthread_mutex_init(&result->lock, &result->lockAttributes); + + result->locked = false; + pthread_cond_init(&result->notification, NULL); + result->notified = false; + return result; +} + +void +parcLock_Display(const PARCLock *lock, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCLock@%p {", lock); + parcDisplayIndented_PrintLine(indentation + 1, ".locked=%s", lock->locked ? "true" : "false"); + + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool +parcLock_IsValid(const PARCLock *instance) +{ + bool result = false; + + if (instance != NULL) { + result = true; + } + + return result; +} + +PARCBufferComposer * +parcLock_BuildString(const PARCLock *lock, PARCBufferComposer *composer) +{ + parcBufferComposer_Format(composer, "lock{.state=%s }", lock->locked ? "true" : "false"); + + return composer; +} + +char * +parcLock_ToString(const PARCLock *lock) +{ + char *result = NULL; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + if (composer != NULL) { + parcLock_BuildString(lock, composer); + result = parcBufferComposer_ToString(composer); + parcBufferComposer_Release(&composer); + } + + return result; +} + +bool +parcLock_Unlock(PARCLock *lock) +{ + bool result = false; + + parcLock_OptionalAssertValid(lock); + + if (lock->locked) { + result = (pthread_mutex_unlock(&lock->lock) == 0); + } + + return result; +} + +bool +parcLock_Lock(PARCLock *lock) +{ + bool result = false; + + parcLock_OptionalAssertValid(lock); + + int error = pthread_mutex_lock(&lock->lock); + + if (error == 0) { + lock->locked = true; + result = true; + errno = 0; + } else { + errno = error; + } + + return result; +} + +bool +parcLock_TryLock(PARCLock *lock) +{ + bool result = false; + + parcLock_OptionalAssertValid(lock); + + result = (pthread_mutex_trylock(&lock->lock) == 0); + if (result) { + lock->locked = true; + } + + return result; +} + +bool +parcLock_IsLocked(const PARCLock *lock) +{ + parcLock_OptionalAssertValid(lock); + + return lock->locked; +} + +void +parcLock_Wait(PARCLock *lock) +{ + parcLock_OptionalAssertValid(lock); + + trapUnexpectedStateIf(lock->locked == false, + "You must Lock the object before calling parcLock_Wait"); + + lock->notified = false; + while (lock->notified == false) { + pthread_cond_wait(&lock->notification, &lock->lock); + } +} + +void +parcLock_Notify(PARCLock *lock) +{ + parcLock_OptionalAssertValid(lock); + + trapUnexpectedStateIf(lock->locked == false, + "You must Lock the object before calling parcLock_Notify"); + + lock->notified = true; + pthread_cond_signal(&lock->notification); +} + diff --git a/libparc/parc/concurrent/parc_Lock.h b/libparc/parc/concurrent/parc_Lock.h new file mode 100755 index 00000000..ecba1c19 --- /dev/null +++ b/libparc/parc/concurrent/parc_Lock.h @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_Lock.h + * @ingroup threading + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef PARCLibrary_parc_Lock +#define PARCLibrary_parc_Lock +#include <stdbool.h> + +#include <parc/algol/parc_HashCode.h> +#include <parc/algol/parc_JSON.h> + +struct PARCLock; +typedef struct PARCLock PARCLock; + +/** + * Increase the number of references to a `PARCLock` instance. + * + * Note that new `PARCLock` is not created, + * only that the given `PARCLock` reference count is incremented. + * Discard the reference by invoking `parcLock_Release`. + * + * @param [in] instance A pointer to a valid PARCLock instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCLock *a = parcLock_Create(); + * + * PARCLock *b = parcLock_Acquire(); + * + * parcLock_Release(&a); + * parcLock_Release(&b); + * } + * @endcode + */ +PARCLock *parcLock_Acquire(const PARCLock *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcLock_OptionalAssertValid(_instance_) +#else +# define parcLock_OptionalAssertValid(_instance_) parcLock_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCLock` instance is valid. + * + * @param [in] instance A pointer to a valid PARCLock instance. + * + * Example: + * @code + * { + * PARCLock *a = parcLock_Create(); + * + * parcLock_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcLock_Release(&b); + * } + * @endcode + */ +void parcLock_AssertValid(const PARCLock *instance); + +/** + * Create an instance of PARCLock + * + * <#Paragraphs Of Explanation#> + * + * @return non-NULL A pointer to a valid PARCLock instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCLock *a = parcLock_Create(); + * + * parcLock_Release(&a); + * } + * @endcode + */ +PARCLock *parcLock_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 PARCLock instance. + * @param [in] other A pointer to a valid PARCLock 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 + * { + * PARCLock *a = parcLock_Create(); + * PARCLock *b = parcLock_Create(); + * + * if (parcLock_Compare(a, b) == 0) { + * printf("Instances are equal.\n"); + * } + * + * parcLock_Release(&a); + * parcLock_Release(&b); + * } + * @endcode + * + * @see parcLock_Equals + */ +int parcLock_Compare(const PARCLock *instance, const PARCLock *other); + +/** + * Print a human readable representation of the given `PARCLock`. + * + * @param [in] instance A pointer to a valid PARCLock instance. + * @param [in] indentation The indentation level to use for printing. + * + * Example: + * @code + * { + * PARCLock *a = parcLock_Create(); + * + * parcLock_Display(a, 0); + * + * parcLock_Release(&a); + * } + * @endcode + */ +void parcLock_Display(const PARCLock *instance, int indentation); + +/** + * Determine if an instance of `PARCLock` 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 PARCLock instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCLock *a = parcLock_Create(); + * + * if (parcLock_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcLock_Release(&a); + * } + * @endcode + * + */ +bool parcLock_IsValid(const PARCLock *instance); + +/** + * Release a previously acquired reference to the given `PARCLock` 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 + * { + * PARCLock *a = parcLock_Create(); + * + * parcLock_Release(&a); + * } + * @endcode + */ +void parcLock_Release(PARCLock **instancePtr); + +/** + * Append a representation of the specified `PARCLock` instance to the given `PARCBufferComposer`. + * + * @param [in] name A pointer to a `PARCLock` instance whose representation should be appended to the @p composer. + * @param [in,out] composer A pointer to a `PARCBufferComposer` instance to be modified. + * + * @return NULL Cannot allocate memory. + * @return non-NULL The @p composer. + * + * Example: + * @code + * { + * PARCBufferComposer *result = parcBufferComposer_Create(); + * + * parcLock_BuildString(instance, result); + * + * char *string = parcBufferComposer_ToString(result); + * printf("Hello: %s\n", string); + * parcMemory_Deallocate(string); + * + * parcBufferComposer_Release(&result); + * } + * @endcode + */ +PARCBufferComposer *parcLock_BuildString(const PARCLock *name, PARCBufferComposer *composer); + +/** + * Produce a null-terminated string representation of the specified `PARCLock`. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to a valid PARCLock instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCLock *a = parcLock_Create(); + * + * char *string = parcLock_ToString(a); + * + * parcLock_Release(&a); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + * + * @see parcLock_Display + */ +char *parcLock_ToString(const PARCLock *instance); + +/** + * Obtain the lock on the given `PARCLock` instance. + * + * If the lock is already held by another thread, this function will block. + * If the lock is aleady held by the current thread, this function will return `false`. + * + * Implementors must avoid deadlock by attempting to lock the object a second time within the same calling thread. + * + * @param [in] lock A pointer to a valid `PARCLock` instance. + * + * @return true The lock was obtained successfully. + * @return false The lock is already held by the current thread, or the `PARCLock` is invalid. + * + * Example: + * @code + * { + * if (parcLock_Lock(lock)) { + * + * } + * } + * @endcode + */ +bool parcLock_Lock(PARCLock *lock); + +/** + * Try to obtain the advisory lock on the given `PARCObject` instance. + * + * Once the lock is obtained, the caller must release the lock via `parcObject_Unlock`. + * + * @param [in] lock A pointer to a valid `PARCObject` instance. + * + * @return true The `PARCObject` is locked. + * @return false The `PARCObject` is unlocked. + * + * Example: + * @code + * { + * while (parcLock_TryLock(object)) + * ; + * } + * @endcode + */ +bool parcLock_TryLock(PARCLock *lock); + +/** + * Try to unlock the advisory lock on the given PARCObject instance. + * + * @param [in] lock A pointer to a valid PARCObject instance. + * + * @return true The PARCObject was locked and now is unlocked. + * @return false The PARCObject was not locked and remains unlocked. + * + * Example: + * @code + * { + * parcLock_Unlock(object); + * } + * @endcode + */ +bool parcLock_Unlock(PARCLock *lock); + +/** + * Determine if the advisory lock on the given PARCObject instance is locked. + * + * @param [in] lock A pointer to a valid PARCObject instance. + * + * @return true The PARCObject is locked. + * @return false The PARCObject is unlocked. + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcLock_IsLocked(const PARCLock *lock); + +/** + * Causes the calling thread to wait until either another thread invokes the parcObject_Notify() function on the same object. + * + * The calling thread must own this object's lock. + * The calling thread will release ownership of this lock and wait until another thread invokes `parcObject_Notify` + * on the same object. The original calling thread then re-obtains ownership of the lock and resumes execution. + * + * This function must only be called by a thread that is the owner of this object's lock. + * + * @param [in] lock A pointer to a valid PARCObject instance. + * + * Example: + * @code + * { + * + * parcObject_Wait(object); + * } + * @endcode + */ +void parcLock_Wait(PARCLock *lock); + +/** + * Wakes up a single thread that is waiting on this object (see `parcObject_Wait)`. + * If any threads are waiting on this object, one of them is chosen to be awakened. + * The choice is arbitrary and occurs at the discretion of the underlying implementation. + * + * The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. + * The awakened thread will compete in the usual manner with any other threads that might be actively + * competing to synchronize on this object; + * for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object. + * + * @param [in] lock A pointer to a valid `PARCObject` instance. + * + * Example: + * @code + * { + * + * parcObject_Notify(object); + * } + * @endcode + */ +void parcLock_Notify(PARCLock *lock); +#endif diff --git a/libparc/parc/concurrent/parc_Notifier.c b/libparc/parc/concurrent/parc_Notifier.c new file mode 100755 index 00000000..6cba9147 --- /dev/null +++ b/libparc/parc/concurrent/parc_Notifier.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +#include <LongBow/runtime.h> + +#include <parc/concurrent/parc_Notifier.h> +#include <parc/algol/parc_Object.h> + +#ifdef __GNUC__ +#define ATOMIC_ADD_AND_FETCH(ptr, increment) __sync_add_and_fetch(ptr, increment) +#define ATOMIC_BOOL_CAS(ptr, oldvalue, newvalue) __sync_bool_compare_and_swap(ptr, oldvalue, newvalue) +#define ATOMIC_FETCH(ptr) ATOMIC_ADD_AND_FETCH(ptr, 0) +#define ATOMIC_SET(ptr, oldvalue, newvalue) ATOMIC_BOOL_CAS(ptr, oldvalue, newvalue) +#else +#error "Only GNUC supported, we need atomic operations" +#endif + +struct parc_notifier { + volatile int paused; + + // If the notifications are paused and there is an event, + // we indicate that we skipped a notify + volatile int skippedNotify; + +#define PARCNotifierWriteFd 1 +#define PARCNotifierReadFd 0 + int fds[2]; +}; + +static void +_parcNotifier_Finalize(PARCNotifier **notifierPtr) +{ + PARCNotifier *notifier = *notifierPtr; + + close(notifier->fds[0]); + close(notifier->fds[1]); +} + +parcObject_ExtendPARCObject(PARCNotifier, _parcNotifier_Finalize, NULL, NULL, NULL, NULL, NULL, NULL); + +static bool +_parcNotifier_MakeNonblocking(PARCNotifier *notifier) +{ + // set the read side to be non-blocking + int flags = fcntl(notifier->fds[PARCNotifierReadFd], F_GETFL, 0); + if (flags == 0) { + if (fcntl(notifier->fds[PARCNotifierReadFd], F_SETFL, flags | O_NONBLOCK) == 0) { + return true; + } + } + perror("fcntl error"); + return false; +} + +PARCNotifier * +parcNotifier_Create(void) +{ + PARCNotifier *notifier = parcObject_CreateInstance(PARCNotifier); + if (notifier) { + notifier->paused = false; + notifier->skippedNotify = false; + + int failure = pipe(notifier->fds); + assertFalse(failure, "Error on pipe: %s", strerror(errno)); + + if (!_parcNotifier_MakeNonblocking(notifier)) { + parcObject_Release((void **) ¬ifier); + } + } + + return notifier; +} + +parcObject_ImplementAcquire(parcNotifier, PARCNotifier); + +parcObject_ImplementRelease(parcNotifier, PARCNotifier); + + +int +parcNotifier_Socket(PARCNotifier *notifier) +{ + return notifier->fds[PARCNotifierReadFd]; +} + +bool +parcNotifier_Notify(PARCNotifier *notifier) +{ + if (ATOMIC_BOOL_CAS(¬ifier->paused, 0, 1)) { + // old value was "0" so we need to send a notification + uint8_t one = 1; + ssize_t written; + do { + written = write(notifier->fds[PARCNotifierWriteFd], &one, 1); + assertTrue(written >= 0, "Error writing to socket %d: %s", notifier->fds[PARCNotifierWriteFd], strerror(errno)); + } while (written == 0); + + return true; + } else { + // we're paused, so count up the pauses + ATOMIC_ADD_AND_FETCH(¬ifier->skippedNotify, 1); + return false; + } +} + +void +parcNotifier_PauseEvents(PARCNotifier *notifier) +{ + // reset the skipped counter so we count from now until the StartEvents call + notifier->skippedNotify = 0; + ATOMIC_BOOL_CAS(¬ifier->paused, 0, 1); + + // now clear out the socket + uint8_t buffer[16]; + while (read(notifier->fds[PARCNotifierReadFd], &buffer, 16) > 0) { + ; + } +} + +void +parcNotifier_StartEvents(PARCNotifier *notifier) +{ + ATOMIC_BOOL_CAS(¬ifier->paused, 1, 0); + if (notifier->skippedNotify) { + // we missed some notifications, so re-signal ourself + parcNotifier_Notify(notifier); + } +} diff --git a/libparc/parc/concurrent/parc_Notifier.h b/libparc/parc/concurrent/parc_Notifier.h new file mode 100755 index 00000000..44eda3b5 --- /dev/null +++ b/libparc/parc/concurrent/parc_Notifier.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_Notifier.h + * @ingroup threading + * @brief Inter-thread/process notification + * + * A 1-way event notification system. The first call to parcNotifier_SetEvent() will post an + * event to the parcNotifier_Socket(). Subsequent calls will not post an event. When the + * event consumer is ready to handle the event, it calls parcNotifier_PauseEvents(), then processes + * the events, then calls parcNotifier_StartEvents(). + * + * @code + * { + * // example code on the event consumer's side + * struct pollfd pfd; + * pfd.fd = parcNotifier_Socket(notifier); + * pfd.events = POLLIN; + * + * while(1) { + * if (poll(&fd, 1, -1)) { + * parcNotifier_PauseEvents(notifier); + * + * // process events, such as reading from a RingBuffer + * void *data; + * while (parcRingBuffer1x1_Get(ring, &data)) { + * // handle data + * } + * + * parcNotifier_StartEvents(notifier); + * } + * } + * } + * @endcode + * + * The notification system guarantees that no notifications will be missed. However, there may be + * extra notifications. For example, in the above code, if an event is signalled between the + * parcNotifier_PauseEvents() and parcRingBuffer1x1_Get() calls, then on parcNotifier_StartEvents() + * an extra event will be triggered, even though the ring buffer is empty. + * + */ + +#ifndef libparc_parc_Notifier_h +#define libparc_parc_Notifier_h + +#include <stdbool.h> + +struct parc_notifier; +typedef struct parc_notifier PARCNotifier; + +/** + * Create a new instance of `PARCNotifier` + * + * @return A new instance of `PARCNotifier` + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCNotifier *parcNotifier_Create(void); + +/** + * Increase the number of references to a `PARCNotifier`. + * + * Note that new `PARCNotifier` is not created, + * only that the given `PARCNotifier` reference count is incremented. + * Discard the reference by invoking `parcNotifier_Release`. + * + * @param [in] instance A pointer to a `PARCNotifier` instance. + * + * @return The input `PARCNotifier` pointer. + * + * Example: + * @code + * { + * PARCNotifier *a = parcNotifier_Create(...); + * + * PARCNotifier *b = parcNotifier_Acquire(a); + * + * parcNotifier_Release(&a); + * parcNotifier_Release(&b); + * } + * @endcode + */ +PARCNotifier *parcNotifier_Acquire(const PARCNotifier *notifier); + +/** + * Release a previously acquired reference to the specified 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, which will be set to NULL. + * + * Example: + * @code + * { + * PARCNotifier *a = parcNotifier_Create(...); + * + * parcNotifier_Release(&a); + * } + * @endcode + */ +void parcNotifier_Release(PARCNotifier **notifier); + +/** + * Fetches the notification socket + * + * The notification socket may be used in select() or poll() or similar + * functions. You should not read or write to the socket. + * + * @param [in] notifier The instance of `PARCNotifier` + * + * @return The notification socket. + * + * Example: + * @code + * <#example#> + * @endcode + */ +int parcNotifier_Socket(PARCNotifier *notifier); + +/** + * Sends a notification to the notifier socket + * + * @param [in] notifier The instance of `PARCNotifier` + * + * @return True is successsful, else false. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcNotifier_Notify(PARCNotifier *notifier); + +/** + * Pause the event stream of the Notifier + * + * @param [in] notifier The instance of `PARCNotifier` + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcNotifier_PauseEvents(PARCNotifier *notifier); + +/** + * Restart the event stream of the Notifier + * + * @param [in] notifier The instance of `PARCNotifier` + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcNotifier_StartEvents(PARCNotifier *notifier); +#endif // libparc_parc_Notifier_h diff --git a/libparc/parc/concurrent/parc_RingBuffer.c b/libparc/parc/concurrent/parc_RingBuffer.c new file mode 100755 index 00000000..9492ae92 --- /dev/null +++ b/libparc/parc/concurrent/parc_RingBuffer.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This is a facade over the 1x1 or NxM ring buffers. + * + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <errno.h> +#include <string.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <LongBow/runtime.h> + +#include <parc/concurrent/parc_RingBuffer.h> + +struct parc_ringbuffer { + PARCRingBufferInterface *interface; +}; + +static void +_parcRingBuffer_Finalize(PARCRingBuffer **pointer) +{ + PARCRingBuffer *buffer = *pointer; + buffer->interface->release(&(buffer->interface)); +} + +parcObject_ExtendPARCObject(PARCRingBuffer, _parcRingBuffer_Finalize, NULL, NULL, NULL, NULL, NULL, NULL); + +PARCRingBuffer * +parcRingBuffer_Create(PARCRingBufferInterface *interface) +{ + PARCRingBuffer *ring = parcObject_CreateInstance(PARCRingBuffer); + ring->interface = interface; + return ring; +} + +parcObject_ImplementAcquire(parcRingBuffer, PARCRingBuffer); + +parcObject_ImplementRelease(parcRingBuffer, PARCRingBuffer); + +bool +parcRingBuffer_Put(PARCRingBuffer *ring, void *data) +{ + return ring->interface->put(ring->interface->instance, data); +} + +bool +parcRingBuffer_Get(PARCRingBuffer *ring, void **outputDataPtr) +{ + return ring->interface->put(ring->interface->instance, outputDataPtr); +} + +uint32_t +parcRingBuffer_Remaining(PARCRingBuffer *ring) +{ + return ring->interface->remaining(ring->interface->instance); +} diff --git a/libparc/parc/concurrent/parc_RingBuffer.h b/libparc/parc/concurrent/parc_RingBuffer.h new file mode 100755 index 00000000..211f044e --- /dev/null +++ b/libparc/parc/concurrent/parc_RingBuffer.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_RingBuffer.h + * @ingroup threading + * @brief A thread-safe ring buffer. + * + * A fixed size, thread safe ring buffer. It can have multiple producers and multiple + * consumers. All exclusion is done inside the ring buffer. + * + * This is a non-blocking data structure. + * + * If the user knows there is only one producer and one consumer, you can create the ring buffer with + * `parcRingBuffer_CreateSingleProducerSingleConsumer`. Such a ring buffer can have at most 2 references. + * + */ + +#ifndef libparc_parc_RingBuffer_h +#define libparc_parc_RingBuffer_h + +#include <stdbool.h> +#include <stdint.h> + +struct parc_ringbuffer; +typedef struct parc_ringbuffer PARCRingBuffer; + +/**< Will be called for each data item on destroy */ +typedef void (RingBufferEntryDestroyer)(void **entryPtr); + +struct parc_ringbuffer_impl; +typedef struct parc_ringbuffer_interface PARCRingBufferInterface; + +struct parc_ringbuffer_interface { + void *instance; + void * (*acquire)(PARCRingBufferInterface *ring); + void (*release)(PARCRingBufferInterface **ring); + bool (*put)(PARCRingBufferInterface *ring, void *data); + bool (*get)(PARCRingBufferInterface *ring, void *outputDataPtr); + bool (*remaining)(PARCRingBufferInterface *ring); +}; + +/** + * Creates a ring buffer of the given size, which must be a power of 2. + * + * The ring buffer can store up to (elements-1) items in the buffer. The buffer can + * be shared between multiple producers and consumers. Each of them should be + * given out from a call to {@link parcRingBuffer_Acquire} to create reference counted + * copies. + * + * The reference count is "1" on return. + * + * @param [in] interface A pointer to the underlying instance and the interface functions for acquire, + * release,put,get,and remaining. + * + * @return non-null An pointer to a new allocated `PARCRingBuffer`. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCRingBuffer *parcRingBuffer_Create(PARCRingBufferInterface *interface); + +/** + * Acquire a new reference to an instance of `PARCRingBuffer`. + * + * A RING WITHOUT LOCKS CAN ONLY HAVE 2 REFERENCES. + * The reference count to the instance is incremented. + * + * @param [in] ring The instance of `PARCRingBuffer` to which to refer. + * + * @return The same value as the input parameter @p ring + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ + +PARCRingBuffer *parcRingBuffer_Acquire(const PARCRingBuffer *ring); + +/** + * Releases a reference. The buffer will be destroyed after the last release. + * + * If the destroyer was specified on create, it will be called on each entry in the buffer + * when the buffer is destroyed. + * + * @param [in,out] ringPtr The pointer to the pointer of the `PARCRingBuffer` to be destroyed. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcRingBuffer_Release(PARCRingBuffer **ringPtr); + +/** + * Non-blocking attempt to put item on ring. May return false if ring is full. + * + * @param [in,out] ring The instance of `PARCRingBuffer` to modify' + * @param [in] data A pointer to teh data to put on the ring. + * + * @return true Data was put on the queue + * @return false Would have blocked, the queue was full + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcRingBuffer_Put(PARCRingBuffer *ring, void *data); + +/** + * Gets the next item off the ring, or returns false if would have blocked. + * + * Non-blocking, gets an item off the ring, or returns false if would block + * + * @param [in] ring The pointer to the `PARCRingBuffer` + * @param [out] outputDataPtr The output pointer + * + * @return true Data returned in the output argument + * @return false Ring is empty, no data returned. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcRingBuffer_Get(PARCRingBuffer *ring, void **outputDataPtr); + +/** + * Returns the remaining capacity of the ring + * + * Returns the remaining capacity of the ring. This does not guarantee the next + * Put will not block, as other producers might consumer the space between calls. + * + * @param [in] ring The pointer to the `PARCRingBuffer` + * + * @return The uint32_t remaining capacity of the ring. + * + * Example: + * @code + * <#example#> + * @endcode + */ +uint32_t parcRingBuffer_Remaining(PARCRingBuffer *ring); +#endif // libparc_parc_RingBuffer_h diff --git a/libparc/parc/concurrent/parc_RingBuffer_1x1.c b/libparc/parc/concurrent/parc_RingBuffer_1x1.c new file mode 100755 index 00000000..a023f4d7 --- /dev/null +++ b/libparc/parc/concurrent/parc_RingBuffer_1x1.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A thread-safe fixed size ring buffer. + * + * A single-producer/single-consumer version is lock-free, along the lines of Lamport, "Proving the + * Correctness of Multiprocess Programs," IEEE Trans on Software Engineering 3(2), Mar 1977, which + * is based on reading/writing native types upto the data bus width being atomic operations. + * + * It can hold (elements-1) data items. elements must be a power of 2. + * + * The writer_head is where the next element should be inserted. The reader_tail is where the next element + * should be read. + * + * All index variables are unbounded uint32_t. This means they just keep counting up. To get the actual + * index in the ring, we mask with (elements-1). For example, a ring with 16 elements will be masked with + * 0x0000000F. We call this the "ring_mask". + * + * Because we never let the writer_head and reader_tail differ by more than (elements-1), this technique of + * masking works just the same as taking the modulus. There's no problems at the uint32_t wraparound either. + * The only math operation we are doing is "+1" which works just fine to wrap a uint32_t. + * + * Let's look at some exampls. I'm going to use a uint16_t so its easier to write the numbers. Let's assume + * that the ring size is 16, so the first ring is (0 - 15). + * head tail + * initialize 0 0 + * put x 3 3 0 + * get x 2 3 2 + * put x 13 16 2 + * put x 1 17 2 + * put x 1 blocks # (0x11 + 1) & 0x0F == tail & 0x0F + * get x 14 17 16 + * get x 1 17 17 # ring is now empty + * ... + * empty 65534 65534 # 0xFFFE 0xFFFE masked = 14 14 + * put x1 65535 65534 # 0xFFFF 0xFFFE masked = 15 14 + * put x1 0 65534 # 0x0000 0xFFFE masked = 0 14 + * ... + * + * The number of remaining available items is (ring_mask + reader_tail - writer_head) & ring_mask. + * head tail remaining + * initialize 0 0 15 + 0 - 0 = 15 + * put x 3 3 0 15 + 0 - 3 = 12 + * get x 2 3 2 + * put x 13 16 2 15 + 2 - 16 = 1 + * put x 1 17 2 15 + 2 - 17 = 0 + * put x 1 blocks + * get x 14 17 16 15 + 16 - 17 = 14 + * get x 1 17 17 15 + 17 - 17 = 15 + * ... + * empty 65534 65534 15 + 65534 - 65534 = 13 - 65534 = 13 - (-2) = 15 + * put x1 65535 65534 15 + 65534 - 65535 = 13 - 65535 = 13 - (-1) = 14 + * put x1 0 65534 15 + 65534 - 0 = 13 - 65535 = 13 - ( 0) = 13 + * ... + * + * If (writer_head + 1) & ring_mask == reader_tail, then the ring is full. + * If writer_head == reader_tail, then the ring is empty. + * + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <LongBow/runtime.h> + +#include <parc/concurrent/parc_RingBuffer_1x1.h> + +#ifdef __GNUC__ + +// on x86 or x86_64, simple assignment will work +#if (__x86_64__ || __i386__) +#define ATOMIC_ADD_AND_FETCH(ptr, increment) __sync_add_and_fetch(ptr, increment) +#define ATOMIC_BOOL_CAS(ptr, oldvalue, newvalue) __sync_bool_compare_and_swap(ptr, oldvalue, newvalue) +#define ATOMIC_FETCH(ptr) *(ptr) +#define ATOMIC_SET(ptr, oldvalue, newvalue) *(ptr) = newvalue +#else +#define ATOMIC_ADD_AND_FETCH(ptr, increment) __sync_add_and_fetch(ptr, increment) +#define ATOMIC_BOOL_CAS(ptr, oldvalue, newvalue) __sync_bool_compare_and_swap(ptr, oldvalue, newvalue) +#define ATOMIC_FETCH(ptr) ATOMIC_ADD_AND_FETCH(ptr, 0) +#define ATOMIC_SET(ptr, oldvalue, newvalue) ATOMIC_BOOL_CAS(ptr, oldvalue, newvalue) +#endif + +#else +#error "Only GNUC supported, we need atomic operations" +#endif + +struct parc_ringbuffer_1x1 { + // LP64 LP32 + volatile uint32_t writer_head; // 0- 3 0 + volatile uint32_t reader_tail; // 4- 7 4 + uint32_t elements; // 8-11 8 + uint32_t ring_mask; // 12-15 12 + + RingBufferEntryDestroyer *destroyer; // 16-23 16 + void **buffer; // 24-31 24 +}; + +static bool +_isPowerOfTwo(uint32_t x) +{ + return ((x != 0) && !(x & (x - 1))); +} + +static void +_destroy(PARCRingBuffer1x1 **ringptr) +{ + PARCRingBuffer1x1 *ring = *ringptr; + + if (ring->destroyer) { + void *ptr = NULL; + while (parcRingBuffer1x1_Get(ring, &ptr)) { + ring->destroyer(&ptr); + } + } + parcMemory_Deallocate((void **) &(ring->buffer)); +} + +parcObject_ExtendPARCObject(PARCRingBuffer1x1, _destroy, NULL, NULL, NULL, NULL, NULL, NULL); + +static PARCRingBuffer1x1 * +_create(uint32_t elements, RingBufferEntryDestroyer *destroyer) +{ + PARCRingBuffer1x1 *ring = parcObject_CreateInstance(PARCRingBuffer1x1); + assertNotNull(ring, "parcObject_Create returned NULL"); + + ring->buffer = parcMemory_AllocateAndClear(sizeof(void *) * elements); + assertNotNull((ring->buffer), "parcMemory_AllocateAndClear() failed to allocate array of %u pointers", elements); + + ring->writer_head = 0; + ring->reader_tail = 0; + ring->elements = elements; + ring->destroyer = destroyer; + ring->ring_mask = elements - 1; + + return ring; +} + +PARCRingBuffer1x1 * +parcRingBuffer1x1_Create(uint32_t elements, RingBufferEntryDestroyer *destroyer) +{ + assertTrue(_isPowerOfTwo(elements), "Parameter elements must be a power of 2, got %u", elements); + return _create(elements, destroyer); +} + + +parcObject_ImplementAcquire(parcRingBuffer1x1, PARCRingBuffer1x1); + +parcObject_ImplementRelease(parcRingBuffer1x1, PARCRingBuffer1x1); + +/** + * Put is protected by the writer mutex. This means that the tail mutex could + * actually increase while this is happening. That's ok. Increasing the tail + * just means there is _more_ room in the ring. We only modify writer_head. + */ +bool +parcRingBuffer1x1_Put(PARCRingBuffer1x1 *ring, void *data) +{ + // Our speculative operation + // The consumer modifies reader_tail, so make sure that's an atomic read. + // only the prodcuer modifies writer_head, so there's only us + + uint32_t writer_head = ring->writer_head; + uint32_t reader_tail = ATOMIC_FETCH(&ring->reader_tail); + + uint32_t writer_next = (writer_head + 1) & ring->ring_mask; + + // ring is full + if (writer_next == reader_tail) { + return false; + } + + assertNull(ring->buffer[writer_head], "Ring index %u is not null!", writer_head); + ring->buffer[writer_head] = data; + + // we're using this just for atomic write to the integer + ATOMIC_SET(&ring->writer_head, writer_head, writer_next); + + return true; +} + +bool +parcRingBuffer1x1_Get(PARCRingBuffer1x1 *ring, void **outputDataPtr) +{ + // do our speculative operation. + // The producer modifies writer_head, so make sure that's an atomic read. + // only the consumer modifies reader_tail, so there's only us + + uint32_t writer_head = ATOMIC_FETCH(&ring->writer_head); // native type assignment is atomic + uint32_t reader_tail = ring->reader_tail; + uint32_t reader_next = (reader_tail + 1) & ring->ring_mask; + + // ring is empty + if (writer_head == reader_tail) { + return false; + } + + // now try to commit it + ATOMIC_SET(&ring->reader_tail, reader_tail, reader_next); + + *outputDataPtr = ring->buffer[reader_tail]; + + // for sanity's sake + ring->buffer[reader_tail] = NULL; + + return true; +} + +uint32_t +parcRingBuffer1x1_Remaining(PARCRingBuffer1x1 *ring) +{ + uint32_t writer_head = ATOMIC_FETCH(&ring->writer_head); + uint32_t reader_tail = ATOMIC_FETCH(&ring->reader_tail); + + return (ring->ring_mask + reader_tail - writer_head) & ring->ring_mask; +} diff --git a/libparc/parc/concurrent/parc_RingBuffer_1x1.h b/libparc/parc/concurrent/parc_RingBuffer_1x1.h new file mode 100755 index 00000000..4fd48e6b --- /dev/null +++ b/libparc/parc/concurrent/parc_RingBuffer_1x1.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_RingBuffer_1x1.h + * @ingroup threading + * @brief A single producer, single consumer ring buffer + * + * This is useful for synchronizing two (and exactly two) threads in one direction. The + * implementation will use a lock-free algorithm. + * + * Complies with the PARCRingBuffer generic facade. + * + */ + +#ifndef libparc_parc_RingBuffer_1x1_h +#define libparc_parc_RingBuffer_1x1_h + +#include <stdbool.h> +#include <stdint.h> + +struct parc_ringbuffer_1x1; +typedef struct parc_ringbuffer_1x1 PARCRingBuffer1x1; + +/**< Will be called for each data item on destroy */ +typedef void (RingBufferEntryDestroyer)(void **entryPtr); + +/** + * Creates a ring buffer of the given size, which must be a power of 2. + * + * The ring buffer can store up to (elements-1) items in the buffer. The buffer can + * be shared between multiple producers and consumers. Each of them should be + * given out from a call to {@link parcRingBuffer_Acquire} to create reference counted + * copies. + * + * The reference count is "1" on return. + * + * @param [in] elements A power of 2, indicating the maximum size of the buffer. + * @param [in] destroyer Will be called for each ring entry when when the ring is destroyed. May be null. + * + * @return non-null An allocated ring buffer. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCRingBuffer1x1 *parcRingBuffer1x1_Create(uint32_t elements, RingBufferEntryDestroyer *destroyer); + +/** + * A reference counted copy of the buffer. + * + * A RING WITHOUT LOCKS CAN ONLY HAVE 2 REFERENCES. + * + * @param [in] ring The instance of `PARCRingBuffer1x1` to acquire. + * + * @return non-null A reference counted copy of the ring buffer. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCRingBuffer1x1 *parcRingBuffer1x1_Acquire(const PARCRingBuffer1x1 *ring); + +/** + * Releases a reference. The buffer will be destroyed after the last release. + * + * If the destroyer was specified on create, it will be called on each entry in the buffer + * when the buffer is destroyed. + * + * @param [in,out] ringPtr The pointer to the pointer of the `PARCRingBuffer1x1` to be released. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcRingBuffer1x1_Release(PARCRingBuffer1x1 **ringPtr); + +/** + * Non-blocking attempt to put item on ring. May return `false` if ring is full. + * + * @param [in,out] ring The instance of `PARCRingBuffer1x1` on which to put the @p data. + * @param [in] data The data to put on the @p ring. + * + * @return `true` Data was put on the queue + * @return `false` Would have blocked, the queue was full + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcRingBuffer1x1_Put(PARCRingBuffer1x1 *ring, void *data); + +/** + * Gets the next item off the ring, or returns false if would have blocked. + * + * Non-blocking, gets an item off the ring, or returns false if would block + * + * @param [in] ring The ring buffer + * @param [out] outputDataPtr The output pointer + * + * @return true Data returned in the output argument + * @return false Ring is empty, no data returned. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcRingBuffer1x1_Get(PARCRingBuffer1x1 *ring, void **outputDataPtr); + +/** + * Returns the remaining capacity of the ring + * + * Returns the remaining capacity of the ring. This does not guarantee the next + * Put will not block, as other producers might consumer the space between calls. + * + * @param [in] ring The instance of `PARCRingBuffer1x1` . + * + * @return The remaining capacity on the ring. + * + * Example: + * @code + * <#example#> + * @endcode + */ +uint32_t parcRingBuffer1x1_Remaining(PARCRingBuffer1x1 *ring); +#endif // libparc_parc_RingBuffer_1x1_h diff --git a/libparc/parc/concurrent/parc_RingBuffer_NxM.c b/libparc/parc/concurrent/parc_RingBuffer_NxM.c new file mode 100755 index 00000000..15ec849b --- /dev/null +++ b/libparc/parc/concurrent/parc_RingBuffer_NxM.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A thread-safe fixed size ring buffer. + * + * The multiple producer, multiple consumer version uses a pthread mutex around a NxM ring buffer. + * + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <errno.h> +#include <string.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <LongBow/runtime.h> + +#include <parc/concurrent/parc_RingBuffer_1x1.h> +#include <parc/concurrent/parc_RingBuffer_NxM.h> + +struct parc_ringbuffer_NxM { + PARCRingBuffer1x1 *onebyone; + + // This protectes the overall data structure for Acquire and Release + pthread_mutex_t allocation_mutex; + + pthread_mutex_t writer_mutex; + pthread_mutex_t reader_mutex; + + RingBufferEntryDestroyer *destroyer; +}; + +/* + * Attemps a lock and returns false if we cannot get it. + * + * @endcode + */ +static bool +_lock(pthread_mutex_t *mutex) +{ + int failure = pthread_mutex_lock(mutex); + assertFalse(failure, "Error locking mutex: (%d) %s\n", errno, strerror(errno)); + return true; +} + +static bool +_unlock(pthread_mutex_t *mutex) +{ + int failure = pthread_mutex_unlock(mutex); + assertFalse(failure, "Error unlocking mutex: (%d) %s\n", errno, strerror(errno)); + return true; +} + +static void +_destroy(PARCRingBufferNxM **ringptr) +{ + PARCRingBufferNxM *ring = *ringptr; + + if (ring->destroyer) { + void *ptr = NULL; + while (parcRingBufferNxM_Get(ring, &ptr)) { + ring->destroyer(&ptr); + } + } + parcRingBuffer1x1_Release(&ring->onebyone); +} + + +parcObject_ExtendPARCObject(PARCRingBufferNxM, _destroy, NULL, NULL, NULL, NULL, NULL, NULL); + +static PARCRingBufferNxM * +_create(uint32_t elements, RingBufferEntryDestroyer *destroyer) +{ + PARCRingBufferNxM *ring = parcObject_CreateInstance(PARCRingBufferNxM); + assertNotNull(ring, "parcObject_Create returned NULL"); + + ring->onebyone = parcRingBuffer1x1_Create(elements, destroyer); + ring->destroyer = destroyer; + pthread_mutex_init(&ring->allocation_mutex, NULL); + pthread_mutex_init(&ring->writer_mutex, NULL); + pthread_mutex_init(&ring->reader_mutex, NULL); + return ring; +} + +PARCRingBufferNxM * +parcRingBufferNxM_Create(uint32_t elements, RingBufferEntryDestroyer *destroyer) +{ + return _create(elements, destroyer); +} + +PARCRingBufferNxM * +parcRingBufferNxM_Acquire(PARCRingBufferNxM *ring) +{ + PARCRingBufferNxM *acquired; + + _lock(&ring->allocation_mutex); + acquired = parcObject_Acquire(ring); + _unlock(&ring->allocation_mutex); + + return acquired; +} + +void +parcRingBufferNxM_Release(PARCRingBufferNxM **ringPtr) +{ + PARCRingBufferNxM *ring = *ringPtr; + _lock(&ring->allocation_mutex); + parcObject_Release((void **) ringPtr); + _unlock(&ring->allocation_mutex); +} + +/** + * Put is protected by the writer mutex. This means that the tail mutex could + * actually increase while this is happening. That's ok. Increasing the tail + * just means there is _more_ room in the ring. We only modify writer_head. + */ +bool +parcRingBufferNxM_Put(PARCRingBufferNxM *ring, void *data) +{ + // **** LOCK + _lock(&ring->writer_mutex); + bool success = parcRingBuffer1x1_Put(ring->onebyone, data); + // **** UNLOCK + _unlock(&ring->writer_mutex); + return success; +} + +bool +parcRingBufferNxM_Get(PARCRingBufferNxM *ring, void **outputDataPtr) +{ + // **** LOCK + _lock(&ring->reader_mutex); + bool success = parcRingBuffer1x1_Get(ring->onebyone, outputDataPtr); + // **** UNLOCK + _unlock(&ring->reader_mutex); + return success; +} + +uint32_t +parcRingBufferNxM_Remaining(PARCRingBufferNxM *ring) +{ + _lock(&ring->writer_mutex); + _lock(&ring->reader_mutex); + + uint32_t remaining = parcRingBuffer1x1_Remaining(ring->onebyone); + + _unlock(&ring->reader_mutex); + _unlock(&ring->writer_mutex); + + return remaining; +} diff --git a/libparc/parc/concurrent/parc_RingBuffer_NxM.h b/libparc/parc/concurrent/parc_RingBuffer_NxM.h new file mode 100755 index 00000000..8cf38fdc --- /dev/null +++ b/libparc/parc/concurrent/parc_RingBuffer_NxM.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_RingBuffer_NxM.h + * @ingroup threading + * @brief A multiple producer, multiple consumer ring buffer + * + * This is useful for synchronizing one or more producers with one or more consumers. + * The implementation may use locks. + * + * Complies with the PARCRingBuffer generic facade. + * + */ + +#ifndef libparc_parc_RingBuffer_NxM_h +#define libparc_parc_RingBuffer_NxM_h + +#include <stdbool.h> +#include <stdint.h> +#include <parc/concurrent/parc_RingBuffer_1x1.h> + +struct parc_ringbuffer_NxM; +/** + * @typedef PARCRingBufferNxM + */ +typedef struct parc_ringbuffer_NxM PARCRingBufferNxM; + +/** + * Creates a ring buffer of the given size, which must be a power of 2. + * + * The ring buffer can store up to (elements-1) items in the buffer. The buffer can + * be shared between multiple producers and consumers. Each of them should be + * given out from a call to {@link parcRingBuffer_Acquire} to create reference counted + * copies. + * + * The reference count is "1" on return. + * + * @param [in] elements A power of 2, indicating the maximum size of the buffer. + * @param [in] destroyer Will be called for each ring entry when when the ring is destroyed. May be null. + * + * @return non-null An allocated ring buffer. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCRingBufferNxM *parcRingBufferNxM_Create(uint32_t elements, RingBufferEntryDestroyer *destroyer); + +/** + * A reference counted copy of the buffer. + * + * A RING WITHOUT LOCKS CAN ONLY HAVE 2 REFERENCES. + * + * @param [in] ring A pointer to the `PARCRingBufferNxM` to be acquired. + * + * @return non-null A reference counted copy of the ring buffer + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCRingBufferNxM *parcRingBufferNxM_Acquire(PARCRingBufferNxM *ring); + +/** + * Releases a reference. The buffer will be destroyed after the last release. + * + * If the destroyer was specified on create, it will be called on each entry in the buffer + * when the buffer is destroyed. + * + * @param [in,out] ringPtr A pointer to the pointer to the `PARCRingBufferNxM` to be released. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void parcRingBufferNxM_Release(PARCRingBufferNxM **ringPtr); + +/** + * Non-blocking attempt to put item on ring. May return false if ring is full. + * + * <#Paragraphs Of Explanation#> + * + * @param [in,out] ring A pointer to the `PARCRingBufferNxM` on which to put @p data. + * @param [in] data A pointer to data to put on @p ring. + * + * @return `true` Data was put on the queue + * @return `false` Would have blocked, the queue was full + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcRingBufferNxM_Put(PARCRingBufferNxM *ring, void *data); + +/** + * Gets the next item off the ring, or returns false if would have blocked. + * + * Non-blocking, gets an item off the ring, or returns false if would block + * + * @param [in] ring The ring buffer + * @param [out] outputDataPtr The output pointer + * + * @return `true` Data returned in the output argument + * @return `false` Ring is empty, no data returned. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool parcRingBufferNxM_Get(PARCRingBufferNxM *ring, void **outputDataPtr); + +/** + * Returns the remaining capacity of the ring + * + * Returns the remaining capacity of the ring. This does not guarantee the next + * Put will not block, as other producers might consumer the space between calls. + * + * @param [in] ring The ring buffer + * + * @return the remaining capacity of @p ring. + * + * Example: + * @code + * <#example#> + * @endcode + */ +uint32_t parcRingBufferNxM_Remaining(PARCRingBufferNxM *ring); +#endif // libparc_parc_RingBuffer_NxM_h diff --git a/libparc/parc/concurrent/parc_ScheduledTask.c b/libparc/parc/concurrent/parc_ScheduledTask.c new file mode 100755 index 00000000..14c1d556 --- /dev/null +++ b/libparc/parc/concurrent/parc_ScheduledTask.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + */ +#include <config.h> +#include <sys/time.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Time.h> + +#include <parc/concurrent/parc_ScheduledTask.h> +#include <parc/concurrent/parc_FutureTask.h> + +struct PARCScheduledTask { + PARCFutureTask *task; + uint64_t executionTime; +}; + +static bool +_parcScheduledTask_Destructor(PARCScheduledTask **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCScheduledTask pointer."); + PARCScheduledTask *task = *instancePtr; + + parcFutureTask_Release(&task->task); + return true; +} + +parcObject_ImplementAcquire(parcScheduledTask, PARCScheduledTask); + +parcObject_ImplementRelease(parcScheduledTask, PARCScheduledTask); + +parcObject_Override(PARCScheduledTask, PARCObject, + .isLockable = true, + .destructor = (PARCObjectDestructor *) _parcScheduledTask_Destructor, + .copy = (PARCObjectCopy *) parcScheduledTask_Copy, + .toString = (PARCObjectToString *) parcScheduledTask_ToString, + .equals = (PARCObjectEquals *) parcScheduledTask_Equals, + .compare = (PARCObjectCompare *) parcScheduledTask_Compare, + .hashCode = (PARCObjectHashCode *) parcScheduledTask_HashCode, + .display = (PARCObjectDisplay *) parcScheduledTask_Display); + +void +parcScheduledTask_AssertValid(const PARCScheduledTask *instance) +{ + assertTrue(parcScheduledTask_IsValid(instance), + "PARCScheduledTask is not valid."); +} + + +PARCScheduledTask * +parcScheduledTask_Create(PARCFutureTask *task, uint64_t executionTime) +{ + PARCScheduledTask *result = parcObject_CreateInstance(PARCScheduledTask); + + if (result != NULL) { + result->task = parcFutureTask_Acquire(task); + result->executionTime = executionTime; + } + + return result; +} + +int +parcScheduledTask_Compare(const PARCScheduledTask *instance, const PARCScheduledTask *other) +{ + int result = 0; + + return result; +} + +PARCScheduledTask * +parcScheduledTask_Copy(const PARCScheduledTask *original) +{ + PARCScheduledTask *result = parcScheduledTask_Create(original->task, original->executionTime); + + return result; +} + +void +parcScheduledTask_Display(const PARCScheduledTask *instance, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCScheduledTask@%p {", instance); + /* Call Display() functions for the fields here. */ + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool +parcScheduledTask_Equals(const PARCScheduledTask *x, const PARCScheduledTask *y) +{ + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + if (parcFutureTask_Equals(x->task, y->task)) { + if (x->executionTime == y->executionTime) { + result = true; + } + } + } + + return result; +} + +PARCHashCode +parcScheduledTask_HashCode(const PARCScheduledTask *instance) +{ + PARCHashCode result = 0; + + return result; +} + +bool +parcScheduledTask_IsValid(const PARCScheduledTask *instance) +{ + bool result = false; + + if (instance != NULL) { + result = true; + } + + return result; +} + +PARCJSON * +parcScheduledTask_ToJSON(const PARCScheduledTask *instance) +{ + PARCJSON *result = parcJSON_Create(); + + if (result != NULL) { + } + + return result; +} + +char * +parcScheduledTask_ToString(const PARCScheduledTask *instance) +{ + char *result = parcMemory_Format("PARCScheduledTask@%p\n", instance); + + return result; +} + +uint64_t +parcScheduledTask_GetExecutionTime(const PARCScheduledTask *task) +{ + return task->executionTime; +} + +bool +parcScheduledTask_Cancel(PARCScheduledTask *task, bool mayInterruptIfRunning) +{ + return parcFutureTask_Cancel(task->task, mayInterruptIfRunning); +} + +PARCFutureTaskResult +parcScheduledTask_Get(const PARCScheduledTask *task, const PARCTimeout *timeout) +{ + return parcFutureTask_Get(task->task, timeout); +} + +PARCFutureTask * +parcScheduledTask_GetTask(const PARCScheduledTask *task) +{ + return task->task; +} + +void * +parcScheduledTask_Run(const PARCScheduledTask *task) +{ + return parcFutureTask_Run(task->task); +} + +bool +parcScheduledTask_IsCancelled(const PARCScheduledTask *task) +{ + return parcFutureTask_IsCancelled(task->task); +} + +bool +parcScheduledTask_IsDone(const PARCScheduledTask *task) +{ + return parcFutureTask_IsDone(task->task); +} diff --git a/libparc/parc/concurrent/parc_ScheduledTask.h b/libparc/parc/concurrent/parc_ScheduledTask.h new file mode 100755 index 00000000..e56fb5f5 --- /dev/null +++ b/libparc/parc/concurrent/parc_ScheduledTask.h @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * @file parc_ScheduledTask.h + * @ingroup threading + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef PARCLibrary_parc_ScheduledTask +#define PARCLibrary_parc_ScheduledTask +#include <stdbool.h> +#include <stdint.h> + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_HashCode.h> +#include <parc/concurrent/parc_FutureTask.h> +#include <parc/concurrent/parc_Timeout.h> + +struct PARCScheduledTask; +typedef struct PARCScheduledTask PARCScheduledTask; + +/** + * Increase the number of references to a `PARCScheduledTask` instance. + * + * Note that new `PARCScheduledTask` is not created, + * only that the given `PARCScheduledTask` reference count is incremented. + * Discard the reference by invoking `parcScheduledTask_Release`. + * + * @param [in] instance A pointer to a valid PARCScheduledTask instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCScheduledTask *a = parcScheduledTask_Create(); + * + * PARCScheduledTask *b = parcScheduledTask_Acquire(); + * + * parcScheduledTask_Release(&a); + * parcScheduledTask_Release(&b); + * } + * @endcode + */ +PARCScheduledTask *parcScheduledTask_Acquire(const PARCScheduledTask *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcScheduledTask_OptionalAssertValid(_instance_) +#else +# define parcScheduledTask_OptionalAssertValid(_instance_) parcScheduledTask_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCScheduledTask` instance is valid. + * + * @param [in] instance A pointer to a valid PARCScheduledTask instance. + * + * Example: + * @code + * { + * PARCScheduledTask *a = parcScheduledTask_Create(); + * + * parcScheduledTask_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcScheduledTask_Release(&b); + * } + * @endcode + */ +void parcScheduledTask_AssertValid(const PARCScheduledTask *instance); + +/** + * Create an instance of PARCScheduledTask + * + * <#Paragraphs Of Explanation#> + * + * @return non-NULL A pointer to a valid PARCScheduledTask instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCScheduledTask *a = parcScheduledTask_Create(); + * + * parcScheduledTask_Release(&a); + * } + * @endcode + */ +PARCScheduledTask *parcScheduledTask_Create(PARCFutureTask *task, uint64_t executionTime); + +/** + * 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 PARCScheduledTask instance. + * @param [in] other A pointer to a valid PARCScheduledTask 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 + * { + * PARCScheduledTask *a = parcScheduledTask_Create(); + * PARCScheduledTask *b = parcScheduledTask_Create(); + * + * if (parcScheduledTask_Compare(a, b) == 0) { + * printf("Instances are equal.\n"); + * } + * + * parcScheduledTask_Release(&a); + * parcScheduledTask_Release(&b); + * } + * @endcode + * + * @see parcScheduledTask_Equals + */ +int parcScheduledTask_Compare(const PARCScheduledTask *instance, const PARCScheduledTask *other); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A pointer to a valid PARCScheduledTask instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCScheduledTask` instance. + * + * Example: + * @code + * { + * PARCScheduledTask *a = parcScheduledTask_Create(); + * + * PARCScheduledTask *copy = parcScheduledTask_Copy(&b); + * + * parcScheduledTask_Release(&b); + * parcScheduledTask_Release(©); + * } + * @endcode + */ +PARCScheduledTask *parcScheduledTask_Copy(const PARCScheduledTask *original); + +/** + * Print a human readable representation of the given `PARCScheduledTask`. + * + * @param [in] instance A pointer to a valid PARCScheduledTask instance. + * @param [in] indentation The indentation level to use for printing. + * + * Example: + * @code + * { + * PARCScheduledTask *a = parcScheduledTask_Create(); + * + * parcScheduledTask_Display(a, 0); + * + * parcScheduledTask_Release(&a); + * } + * @endcode + */ +void parcScheduledTask_Display(const PARCScheduledTask *instance, int indentation); + +/** + * Determine if two `PARCScheduledTask` instances are equal. + * + * The following equivalence relations on non-null `PARCScheduledTask` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcScheduledTask_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcScheduledTask_Equals(x, y)` must return true if and only if + * `parcScheduledTask_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcScheduledTask_Equals(x, y)` returns true and + * `parcScheduledTask_Equals(y, z)` returns true, + * then `parcScheduledTask_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcScheduledTask_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcScheduledTask_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCScheduledTask instance. + * @param [in] y A pointer to a valid PARCScheduledTask instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCScheduledTask *a = parcScheduledTask_Create(); + * PARCScheduledTask *b = parcScheduledTask_Create(); + * + * if (parcScheduledTask_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcScheduledTask_Release(&a); + * parcScheduledTask_Release(&b); + * } + * @endcode + * @see parcScheduledTask_HashCode + */ +bool parcScheduledTask_Equals(const PARCScheduledTask *x, const PARCScheduledTask *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcScheduledTask_Equals} method, + * then calling the {@link parcScheduledTask_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcScheduledTask_Equals} function, + * then calling the `parcScheduledTask_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCScheduledTask instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCScheduledTask *a = parcScheduledTask_Create(); + * + * PARCHashCode hashValue = parcScheduledTask_HashCode(buffer); + * parcScheduledTask_Release(&a); + * } + * @endcode + */ +PARCHashCode parcScheduledTask_HashCode(const PARCScheduledTask *instance); + +/** + * Determine if an instance of `PARCScheduledTask` 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 PARCScheduledTask instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCScheduledTask *a = parcScheduledTask_Create(); + * + * if (parcScheduledTask_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcScheduledTask_Release(&a); + * } + * @endcode + * + */ +bool parcScheduledTask_IsValid(const PARCScheduledTask *instance); + +/** + * Release a previously acquired reference to the given `PARCScheduledTask` 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 + * { + * PARCScheduledTask *a = parcScheduledTask_Create(); + * + * parcScheduledTask_Release(&a); + * } + * @endcode + */ +void parcScheduledTask_Release(PARCScheduledTask **instancePtr); + +/** + * Create a `PARCJSON` instance (representation) of the given object. + * + * @param [in] instance A pointer to a valid PARCScheduledTask instance. + * + * @return NULL Memory could not be allocated to contain the `PARCJSON` instance. + * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate(). + * + * Example: + * @code + * { + * PARCScheduledTask *a = parcScheduledTask_Create(); + * + * PARCJSON *json = parcScheduledTask_ToJSON(a); + * + * printf("JSON representation: %s\n", parcJSON_ToString(json)); + * parcJSON_Release(&json); + * + * parcScheduledTask_Release(&a); + * } + * @endcode + */ +PARCJSON *parcScheduledTask_ToJSON(const PARCScheduledTask *instance); + +/** + * Produce a null-terminated string representation of the specified `PARCScheduledTask`. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to a valid PARCScheduledTask instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCScheduledTask *a = parcScheduledTask_Create(); + * + * char *string = parcScheduledTask_ToString(a); + * + * parcScheduledTask_Release(&a); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + * + * @see parcScheduledTask_Display + */ +char *parcScheduledTask_ToString(const PARCScheduledTask *instance); + +/** + * Returns the remaining delay associated with this object. + * + * @param [in] task A pointer to a valid PARCScheduledTask instance. + * + * @return the remaining delay; zero or negative values indicate that the delay has already elapsed + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +uint64_t parcScheduledTask_GetExecutionTime(const PARCScheduledTask *task); + +/** + * Attempts to cancel execution of this task. + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcScheduledTask_Cancel(PARCScheduledTask *task, bool mayInterruptIfRunning); + +/** + * Waits if necessary for at most the given time for the computation to complete, and then retrieves its result, if available. + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCFutureTaskResult parcScheduledTask_Get(const PARCScheduledTask *task, const PARCTimeout *timeout); + + +void *parcScheduledTask_Run(const PARCScheduledTask *task); + + +/** + * Get the `PARCFutureTask` instance for the given `PARCScheduledTask` + * + * @param [in] task A pointer to a valid `PARCScheduledTask` instance. + * + * @return the `PARCFutureTask` instance for the given `PARCScheduledTask` + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCFutureTask *parcScheduledTask_GetTask(const PARCScheduledTask *task); + +/** + * Returns true if this task was cancelled before it completed normally. + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcScheduledTask_IsCancelled(const PARCScheduledTask *task); + +/** + * Returns true if this task completed. + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcScheduledTask_IsDone(const PARCScheduledTask *task); +#endif diff --git a/libparc/parc/concurrent/parc_ScheduledThreadPool.c b/libparc/parc/concurrent/parc_ScheduledThreadPool.c new file mode 100644 index 00000000..3d6c3e57 --- /dev/null +++ b/libparc/parc/concurrent/parc_ScheduledThreadPool.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SortedList.h> +#include <parc/algol/parc_LinkedList.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Time.h> + +#include <parc/concurrent/parc_ScheduledThreadPool.h> +#include <parc/concurrent/parc_Thread.h> +#include <parc/concurrent/parc_ThreadPool.h> + +struct PARCScheduledThreadPool { + bool continueExistingPeriodicTasksAfterShutdown; + bool executeExistingDelayedTasksAfterShutdown; + bool removeOnCancel; + PARCSortedList *workQueue; + PARCThread *workerThread; + PARCThreadPool *threadPool; + int poolSize; +}; + +static void * +_workerThread(PARCThread *thread, PARCScheduledThreadPool *pool) +{ + while (parcThread_IsCancelled(thread) == false) { + if (parcSortedList_Lock(pool->workQueue)) { + if (parcSortedList_Size(pool->workQueue) > 0) { + PARCScheduledTask *task = parcSortedList_GetFirst(pool->workQueue); + int64_t executionDelay = parcScheduledTask_GetExecutionTime(task) - parcTime_NowNanoseconds(); + if (task != NULL && executionDelay <= 0) { + parcSortedList_RemoveFirst(pool->workQueue); + parcSortedList_Unlock(pool->workQueue); + parcThreadPool_Execute(pool->threadPool, parcScheduledTask_GetTask(task)); + parcScheduledTask_Release(&task); + parcSortedList_Lock(pool->workQueue); + + parcSortedList_Notify(pool->workQueue); + } else { + parcSortedList_WaitFor(pool->workQueue, executionDelay); + } + } else { + parcSortedList_Wait(pool->workQueue); + } + } + parcSortedList_Unlock(pool->workQueue); + } + + return NULL; +} + +static bool +_parcScheduledThreadPool_Destructor(PARCScheduledThreadPool **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCScheduledThreadPool pointer."); + PARCScheduledThreadPool *pool = *instancePtr; + parcThreadPool_Release(&pool->threadPool); + + parcThread_Release(&pool->workerThread); + + if (parcObject_Lock(pool->workQueue)) { + parcSortedList_Release(&pool->workQueue); + } else { + assertTrue(false, "Cannot lock the work queue."); + } + + return true; +} + +parcObject_ImplementAcquire(parcScheduledThreadPool, PARCScheduledThreadPool); + +parcObject_ImplementRelease(parcScheduledThreadPool, PARCScheduledThreadPool); + +parcObject_Override(PARCScheduledThreadPool, PARCObject, + .isLockable = true, + .destructor = (PARCObjectDestructor *) _parcScheduledThreadPool_Destructor, + .copy = (PARCObjectCopy *) parcScheduledThreadPool_Copy, + .toString = (PARCObjectToString *) parcScheduledThreadPool_ToString, + .equals = (PARCObjectEquals *) parcScheduledThreadPool_Equals, + .compare = (PARCObjectCompare *) parcScheduledThreadPool_Compare, + .hashCode = (PARCObjectHashCode *) parcScheduledThreadPool_HashCode); + +void +parcScheduledThreadPool_AssertValid(const PARCScheduledThreadPool *instance) +{ + assertTrue(parcScheduledThreadPool_IsValid(instance), + "PARCScheduledThreadPool is not valid."); +} + +PARCScheduledThreadPool * +parcScheduledThreadPool_Create(int poolSize) +{ + PARCScheduledThreadPool *result = parcObject_CreateInstance(PARCScheduledThreadPool); + + if (result != NULL) { + result->poolSize = poolSize; + result->workQueue = parcSortedList_Create(); + result->threadPool = parcThreadPool_Create(poolSize); + + result->continueExistingPeriodicTasksAfterShutdown = false; + result->executeExistingDelayedTasksAfterShutdown = false; + result->removeOnCancel = true; + + if (parcObject_Lock(result)) { + result->workerThread = parcThread_Create((void *(*)(PARCThread *, PARCObject *))_workerThread, (PARCObject *) result); + parcThread_Start(result->workerThread); + parcObject_Unlock(result); + } + } + + return result; +} + +int +parcScheduledThreadPool_Compare(const PARCScheduledThreadPool *instance, const PARCScheduledThreadPool *other) +{ + int result = 0; + + return result; +} + +PARCScheduledThreadPool * +parcScheduledThreadPool_Copy(const PARCScheduledThreadPool *original) +{ + PARCScheduledThreadPool *result = parcScheduledThreadPool_Create(original->poolSize); + + return result; +} + +void +parcScheduledThreadPool_Display(const PARCScheduledThreadPool *instance, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCScheduledThreadPool@%p {", instance); + /* Call Display() functions for the fields here. */ + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool +parcScheduledThreadPool_Equals(const PARCScheduledThreadPool *x, const PARCScheduledThreadPool *y) +{ + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + if (x->poolSize == y->poolSize) { + result = true; + } + } + + return result; +} + +PARCHashCode +parcScheduledThreadPool_HashCode(const PARCScheduledThreadPool *instance) +{ + PARCHashCode result = 0; + + return result; +} + +bool +parcScheduledThreadPool_IsValid(const PARCScheduledThreadPool *instance) +{ + bool result = false; + + if (instance != NULL) { + result = true; + } + + return result; +} + +PARCJSON * +parcScheduledThreadPool_ToJSON(const PARCScheduledThreadPool *instance) +{ + PARCJSON *result = parcJSON_Create(); + + if (result != NULL) { + } + + return result; +} + +char * +parcScheduledThreadPool_ToString(const PARCScheduledThreadPool *instance) +{ + char *result = parcMemory_Format("PARCScheduledThreadPool@%p\n", instance); + + return result; +} + +void +parcScheduledThreadPool_Execute(PARCScheduledThreadPool *pool, PARCFutureTask *command) +{ +} + +bool +parcScheduledThreadPool_GetContinueExistingPeriodicTasksAfterShutdownPolicy(PARCScheduledThreadPool *pool) +{ + return pool->continueExistingPeriodicTasksAfterShutdown; +} + +bool +parcScheduledThreadPool_GetExecuteExistingDelayedTasksAfterShutdownPolicy(PARCScheduledThreadPool *pool) +{ + return pool->executeExistingDelayedTasksAfterShutdown; +} + +PARCSortedList * +parcScheduledThreadPool_GetQueue(const PARCScheduledThreadPool *pool) +{ + return pool->workQueue; +} + +bool +parcScheduledThreadPool_GetRemoveOnCancelPolicy(const PARCScheduledThreadPool *pool) +{ + return pool->removeOnCancel; +} + +PARCScheduledTask * +parcScheduledThreadPool_Schedule(PARCScheduledThreadPool *pool, PARCFutureTask *task, const PARCTimeout *delay) +{ + uint64_t executionTime = parcTime_NowNanoseconds() + parcTimeout_InNanoSeconds(delay); + + PARCScheduledTask *scheduledTask = parcScheduledTask_Create(task, executionTime); + + if (parcSortedList_Lock(pool->workQueue)) { + parcSortedList_Add(pool->workQueue, scheduledTask); + parcScheduledTask_Release(&scheduledTask); + parcSortedList_Notify(pool->workQueue); + parcSortedList_Unlock(pool->workQueue); + } + return scheduledTask; +} + +PARCScheduledTask * +parcScheduledThreadPool_ScheduleAtFixedRate(PARCScheduledThreadPool *pool, PARCFutureTask *task, PARCTimeout initialDelay, PARCTimeout period) +{ + return NULL; +} + +PARCScheduledTask * +parcScheduledThreadPool_ScheduleWithFixedDelay(PARCScheduledThreadPool *pool, PARCFutureTask *task, PARCTimeout initialDelay, PARCTimeout delay) +{ + return NULL; +} + +void +parcScheduledThreadPool_SetContinueExistingPeriodicTasksAfterShutdownPolicy(PARCScheduledThreadPool *pool, bool value) +{ +} + +void +parcScheduledThreadPool_SetExecuteExistingDelayedTasksAfterShutdownPolicy(PARCScheduledThreadPool *pool, bool value) +{ +} + +void +parcScheduledThreadPool_SetRemoveOnCancelPolicy(PARCScheduledThreadPool *pool, bool value) +{ +} + +void +parcScheduledThreadPool_Shutdown(PARCScheduledThreadPool *pool) +{ + parcScheduledThreadPool_ShutdownNow(pool); +} + +PARCList * +parcScheduledThreadPool_ShutdownNow(PARCScheduledThreadPool *pool) +{ + parcThread_Cancel(pool->workerThread); + + parcThreadPool_ShutdownNow(pool->threadPool); + + // Wake them all up so they detect that they are cancelled. + if (parcObject_Lock(pool)) { + parcObject_NotifyAll(pool); + parcObject_Unlock(pool); + } + if (parcObject_Lock(pool->workQueue)) { + parcObject_NotifyAll(pool->workQueue); + parcObject_Unlock(pool->workQueue); + } + + parcThread_Join(pool->workerThread); + + return NULL; +} + +PARCScheduledTask * +parcScheduledThreadPool_Submit(PARCScheduledThreadPool *pool, PARCFutureTask *task) +{ + PARCScheduledTask *scheduledTask = parcScheduledTask_Create(task, 0); + + parcSortedList_Add(pool->workQueue, scheduledTask); + + return scheduledTask; +} diff --git a/libparc/parc/concurrent/parc_ScheduledThreadPool.h b/libparc/parc/concurrent/parc_ScheduledThreadPool.h new file mode 100755 index 00000000..36c26909 --- /dev/null +++ b/libparc/parc/concurrent/parc_ScheduledThreadPool.h @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_ScheduledThreadPool.h + * @ingroup threading + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef PARCLibrary_parc_ScheduledThreadPool +#define PARCLibrary_parc_ScheduledThreadPool +#include <stdbool.h> + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_HashCode.h> +#include <parc/algol/parc_SortedList.h> +#include <parc/concurrent/parc_FutureTask.h> +#include <parc/concurrent/parc_ScheduledTask.h> +#include <parc/concurrent/parc_Timeout.h> + +struct PARCScheduledThreadPool; +typedef struct PARCScheduledThreadPool PARCScheduledThreadPool; + +/** + * Increase the number of references to a `PARCScheduledThreadPool` instance. + * + * Note that new `PARCScheduledThreadPool` is not created, + * only that the given `PARCScheduledThreadPool` reference count is incremented. + * Discard the reference by invoking `parcScheduledThreadPool_Release`. + * + * @param [in] instance A pointer to a valid PARCScheduledThreadPool instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCScheduledThreadPool *a = parcScheduledThreadPool_Create(); + * + * PARCScheduledThreadPool *b = parcScheduledThreadPool_Acquire(); + * + * parcScheduledThreadPool_Release(&a); + * parcScheduledThreadPool_Release(&b); + * } + * @endcode + */ +PARCScheduledThreadPool *parcScheduledThreadPool_Acquire(const PARCScheduledThreadPool *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcScheduledThreadPool_OptionalAssertValid(_instance_) +#else +# define parcScheduledThreadPool_OptionalAssertValid(_instance_) parcScheduledThreadPool_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCScheduledThreadPool` instance is valid. + * + * @param [in] instance A pointer to a valid PARCScheduledThreadPool instance. + * + * Example: + * @code + * { + * PARCScheduledThreadPool *a = parcScheduledThreadPool_Create(); + * + * parcScheduledThreadPool_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcScheduledThreadPool_Release(&b); + * } + * @endcode + */ +void parcScheduledThreadPool_AssertValid(const PARCScheduledThreadPool *instance); + +/** + * Create an instance of PARCScheduledThreadPool + * + * <#Paragraphs Of Explanation#> + * + * @return non-NULL A pointer to a valid PARCScheduledThreadPool instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCScheduledThreadPool *a = parcScheduledThreadPool_Create(); + * + * parcScheduledThreadPool_Release(&a); + * } + * @endcode + */ +PARCScheduledThreadPool *parcScheduledThreadPool_Create(int poolSize); + +/** + * 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 PARCScheduledThreadPool instance. + * @param [in] other A pointer to a valid PARCScheduledThreadPool 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 + * { + * PARCScheduledThreadPool *a = parcScheduledThreadPool_Create(); + * PARCScheduledThreadPool *b = parcScheduledThreadPool_Create(); + * + * if (parcScheduledThreadPool_Compare(a, b) == 0) { + * printf("Instances are equal.\n"); + * } + * + * parcScheduledThreadPool_Release(&a); + * parcScheduledThreadPool_Release(&b); + * } + * @endcode + * + * @see parcScheduledThreadPool_Equals + */ +int parcScheduledThreadPool_Compare(const PARCScheduledThreadPool *instance, const PARCScheduledThreadPool *other); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A pointer to a valid PARCScheduledThreadPool instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCScheduledThreadPool` instance. + * + * Example: + * @code + * { + * PARCScheduledThreadPool *a = parcScheduledThreadPool_Create(); + * + * PARCScheduledThreadPool *copy = parcScheduledThreadPool_Copy(&b); + * + * parcScheduledThreadPool_Release(&b); + * parcScheduledThreadPool_Release(©); + * } + * @endcode + */ +PARCScheduledThreadPool *parcScheduledThreadPool_Copy(const PARCScheduledThreadPool *original); + +/** + * Print a human readable representation of the given `PARCScheduledThreadPool`. + * + * @param [in] instance A pointer to a valid PARCScheduledThreadPool instance. + * @param [in] indentation The indentation level to use for printing. + * + * Example: + * @code + * { + * PARCScheduledThreadPool *a = parcScheduledThreadPool_Create(); + * + * parcScheduledThreadPool_Display(a, 0); + * + * parcScheduledThreadPool_Release(&a); + * } + * @endcode + */ +void parcScheduledThreadPool_Display(const PARCScheduledThreadPool *instance, int indentation); + +/** + * Determine if two `PARCScheduledThreadPool` instances are equal. + * + * The following equivalence relations on non-null `PARCScheduledThreadPool` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcScheduledThreadPool_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcScheduledThreadPool_Equals(x, y)` must return true if and only if + * `parcScheduledThreadPool_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcScheduledThreadPool_Equals(x, y)` returns true and + * `parcScheduledThreadPool_Equals(y, z)` returns true, + * then `parcScheduledThreadPool_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcScheduledThreadPool_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcScheduledThreadPool_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCScheduledThreadPool instance. + * @param [in] y A pointer to a valid PARCScheduledThreadPool instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCScheduledThreadPool *a = parcScheduledThreadPool_Create(); + * PARCScheduledThreadPool *b = parcScheduledThreadPool_Create(); + * + * if (parcScheduledThreadPool_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcScheduledThreadPool_Release(&a); + * parcScheduledThreadPool_Release(&b); + * } + * @endcode + * @see parcScheduledThreadPool_HashCode + */ +bool parcScheduledThreadPool_Equals(const PARCScheduledThreadPool *x, const PARCScheduledThreadPool *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcScheduledThreadPool_Equals} method, + * then calling the {@link parcScheduledThreadPool_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcScheduledThreadPool_Equals} function, + * then calling the `parcScheduledThreadPool_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCScheduledThreadPool instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCScheduledThreadPool *a = parcScheduledThreadPool_Create(); + * + * PARCHashCode hashValue = parcScheduledThreadPool_HashCode(buffer); + * parcScheduledThreadPool_Release(&a); + * } + * @endcode + */ +PARCHashCode parcScheduledThreadPool_HashCode(const PARCScheduledThreadPool *instance); + +/** + * Determine if an instance of `PARCScheduledThreadPool` 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 PARCScheduledThreadPool instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCScheduledThreadPool *a = parcScheduledThreadPool_Create(); + * + * if (parcScheduledThreadPool_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcScheduledThreadPool_Release(&a); + * } + * @endcode + * + */ +bool parcScheduledThreadPool_IsValid(const PARCScheduledThreadPool *instance); + +/** + * Release a previously acquired reference to the given `PARCScheduledThreadPool` 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 + * { + * PARCScheduledThreadPool *a = parcScheduledThreadPool_Create(); + * + * parcScheduledThreadPool_Release(&a); + * } + * @endcode + */ +void parcScheduledThreadPool_Release(PARCScheduledThreadPool **instancePtr); + +/** + * Create a `PARCJSON` instance (representation) of the given object. + * + * @param [in] instance A pointer to a valid PARCScheduledThreadPool instance. + * + * @return NULL Memory could not be allocated to contain the `PARCJSON` instance. + * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate(). + * + * Example: + * @code + * { + * PARCScheduledThreadPool *a = parcScheduledThreadPool_Create(); + * + * PARCJSON *json = parcScheduledThreadPool_ToJSON(a); + * + * printf("JSON representation: %s\n", parcJSON_ToString(json)); + * parcJSON_Release(&json); + * + * parcScheduledThreadPool_Release(&a); + * } + * @endcode + */ +PARCJSON *parcScheduledThreadPool_ToJSON(const PARCScheduledThreadPool *instance); + +/** + * Produce a null-terminated string representation of the specified `PARCScheduledThreadPool`. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to a valid PARCScheduledThreadPool instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCScheduledThreadPool *a = parcScheduledThreadPool_Create(); + * + * char *string = parcScheduledThreadPool_ToString(a); + * + * parcScheduledThreadPool_Release(&a); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + * + * @see parcScheduledThreadPool_Display + */ +char *parcScheduledThreadPool_ToString(const PARCScheduledThreadPool *instance); + +/** + * Executes command with zero required delay. + */ +void parcScheduledThreadPool_Execute(PARCScheduledThreadPool *pool, PARCFutureTask *command); + +/** + * Gets the policy on whether to continue executing existing periodic tasks even when this executor has been shutdown. + */ +bool parcScheduledThreadPool_GetContinueExistingPeriodicTasksAfterShutdownPolicy(PARCScheduledThreadPool *pool); + +/** + * Gets the policy on whether to execute existing delayed tasks even when this executor has been shutdown. + */ +bool parcScheduledThreadPool_GetExecuteExistingDelayedTasksAfterShutdownPolicy(PARCScheduledThreadPool *pool); + +/** + * Returns the task queue used by this executor. + */ +PARCSortedList *parcScheduledThreadPool_GetQueue(const PARCScheduledThreadPool *pool); + +/** + * Gets the policy on whether cancelled tasks should be immediately removed from the work queue at time of cancellation. + */ +bool parcScheduledThreadPool_GetRemoveOnCancelPolicy(const PARCScheduledThreadPool *pool); + +/** + * Creates and executes a one-shot action that becomes enabled after the given delay. + */ +PARCScheduledTask *parcScheduledThreadPool_Schedule(PARCScheduledThreadPool *pool, PARCFutureTask *task, const PARCTimeout *delay); + +/** + * Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given period; that is executions will commence after initialDelay then initialDelay+period, then initialDelay + 2 * period, and so on. + */ +PARCScheduledTask *parcScheduledThreadPool_ScheduleAtFixedRate(PARCScheduledThreadPool *pool, PARCFutureTask *task, PARCTimeout initialDelay, PARCTimeout period); + +/** + * Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given delay between the termination of one execution and the commencement of the next. + */ +PARCScheduledTask *parcScheduledThreadPool_ScheduleWithFixedDelay(PARCScheduledThreadPool *pool, PARCFutureTask *task, PARCTimeout initialDelay, PARCTimeout delay); + +/** + * Sets the policy on whether to continue executing existing periodic tasks even when this executor has been shutdown. + */ +void parcScheduledThreadPool_SetContinueExistingPeriodicTasksAfterShutdownPolicy(PARCScheduledThreadPool *pool, bool value); + +/** + * Sets the policy on whether to execute existing delayed tasks even when this executor has been shutdown. + */ +void parcScheduledThreadPool_SetExecuteExistingDelayedTasksAfterShutdownPolicy(PARCScheduledThreadPool *pool, bool value); + +/** + * Sets the policy on whether cancelled tasks should be immediately removed from the work queue at time of cancellation. + */ +void parcScheduledThreadPool_SetRemoveOnCancelPolicy(PARCScheduledThreadPool *pool, bool value); + +/** + * Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted. + */ +void parcScheduledThreadPool_Shutdown(PARCScheduledThreadPool *pool); + +/** + * Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution. + */ +PARCList *parcScheduledThreadPool_ShutdownNow(PARCScheduledThreadPool *pool); + +/** + * Submits a PARCFutureTask task for execution and returns the PARCFutureTask representing that task. + */ +PARCScheduledTask *parcScheduledThreadPool_Submit(PARCScheduledThreadPool *pool, PARCFutureTask *task); +#endif diff --git a/libparc/parc/concurrent/parc_Synchronizer.c b/libparc/parc/concurrent/parc_Synchronizer.c new file mode 100755 index 00000000..c4475476 --- /dev/null +++ b/libparc/parc/concurrent/parc_Synchronizer.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/concurrent/parc_Synchronizer.h> + +#ifdef PARCLibrary_DISABLE_ATOMICS +# include <pthread.h> +#else +//# include <pthread.h> +#endif + +typedef enum { + _PARCSynchronizer_Unlocked = 0, + _PARCSynchronizer_Locked = 1 +} _PARCSynchronizer; + +struct PARCSynchronizer { +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_t mutex; +#else + int mutex; +#endif +}; + +static void +_parcSynchronizer_Finalize(PARCSynchronizer **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCSynchronizer pointer."); + + parcSynchronizer_OptionalAssertValid((*instancePtr)); +} + +parcObject_ImplementAcquire(parcSynchronizer, PARCSynchronizer); + +parcObject_ImplementRelease(parcSynchronizer, PARCSynchronizer); + +parcObject_ExtendPARCObject(PARCSynchronizer, _parcSynchronizer_Finalize, NULL, NULL, NULL, NULL, NULL, NULL); + +void +parcSynchronizer_AssertValid(const PARCSynchronizer *instance) +{ + assertTrue(parcSynchronizer_IsValid(instance), + "PARCSynchronizer is not valid."); +} + +PARCSynchronizer * +parcSynchronizer_Create(void) +{ + PARCSynchronizer *result = parcObject_CreateInstance(PARCSynchronizer); + +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_init(&result->mutex, NULL); +#else + result->mutex = _PARCSynchronizer_Unlocked; +#endif + + return result; +} + +void +parcSynchronizer_Display(const PARCSynchronizer *instance, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCSynchronizer@%p {", instance); + /* Call Display() functions for the fields here. */ + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool +parcSynchronizer_IsValid(const PARCSynchronizer *instance) +{ + bool result = false; + + if (instance != NULL) { + result = true; + } + + return result; +} + +bool +parcSynchronizer_TryLock(PARCSynchronizer *instance) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + bool result = pthread_mutex_trylock(&instance->mutex) == 0; +#else + bool result = __sync_bool_compare_and_swap(&instance->mutex, _PARCSynchronizer_Unlocked, _PARCSynchronizer_Locked); +#endif + return result; +} + +void +parcSynchronizer_Lock(PARCSynchronizer *instance) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_lock(&instance->mutex); +#else + while (!__sync_bool_compare_and_swap(&instance->mutex, _PARCSynchronizer_Unlocked, _PARCSynchronizer_Locked)) { + ; + } +#endif +} + +void +parcSynchronizer_Unlock(PARCSynchronizer *instance) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + pthread_mutex_unlock(&instance->mutex); +#else + while (!__sync_bool_compare_and_swap(&instance->mutex, _PARCSynchronizer_Locked, _PARCSynchronizer_Unlocked)) { + ; + } +#endif +} + +bool +parcSynchronizer_IsLocked(const PARCSynchronizer *barrier) +{ +#ifdef PARCLibrary_DISABLE_ATOMICS + PARCSynchronizer *instance = (PARCSynchronizer *) barrier; + + bool result = pthread_mutex_trylock(&instance->mutex) != 0; + pthread_mutex_unlock(&instance->mutex); + return result; +#else + return barrier->mutex == _PARCSynchronizer_Locked; +#endif +} diff --git a/libparc/parc/concurrent/parc_Synchronizer.h b/libparc/parc/concurrent/parc_Synchronizer.h new file mode 100755 index 00000000..a387888f --- /dev/null +++ b/libparc/parc/concurrent/parc_Synchronizer.h @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_Synchronizer.h + * @ingroup threading + * @brief A simple mutual exclusive synchronization implementation. + * + * Detailed Description + * + */ +#ifndef PARCLibrary_parc_Barrier +#define PARCLibrary_parc_Barrier +#include <stdbool.h> + +#include <parc/algol/parc_JSON.h> + +struct PARCSynchronizer; +typedef struct PARCSynchronizer PARCSynchronizer; + +/** + * Increase the number of references to a `PARCSynchronizer` instance. + * + * Note that new `PARCSynchronizer` is not created, + * only that the given `PARCSynchronizer` reference count is incremented. + * Discard the reference by invoking `parcSynchronizer_Release`. + * + * @param [in] instance A pointer to a valid PARCSynchronizer instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCSynchronizer *a = parcSynchronizer_Create(); + * + * PARCSynchronizer *b = parcSynchronizer_Acquire(); + * + * parcSynchronizer_Release(&a); + * parcSynchronizer_Release(&b); + * } + * @endcode + */ +PARCSynchronizer *parcSynchronizer_Acquire(const PARCSynchronizer *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcSynchronizer_OptionalAssertValid(_instance_) +#else +# define parcSynchronizer_OptionalAssertValid(_instance_) parcSynchronizer_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCSynchronizer` instance is valid. + * + * @param [in] instance A pointer to a valid PARCSynchronizer instance. + * + * Example: + * @code + * { + * PARCSynchronizer *a = parcSynchronizer_Create(); + * + * parcSynchronizer_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcSynchronizer_Release(&b); + * } + * @endcode + */ +void parcSynchronizer_AssertValid(const PARCSynchronizer *instance); + +/** + * Create an instance of PARCSynchronizer + * + * <#Paragraphs Of Explanation#> + * + * @return non-NULL A pointer to a valid PARCSynchronizer instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCSynchronizer *a = parcSynchronizer_Create(); + * + * parcSynchronizer_Release(&a); + * } + * @endcode + */ +PARCSynchronizer *parcSynchronizer_Create(void); + +/** + * Print a human readable representation of the given `PARCSynchronizer`. + * + * @param [in] instance A pointer to a valid PARCSynchronizer instance. + * @param [in] indentation The indentation level to use for printing. + * + * Example: + * @code + * { + * PARCSynchronizer *a = parcSynchronizer_Create(); + * + * parcSynchronizer_Display(a, 0); + * + * parcSynchronizer_Release(&a); + * } + * @endcode + */ +void parcSynchronizer_Display(const PARCSynchronizer *instance, int indentation); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcSynchronizer_Equals} method, + * then calling the {@link parcSynchronizer_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcSynchronizer_Equals} function, + * then calling the `parcSynchronizer_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCSynchronizer instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCSynchronizer *a = parcSynchronizer_Create(); + * + * uint32_t hashValue = parcSynchronizer_HashCode(buffer); + * parcSynchronizer_Release(&a); + * } + * @endcode + */ +int parcSynchronizer_HashCode(const PARCSynchronizer *instance); + +/** + * Determine if an instance of `PARCSynchronizer` 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 PARCSynchronizer instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCSynchronizer *a = parcSynchronizer_Create(); + * + * if (parcSynchronizer_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcSynchronizer_Release(&a); + * } + * @endcode + * + */ +bool parcSynchronizer_IsValid(const PARCSynchronizer *instance); + +/** + * Release a previously acquired reference to the given `PARCSynchronizer` 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 + * { + * PARCSynchronizer *a = parcSynchronizer_Create(); + * + * parcSynchronizer_Release(&a); + * } + * @endcode + */ +void parcSynchronizer_Release(PARCSynchronizer **instancePtr); + +/** + * Attempt to lock the given PARCSynchronizer. + * + * If the synchronizer is already locked, this function returns `false`. + * Otherwise, the lock is established and this function returns `true`. + * + * @param [in] barrier A pointer to a valid PARCSynchronizer instance. + * + * @return `true` The PARCSynchronizer was successfully set. + * @return `false` The PARCSynchronizer could not be set. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcSynchronizer_TryLock(PARCSynchronizer *barrier); + +/** + * Lock the given PARCSynchronizer. + * + * If the synchronizer is already locked, this function blocks the caller until it is able to acquire the lock. + * + * @param [in] barrier A pointer to a valid PARCSynchronizer instance. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +void parcSynchronizer_Lock(PARCSynchronizer *barrier); + +/** + * Unlock the given PARCSynchronizer. + * + * @param [in] barrier A pointer to a valid PARCSynchronizer instance. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +void parcSynchronizer_Unlock(PARCSynchronizer *barrier); + +/** + * Check if a PARCSynchronizer is locked. + * + * @param [in] synchronizer A pointer to a valid PARCSynchronizer instance. + * + * @return true The specified synchronizer is currently locked. + * + * Example: + * @code + * { + * PARCSynchronizer *a = parcSynchronizer_Create(); + * + * if (parcSynchronizer_IsLocked(a) == true) { + * printf("A PARCSynchronizer cannot be created in the locked state.\n"); + * } + * + * parcSynchronizer_Release(&a); + * } + * @endcode + */ +bool parcSynchronizer_IsLocked(const PARCSynchronizer *synchronizer); +#endif diff --git a/libparc/parc/concurrent/parc_Thread.c b/libparc/parc/concurrent/parc_Thread.c new file mode 100644 index 00000000..4bccc47f --- /dev/null +++ b/libparc/parc/concurrent/parc_Thread.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <pthread.h> +#include <stdio.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Memory.h> + +#include <parc/concurrent/parc_Thread.h> + +struct PARCThread { + void *(*run)(PARCThread *, PARCObject *param); + PARCObject *argument; + bool isCancelled; + bool isRunning; + pthread_t thread; +}; + +static bool +_parcThread_Destructor(PARCThread **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCThread pointer."); + PARCThread *thread = *instancePtr; + + if (thread->argument != NULL) { + parcObject_Release(&thread->argument); + } + + thread->isCancelled = true; + parcThread_Join(thread); + + return true; +} + +parcObject_ImplementAcquire(parcThread, PARCThread); + +parcObject_ImplementRelease(parcThread, PARCThread); + +parcObject_Override(PARCThread, PARCObject, + .isLockable = true, + .destructor = (PARCObjectDestructor *) _parcThread_Destructor, + .copy = (PARCObjectCopy *) parcThread_Copy, + .toString = (PARCObjectToString *) parcThread_ToString, + .equals = (PARCObjectEquals *) parcThread_Equals, + .compare = (PARCObjectCompare *) parcThread_Compare, + .hashCode = (PARCObjectHashCode *) parcThread_HashCode, + .display = (PARCObjectDisplay *) parcThread_Display + ); + +void +parcThread_AssertValid(const PARCThread *instance) +{ + assertTrue(parcThread_IsValid(instance), + "PARCThread is not valid."); +} + +PARCThread * +parcThread_Create(void *(*runFunction)(PARCThread *, PARCObject *), PARCObject *restrict parameter) +{ + assertNotNull(parameter, "Parameter cannot be NULL."); + + PARCThread *result = parcObject_CreateAndClearInstance(PARCThread); + + if (result) { + result->run = runFunction; + result->argument = parcObject_Acquire(parameter); + result->isCancelled = false; + result->isRunning = false; + } + + return result; +} + +int +parcThread_Compare(const PARCThread *instance, const PARCThread *other) +{ + int result = 0; + return result; +} + +PARCThread * +parcThread_Copy(const PARCThread *original) +{ + PARCThread *result = parcThread_Create(original->run, original->argument); + result->isCancelled = original->isCancelled; + result->isRunning = original->isRunning; + + return result; +} + +void +parcThread_Display(const PARCThread *instance, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCThread@%p {", instance); + /* Call Display() functions for the fields here. */ + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool +parcThread_Equals(const PARCThread *x, const PARCThread *y) +{ + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + result = pthread_equal(x->thread, y->thread); + } + + return result; +} + +PARCHashCode +parcThread_HashCode(const PARCThread *instance) +{ + PARCHashCode result = 0; + + return result; +} + +bool +parcThread_IsValid(const PARCThread *thread) +{ + bool result = false; + + if (thread != NULL) { + result = true; + } + + return result; +} + +PARCJSON * +parcThread_ToJSON(const PARCThread *thread) +{ + PARCJSON *result = parcJSON_Create(); + + return result; +} + +char * +parcThread_ToString(const PARCThread *thread) +{ + char *result = parcMemory_Format("PARCThread@%p{.id=%p, .isCancelled=%s}", thread, thread->thread, thread->isCancelled ? "true" : "false"); + + return result; +} + +static void * +_parcThread_Run(PARCThread *thread) +{ + thread->isRunning = true; + thread->run(thread, thread->argument); + thread->isRunning = false; + + // The thread is done, release the reference to the argument acquired when this PARCThread was created. + // This prevents the reference from lingering leading to memory leaks if the thread is not properly joined. + if (thread->argument != NULL) { + parcObject_Release(&thread->argument); + } + // Release the thread reference that was acquired *before* this thread was started. + parcThread_Release(&thread); + + return NULL; +} + +void +parcThread_Start(PARCThread *thread) +{ + PARCThread *parameter = parcThread_Acquire(thread); + pthread_create(&thread->thread, NULL, (void *(*)(void *))_parcThread_Run, parameter); +} + +PARCObject * +parcThread_GetParameter(const PARCThread *thread) +{ + return thread->argument; +} + +bool +parcThread_Cancel(PARCThread *thread) +{ + if (parcThread_Lock(thread)) { + thread->isCancelled = true; + parcThread_Notify(thread); + parcThread_Unlock(thread); + } + return true; +} + +int +parcThread_GetId(const PARCThread *thread) +{ + return (int) thread->thread; +} + +bool +parcThread_IsRunning(const PARCThread *thread) +{ + return thread->isRunning; +} + +bool +parcThread_IsCancelled(const PARCThread *thread) +{ + return thread->isCancelled; +} + +void +parcThread_Join(PARCThread *thread) +{ + pthread_join(thread->thread, NULL); +} diff --git a/libparc/parc/concurrent/parc_Thread.h b/libparc/parc/concurrent/parc_Thread.h new file mode 100755 index 00000000..f24306ae --- /dev/null +++ b/libparc/parc/concurrent/parc_Thread.h @@ -0,0 +1,601 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_Thread.h + * @ingroup threading + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef PARCLibrary_parc_Thread +#define PARCLibrary_parc_Thread +#include <stdbool.h> + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_HashCode.h> + +struct PARCThread; +typedef struct PARCThread PARCThread; + +/** + * Increase the number of references to a `PARCThread` instance. + * + * Note that new `PARCThread` is not created, + * only that the given `PARCThread` reference count is incremented. + * Discard the reference by invoking `parcThread_Release`. + * + * @param [in] instance A pointer to a valid PARCThread instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCThread *a = parcThread_Create(); + * + * PARCThread *b = parcThread_Acquire(); + * + * parcThread_Release(&a); + * parcThread_Release(&b); + * } + * @endcode + */ +PARCThread *parcThread_Acquire(const PARCThread *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcThread_OptionalAssertValid(_instance_) +#else +# define parcThread_OptionalAssertValid(_instance_) parcThread_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCThread` instance is valid. + * + * @param [in] instance A pointer to a valid PARCThread instance. + * + * Example: + * @code + * { + * PARCThread *a = parcThread_Create(); + * + * parcThread_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcThread_Release(&b); + * } + * @endcode + */ +void parcThread_AssertValid(const PARCThread *instance); + +/** + * Create an instance of PARCThread + * + * @return non-NULL A pointer to a valid PARCThread instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * + * MyTask *task = myTask_Create(); + * PARCThread *thread = parcThread_Create(myTask_Run, myTask); + * + * parcThread_Start(a); + * + * parcThread_Release(&a); + * } + * @endcode + */ +//#define parcThread_Create(_runFunction_, _argument_) parcThread_CreateImpl((void (*)(PARCObject *)) _runFunction_, (PARCObject *) _argument_) + +PARCThread *parcThread_Create(void *(*run)(PARCThread *, PARCObject *), PARCObject *restrict argument); + +/** + * 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 PARCThread instance. + * @param [in] other A pointer to a valid PARCThread 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 + * { + * PARCThread *a = parcThread_Create(); + * PARCThread *b = parcThread_Create(); + * + * if (parcThread_Compare(a, b) == 0) { + * printf("Instances are equal.\n"); + * } + * + * parcThread_Release(&a); + * parcThread_Release(&b); + * } + * @endcode + * + * @see parcThread_Equals + */ +int parcThread_Compare(const PARCThread *instance, const PARCThread *other); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A pointer to a valid PARCThread instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCThread` instance. + * + * Example: + * @code + * { + * PARCThread *a = parcThread_Create(); + * + * PARCThread *copy = parcThread_Copy(&b); + * + * parcThread_Release(&b); + * parcThread_Release(©); + * } + * @endcode + */ +PARCThread *parcThread_Copy(const PARCThread *original); + +/** + * Print a human readable representation of the given `PARCThread`. + * + * @param [in] instance A pointer to a valid PARCThread instance. + * @param [in] indentation The indentation level to use for printing. + * + * Example: + * @code + * { + * PARCThread *a = parcThread_Create(); + * + * parcThread_Display(a, 0); + * + * parcThread_Release(&a); + * } + * @endcode + */ +void parcThread_Display(const PARCThread *instance, int indentation); + +/** + * Determine if two `PARCThread` instances are equal. + * + * The following equivalence relations on non-null `PARCThread` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcThread_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcThread_Equals(x, y)` must return true if and only if + * `parcThread_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcThread_Equals(x, y)` returns true and + * `parcThread_Equals(y, z)` returns true, + * then `parcThread_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcThread_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcThread_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCThread instance. + * @param [in] y A pointer to a valid PARCThread instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCThread *a = parcThread_Create(); + * PARCThread *b = parcThread_Create(); + * + * if (parcThread_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcThread_Release(&a); + * parcThread_Release(&b); + * } + * @endcode + * @see parcThread_HashCode + */ +bool parcThread_Equals(const PARCThread *x, const PARCThread *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcThread_Equals} method, + * then calling the {@link parcThread_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcThread_Equals} function, + * then calling the `parcThread_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCThread instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCThread *a = parcThread_Create(); + * + * PARCHashCode hashValue = parcThread_HashCode(buffer); + * parcThread_Release(&a); + * } + * @endcode + */ +PARCHashCode parcThread_HashCode(const PARCThread *instance); + +/** + * Determine if an instance of `PARCThread` 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 PARCThread instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCThread *a = parcThread_Create(); + * + * if (parcThread_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcThread_Release(&a); + * } + * @endcode + * + */ +bool parcThread_IsValid(const PARCThread *instance); + +/** + * Release a previously acquired reference to the given `PARCThread` 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 + * { + * PARCThread *a = parcThread_Create(); + * + * parcThread_Release(&a); + * } + * @endcode + */ +void parcThread_Release(PARCThread **instancePtr); + +/** + * Create a `PARCJSON` instance (representation) of the given object. + * + * @param [in] instance A pointer to a valid PARCThread instance. + * + * @return NULL Memory could not be allocated to contain the `PARCJSON` instance. + * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate(). + * + * Example: + * @code + * { + * PARCThread *a = parcThread_Create(); + * + * PARCJSON *json = parcThread_ToJSON(a); + * + * printf("JSON representation: %s\n", parcJSON_ToString(json)); + * parcJSON_Release(&json); + * + * parcThread_Release(&a); + * } + * @endcode + */ +PARCJSON *parcThread_ToJSON(const PARCThread *instance); + +/** + * Produce a null-terminated string representation of the specified `PARCThread`. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to a valid PARCThread instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCThread *a = parcThread_Create(); + * + * char *string = parcThread_ToString(a); + * + * parcThread_Release(&a); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + * + * @see parcThread_Display + */ +char *parcThread_ToString(const PARCThread *instance); + +/** + * Wakes up a single thread that is waiting on this object (see `parcLinkedList_Wait)`. + * If any threads are waiting on this object, one of them is chosen to be awakened. + * The choice is arbitrary and occurs at the discretion of the underlying implementation. + * + * The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. + * The awakened thread will compete in the usual manner with any other threads that might be actively + * competing to synchronize on this object; + * for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object. + * + * @param [in] object A pointer to a valid PARCThread instance. + * + * Example: + * @code + * { + * + * parcThread_Notify(object); + * } + * @endcode + */ +parcObject_ImplementNotify(parcThread, PARCThread); + +/** + * Causes the calling thread to wait until either another thread invokes the parcHashMap_Notify() function on the same object. + * * + * @param [in] object A pointer to a valid `PARCThread` instance. + * + * Example: + * @code + * { + * + * parcThread_Wait(object); + * } + * @endcode + */ +parcObject_ImplementWait(parcThread, PARCThread); + +/** + * Obtain the lock on the given `PARCThread` instance. + * + * If the lock is already held by another thread, this function will block. + * If the lock is aleady held by the current thread, this function will return `false`. + * + * Implementors must avoid deadlock by attempting to lock the object a second time within the same calling thread. + * + * @param [in] object A pointer to a valid `PARCThread` instance. + * + * @return true The lock was obtained successfully. + * @return false The lock is already held by the current thread, or the `PARCThread` is invalid. + * + * Example: + * @code + * { + * if (parcThread_Lock(object)) { + * + * } + * } + * @endcode + */ +parcObject_ImplementLock(parcThread, PARCThread); + +/** + * Try to obtain the advisory lock on the given PARCThread instance. + * + * Once the lock is obtained, the caller must release the lock as soon as possible. + * + * @param [in] object A pointer to a valid PARCThread instance. + * + * @return true The PARCThread is locked. + * @return false The PARCThread is unlocked. + * + * Example: + * @code + * { + * parcThread_TryLock(object); + * } + * @endcode + */ +parcObject_ImplementTryLock(parcThread, PARCThread); + +/** + * Try to unlock the advisory lock on the given `PARCHashMap` instance. + * + * @param [in] object A pointer to a valid `PARCThread` instance. + * + * @return true The `PARCThread` was locked and now is unlocked. + * @return false The `PARCThread` was not locked and remains unlocked. + * + * Example: + * @code + * { + * parcThread_Unlock(object); + * } + * @endcode + */ +parcObject_ImplementUnlock(parcThread, PARCThread); + +/** + * Determine if the advisory lock on the given `PARCThread` instance is locked. + * + * @param [in] object A pointer to a valid `PARCThread` instance. + * + * @return true The `PARCThread` is locked. + * @return false The `PARCThread` is unlocked. + * Example: + * @code + * { + * if (parcThread_IsLocked(object)) { + * ... + * } + * } + * @endcode + */ +parcObject_ImplementIsLocked(parcThread, PARCThread); + + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +void parcThread_Start(PARCThread *thread); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCObject *parcThread_GetParameter(const PARCThread *thread); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcThread_Cancel(PARCThread *thread); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcThread_IsCancelled(const PARCThread *thread); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +bool parcThread_IsRunning(const PARCThread *thread); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +int parcThread_GetId(const PARCThread *thread); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +void parcThread_Join(PARCThread *thread); +#endif diff --git a/libparc/parc/concurrent/parc_ThreadPool.c b/libparc/parc/concurrent/parc_ThreadPool.c new file mode 100644 index 00000000..80b093ba --- /dev/null +++ b/libparc/parc/concurrent/parc_ThreadPool.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Memory.h> + +#include <parc/algol/parc_SortedList.h> +#include <parc/algol/parc_LinkedList.h> + +#include <parc/concurrent/parc_AtomicUint64.h> +#include <parc/concurrent/parc_ThreadPool.h> +#include <parc/concurrent/parc_Thread.h> + +struct PARCThreadPool { + bool continueExistingPeriodicTasksAfterShutdown; + bool executeExistingDelayedTasksAfterShutdown; + bool removeOnCancel; + PARCLinkedList *workQueue; + PARCLinkedList *threads; + int poolSize; + int maximumPoolSize; + long taskCount; + bool isShutdown; + bool isTerminated; + bool isTerminating; + + PARCAtomicUint64 *completedTaskCount; +}; + +static void * +_parcThreadPool_Worker(const PARCThread *thread, const PARCThreadPool *pool) +{ + while (parcThread_IsCancelled(thread) == false && pool->isTerminated == false) { + if (parcLinkedList_Lock(pool->workQueue)) { + PARCFutureTask *task = parcLinkedList_RemoveFirst(pool->workQueue); + if (task != NULL) { + parcAtomicUint64_Increment(pool->completedTaskCount); + parcLinkedList_Unlock(pool->workQueue); + parcFutureTask_Run(task); + parcFutureTask_Release(&task); + parcLinkedList_Lock(pool->workQueue); + + parcLinkedList_Notify(pool->workQueue); + } else { + parcLinkedList_WaitFor(pool->workQueue, 1000000000); + } + parcLinkedList_Unlock(pool->workQueue); + } + } + + return NULL; +} + +static void +_parcThreadPool_CancelAll(const PARCThreadPool *pool) +{ + PARCIterator *iterator = parcLinkedList_CreateIterator(pool->threads); + + while (parcIterator_HasNext(iterator)) { + PARCThread *thread = parcIterator_Next(iterator); + parcThread_Cancel(thread); + } + parcIterator_Release(&iterator); +} + +static void +_parcThreadPool_JoinAll(const PARCThreadPool *pool) +{ + PARCIterator *iterator = parcLinkedList_CreateIterator(pool->threads); + + while (parcIterator_HasNext(iterator)) { + PARCThread *thread = parcIterator_Next(iterator); + parcThread_Join(thread); + } + parcIterator_Release(&iterator); +} + +static bool +_parcThreadPool_Destructor(PARCThreadPool **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCThreadPool pointer."); + PARCThreadPool *pool = *instancePtr; + + if (pool->isShutdown == false) { + _parcThreadPool_CancelAll(pool); + _parcThreadPool_JoinAll(pool); + } + + parcAtomicUint64_Release(&pool->completedTaskCount); + parcLinkedList_Release(&pool->threads); + + if (parcObject_Lock(pool->workQueue)) { + parcLinkedList_Release(&pool->workQueue); + } + + return true; +} + +parcObject_ImplementAcquire(parcThreadPool, PARCThreadPool); + +parcObject_ImplementRelease(parcThreadPool, PARCThreadPool); + +parcObject_Override(PARCThreadPool, PARCObject, + .isLockable = true, + .destructor = (PARCObjectDestructor *) _parcThreadPool_Destructor, + .copy = (PARCObjectCopy *) parcThreadPool_Copy, + .toString = (PARCObjectToString *) parcThreadPool_ToString, + .equals = (PARCObjectEquals *) parcThreadPool_Equals, + .compare = (PARCObjectCompare *) parcThreadPool_Compare, + .hashCode = (PARCObjectHashCode *) parcThreadPool_HashCode); + +void +parcThreadPool_AssertValid(const PARCThreadPool *instance) +{ + assertTrue(parcThreadPool_IsValid(instance), + "PARCThreadPool is not valid."); +} + + +PARCThreadPool * +parcThreadPool_Create(int poolSize) +{ + PARCThreadPool *result = parcObject_CreateInstance(PARCThreadPool); + + if (result != NULL) { + result->poolSize = poolSize; + result->maximumPoolSize = poolSize; + result->taskCount = 0; + result->isShutdown = false; + result->isTerminated = false; + result->isTerminating = false; + result->workQueue = parcLinkedList_Create(); + result->threads = parcLinkedList_Create(); + + result->completedTaskCount = parcAtomicUint64_Create(0); + + result->continueExistingPeriodicTasksAfterShutdown = false; + result->executeExistingDelayedTasksAfterShutdown = false; + result->removeOnCancel = true; + + if (parcObject_Lock(result)) { + for (int i = 0; i < poolSize; i++) { + PARCThread *thread = parcThread_Create((void *(*)(PARCThread *, PARCObject *))_parcThreadPool_Worker, (PARCObject *) result); + parcLinkedList_Append(result->threads, thread); + parcThread_Start(thread); + parcThread_Release(&thread); + } + parcObject_Unlock(result); + } + } + + return result; +} + +int +parcThreadPool_Compare(const PARCThreadPool *instance, const PARCThreadPool *other) +{ + int result = 0; + + return result; +} + +PARCThreadPool * +parcThreadPool_Copy(const PARCThreadPool *original) +{ + PARCThreadPool *result = parcThreadPool_Create(original->poolSize); + + return result; +} + +void +parcThreadPool_Display(const PARCThreadPool *instance, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCThreadPool@%p {", instance); + /* Call Display() functions for the fields here. */ + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool +parcThreadPool_Equals(const PARCThreadPool *x, const PARCThreadPool *y) +{ + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + /* perform instance specific equality tests here. */ + if (x->poolSize == y->poolSize) { + result = true; + } + } + + return result; +} + +PARCHashCode +parcThreadPool_HashCode(const PARCThreadPool *instance) +{ + PARCHashCode result = 0; + + return result; +} + +bool +parcThreadPool_IsValid(const PARCThreadPool *instance) +{ + bool result = false; + + if (instance != NULL) { + result = true; + } + + return result; +} + +PARCJSON * +parcThreadPool_ToJSON(const PARCThreadPool *instance) +{ + PARCJSON *result = parcJSON_Create(); + + if (result != NULL) { + } + + return result; +} + +char * +parcThreadPool_ToString(const PARCThreadPool *instance) +{ + char *result = parcMemory_Format("PARCThreadPool@%p\n", instance); + + return result; +} + +void +parcThreadPool_SetAllowCoreThreadTimeOut(PARCThreadPool *pool, bool value) +{ +} + +bool +parcThreadPool_GetAllowsCoreThreadTimeOut(const PARCThreadPool *pool) +{ + return false; +} + +bool +parcThreadPool_AwaitTermination(PARCThreadPool *pool, PARCTimeout *timeout) +{ + bool result = false; + + if (pool->isTerminating) { + if (parcLinkedList_Lock(pool->workQueue)) { + while (parcLinkedList_Size(pool->workQueue) > 0) { + if (parcTimeout_IsNever(timeout)) { + parcLinkedList_Wait(pool->workQueue); + } else { + // This is not accurate as this will continue the delay, rather than keep a cumulative amount of delay. + uint64_t delay = parcTimeout_InNanoSeconds(timeout); + parcLinkedList_WaitFor(pool->workQueue, delay); + } + } + result = true; + parcLinkedList_Unlock(pool->workQueue); + } + + parcThreadPool_ShutdownNow(pool); + } + + return result; +} + +bool +parcThreadPool_Execute(PARCThreadPool *pool, PARCFutureTask *task) +{ + bool result = false; + + if (parcThreadPool_Lock(pool)) { + if (pool->isShutdown == false) { + parcThreadPool_Unlock(pool); + if (parcLinkedList_Lock(pool->workQueue)) { + parcLinkedList_Append(pool->workQueue, task); + parcLinkedList_Notify(pool->workQueue); + parcLinkedList_Unlock(pool->workQueue); + result = true; + } + } else { + parcThreadPool_Unlock(pool); + } + } + + return result; +} + +int +parcThreadPool_GetActiveCount(const PARCThreadPool *pool) +{ + return pool->poolSize; +} + +uint64_t +parcThreadPool_GetCompletedTaskCount(const PARCThreadPool *pool) +{ + return parcAtomicUint64_GetValue(pool->completedTaskCount); +} + +int +parcThreadPool_GetCorePoolSize(const PARCThreadPool *pool) +{ + return pool->poolSize; +} + +PARCTimeout * +parcThreadPool_GetKeepAliveTime(const PARCThreadPool *pool) +{ + return PARCTimeout_Never; +} + +int +parcThreadPool_GetLargestPoolSize(const PARCThreadPool *pool) +{ + return pool->poolSize; +} + +int +parcThreadPool_GetMaximumPoolSize(const PARCThreadPool *pool) +{ + return pool->maximumPoolSize; +} + +int +parcThreadPool_GetPoolSize(const PARCThreadPool *pool) +{ + return pool->poolSize; +} + +PARCLinkedList * +parcThreadPool_GetQueue(const PARCThreadPool *pool) +{ + return pool->workQueue; +} + +long +parcThreadPool_GetTaskCount(const PARCThreadPool *pool) +{ + return pool->taskCount; +} + +bool +parcThreadPool_IsShutdown(const PARCThreadPool *pool) +{ + return pool->isShutdown; +} + +bool +parcThreadPool_IsTerminated(const PARCThreadPool *pool) +{ + return pool->isTerminated; +} + +bool +parcThreadPool_IsTerminating(const PARCThreadPool *pool) +{ + return pool->isTerminating; +} + +int +parcThreadPool_PrestartAllCoreThreads(PARCThreadPool *pool) +{ + return 0; +} + +bool +parcThreadPool_PrestartCoreThread(PARCThreadPool *pool) +{ + return 0; +} + +void +parcThreadPool_Purge(PARCThreadPool *pool) +{ +} + +bool +parcThreadPool_Remove(PARCThreadPool *pool, PARCFutureTask *task) +{ + return false; +} + +void +parcThreadPool_SetCorePoolSize(PARCThreadPool *pool, int corePoolSize) +{ +} + +void +parcThreadPool_SetKeepAliveTime(PARCThreadPool *pool, PARCTimeout *timeout) +{ +} + +void +parcThreadPool_SetMaximumPoolSize(PARCThreadPool *pool, int maximumPoolSize) +{ +} + +void +parcThreadPool_Shutdown(PARCThreadPool *pool) +{ + if (parcThreadPool_Lock(pool)) { + pool->isShutdown = true; + pool->isTerminating = true; + parcThreadPool_Unlock(pool); + } +} + +PARCLinkedList * +parcThreadPool_ShutdownNow(PARCThreadPool *pool) +{ + parcThreadPool_Shutdown(pool); + + // Cause all of the worker threads to exit. + _parcThreadPool_CancelAll(pool); + + // Wake them all up so they detect that they are cancelled. + if (parcThreadPool_Lock(pool)) { + parcThreadPool_NotifyAll(pool); + parcThreadPool_Unlock(pool); + } + + if (parcLinkedList_Lock(pool->workQueue)) { + parcLinkedList_NotifyAll(pool->workQueue); + parcLinkedList_Unlock(pool->workQueue); + } + // Join with all of them, thereby cleaning up all of them. + _parcThreadPool_JoinAll(pool); + + pool->isTerminated = true; + return NULL; +} diff --git a/libparc/parc/concurrent/parc_ThreadPool.h b/libparc/parc/concurrent/parc_ThreadPool.h new file mode 100755 index 00000000..7a7b548e --- /dev/null +++ b/libparc/parc/concurrent/parc_ThreadPool.h @@ -0,0 +1,625 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_ThreadPool.h + * @ingroup threading + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef PARCLibrary_parc_ThreadPool +#define PARCLibrary_parc_ThreadPool +#include <stdbool.h> + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_HashCode.h> +#include <parc/algol/parc_LinkedList.h> +#include <parc/concurrent/parc_Timeout.h> +#include <parc/concurrent/parc_FutureTask.h> + +struct PARCThreadPool; +typedef struct PARCThreadPool PARCThreadPool; + +/** + * Increase the number of references to a `PARCThreadPool` instance. + * + * Note that new `PARCThreadPool` is not created, + * only that the given `PARCThreadPool` reference count is incremented. + * Discard the reference by invoking `parcThreadPool_Release`. + * + * @param [in] instance A pointer to a valid PARCThreadPool instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCThreadPool *a = parcThreadPool_Create(); + * + * PARCThreadPool *b = parcThreadPool_Acquire(); + * + * parcThreadPool_Release(&a); + * parcThreadPool_Release(&b); + * } + * @endcode + */ +PARCThreadPool *parcThreadPool_Acquire(const PARCThreadPool *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcThreadPool_OptionalAssertValid(_instance_) +#else +# define parcThreadPool_OptionalAssertValid(_instance_) parcThreadPool_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCThreadPool` instance is valid. + * + * @param [in] instance A pointer to a valid PARCThreadPool instance. + * + * Example: + * @code + * { + * PARCThreadPool *a = parcThreadPool_Create(); + * + * parcThreadPool_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcThreadPool_Release(&b); + * } + * @endcode + */ +void parcThreadPool_AssertValid(const PARCThreadPool *instance); + +/** + * Create an instance of PARCThreadPool + * + * <#Paragraphs Of Explanation#> + * + * @return non-NULL A pointer to a valid PARCThreadPool instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCThreadPool *a = parcThreadPool_Create(); + * + * parcThreadPool_Release(&a); + * } + * @endcode + */ +PARCThreadPool *parcThreadPool_Create(int poolSize); + +/** + * 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 PARCThreadPool instance. + * @param [in] other A pointer to a valid PARCThreadPool 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 + * { + * PARCThreadPool *a = parcThreadPool_Create(); + * PARCThreadPool *b = parcThreadPool_Create(); + * + * if (parcThreadPool_Compare(a, b) == 0) { + * printf("Instances are equal.\n"); + * } + * + * parcThreadPool_Release(&a); + * parcThreadPool_Release(&b); + * } + * @endcode + * + * @see parcThreadPool_Equals + */ +int parcThreadPool_Compare(const PARCThreadPool *instance, const PARCThreadPool *other); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A pointer to a valid PARCThreadPool instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCThreadPool` instance. + * + * Example: + * @code + * { + * PARCThreadPool *a = parcThreadPool_Create(); + * + * PARCThreadPool *copy = parcThreadPool_Copy(&b); + * + * parcThreadPool_Release(&b); + * parcThreadPool_Release(©); + * } + * @endcode + */ +PARCThreadPool *parcThreadPool_Copy(const PARCThreadPool *original); + +/** + * Print a human readable representation of the given `PARCThreadPool`. + * + * @param [in] instance A pointer to a valid PARCThreadPool instance. + * @param [in] indentation The indentation level to use for printing. + * + * Example: + * @code + * { + * PARCThreadPool *a = parcThreadPool_Create(); + * + * parcThreadPool_Display(a, 0); + * + * parcThreadPool_Release(&a); + * } + * @endcode + */ +void parcThreadPool_Display(const PARCThreadPool *instance, int indentation); + +/** + * Determine if two `PARCThreadPool` instances are equal. + * + * The following equivalence relations on non-null `PARCThreadPool` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcThreadPool_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcThreadPool_Equals(x, y)` must return true if and only if + * `parcThreadPool_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcThreadPool_Equals(x, y)` returns true and + * `parcThreadPool_Equals(y, z)` returns true, + * then `parcThreadPool_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcThreadPool_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcThreadPool_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCThreadPool instance. + * @param [in] y A pointer to a valid PARCThreadPool instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCThreadPool *a = parcThreadPool_Create(); + * PARCThreadPool *b = parcThreadPool_Create(); + * + * if (parcThreadPool_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcThreadPool_Release(&a); + * parcThreadPool_Release(&b); + * } + * @endcode + * @see parcThreadPool_HashCode + */ +bool parcThreadPool_Equals(const PARCThreadPool *x, const PARCThreadPool *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcThreadPool_Equals} method, + * then calling the {@link parcThreadPool_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcThreadPool_Equals} function, + * then calling the `parcThreadPool_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCThreadPool instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCThreadPool *a = parcThreadPool_Create(); + * + * PARCHashCode hashValue = parcThreadPool_HashCode(buffer); + * parcThreadPool_Release(&a); + * } + * @endcode + */ +PARCHashCode parcThreadPool_HashCode(const PARCThreadPool *instance); + +/** + * Determine if an instance of `PARCThreadPool` 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 PARCThreadPool instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCThreadPool *a = parcThreadPool_Create(); + * + * if (parcThreadPool_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcThreadPool_Release(&a); + * } + * @endcode + * + */ +bool parcThreadPool_IsValid(const PARCThreadPool *instance); + +/** + * Release a previously acquired reference to the given `PARCThreadPool` 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 + * { + * PARCThreadPool *a = parcThreadPool_Create(); + * + * parcThreadPool_Release(&a); + * } + * @endcode + */ +void parcThreadPool_Release(PARCThreadPool **instancePtr); + +/** + * Create a `PARCJSON` instance (representation) of the given object. + * + * @param [in] instance A pointer to a valid PARCThreadPool instance. + * + * @return NULL Memory could not be allocated to contain the `PARCJSON` instance. + * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate(). + * + * Example: + * @code + * { + * PARCThreadPool *a = parcThreadPool_Create(); + * + * PARCJSON *json = parcThreadPool_ToJSON(a); + * + * printf("JSON representation: %s\n", parcJSON_ToString(json)); + * parcJSON_Release(&json); + * + * parcThreadPool_Release(&a); + * } + * @endcode + */ +PARCJSON *parcThreadPool_ToJSON(const PARCThreadPool *instance); + +/** + * Produce a null-terminated string representation of the specified `PARCThreadPool`. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to a valid PARCThreadPool instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCThreadPool *a = parcThreadPool_Create(); + * + * char *string = parcThreadPool_ToString(a); + * + * parcThreadPool_Release(&a); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + * + * @see parcThreadPool_Display + */ +char *parcThreadPool_ToString(const PARCThreadPool *instance); + +/** + * Wakes up a single thread that is waiting on this object (see `parcThreadPool_Wait)`. + * If any threads are waiting on this object, one of them is chosen to be awakened. + * The choice is arbitrary and occurs at the discretion of the underlying implementation. + * + * The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. + * The awakened thread will compete in the usual manner with any other threads that might be actively + * competing to synchronize on this object; + * for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object. + * + * @param [in] object A pointer to a valid PARCThreadPool instance. + * + * Example: + * @code + * { + * if (parcThreadPool_Lock(object)) { + * parcThreadPool_Notify(object); + * parcThreadPool_Unlock(object); + * } + * } + * @endcode + */ +parcObject_ImplementNotify(parcThreadPool, PARCThreadPool); + +/** + * Wakes up all threads that are waiting on the given object's lock. + * + * A thread waits on an object by calling one of the wait methods, `parcThreadPool_Wait`, `parcThreadPool_WaitFor`, `parcThreadPool_WaitUntil`. + * The awakened threads will proceed after the current thread relinquishes the lock on the given object. + * The awakened threads will compete in the usual manner with any other threads that might be actively competing + * to synchronize on this object. + * Awakened threads have no priority between them in being the next thread to lock this object. + * + * This method can only be called by a thread that is the owner of this object's lock. + * + * @param [in] object A pointer to a valid `PARCThreadPool` instance. + * + * Example: + * @code + * { + * if (parcThreadPool_Lock(object)) { + * parcThreadPool_NotifyAll(object); + * parcThreadPool_Unlock(object); + * } + * } + * @endcode + */ +parcObject_ImplementNotifyAll(parcThreadPool, PARCThreadPool); + +/** + * Causes the calling thread to wait until either another thread invokes the parcThreadPool_Notify() function on the same object. + * * + * @param [in] object A pointer to a valid `PARCThreadPool` instance. + * + * Example: + * @code + * { + * + * parcThreadPool_Wait(object); + * } + * @endcode + */ +parcObject_ImplementWait(parcThreadPool, PARCThreadPool); + +/** + * Obtain the lock on the given `PARCThreadPool` instance. + * + * If the lock is already held by another thread, this function will block. + * If the lock is aleady held by the current thread, this function will return `false`. + * + * Implementors must avoid deadlock by attempting to lock the object a second time within the same calling thread. + * + * @param [in] object A pointer to a valid `PARCThreadPool` instance. + * + * @return true The lock was obtained successfully. + * @return false The lock is already held by the current thread, or the `PARCThreadPool` is invalid. + * + * Example: + * @code + * { + * if (parcThreadPool_Lock(object)) { + * + * } + * } + * @endcode + */ +parcObject_ImplementLock(parcThreadPool, PARCThreadPool); + +/** + * Try to obtain the advisory lock on the given PARCThreadPool instance. + * + * Once the lock is obtained, the caller must release the lock as soon as possible. + * + * @param [in] object A pointer to a valid PARCThreadPool instance. + * + * @return true The PARCThreadPool is locked. + * @return false The PARCThreadPool is unlocked. + * + * Example: + * @code + * { + * parcThreadPool_TryLock(object); + * } + * @endcode + */ +parcObject_ImplementTryLock(parcThreadPool, PARCThreadPool); + +/** + * Try to unlock the advisory lock on the given `PARCThreadPool` instance. + * + * @param [in] object A pointer to a valid `PARCThreadPool` instance. + * + * @return true The `PARCThreadPool` was locked and now is unlocked. + * @return false The `PARCThreadPool` was not locked and remains unlocked. + * + * Example: + * @code + * { + * parcThreadPool_Unlock(object); + * } + * @endcode + */ +parcObject_ImplementUnlock(parcThreadPool, PARCThreadPool); + +/** + * Determine if the advisory lock on the given `PARCThreadPool` instance is locked. + * + * @param [in] object A pointer to a valid `PARCThreadPool` instance. + * + * @return true The `PARCThreadPool` is locked. + * @return false The `PARCThreadPool` is unlocked. + * Example: + * @code + * { + * if (parcThreadPool_IsLocked(object)) { + * ... + * } + * } + * @endcode + */ +parcObject_ImplementIsLocked(parcThreadPool, PARCThreadPool); + +/** + * Sets the policy governing whether core threads may time out and terminate if no tasks arrive within the keep-alive time, being replaced if needed when new tasks arrive. + */ +void parcThreadPool_SetAllowCoreThreadTimeOut(PARCThreadPool *pool, bool value); + +/** + * Returns true if this pool allows core threads to time out and terminate if no tasks arrive within the keepAlive time, being replaced if needed when new tasks arrive. + */ +bool parcThreadPool_GetAllowsCoreThreadTimeOut(const PARCThreadPool *pool); + +/** + * Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, whichever happens first. + */ +bool parcThreadPool_AwaitTermination(PARCThreadPool *pool, PARCTimeout *timeout); + +/** + * Executes the given task sometime in the future. + */ +bool parcThreadPool_Execute(PARCThreadPool *pool, PARCFutureTask *task); + +/** + * Returns the approximate number of threads that are actively executing tasks. + */ +int parcThreadPool_GetActiveCount(const PARCThreadPool *pool); + +/** + * Returns the approximate total number of tasks that have completed execution. + */ +uint64_t parcThreadPool_GetCompletedTaskCount(const PARCThreadPool *pool); + +/** + * Returns the core number of threads. + */ +int parcThreadPool_GetCorePoolSize(const PARCThreadPool *pool); + +/** + * Returns the thread keep-alive time, which is the amount of time that threads in excess of the core pool size may remain idle before being terminated. + */ +PARCTimeout *parcThreadPool_GetKeepAliveTime(const PARCThreadPool *pool); + +/** + * Returns the largest number of threads that have ever simultaneously been in the pool. + */ +int parcThreadPool_GetLargestPoolSize(const PARCThreadPool *pool); + +/** + * Returns the maximum allowed number of threads. + */ +int parcThreadPool_GetMaximumPoolSize(const PARCThreadPool *pool); + +/** + * Returns the current number of threads in the pool. + */ +int parcThreadPool_GetPoolSize(const PARCThreadPool *pool); + +/** + * Returns the task queue used by this executor. + */ +PARCLinkedList *parcThreadPool_GetQueue(const PARCThreadPool *pool); + +/** + * Returns the approximate total number of tasks that have ever been scheduled for execution. + */ +long parcThreadPool_GetTaskCount(const PARCThreadPool *pool); + +/** + * Returns true if this executor has been shut down. + */ +bool parcThreadPool_IsShutdown(const PARCThreadPool *pool); + +/** + * Returns true if all tasks have completed following shut down. + */ +bool parcThreadPool_IsTerminated(const PARCThreadPool *pool); + +/** + * Returns true if this executor is in the process of terminating after shutdown() or shutdownNow() but has not completely terminated. + */ +bool parcThreadPool_IsTerminating(const PARCThreadPool *pool); + +/** + * Starts all core threads, causing them to idly wait for work. + */ +int parcThreadPool_PrestartAllCoreThreads(PARCThreadPool *pool); + +/** + * Starts a core thread, causing it to idly wait for work. + */ +bool parcThreadPool_PrestartCoreThread(PARCThreadPool *pool); + +/** + * Tries to remove from the work queue all Future tasks that have been cancelled. + */ +void parcThreadPool_Purge(PARCThreadPool *pool); + +/** + * Removes this task from the executor's internal queue if it is present, thus causing it not to be run if it has not already started. + */ +bool parcThreadPool_Remove(PARCThreadPool *pool, PARCFutureTask *task); + +/** + * Sets the core number of threads. + */ +void parcThreadPool_SetCorePoolSize(PARCThreadPool *pool, int corePoolSize); + +/** + * Sets the time limit for which threads may remain idle before being terminated. + */ +void parcThreadPool_SetKeepAliveTime(PARCThreadPool *pool, PARCTimeout *timeout); + +/** + * Sets the maximum allowed number of threads. + */ +void parcThreadPool_SetMaximumPoolSize(PARCThreadPool *pool, int maximumPoolSize); + +/** + * Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted. + */ +void parcThreadPool_Shutdown(PARCThreadPool *pool); + +/** + * Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution. + */ +PARCLinkedList *parcThreadPool_ShutdownNow(PARCThreadPool *pool); +#endif diff --git a/libparc/parc/concurrent/parc_Timeout.c b/libparc/parc/concurrent/parc_Timeout.c new file mode 100755 index 00000000..4ea03977 --- /dev/null +++ b/libparc/parc/concurrent/parc_Timeout.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <parc/concurrent/parc_Timeout.h> + + +bool +parcTimeout_Equals(PARCTimeout x, PARCTimeout y) +{ + return x == y; +} diff --git a/libparc/parc/concurrent/parc_Timeout.h b/libparc/parc/concurrent/parc_Timeout.h new file mode 100644 index 00000000..9edc24a0 --- /dev/null +++ b/libparc/parc/concurrent/parc_Timeout.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @ingroup threading + * + */ +#ifndef parc_Timeout_h +#define parc_Timeout_h +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> + +typedef uint64_t PARCTimeout; + +/** + * @def PARCTimeout_Never + * This represents a timeout that will never happen. + */ +#define PARCTimeout_Never NULL + +/* + * @def PARCTimeout_Immediate + * This represents a timeout that immediately happens. + * Equivalent to parcTimeout_NanoSeconds(0) + */ +#define PARCTimeout_Immediate (&(PARCTimeout) { 0 }) + +/* + * @def PARCTimeout_NanoSeconds + * This represents a timeout that will occur in the specified number of nanoseconds. + */ +#define parcTimeout_NanoSeconds(_nsec_) (&(PARCTimeout) { _nsec_ }) + +/* + * @def PARCTimeout_MicroSeconds + * This represents a timeout that will occur in the specified number of microseconds. + */ +#define parcTimeout_MicroSeconds(_usec_) parcTimeout_NanoSeconds(_usec_ * 1000) + +/* + * @def PARCTimeout_MilliSeconds + * This represents a timeout that will occur in the specified number of microseconds. + */ +#define parcTimeout_MilliSeconds(_msec_) parcTimeout_MicroSeconds(_msec_ * 1000) + +/** + * Determine if two `PARCTimeout` instances are equal. + * + * The following equivalence relations on non-null `PARCTimeout` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcTimeout_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcTimeout_Equals(x, y)` must return true if and only if + * `parcTimeout_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcTimeout_Equals(x, y)` returns true and + * `parcTimeout_Equals(y, z)` returns true, + * then `parcTimeout_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcTimeout_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcTimeout_Equals(x, NULL)` must return false. + * + * @param [in] x A valid PARCTimeout instance. + * @param [in] y A valid PARCTimeout instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCTimeout *a = parcTimeout_Create(); + * PARCTimeout *b = parcTimeout_Create(); + * + * if (parcTimeout_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcTimeout_Release(&a); + * parcTimeout_Release(&b); + * } + * @endcode + * @see parcTimeout_HashCode + */ +bool parcTimeout_Equals(PARCTimeout x, PARCTimeout y); + +/** + * Predicate function returning true if the given timeout represents an infinite delay. + * + * @param [in] timeout A pointer to a valid PARCTimeout value. + * + * @return true The given timeout represents an infinite delay. + */ +static inline bool +parcTimeout_IsNever(const PARCTimeout *timeout) +{ + return (timeout == PARCTimeout_Never); +} + +/** + * Predicate function returning true if the given timeout represents an immediate, no-delay timeout. + * + * @param [in] timeout A pointer to a valid PARCTimeout value. + * + * @return true The given timeout represents an immediate, no-delay timeout. + */ +static inline bool +parcTimeout_IsImmediate(const PARCTimeout *timeout) +{ + return parcTimeout_IsNever(timeout) ? false : (*timeout == 0); +} + +/** + * Return the number of nano-seconds in the given PARCTimeout instance. + * + * If the PARCTimeout is never (`parcTimeout_IsNever` returns `true`), the returned value is UINT64_MAX. + * + * @param [in] timeout A pointer to a valid PARCTimeout value. + * + * @return The number of nano-seconds in the given PARCTimeout instance, UINT64_MAX. + */ +static inline uint64_t +parcTimeout_InNanoSeconds(const PARCTimeout *timeout) +{ + return parcTimeout_IsNever(timeout) ? UINT64_MAX : *timeout; +} +#endif /* parc_Timeout_h */ diff --git a/libparc/parc/concurrent/parc_Timer.c b/libparc/parc/concurrent/parc_Timer.c new file mode 100755 index 00000000..e6d84de4 --- /dev/null +++ b/libparc/parc/concurrent/parc_Timer.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Memory.h> + +#include "parc_Timer.h" + +struct PARCTimer { + int delay; +}; + +static void +_parcTimer_Finalize(PARCTimer **instancePtr) +{ + assertNotNull(instancePtr, "Parameter must be a non-null pointer to a PARCTimer pointer."); + + + /* cleanup the instance fields here */ +} + +parcObject_ImplementAcquire(parcTimer, PARCTimer); + +parcObject_ImplementRelease(parcTimer, PARCTimer); + +parcObject_ExtendPARCObject(PARCTimer, _parcTimer_Finalize, parcTimer_Copy, parcTimer_ToString, parcTimer_Equals, parcTimer_Compare, parcTimer_HashCode, parcTimer_ToJSON); + + +void +parcTimer_AssertValid(const PARCTimer *instance) +{ + assertTrue(parcTimer_IsValid(instance), + "PARCTimer is not valid."); +} + +PARCTimer * +parcTimer_Create(void) +{ + PARCTimer *result = parcObject_CreateInstance(PARCTimer); + + if (result != NULL) { + } + + return result; +} + +int +parcTimer_Compare(const PARCTimer *instance, const PARCTimer *other) +{ + int result = 0; + + return result; +} + +PARCTimer * +parcTimer_Copy(const PARCTimer *original) +{ + PARCTimer *result = parcTimer_Create(); + + return result; +} + +void +parcTimer_Display(const PARCTimer *instance, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "PARCTimer@%p {", instance); + /* Call Display() functions for the fields here. */ + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool +parcTimer_Equals(const PARCTimer *x, const PARCTimer *y) +{ + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + return true; + } + + return result; +} + +PARCHashCode +parcTimer_HashCode(const PARCTimer *instance) +{ + PARCHashCode result = 0; + + return result; +} + +bool +parcTimer_IsValid(const PARCTimer *instance) +{ + bool result = false; + + if (instance != NULL) { + result = true; + } + + return result; +} + +PARCJSON * +parcTimer_ToJSON(const PARCTimer *instance) +{ + PARCJSON *result = parcJSON_Create(); + + if (result != NULL) { + } + + return result; +} + +char * +parcTimer_ToString(const PARCTimer *instance) +{ + char *result = parcMemory_Format("PARCTimer@%p\n", instance); + + return result; +} + +void +parcTimer_Cancel(PARCTimer *timer) +{ +} + +int +parcTimer_Purge(PARCTimer *timer) +{ + return 0; +} + +void +parcTimer_ScheduleAtTime(PARCTimer *timer, PARCFutureTask *task, time_t absoluteTime) +{ +} + +void +parcTimer_ScheduleAtTimeAndRepeat(PARCTimer *timer, PARCFutureTask *task, time_t firstTime, long period) +{ +} + +void +parcTimer_ScheduleAfterDelay(PARCTimer *timer, PARCFutureTask *task, long delay) +{ +} + +void +parcTimer_ScheduleAfterDelayAndRepeat(PARCTimer *timer, PARCFutureTask *task, long delay, long period) +{ +} + diff --git a/libparc/parc/concurrent/parc_Timer.h b/libparc/parc/concurrent/parc_Timer.h new file mode 100755 index 00000000..416bfba4 --- /dev/null +++ b/libparc/parc/concurrent/parc_Timer.h @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file parc_Timer.h + * @ingroup threading + * @brief A facility for threads to schedule tasks for future execution in a background thread. + * + * Tasks may be scheduled for one-time execution, or for repeated execution at regular intervals. + * + * Corresponding to each Timer object is a single background thread that is used to execute all of the timer's tasks, + * sequentially. Timer tasks should complete quickly. + * If a timer task takes excessive time to complete, it "hogs" the timer's task execution thread. + * This can, in turn, delay the execution of subsequent tasks, + * which may "bunch up" and execute in rapid succession when (and if) the offending task finally completes. + * + * After the last live reference to a Timer object goes away and all outstanding tasks have completed execution, + * the timer's task execution thread terminates gracefully (and becomes subject to garbage collection). + * However, this can take arbitrarily long to occur. + * By default, the task execution thread does not run as a daemon thread, + * so it is capable of keeping an application from terminating. + * If a caller wants to terminate a timer's task execution thread rapidly, the caller should invoke the timer's cancel method. + * + * If the timer's task execution thread terminates unexpectedly, + * for example, because its stop method is invoked, + * any further attempt to schedule a task on the timer will result in an IllegalStateException, + * as if the timer's cancel method had been invoked. + * + * This class is thread-safe: multiple threads can share a single Timer object without the need for external synchronization. + * + * This class does not offer real-time guarantees: it schedules tasks using the Object.wait(long) method. + * + */ +#ifndef PARCLibrary_parc_Timer +#define PARCLibrary_parc_Timer +#include <stdbool.h> + +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_HashCode.h> +#include <parc/concurrent/parc_FutureTask.h> + +struct PARCTimer; +typedef struct PARCTimer PARCTimer; + +/** + * Increase the number of references to a `PARCTimer` instance. + * + * Note that new `PARCTimer` is not created, + * only that the given `PARCTimer` reference count is incremented. + * Discard the reference by invoking `parcTimer_Release`. + * + * @param [in] instance A pointer to a valid PARCTimer instance. + * + * @return The same value as @p instance. + * + * Example: + * @code + * { + * PARCTimer *a = parcTimer_Create(); + * + * PARCTimer *b = parcTimer_Acquire(); + * + * parcTimer_Release(&a); + * parcTimer_Release(&b); + * } + * @endcode + */ +PARCTimer *parcTimer_Acquire(const PARCTimer *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +# define parcTimer_OptionalAssertValid(_instance_) +#else +# define parcTimer_OptionalAssertValid(_instance_) parcTimer_AssertValid(_instance_) +#endif + +/** + * Assert that the given `PARCTimer` instance is valid. + * + * @param [in] instance A pointer to a valid PARCTimer instance. + * + * Example: + * @code + * { + * PARCTimer *a = parcTimer_Create(); + * + * parcTimer_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * parcTimer_Release(&b); + * } + * @endcode + */ +void parcTimer_AssertValid(const PARCTimer *instance); + +/** + * Create an instance of PARCTimer + * + * <#Paragraphs Of Explanation#> + * + * @return non-NULL A pointer to a valid PARCTimer instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCTimer *a = parcTimer_Create(); + * + * parcTimer_Release(&a); + * } + * @endcode + */ +PARCTimer *parcTimer_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 PARCTimer instance. + * @param [in] other A pointer to a valid PARCTimer 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 + * { + * PARCTimer *a = parcTimer_Create(); + * PARCTimer *b = parcTimer_Create(); + * + * if (parcTimer_Compare(a, b) == 0) { + * printf("Instances are equal.\n"); + * } + * + * parcTimer_Release(&a); + * parcTimer_Release(&b); + * } + * @endcode + * + * @see parcTimer_Equals + */ +int parcTimer_Compare(const PARCTimer *instance, const PARCTimer *other); + +/** + * Create an independent copy the given `PARCBuffer` + * + * A new buffer is created as a complete copy of the original. + * + * @param [in] original A pointer to a valid PARCTimer instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new `PARCTimer` instance. + * + * Example: + * @code + * { + * PARCTimer *a = parcTimer_Create(); + * + * PARCTimer *copy = parcTimer_Copy(&b); + * + * parcTimer_Release(&b); + * parcTimer_Release(©); + * } + * @endcode + */ +PARCTimer *parcTimer_Copy(const PARCTimer *original); + +/** + * Print a human readable representation of the given `PARCTimer`. + * + * @param [in] instance A pointer to a valid PARCTimer instance. + * @param [in] indentation The indentation level to use for printing. + * + * Example: + * @code + * { + * PARCTimer *a = parcTimer_Create(); + * + * parcTimer_Display(a, 0); + * + * parcTimer_Release(&a); + * } + * @endcode + */ +void parcTimer_Display(const PARCTimer *instance, int indentation); + +/** + * Determine if two `PARCTimer` instances are equal. + * + * The following equivalence relations on non-null `PARCTimer` instances are maintained: * + * * It is reflexive: for any non-null reference value x, `parcTimer_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `parcTimer_Equals(x, y)` must return true if and only if + * `parcTimer_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `parcTimer_Equals(x, y)` returns true and + * `parcTimer_Equals(y, z)` returns true, + * then `parcTimer_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `parcTimer_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `parcTimer_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a valid PARCTimer instance. + * @param [in] y A pointer to a valid PARCTimer instance. + * + * @return true The instances x and y are equal. + * + * Example: + * @code + * { + * PARCTimer *a = parcTimer_Create(); + * PARCTimer *b = parcTimer_Create(); + * + * if (parcTimer_Equals(a, b)) { + * printf("Instances are equal.\n"); + * } + * + * parcTimer_Release(&a); + * parcTimer_Release(&b); + * } + * @endcode + * @see parcTimer_HashCode + */ +bool parcTimer_Equals(const PARCTimer *x, const PARCTimer *y); + +/** + * Returns a hash code value for the given instance. + * + * The general contract of `HashCode` is: + * + * Whenever it is invoked on the same instance more than once during an execution of an application, + * the `HashCode` function must consistently return the same value, + * provided no information used in a corresponding comparisons on the instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * If two instances are equal according to the {@link parcTimer_Equals} method, + * then calling the {@link parcTimer_HashCode} method on each of the two instances must produce the same integer result. + * + * It is not required that if two instances are unequal according to the + * {@link parcTimer_Equals} function, + * then calling the `parcTimer_HashCode` + * method on each of the two objects must produce distinct integer results. + * + * @param [in] instance A pointer to a valid PARCTimer instance. + * + * @return The hashcode for the given instance. + * + * Example: + * @code + * { + * PARCTimer *a = parcTimer_Create(); + * + * PARCHashCode hashValue = parcTimer_HashCode(buffer); + * parcTimer_Release(&a); + * } + * @endcode + */ +PARCHashCode parcTimer_HashCode(const PARCTimer *instance); + +/** + * Determine if an instance of `PARCTimer` 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 PARCTimer instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * PARCTimer *a = parcTimer_Create(); + * + * if (parcTimer_IsValid(a)) { + * printf("Instance is valid.\n"); + * } + * + * parcTimer_Release(&a); + * } + * @endcode + * + */ +bool parcTimer_IsValid(const PARCTimer *instance); + +/** + * Release a previously acquired reference to the given `PARCTimer` 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 + * { + * PARCTimer *a = parcTimer_Create(); + * + * parcTimer_Release(&a); + * } + * @endcode + */ +void parcTimer_Release(PARCTimer **instancePtr); + +/** + * Create a `PARCJSON` instance (representation) of the given object. + * + * @param [in] instance A pointer to a valid PARCTimer instance. + * + * @return NULL Memory could not be allocated to contain the `PARCJSON` instance. + * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate(). + * + * Example: + * @code + * { + * PARCTimer *a = parcTimer_Create(); + * + * PARCJSON *json = parcTimer_ToJSON(a); + * + * printf("JSON representation: %s\n", parcJSON_ToString(json)); + * parcJSON_Release(&json); + * + * parcTimer_Release(&a); + * } + * @endcode + */ +PARCJSON *parcTimer_ToJSON(const PARCTimer *instance); + +/** + * Produce a null-terminated string representation of the specified `PARCTimer`. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to a valid PARCTimer instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}. + * + * Example: + * @code + * { + * PARCTimer *a = parcTimer_Create(); + * + * char *string = parcTimer_ToString(a); + * + * parcTimer_Release(&a); + * + * parcMemory_Deallocate(&string); + * } + * @endcode + * + * @see parcTimer_Display + */ +char *parcTimer_ToString(const PARCTimer *timer); + +/** + * Terminates this timer, discarding any currently scheduled tasks. + * + * Does not interfere with a currently executing task (if it exists). + * Once a timer has been terminated, its execution thread terminates gracefully, + * and no more tasks may be scheduled on it. + * + * Note that calling this method from within the run method of a timer task that was invoked + * by this timer absolutely guarantees that the ongoing task execution is the last task execution + * that will ever be performed by this timer. + * + * This method may be called repeatedly; the second and subsequent calls have no effect. + */ +void parcTimer_Cancel(PARCTimer *timer); + +/** + * Removes all cancelled tasks from this timer's task queue. + * + * Calling this method has no effect on the behavior of the timer, + * but eliminates the references to the cancelled tasks from the queue. + * If there are no external references to these tasks, they become eligible for garbage collection. + * + * Most programs will have no need to call this method. + * It is designed for use by the rare application that cancels a large number of tasks. + * Calling this method trades time for space: the runtime of the method may be proportional to + * n + c log n, where n is the number of tasks in the queue and c is the number of cancelled tasks. + * + * It is permissible to call this method from within a task scheduled on this timer. + * + * @returns the number of tasks removed from the queue. + */ +int parcTimer_Purge(PARCTimer *timer); + +/** + * Schedules the specified task for execution at the specified time. + */ +void parcTimer_ScheduleAtTime(PARCTimer *timer, PARCFutureTask *task, time_t absoluteTime); + +/** + * Schedules the specified task for repeated fixed-delay execution, beginning at the specified time. + */ +void parcTimer_ScheduleAtTimeAndRepeat(PARCTimer *timer, PARCFutureTask *task, time_t firstTime, long period); + +/** + * Schedules the specified task for execution after the specified delay. + */ +void parcTimer_ScheduleAfterDelay(PARCTimer *timer, PARCFutureTask *task, long delay); + +/** + * Schedules the specified task for repeated fixed-delay execution, beginning after the specified delay. + */ +void parcTimer_ScheduleAfterDelayAndRepeat(PARCTimer *timer, PARCFutureTask *task, long delay, long period); +#endif diff --git a/libparc/parc/concurrent/test/.gitignore b/libparc/parc/concurrent/test/.gitignore new file mode 100644 index 00000000..2c9fe4f5 --- /dev/null +++ b/libparc/parc/concurrent/test/.gitignore @@ -0,0 +1,10 @@ +test_parc_Notifier +test_parc_RingBuffer_1x1 +test_parc_RingBuffer_NxM +test_parc_AtomicUint64 +test_parc_Barrier +test_parc_AtomicUint16 +test_parc_AtomicUint32 +test_parc_AtomicUint8 +test_parc_Synchronizer +test_parc_Lock diff --git a/libparc/parc/concurrent/test/CMakeLists.txt b/libparc/parc/concurrent/test/CMakeLists.txt new file mode 100644 index 00000000..092d0202 --- /dev/null +++ b/libparc/parc/concurrent/test/CMakeLists.txt @@ -0,0 +1,25 @@ +set(TestsExpectedToPass + test_parc_AtomicUint16 + test_parc_AtomicUint32 + test_parc_AtomicUint64 + test_parc_AtomicUint8 + test_parc_FutureTask + test_parc_Lock + test_parc_Notifier + test_parc_RingBuffer_1x1 + test_parc_RingBuffer_NxM + test_parc_ScheduledTask + test_parc_ScheduledThreadPool + test_parc_Synchronizer + test_parc_Thread + test_parc_ThreadPool + test_parc_Timer + ) + +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() diff --git a/libparc/parc/concurrent/test/test_parc_AtomicUint16.c b/libparc/parc/concurrent/test/test_parc_AtomicUint16.c new file mode 100644 index 00000000..ed97cc95 --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_AtomicUint16.c @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include "../parc_AtomicUint16.c" + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> + +#include <inttypes.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_AtomicUint16) +{ + // 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(Global); + LONGBOW_RUN_TEST_FIXTURE(Macros); + LONGBOW_RUN_TEST_FIXTURE(Performance); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_AtomicUint16) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_AtomicUint16) +{ + 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) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(7); + assertNotNull(instance, "Expeced non-null result from parcAtomicUint16_Create();"); + + parcObjectTesting_AssertAcquireReleaseContract(parcAtomicUint16_Acquire, instance); + + parcAtomicUint16_Release(&instance); + assertNull(instance, "Expected null result from parcAtomicUint16_Release();"); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint16_Compare); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint16_Copy); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint16_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint16_HashCode); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint16_IsValid); + + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint16_SubtractImpl); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint16_AddImpl); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint16_CompareAndSwapImpl); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint16_Compare) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(7); + PARCAtomicUint16 *high = parcAtomicUint16_Create(8); + PARCAtomicUint16 *low = parcAtomicUint16_Create(6); + PARCAtomicUint16 *equal = parcAtomicUint16_Create(7); + + int actual = parcAtomicUint16_Compare(instance, high); + assertTrue(actual < 0, "Expected < 0"); + actual = parcAtomicUint16_Compare(instance, low); + assertTrue(actual > 0, "Expected > 0"); + actual = parcAtomicUint16_Compare(instance, equal); + assertTrue(actual == 0, "Expected == 0"); + + parcAtomicUint16_Release(&instance); + parcAtomicUint16_Release(&high); + parcAtomicUint16_Release(&low); + parcAtomicUint16_Release(&equal); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint16_Copy) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(7); + PARCAtomicUint16 *copy = parcAtomicUint16_Copy(instance); + + assertTrue(parcAtomicUint16_Equals(instance, copy), "Expected the copy to be equal to the original"); + + parcAtomicUint16_Release(&instance); + parcAtomicUint16_Release(©); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint16_Equals) +{ + PARCAtomicUint16 *x = parcAtomicUint16_Create(7); + PARCAtomicUint16 *y = parcAtomicUint16_Create(7); + PARCAtomicUint16 *z = parcAtomicUint16_Create(7); + PARCAtomicUint16 *u1 = parcAtomicUint16_Create(6); + + parcObjectTesting_AssertEquals(x, y, z, u1, NULL); + + parcAtomicUint16_Release(&x); + parcAtomicUint16_Release(&y); + parcAtomicUint16_Release(&z); + parcAtomicUint16_Release(&u1); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint16_HashCode) +{ + PARCAtomicUint16 *x = parcAtomicUint16_Create(7); + parcAtomicUint16_HashCode(x); + parcAtomicUint16_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint16_IsValid) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(7); + assertTrue(parcAtomicUint16_IsValid(instance), "Expected parcAtomicUint16_Create to result in a valid instance."); + + parcAtomicUint16_Release(&instance); + assertFalse(parcAtomicUint16_IsValid(instance), "Expected parcAtomicUint16_Release to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint16_SubtractImpl) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(7); + + parcAtomicUint16_SubtractImpl(instance, 1); + + uint16_t actual = parcAtomicUint16_GetValue(instance); + + assertTrue(actual == 6, "Expected 6, actual %" PRIu16, actual); + parcAtomicUint16_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint16_AddImpl) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(7); + + parcAtomicUint16_AddImpl(instance, 1); + + uint16_t actual = parcAtomicUint16_GetValue(instance); + + assertTrue(actual == 8, "Expected 8, actual %" PRIu16, actual); + parcAtomicUint16_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint16_CompareAndSwapImpl) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(7); + + bool actual = parcAtomicUint16_CompareAndSwapImpl(instance, 7, 8); + + assertTrue(actual, "Expected parcAtomicUint16_CompareAndSwapImpl to return true"); + parcAtomicUint16_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Macros) +{ + LONGBOW_RUN_TEST_CASE(Macros, parcAtomicUint16_Subtract); + LONGBOW_RUN_TEST_CASE(Macros, parcAtomicUint16_Add); + LONGBOW_RUN_TEST_CASE(Macros, parcAtomicUint16_CompareAndSwap); +} + +LONGBOW_TEST_FIXTURE_SETUP(Macros) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Macros) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Macros, parcAtomicUint16_Subtract) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(7); + + parcAtomicUint16_Subtract(instance, 1); + + uint16_t actual = parcAtomicUint16_GetValue(instance); + + assertTrue(actual == 6, "Expected 6, actual %" PRIu16, actual); + parcAtomicUint16_Release(&instance); +} + +LONGBOW_TEST_CASE(Macros, parcAtomicUint16_Add) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(7); + + parcAtomicUint16_Add(instance, 1); + + uint16_t actual = parcAtomicUint16_GetValue(instance); + + assertTrue(actual == 8, "Expected 8, actual %" PRIu16, actual); + parcAtomicUint16_Release(&instance); +} + +LONGBOW_TEST_CASE(Macros, parcAtomicUint16_CompareAndSwap) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(7); + + bool actual = parcAtomicUint16_CompareAndSwap(instance, 7, 8); + + assertTrue(actual, "Expected parcAtomicUint16_CompareAndSwap to return true"); + parcAtomicUint16_Release(&instance); +} + + +LONGBOW_TEST_FIXTURE_OPTIONS(Performance, .enabled = false) +{ + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint16_Subtract_MACRO); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint16_Add_MACRO); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint16_CompareAndSwap_MACRO); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint16_SubtractImpl); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint16_AddImpl); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint16_CompareAndSwapImpl); +} + +LONGBOW_TEST_FIXTURE_SETUP(Performance) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Performance) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint16_Subtract_MACRO) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(65535); + + while (parcAtomicUint16_Subtract(instance, 1) > 0) { + ; + } + + parcAtomicUint16_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint16_Add_MACRO) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(1); + + while (parcAtomicUint16_Add(instance, 1) < 65535) { + ; + } + + parcAtomicUint16_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint16_CompareAndSwap_MACRO) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(0); + + for (uint16_t i = 0; i < 65535; i++) { + bool actual = parcAtomicUint16_CompareAndSwap(instance, i, i + 1); + assertTrue(actual, "Expected parcAtomicUint16_CompareAndSwap to return true"); + } + + parcAtomicUint16_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint16_SubtractImpl) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(65535); + + while (parcAtomicUint16_SubtractImpl(instance, 1) > 0) { + ; + } + + parcAtomicUint16_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint16_AddImpl) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(1); + + while (parcAtomicUint16_AddImpl(instance, 1) < 65535) { + ; + } + + parcAtomicUint16_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint16_CompareAndSwapImpl) +{ + PARCAtomicUint16 *instance = parcAtomicUint16_Create(0); + + for (uint16_t i = 0; i < 65535; i++) { + bool actual = parcAtomicUint16_CompareAndSwapImpl(instance, i, i + 1); + assertTrue(actual, "Expected parcAtomicUint16_CompareAndSwapImpl to return true"); + } + + parcAtomicUint16_Release(&instance); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_AtomicUint16); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/concurrent/test/test_parc_AtomicUint32.c b/libparc/parc/concurrent/test/test_parc_AtomicUint32.c new file mode 100644 index 00000000..1649cf1c --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_AtomicUint32.c @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include "../parc_AtomicUint64.c" + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> + +#include <inttypes.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_AtomicUint64) +{ + // 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(Global); + LONGBOW_RUN_TEST_FIXTURE(Macros); + LONGBOW_RUN_TEST_FIXTURE(Performance); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_AtomicUint64) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_AtomicUint64) +{ + 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) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + assertNotNull(instance, "Expeced non-null result from parcAtomicUint64_Create();"); + + parcObjectTesting_AssertAcquireReleaseContract(parcAtomicUint64_Acquire, instance); + + parcAtomicUint64_Release(&instance); + assertNull(instance, "Expected null result from parcAtomicUint64_Release();"); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_Compare); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_Copy); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_HashCode); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_IsValid); + + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_SubtractImpl); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_AddImpl); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_CompareAndSwapImpl); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_Compare) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + PARCAtomicUint64 *high = parcAtomicUint64_Create(8); + PARCAtomicUint64 *low = parcAtomicUint64_Create(6); + PARCAtomicUint64 *equal = parcAtomicUint64_Create(7); + + int actual = parcAtomicUint64_Compare(instance, high); + assertTrue(actual < 0, "Expected < 0"); + actual = parcAtomicUint64_Compare(instance, low); + assertTrue(actual > 0, "Expected > 0"); + actual = parcAtomicUint64_Compare(instance, equal); + assertTrue(actual == 0, "Expected == 0"); + + parcAtomicUint64_Release(&instance); + parcAtomicUint64_Release(&high); + parcAtomicUint64_Release(&low); + parcAtomicUint64_Release(&equal); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_Copy) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + PARCAtomicUint64 *copy = parcAtomicUint64_Copy(instance); + + assertTrue(parcAtomicUint64_Equals(instance, copy), "Expected the copy to be equal to the original"); + + parcAtomicUint64_Release(&instance); + parcAtomicUint64_Release(©); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_Equals) +{ + PARCAtomicUint64 *x = parcAtomicUint64_Create(7); + PARCAtomicUint64 *y = parcAtomicUint64_Create(7); + PARCAtomicUint64 *z = parcAtomicUint64_Create(7); + PARCAtomicUint64 *u1 = parcAtomicUint64_Create(6); + + parcObjectTesting_AssertEquals(x, y, z, u1, NULL); + + parcAtomicUint64_Release(&x); + parcAtomicUint64_Release(&y); + parcAtomicUint64_Release(&z); + parcAtomicUint64_Release(&u1); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_HashCode) +{ + PARCAtomicUint64 *x = parcAtomicUint64_Create(7); + parcAtomicUint64_HashCode(x); + parcAtomicUint64_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_IsValid) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + assertTrue(parcAtomicUint64_IsValid(instance), "Expected parcAtomicUint64_Create to result in a valid instance."); + + parcAtomicUint64_Release(&instance); + assertFalse(parcAtomicUint64_IsValid(instance), "Expected parcAtomicUint64_Release to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_SubtractImpl) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + + parcAtomicUint64_SubtractImpl(instance, 1); + + uint64_t actual = parcAtomicUint64_GetValue(instance); + + assertTrue(actual == 6, "Expected 6, actual %" PRIu64, actual); + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_AddImpl) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + + parcAtomicUint64_AddImpl(instance, 1); + + uint64_t actual = parcAtomicUint64_GetValue(instance); + + assertTrue(actual == 8, "Expected 8, actual %" PRIu64, actual); + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_CompareAndSwapImpl) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + + bool actual = parcAtomicUint64_CompareAndSwapImpl(instance, 7, 8); + + assertTrue(actual, "Expected parcAtomicUint64_CompareAndSwapImpl to return true"); + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Macros) +{ + LONGBOW_RUN_TEST_CASE(Macros, parcAtomicUint64_Subtract); + LONGBOW_RUN_TEST_CASE(Macros, parcAtomicUint64_Add); + LONGBOW_RUN_TEST_CASE(Macros, parcAtomicUint64_CompareAndSwap); +} + +LONGBOW_TEST_FIXTURE_SETUP(Macros) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Macros) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Macros, parcAtomicUint64_Subtract) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + + parcAtomicUint64_Subtract(instance, 1); + + uint64_t actual = parcAtomicUint64_GetValue(instance); + + assertTrue(actual == 6, "Expected 6, actual %" PRIu64, actual); + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Macros, parcAtomicUint64_Add) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + + parcAtomicUint64_Add(instance, 1); + + uint64_t actual = parcAtomicUint64_GetValue(instance); + + assertTrue(actual == 8, "Expected 8, actual %" PRIu64, actual); + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Macros, parcAtomicUint64_CompareAndSwap) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + + bool actual = parcAtomicUint64_CompareAndSwap(instance, 7, 8); + + assertTrue(actual, "Expected parcAtomicUint64_CompareAndSwap to return true"); + parcAtomicUint64_Release(&instance); +} + + +LONGBOW_TEST_FIXTURE_OPTIONS(Performance, .enabled = false) +{ + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint64_Subtract_MACRO); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint64_Add_MACRO); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint64_CompareAndSwap_MACRO); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint64_SubtractImpl); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint64_AddImpl); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint64_CompareAndSwapImpl); +} + +LONGBOW_TEST_FIXTURE_SETUP(Performance) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Performance) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint64_Subtract_MACRO) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(100000000); + + while (parcAtomicUint64_Subtract(instance, 1) > 0) { + ; + } + + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint64_Add_MACRO) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(1); + + while (parcAtomicUint64_Add(instance, 1) < 100000000) { + ; + } + + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint64_CompareAndSwap_MACRO) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(0); + + for (uint64_t i = 0; i < 100000000; i++) { + bool actual = parcAtomicUint64_CompareAndSwap(instance, i, i + 1); + assertTrue(actual, "Expected parcAtomicUint64_CompareAndSwap to return true"); + } + + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint64_SubtractImpl) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(100000000); + + while (parcAtomicUint64_SubtractImpl(instance, 1) > 0) { + ; + } + + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint64_AddImpl) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(1); + + while (parcAtomicUint64_AddImpl(instance, 1) < 100000000) { + ; + } + + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint64_CompareAndSwapImpl) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(0); + + for (uint64_t i = 0; i < 100000000; i++) { + bool actual = parcAtomicUint64_CompareAndSwapImpl(instance, i, i + 1); + assertTrue(actual, "Expected parcAtomicUint64_CompareAndSwapImpl to return true"); + } + + parcAtomicUint64_Release(&instance); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_AtomicUint64); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/concurrent/test/test_parc_AtomicUint64.c b/libparc/parc/concurrent/test/test_parc_AtomicUint64.c new file mode 100644 index 00000000..1649cf1c --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_AtomicUint64.c @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include "../parc_AtomicUint64.c" + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> + +#include <inttypes.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_AtomicUint64) +{ + // 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(Global); + LONGBOW_RUN_TEST_FIXTURE(Macros); + LONGBOW_RUN_TEST_FIXTURE(Performance); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_AtomicUint64) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_AtomicUint64) +{ + 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) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + assertNotNull(instance, "Expeced non-null result from parcAtomicUint64_Create();"); + + parcObjectTesting_AssertAcquireReleaseContract(parcAtomicUint64_Acquire, instance); + + parcAtomicUint64_Release(&instance); + assertNull(instance, "Expected null result from parcAtomicUint64_Release();"); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_Compare); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_Copy); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_HashCode); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_IsValid); + + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_SubtractImpl); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_AddImpl); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint64_CompareAndSwapImpl); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_Compare) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + PARCAtomicUint64 *high = parcAtomicUint64_Create(8); + PARCAtomicUint64 *low = parcAtomicUint64_Create(6); + PARCAtomicUint64 *equal = parcAtomicUint64_Create(7); + + int actual = parcAtomicUint64_Compare(instance, high); + assertTrue(actual < 0, "Expected < 0"); + actual = parcAtomicUint64_Compare(instance, low); + assertTrue(actual > 0, "Expected > 0"); + actual = parcAtomicUint64_Compare(instance, equal); + assertTrue(actual == 0, "Expected == 0"); + + parcAtomicUint64_Release(&instance); + parcAtomicUint64_Release(&high); + parcAtomicUint64_Release(&low); + parcAtomicUint64_Release(&equal); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_Copy) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + PARCAtomicUint64 *copy = parcAtomicUint64_Copy(instance); + + assertTrue(parcAtomicUint64_Equals(instance, copy), "Expected the copy to be equal to the original"); + + parcAtomicUint64_Release(&instance); + parcAtomicUint64_Release(©); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_Equals) +{ + PARCAtomicUint64 *x = parcAtomicUint64_Create(7); + PARCAtomicUint64 *y = parcAtomicUint64_Create(7); + PARCAtomicUint64 *z = parcAtomicUint64_Create(7); + PARCAtomicUint64 *u1 = parcAtomicUint64_Create(6); + + parcObjectTesting_AssertEquals(x, y, z, u1, NULL); + + parcAtomicUint64_Release(&x); + parcAtomicUint64_Release(&y); + parcAtomicUint64_Release(&z); + parcAtomicUint64_Release(&u1); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_HashCode) +{ + PARCAtomicUint64 *x = parcAtomicUint64_Create(7); + parcAtomicUint64_HashCode(x); + parcAtomicUint64_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_IsValid) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + assertTrue(parcAtomicUint64_IsValid(instance), "Expected parcAtomicUint64_Create to result in a valid instance."); + + parcAtomicUint64_Release(&instance); + assertFalse(parcAtomicUint64_IsValid(instance), "Expected parcAtomicUint64_Release to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_SubtractImpl) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + + parcAtomicUint64_SubtractImpl(instance, 1); + + uint64_t actual = parcAtomicUint64_GetValue(instance); + + assertTrue(actual == 6, "Expected 6, actual %" PRIu64, actual); + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_AddImpl) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + + parcAtomicUint64_AddImpl(instance, 1); + + uint64_t actual = parcAtomicUint64_GetValue(instance); + + assertTrue(actual == 8, "Expected 8, actual %" PRIu64, actual); + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint64_CompareAndSwapImpl) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + + bool actual = parcAtomicUint64_CompareAndSwapImpl(instance, 7, 8); + + assertTrue(actual, "Expected parcAtomicUint64_CompareAndSwapImpl to return true"); + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Macros) +{ + LONGBOW_RUN_TEST_CASE(Macros, parcAtomicUint64_Subtract); + LONGBOW_RUN_TEST_CASE(Macros, parcAtomicUint64_Add); + LONGBOW_RUN_TEST_CASE(Macros, parcAtomicUint64_CompareAndSwap); +} + +LONGBOW_TEST_FIXTURE_SETUP(Macros) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Macros) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Macros, parcAtomicUint64_Subtract) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + + parcAtomicUint64_Subtract(instance, 1); + + uint64_t actual = parcAtomicUint64_GetValue(instance); + + assertTrue(actual == 6, "Expected 6, actual %" PRIu64, actual); + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Macros, parcAtomicUint64_Add) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + + parcAtomicUint64_Add(instance, 1); + + uint64_t actual = parcAtomicUint64_GetValue(instance); + + assertTrue(actual == 8, "Expected 8, actual %" PRIu64, actual); + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Macros, parcAtomicUint64_CompareAndSwap) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(7); + + bool actual = parcAtomicUint64_CompareAndSwap(instance, 7, 8); + + assertTrue(actual, "Expected parcAtomicUint64_CompareAndSwap to return true"); + parcAtomicUint64_Release(&instance); +} + + +LONGBOW_TEST_FIXTURE_OPTIONS(Performance, .enabled = false) +{ + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint64_Subtract_MACRO); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint64_Add_MACRO); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint64_CompareAndSwap_MACRO); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint64_SubtractImpl); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint64_AddImpl); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint64_CompareAndSwapImpl); +} + +LONGBOW_TEST_FIXTURE_SETUP(Performance) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Performance) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint64_Subtract_MACRO) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(100000000); + + while (parcAtomicUint64_Subtract(instance, 1) > 0) { + ; + } + + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint64_Add_MACRO) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(1); + + while (parcAtomicUint64_Add(instance, 1) < 100000000) { + ; + } + + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint64_CompareAndSwap_MACRO) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(0); + + for (uint64_t i = 0; i < 100000000; i++) { + bool actual = parcAtomicUint64_CompareAndSwap(instance, i, i + 1); + assertTrue(actual, "Expected parcAtomicUint64_CompareAndSwap to return true"); + } + + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint64_SubtractImpl) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(100000000); + + while (parcAtomicUint64_SubtractImpl(instance, 1) > 0) { + ; + } + + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint64_AddImpl) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(1); + + while (parcAtomicUint64_AddImpl(instance, 1) < 100000000) { + ; + } + + parcAtomicUint64_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint64_CompareAndSwapImpl) +{ + PARCAtomicUint64 *instance = parcAtomicUint64_Create(0); + + for (uint64_t i = 0; i < 100000000; i++) { + bool actual = parcAtomicUint64_CompareAndSwapImpl(instance, i, i + 1); + assertTrue(actual, "Expected parcAtomicUint64_CompareAndSwapImpl to return true"); + } + + parcAtomicUint64_Release(&instance); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_AtomicUint64); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/concurrent/test/test_parc_AtomicUint8.c b/libparc/parc/concurrent/test/test_parc_AtomicUint8.c new file mode 100644 index 00000000..5b89d052 --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_AtomicUint8.c @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include "../parc_AtomicUint8.c" + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> + +#include <inttypes.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_AtomicUint8) +{ + // 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(Global); + LONGBOW_RUN_TEST_FIXTURE(Macros); + LONGBOW_RUN_TEST_FIXTURE(Performance); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_AtomicUint8) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_AtomicUint8) +{ + 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) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(7); + assertNotNull(instance, "Expeced non-null result from parcAtomicUint8_Create();"); + + parcObjectTesting_AssertAcquireReleaseContract(parcAtomicUint8_Acquire, instance); + + parcAtomicUint8_Release(&instance); + assertNull(instance, "Expected null result from parcAtomicUint8_Release();"); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint8_Compare); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint8_Copy); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint8_Equals); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint8_HashCode); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint8_IsValid); + + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint8_SubtractImpl); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint8_AddImpl); + LONGBOW_RUN_TEST_CASE(Global, parcAtomicUint8_CompareAndSwapImpl); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint8_Compare) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(7); + PARCAtomicUint8 *high = parcAtomicUint8_Create(8); + PARCAtomicUint8 *low = parcAtomicUint8_Create(6); + PARCAtomicUint8 *equal = parcAtomicUint8_Create(7); + + int actual = parcAtomicUint8_Compare(instance, high); + assertTrue(actual < 0, "Expected < 0"); + actual = parcAtomicUint8_Compare(instance, low); + assertTrue(actual > 0, "Expected > 0"); + actual = parcAtomicUint8_Compare(instance, equal); + assertTrue(actual == 0, "Expected == 0"); + + parcAtomicUint8_Release(&instance); + parcAtomicUint8_Release(&high); + parcAtomicUint8_Release(&low); + parcAtomicUint8_Release(&equal); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint8_Copy) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(7); + PARCAtomicUint8 *copy = parcAtomicUint8_Copy(instance); + + assertTrue(parcAtomicUint8_Equals(instance, copy), "Expected the copy to be equal to the original"); + + parcAtomicUint8_Release(&instance); + parcAtomicUint8_Release(©); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint8_Equals) +{ + PARCAtomicUint8 *x = parcAtomicUint8_Create(7); + PARCAtomicUint8 *y = parcAtomicUint8_Create(7); + PARCAtomicUint8 *z = parcAtomicUint8_Create(7); + PARCAtomicUint8 *u1 = parcAtomicUint8_Create(6); + + parcObjectTesting_AssertEquals(x, y, z, u1, NULL); + + parcAtomicUint8_Release(&x); + parcAtomicUint8_Release(&y); + parcAtomicUint8_Release(&z); + parcAtomicUint8_Release(&u1); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint8_HashCode) +{ + PARCAtomicUint8 *x = parcAtomicUint8_Create(7); + parcAtomicUint8_HashCode(x); + parcAtomicUint8_Release(&x); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint8_IsValid) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(7); + assertTrue(parcAtomicUint8_IsValid(instance), "Expected parcAtomicUint8_Create to result in a valid instance."); + + parcAtomicUint8_Release(&instance); + assertFalse(parcAtomicUint8_IsValid(instance), "Expected parcAtomicUint8_Release to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint8_SubtractImpl) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(7); + + parcAtomicUint8_SubtractImpl(instance, 1); + + uint8_t actual = parcAtomicUint8_GetValue(instance); + + assertTrue(actual == 6, "Expected 6, actual %" PRIu8, actual); + parcAtomicUint8_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint8_AddImpl) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(7); + + parcAtomicUint8_AddImpl(instance, 1); + + uint8_t actual = parcAtomicUint8_GetValue(instance); + + assertTrue(actual == 8, "Expected 8, actual %" PRIu8, actual); + parcAtomicUint8_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcAtomicUint8_CompareAndSwapImpl) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(7); + + bool actual = parcAtomicUint8_CompareAndSwapImpl(instance, 7, 8); + + assertTrue(actual, "Expected parcAtomicUint8_CompareAndSwapImpl to return true"); + parcAtomicUint8_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Macros) +{ + LONGBOW_RUN_TEST_CASE(Macros, parcAtomicUint8_Subtract); + LONGBOW_RUN_TEST_CASE(Macros, parcAtomicUint8_Add); + LONGBOW_RUN_TEST_CASE(Macros, parcAtomicUint8_CompareAndSwap); +} + +LONGBOW_TEST_FIXTURE_SETUP(Macros) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Macros) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Macros, parcAtomicUint8_Subtract) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(7); + + parcAtomicUint8_Subtract(instance, 1); + + uint8_t actual = parcAtomicUint8_GetValue(instance); + + assertTrue(actual == 6, "Expected 6, actual %" PRIu8, actual); + parcAtomicUint8_Release(&instance); +} + +LONGBOW_TEST_CASE(Macros, parcAtomicUint8_Add) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(7); + + parcAtomicUint8_Add(instance, 1); + + uint8_t actual = parcAtomicUint8_GetValue(instance); + + assertTrue(actual == 8, "Expected 8, actual %" PRIu8, actual); + parcAtomicUint8_Release(&instance); +} + +LONGBOW_TEST_CASE(Macros, parcAtomicUint8_CompareAndSwap) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(7); + + bool actual = parcAtomicUint8_CompareAndSwap(instance, 7, 8); + + assertTrue(actual, "Expected parcAtomicUint8_CompareAndSwap to return true"); + parcAtomicUint8_Release(&instance); +} + + +LONGBOW_TEST_FIXTURE_OPTIONS(Performance, .enabled = false) +{ + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint8_Subtract_MACRO); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint8_Add_MACRO); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint8_CompareAndSwap_MACRO); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint8_SubtractImpl); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint8_AddImpl); + LONGBOW_RUN_TEST_CASE(Performance, parcAtomicUint8_CompareAndSwapImpl); +} + +LONGBOW_TEST_FIXTURE_SETUP(Performance) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Performance) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint8_Subtract_MACRO) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(255); + + while (parcAtomicUint8_Subtract(instance, 1) > 0) { + ; + } + + parcAtomicUint8_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint8_Add_MACRO) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(1); + + while (parcAtomicUint8_Add(instance, 1) < 255) { + ; + } + + parcAtomicUint8_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint8_CompareAndSwap_MACRO) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(0); + + for (uint8_t i = 0; i < 255; i++) { + bool actual = parcAtomicUint8_CompareAndSwap(instance, i, i + 1); + assertTrue(actual, "Expected parcAtomicUint8_CompareAndSwap to return true"); + } + + parcAtomicUint8_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint8_SubtractImpl) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(255); + + while (parcAtomicUint8_SubtractImpl(instance, 1) > 0) { + ; + } + + parcAtomicUint8_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint8_AddImpl) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(1); + + while (parcAtomicUint8_AddImpl(instance, 1) < 255) { + ; + } + + parcAtomicUint8_Release(&instance); +} + +LONGBOW_TEST_CASE(Performance, parcAtomicUint8_CompareAndSwapImpl) +{ + PARCAtomicUint8 *instance = parcAtomicUint8_Create(0); + + for (uint8_t i = 0; i < 255; i++) { + bool actual = parcAtomicUint8_CompareAndSwapImpl(instance, i, i + 1); + assertTrue(actual, "Expected parcAtomicUint8_CompareAndSwapImpl to return true"); + } + + parcAtomicUint8_Release(&instance); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_AtomicUint8); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/concurrent/test/test_parc_FutureTask.c b/libparc/parc/concurrent/test/test_parc_FutureTask.c new file mode 100644 index 00000000..c30d2cae --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_FutureTask.c @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + */ +#include "../parc_FutureTask.c" + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_FutureTask) +{ + // 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(Object); + LONGBOW_RUN_TEST_FIXTURE(Specialization); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_FutureTask) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_FutureTask) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(CreateAcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, CreateRelease); + LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, CreateRelease_PARCObject); +} + +LONGBOW_TEST_FIXTURE_SETUP(CreateAcquireRelease) +{ + longBowTestCase_SetInt(testCase, "initialAllocations", parcMemory_Outstanding()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(CreateAcquireRelease) +{ + int initialAllocations = longBowTestCase_GetInt(testCase, "initialAllocations"); + + if (!parcMemoryTesting_ExpectedOutstanding(initialAllocations, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +static void * +_function(PARCFutureTask *task, void *parameter) +{ + return parameter; +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, CreateRelease) +{ + PARCFutureTask *instance = parcFutureTask_Create(_function, _function); + assertNotNull(instance, "Expected non-null result from parcFutureTask_Create(_function, _function);"); + + parcObjectTesting_AssertAcquireReleaseContract(parcFutureTask_Acquire, instance); + + parcFutureTask_Release(&instance); + assertNull(instance, "Expected null result from parcFutureTask_Release();"); +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, CreateRelease_PARCObject) +{ + PARCBuffer *object = parcBuffer_Allocate(10); + + PARCFutureTask *instance = parcFutureTask_Create(_function, object); + parcBuffer_Release(&object); + + assertNotNull(instance, "Expected non-null result from parcFutureTask_Create(_function, object);"); + + parcObjectTesting_AssertAcquireReleaseContract(parcFutureTask_Acquire, instance); + + parcFutureTask_Release(&instance); + assertNull(instance, "Expected null result from parcFutureTask_Release();"); +} + +LONGBOW_TEST_FIXTURE(Object) +{ + LONGBOW_RUN_TEST_CASE(Object, parcFutureTask_Compare); + LONGBOW_RUN_TEST_CASE(Object, parcFutureTask_Copy); + LONGBOW_RUN_TEST_CASE(Object, parcFutureTask_Display); + LONGBOW_RUN_TEST_CASE(Object, parcFutureTask_Equals); + LONGBOW_RUN_TEST_CASE(Object, parcFutureTask_HashCode); + LONGBOW_RUN_TEST_CASE(Object, parcFutureTask_IsValid); + LONGBOW_RUN_TEST_CASE(Object, parcFutureTask_ToJSON); + LONGBOW_RUN_TEST_CASE(Object, parcFutureTask_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Object) +{ + longBowTestCase_SetInt(testCase, "initialAllocations", parcMemory_Outstanding()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Object) +{ + int initialAllocations = longBowTestCase_GetInt(testCase, "initialAllocations"); + + if (!parcMemoryTesting_ExpectedOutstanding(initialAllocations, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Object, parcFutureTask_Compare) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Object, parcFutureTask_Copy) +{ + PARCFutureTask *instance = parcFutureTask_Create(_function, _function); + PARCFutureTask *copy = parcFutureTask_Copy(instance); + assertTrue(parcFutureTask_Equals(instance, copy), "Expected the copy to be equal to the original"); + + parcFutureTask_Release(&instance); + parcFutureTask_Release(©); +} + +LONGBOW_TEST_CASE(Object, parcFutureTask_Display) +{ + PARCFutureTask *instance = parcFutureTask_Create(_function, _function); + parcFutureTask_Display(instance, 0); + parcFutureTask_Release(&instance); +} + +LONGBOW_TEST_CASE(Object, parcFutureTask_Equals) +{ + PARCFutureTask *x = parcFutureTask_Create(_function, _function); + PARCFutureTask *y = parcFutureTask_Create(_function, _function); + PARCFutureTask *z = parcFutureTask_Create(_function, _function); + PARCFutureTask *u1 = parcFutureTask_Create(_function, NULL); + + parcObjectTesting_AssertEquals(x, y, z, u1, NULL); + + parcFutureTask_Release(&x); + parcFutureTask_Release(&y); + parcFutureTask_Release(&z); + parcFutureTask_Release(&u1); +} + +LONGBOW_TEST_CASE(Object, parcFutureTask_HashCode) +{ + PARCFutureTask *x = parcFutureTask_Create(_function, _function); + PARCFutureTask *y = parcFutureTask_Create(_function, _function); + + parcObjectTesting_AssertHashCode(x, y); + + parcFutureTask_Release(&x); + parcFutureTask_Release(&y); +} + +LONGBOW_TEST_CASE(Object, parcFutureTask_IsValid) +{ + PARCFutureTask *instance = parcFutureTask_Create(_function, _function); + assertTrue(parcFutureTask_IsValid(instance), "Expected parcFutureTask_Create to result in a valid instance."); + + parcFutureTask_Release(&instance); + assertFalse(parcFutureTask_IsValid(instance), "Expected parcFutureTask_Release to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Object, parcFutureTask_ToJSON) +{ + PARCFutureTask *instance = parcFutureTask_Create(_function, _function); + + PARCJSON *json = parcFutureTask_ToJSON(instance); + + parcJSON_Release(&json); + + parcFutureTask_Release(&instance); +} + +LONGBOW_TEST_CASE(Object, parcFutureTask_ToString) +{ + PARCFutureTask *instance = parcFutureTask_Create(_function, _function); + + char *string = parcFutureTask_ToString(instance); + + assertNotNull(string, "Expected non-NULL result from parcFutureTask_ToString"); + + parcMemory_Deallocate((void **) &string); + parcFutureTask_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Specialization) +{ + LONGBOW_RUN_TEST_CASE(Specialization, parcFutureTask_Cancel); + LONGBOW_RUN_TEST_CASE(Specialization, parcFutureTask_Get); + LONGBOW_RUN_TEST_CASE(Specialization, parcFutureTask_IsCancelled); + LONGBOW_RUN_TEST_CASE(Specialization, parcFutureTask_IsDone); + LONGBOW_RUN_TEST_CASE(Specialization, parcFutureTask_Run); + LONGBOW_RUN_TEST_CASE(Specialization, parcFutureTask_RunAndReset); +} + +LONGBOW_TEST_FIXTURE_SETUP(Specialization) +{ + longBowTestCase_SetInt(testCase, "initialAllocations", parcMemory_Outstanding()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Specialization) +{ + int initialAllocations = longBowTestCase_GetInt(testCase, "initialAllocations"); + + if (!parcMemoryTesting_ExpectedOutstanding(initialAllocations, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Specialization, parcFutureTask_Cancel) +{ + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + bool parcFutureTask_Cancel(PARCFutureTask *task, bool mayInterruptIfRunning); + + parcFutureTask_Release(&task); +} + +LONGBOW_TEST_CASE(Specialization, parcFutureTask_Get) +{ + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + PARCFutureTaskResult result = parcFutureTask_Get(task, PARCTimeout_Immediate); + + assertTrue(parcExecution_Is(result.execution, PARCExecution_Timeout), "Expected Timeout, actual %s", + parcExecution_GetMessage(result.execution)); + parcFutureTask_Release(&task); +} + +LONGBOW_TEST_CASE(Specialization, parcFutureTask_IsCancelled) +{ + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + bool actual = parcFutureTask_IsCancelled(task); + assertFalse(actual, "Expected true."); + + parcFutureTask_Release(&task); +} + +LONGBOW_TEST_CASE(Specialization, parcFutureTask_IsDone) +{ + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + bool actual = parcFutureTask_IsDone(task); + + assertFalse(actual, "Expected false."); + + parcFutureTask_Release(&task); +} + +LONGBOW_TEST_CASE(Specialization, parcFutureTask_Run) +{ + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + parcFutureTask_Run(task); + + PARCFutureTaskResult actual = parcFutureTask_Get(task, PARCTimeout_Immediate); + + assertTrue(parcFutureTask_IsDone(task), "Expected parcFutureTask_IsDone to be true."); + assertTrue(parcExecution_Is(actual.execution, PARCExecution_OK), + "Expected OK, actual %s", parcExecution_GetMessage(actual.execution)); + assertTrue(actual.value == _function, "Expected actual to point to _function"); + parcFutureTask_Release(&task); +} + +LONGBOW_TEST_CASE(Specialization, parcFutureTask_RunAndReset) +{ + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + bool actual = parcFutureTask_RunAndReset(task); + + assertTrue(actual, "Expectd parcFutureTask_RunAndReset to return true."); + assertFalse(parcFutureTask_IsDone(task), "Expected parcFutureTask_IsDone to be false"); + parcFutureTask_Release(&task); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_FutureTask); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/concurrent/test/test_parc_Lock.c b/libparc/parc/concurrent/test/test_parc_Lock.c new file mode 100644 index 00000000..731df04a --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_Lock.c @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include "../parc_Lock.c" + +#include <stdio.h> + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_Lock) +{ + // 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(Global); + LONGBOW_RUN_TEST_FIXTURE(Locking); + LONGBOW_RUN_TEST_FIXTURE(WaitNotify); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Lock) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Lock) +{ + 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) +{ + PARCLock *instance = parcLock_Create(); + assertNotNull(instance, "Expected non-null result from parcLock_Create()."); + + parcObjectTesting_AssertAcquire(instance); + + parcObjectTesting_AssertAcquireReleaseContract(parcLock_Acquire, instance); + + parcLock_Release(&instance); + assertNull(instance, "Expected null result from parcLock_Release()."); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcLock_Display); + LONGBOW_RUN_TEST_CASE(Global, parcLock_IsValid); + LONGBOW_RUN_TEST_CASE(Global, parcLock_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcLock_Display) +{ + PARCLock *lock = parcLock_Create(); + parcLock_Display(lock, 0); + parcLock_Release(&lock); +} + +LONGBOW_TEST_CASE(Global, parcLock_IsValid) +{ + PARCLock *instance = parcLock_Create(); + assertTrue(parcLock_IsValid(instance), "Expected parcLock_Create to result in a valid instance."); + + parcLock_Release(&instance); + assertFalse(parcLock_IsValid(instance), "Expected parcLock_Release to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Global, parcLock_ToString) +{ + PARCLock *instance = parcLock_Create(); + + char *string = parcLock_ToString(instance); + + assertNotNull(string, "Expected non-NULL result from parcLock_ToString"); + + parcMemory_Deallocate((void **) &string); + parcLock_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Locking) +{ + LONGBOW_RUN_TEST_CASE(Locking, parcLock_TryLock_Unlock); + LONGBOW_RUN_TEST_CASE(Locking, parcLock_TryLock_AlreadyLocked); + LONGBOW_RUN_TEST_CASE(Locking, parcLock_Lock_Unlock); + LONGBOW_RUN_TEST_CASE(Locking, parcLock_Lock_AlreadyLocked); + LONGBOW_RUN_TEST_CASE(Locking, parcLock_Lock_AlreadyLocked); +} + +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, parcLock_TryLock_Unlock) +{ + PARCLock *lock = parcLock_Create(); + + bool actual = parcLock_TryLock(lock); + + assertTrue(actual, "Expected parcObject_TryLock to succeed."); + + actual = parcLock_IsLocked(lock); + assertTrue(actual, "Expected parcObject_IsLocked to be true."); + + actual = parcLock_Unlock(lock); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); + + actual = parcLock_IsLocked(lock); + assertTrue(actual, "Expected parcObject_IsLocked to be false."); + + parcLock_Release((PARCLock **) &lock); +} + +LONGBOW_TEST_CASE(Locking, parcLock_Lock_Unlock) +{ + PARCLock *lock = parcLock_Create(); + + bool actual = parcLock_Lock(lock); + + assertTrue(actual, "Expected parcObject_Lock to succeed."); + + actual = parcLock_IsLocked(lock); + assertTrue(actual, "Expected parcObject_IsLocked to be true."); + + actual = parcLock_Unlock(lock); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); + + actual = parcLock_IsLocked(lock); + assertTrue(actual, "Expected parcObject_IsLocked to be false."); + + parcLock_Release((PARCLock **) &lock); +} + +LONGBOW_TEST_CASE(Locking, parcLock_TryLock_AlreadyLocked) +{ + PARCLock *lock = parcLock_Create(); + + bool actual = parcLock_TryLock(lock); + + assertTrue(actual, "Expected parcObject_TryLock to succeed."); + + actual = parcLock_TryLock(lock); + + assertFalse(actual, "Expected parcObject_TryLock to fail when already locked."); + + actual = parcLock_Unlock(lock); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); + + parcLock_Release((PARCLock **) &lock); +} + +LONGBOW_TEST_CASE(Locking, parcLock_Lock_AlreadyLocked) +{ + PARCLock *lock = parcLock_Create(); + + bool actual = parcLock_Lock(lock); + + assertTrue(actual, "Expected parcObject_Lock to succeed."); + + actual = parcLock_Lock(lock); + + assertFalse(actual, "Expected parcObject_Lock to fail when already locked by the same thread."); + + actual = parcLock_Unlock(lock); + assertTrue(actual, "Expected parcObject_Unlock to succeed."); + + parcLock_Release((PARCLock **) &lock); +} + +LONGBOW_TEST_FIXTURE(WaitNotify) +{ + LONGBOW_RUN_TEST_CASE(WaitNotify, parcLock_WaitNotify); + LONGBOW_RUN_TEST_CASE(WaitNotify, parcLock_WaitNotify2); +} + +LONGBOW_TEST_FIXTURE_SETUP(WaitNotify) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(WaitNotify) +{ + 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; +} + +static int _sharedValue; + +static void * +waiter(void *data) +{ + PARCLock *lock = data; + + while (parcLock_TryLock(lock) == false) { + ; + } + parcLock_Wait(lock); + + _sharedValue++; + parcLock_Unlock(lock); + + return data; +} + +LONGBOW_TEST_CASE(WaitNotify, parcLock_WaitNotify) +{ + PARCLock *lock = parcLock_Create(); + + _sharedValue = 0; + + pthread_t thread_A; + pthread_t thread_B; + pthread_t thread_C; + pthread_create(&thread_A, NULL, waiter, lock); + pthread_create(&thread_B, NULL, waiter, lock); + pthread_create(&thread_C, NULL, waiter, lock); + + while (_sharedValue != 3) { + while (parcLock_TryLock(lock) == false) { + ; + } + parcLock_Notify(lock); + parcLock_Unlock(lock); + } + + pthread_join(thread_A, NULL); + + parcLock_Release((PARCLock **) &lock); +} + +static void * +decrement(void *data) +{ + PARCLock *lock = data; + + while (parcLock_TryLock(lock) == false) { + assertTrue(write(1, ".", 1) == 1, "Write failed."); + } + while (_sharedValue < 12) { + parcLock_Wait(lock); + _sharedValue--; + } + parcLock_Unlock(lock); + + return data; +} + +LONGBOW_TEST_CASE(WaitNotify, parcLock_WaitNotify2) +{ + PARCLock *lock = parcLock_Create(); + + _sharedValue = 0; + + pthread_t thread_A; + pthread_create(&thread_A, NULL, decrement, lock); + + _sharedValue = 2; + while (parcLock_TryLock(lock) == false) { + assertTrue(write(1, ".", 1) == 1, "Write failed."); + } + while (_sharedValue <= 12) { + printf("%d\n", _sharedValue); + parcLock_Notify(lock); + _sharedValue += 2; + } + parcLock_Unlock(lock); + + parcLock_Notify(lock); + pthread_join(thread_A, NULL); + + parcLock_Release((PARCLock **) &lock); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Lock); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/concurrent/test/test_parc_Notifier.c b/libparc/parc/concurrent/test/test_parc_Notifier.c new file mode 100755 index 00000000..97ce6615 --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_Notifier.c @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_Notifier.c" + +#include <sys/time.h> +#include <pthread.h> +#include <poll.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(parc_Notifier) +{ + // 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(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Notifier) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Notifier) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcNotifier_Acquire); + LONGBOW_RUN_TEST_CASE(Global, parcNotifier_Create_Release); + LONGBOW_RUN_TEST_CASE(Global, parcNotifier_PauseEvent_NotPaused); + LONGBOW_RUN_TEST_CASE(Global, parcNotifier_PauseEvent_AlreadyPaused); + LONGBOW_RUN_TEST_CASE(Global, parcNotifier_StartEvents); + + LONGBOW_RUN_TEST_CASE(Global, parcNotifier_Notify_First); + LONGBOW_RUN_TEST_CASE(Global, parcNotifier_Notify_Twice); + + LONGBOW_RUN_TEST_CASE(Global, parcNotifier_ThreadedTest); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +// ------ +typedef struct test_data { + volatile int barrier; + PARCNotifier *notifier; + + unsigned notificationsToSend; + unsigned notificationsSent; + + unsigned notificationsToRecieve; + unsigned notificationsReceived; + + pthread_t producerThread; + pthread_t consumerThread; +} TestData; + + +void * +consumer(void *p) +{ + TestData *data = (TestData *) p; + --data->barrier; + while (data->barrier) { + ; + } + + struct pollfd pfd; + pfd.fd = parcNotifier_Socket(data->notifier); + pfd.events = POLLIN; + + while (data->notificationsReceived < data->notificationsToRecieve) { + if (poll(&pfd, 1, -1)) { + data->notificationsReceived++; + parcNotifier_PauseEvents(data->notifier); + usleep(rand() % 1024 + 1024); + printf("skipped = %d\n", data->notifier->skippedNotify); + parcNotifier_StartEvents(data->notifier); + } + } + + --data->barrier; + + printf("Consumer exiting: received %d\n", data->notificationsReceived); + pthread_exit((void *) NULL); +} + +void * +producer(void *p) +{ + TestData *data = (TestData *) p; + --data->barrier; + while (data->barrier) { + ; + } + + while (data->barrier == 0) { + if (parcNotifier_Notify(data->notifier)) { + } + data->notificationsSent++; + usleep(rand() % 1024 + 512); + } + + printf("Producer exiting: sent %d\n", data->notificationsSent); + pthread_exit((void *) NULL); +} + +LONGBOW_TEST_CASE(Global, parcNotifier_Acquire) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, parcNotifier_Create_Release) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Global, parcNotifier_PauseEvent_NotPaused) +{ + PARCNotifier *notifier = parcNotifier_Create(); + + parcNotifier_PauseEvents(notifier); + assertTrue(notifier->paused == 1, "Not paused, got %d expected %d", notifier->paused, 1); + assertTrue(notifier->skippedNotify == 0, "Wrong skipped, got %d expected %d", notifier->skippedNotify, 0); + + parcNotifier_Release(¬ifier); +} + +LONGBOW_TEST_CASE(Global, parcNotifier_PauseEvent_AlreadyPaused) +{ + PARCNotifier *notifier = parcNotifier_Create(); + + parcNotifier_PauseEvents(notifier); + + // now pause again + parcNotifier_PauseEvents(notifier); + + assertTrue(notifier->paused == 1, "Not paused, got %d expected %d", notifier->paused, 1); + assertTrue(notifier->skippedNotify == 0, "Wrong skipped, got %d expected %d", notifier->skippedNotify, 0); + + parcNotifier_Release(¬ifier); +} + + +LONGBOW_TEST_CASE(Global, parcNotifier_ThreadedTest) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + data->notifier = parcNotifier_Create(); + data->notificationsToSend = 10; + data->notificationsToRecieve = data->notificationsToSend; + data->notificationsSent = 0; + data->notificationsReceived = 0; + data->barrier = 2; + + pthread_create(&data->consumerThread, NULL, consumer, data); + pthread_create(&data->producerThread, NULL, producer, data); + + // wait for them to exit + pthread_join(data->producerThread, NULL); + pthread_join(data->consumerThread, NULL); + + assertTrue(data->notificationsReceived >= data->notificationsToRecieve, + "Did not write all items got %u expected %u\n", + data->notificationsReceived, + data->notificationsToRecieve); + + parcNotifier_Release(&data->notifier); + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_CASE(Global, parcNotifier_StartEvents) +{ + testUnimplemented("unimplemented"); +} + +LONGBOW_TEST_CASE(Global, parcNotifier_Notify_First) +{ + PARCNotifier *notifier = parcNotifier_Create(); + + bool success = parcNotifier_Notify(notifier); + assertTrue(success, "Did not succeed on first notify"); + assertTrue(notifier->paused == 1, "Not paused, got %d expected %d", notifier->paused, 1); + assertTrue(notifier->skippedNotify == 0, "Wrong skipped, got %d expected %d", notifier->skippedNotify, 0); + + parcNotifier_Release(¬ifier); +} + + +LONGBOW_TEST_CASE(Global, parcNotifier_Notify_Twice) +{ + PARCNotifier *notifier = parcNotifier_Create(); + + parcNotifier_Notify(notifier); + + bool success = parcNotifier_Notify(notifier); + assertFalse(success, "Should have failed on second notify"); + assertTrue(notifier->paused == 1, "Not paused, got %d expected %d", notifier->paused, 1); + assertTrue(notifier->skippedNotify == 1, "Wrong skipped, got %d expected %d", notifier->skippedNotify, 1); + + parcNotifier_Release(¬ifier); +} + +// =============================================================== + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Notifier); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/concurrent/test/test_parc_RingBuffer_1x1.c b/libparc/parc/concurrent/test/test_parc_RingBuffer_1x1.c new file mode 100755 index 00000000..a977dfbc --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_RingBuffer_1x1.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_RingBuffer_1x1.c" + +#include <sys/time.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(parc_RingBuffer_1x1) +{ + // 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(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_RingBuffer_1x1) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_RingBuffer_1x1) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcRingBuffer1x1_Acquire); + LONGBOW_RUN_TEST_CASE(Global, parcRingBuffer1x1_Create_Release); + LONGBOW_RUN_TEST_CASE(Global, parcRingBuffer1x1_Create_NonPower2); + LONGBOW_RUN_TEST_CASE(Global, parcRingBuffer1x1_Get_Put); + LONGBOW_RUN_TEST_CASE(Global, parcRingBuffer1x1_Remaining_Empty); + LONGBOW_RUN_TEST_CASE(Global, parcRingBuffer1x1_Remaining_Half); + LONGBOW_RUN_TEST_CASE(Global, parcRingBuffer1x1_Remaining_Full); + LONGBOW_RUN_TEST_CASE(Global, parcRingBuffer1x1_Put_ToCapacity); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +// ------ +typedef struct test_ringbuffer { + unsigned itemsToWrite; + volatile unsigned itemsWritten; + volatile unsigned itemsRead; + volatile bool blocked; + + PARCRingBuffer1x1 *producerBuffer; + PARCRingBuffer1x1 *consumerBuffer; + + pthread_t producerThread; + pthread_t consumerThread; +} TestRingBuffer; + + +void * +consumer(void *p) +{ + TestRingBuffer *trb = (TestRingBuffer *) p; + + while (trb->blocked) { + // nothing to do. + } + + while (trb->itemsRead < trb->itemsToWrite) { + uint32_t *data; + bool success = parcRingBuffer1x1_Get(trb->consumerBuffer, (void **) &data); + if (success) { + assertTrue(*data == trb->itemsRead, "Got out of order item %u expected %u\n", *data, trb->itemsRead); + parcMemory_Deallocate((void **) &data); + trb->itemsRead++; + } + } + + pthread_exit((void *) NULL); +} + +void * +producer(void *p) +{ + TestRingBuffer *trb = (TestRingBuffer *) p; + + while (trb->blocked) { + // nothing to do + } + + while (trb->itemsWritten < trb->itemsToWrite) { + uint32_t *data = parcMemory_Allocate(sizeof(uint32_t)); + assertNotNull(data, "parcMemory_Allocate(%zu) returned NULL", sizeof(uint32_t)); + *data = trb->itemsWritten; + + bool success = false; + do { + success = parcRingBuffer1x1_Put(trb->producerBuffer, data); + } while (!success); + trb->itemsWritten++; + } + + pthread_exit((void *) NULL); +} + +LONGBOW_TEST_CASE(Global, parcRingBuffer1x1_Acquire) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE_EXPECTS(Global, parcRingBuffer1x1_Create_NonPower2, .event = &LongBowAssertEvent) +{ + // this will assert because the number of elements is not a power of 2 + parcRingBuffer1x1_Create(3, NULL); +} + +LONGBOW_TEST_CASE(Global, parcRingBuffer1x1_Create_Release) +{ + PARCRingBuffer1x1 *ring = parcRingBuffer1x1_Create(1024, NULL); + parcRingBuffer1x1_Release(&ring); + assertTrue(parcMemory_Outstanding() == 0, "Non-zero memory balance: %u", parcMemory_Outstanding()); + + printf("ring buffer entry size: %zu\n", sizeof(PARCRingBuffer1x1)); +} + +LONGBOW_TEST_CASE(Global, parcRingBuffer1x1_Get_Put) +{ + TestRingBuffer *trb = parcMemory_AllocateAndClear(sizeof(TestRingBuffer)); + assertNotNull(trb, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestRingBuffer)); + trb->producerBuffer = parcRingBuffer1x1_Create(128, NULL); + trb->consumerBuffer = parcRingBuffer1x1_Acquire(trb->producerBuffer); + + trb->itemsToWrite = 100000; + trb->blocked = true; + + pthread_create(&trb->consumerThread, NULL, consumer, trb); + pthread_create(&trb->producerThread, NULL, producer, trb); + + struct timeval t0, t1; + + gettimeofday(&t0, NULL); + trb->blocked = false; + + // wait for them to exit + pthread_join(trb->producerThread, NULL); + pthread_join(trb->consumerThread, NULL); + gettimeofday(&t1, NULL); + + timersub(&t1, &t0, &t1); + + assertTrue(trb->itemsWritten == trb->itemsToWrite, + "Did not write all items got %u expected %u\n", + trb->itemsWritten, + trb->itemsToWrite); + + assertTrue(trb->itemsRead == trb->itemsToWrite, + "Did not read all items got %u expected %u\n", + trb->itemsRead, + trb->itemsToWrite); + + double sec = t1.tv_sec + t1.tv_usec * 1E-6; + + printf("Passed %u items in %.6f seconds, %.2f items/sec\n", + trb->itemsWritten, + sec, + trb->itemsWritten / sec); + + parcRingBuffer1x1_Release(&trb->consumerBuffer); + parcRingBuffer1x1_Release(&trb->producerBuffer); + parcMemory_Deallocate((void **) &trb); +} + +LONGBOW_TEST_CASE(Global, parcRingBuffer1x1_Remaining_Empty) +{ + uint32_t capacity = 128; + PARCRingBuffer1x1 *ring = parcRingBuffer1x1_Create(capacity, NULL); + uint32_t remaining = parcRingBuffer1x1_Remaining(ring); + parcRingBuffer1x1_Release(&ring); + + // -1 because the ring buffer is always -1 + assertTrue(remaining == capacity - 1, "Got wrong remaining, got %u expecting %u\n", remaining, capacity); +} + +LONGBOW_TEST_CASE(Global, parcRingBuffer1x1_Remaining_Half) +{ + uint32_t capacity = 128; + PARCRingBuffer1x1 *ring = parcRingBuffer1x1_Create(capacity, NULL); + for (int i = 0; i < capacity / 2; i++) { + parcRingBuffer1x1_Put(ring, &i); + } + + uint32_t remaining = parcRingBuffer1x1_Remaining(ring); + parcRingBuffer1x1_Release(&ring); + + // -1 because the ring buffer is always -1 + assertTrue(remaining == capacity / 2 - 1, "Got wrong remaining, got %u expecting %u\n", remaining, capacity / 2 - 1); +} + +LONGBOW_TEST_CASE(Global, parcRingBuffer1x1_Remaining_Full) +{ + uint32_t capacity = 128; + PARCRingBuffer1x1 *ring = parcRingBuffer1x1_Create(capacity, NULL); + for (int i = 0; i < capacity - 1; i++) { + parcRingBuffer1x1_Put(ring, &i); + } + + uint32_t remaining = parcRingBuffer1x1_Remaining(ring); + parcRingBuffer1x1_Release(&ring); + + assertTrue(remaining == 0, "Got wrong remaining, got %u expecting %u\n", remaining, 0); +} + +LONGBOW_TEST_CASE(Global, parcRingBuffer1x1_Put_ToCapacity) +{ + uint32_t capacity = 128; + PARCRingBuffer1x1 *ring = parcRingBuffer1x1_Create(capacity, NULL); + for (int i = 0; i < capacity - 1; i++) { + parcRingBuffer1x1_Put(ring, &i); + } + + // this next put should fail + bool success = parcRingBuffer1x1_Put(ring, &capacity); + + parcRingBuffer1x1_Release(&ring); + + assertFalse(success, "Should have failed on final put because data structure is full\n"); +} + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _create); + LONGBOW_RUN_TEST_CASE(Local, _destroy); + LONGBOW_RUN_TEST_CASE(Local, _isPowerOfTwo); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, _create) +{ + testUnimplemented(""); +} + +static void +_testDestoryer(void **ptr) +{ + parcBuffer_Release((PARCBuffer **) ptr); +} + +LONGBOW_TEST_CASE(Local, _destroy) +{ + // put something in the ring and don't remove it. Make sure the destroyer catches it. + + uint32_t capacity = 128; + PARCRingBuffer1x1 *ring = parcRingBuffer1x1_Create(capacity, _testDestoryer); + + PARCBuffer *buffer = parcBuffer_Allocate(5); + parcRingBuffer1x1_Put(ring, buffer); + + parcRingBuffer1x1_Release(&ring); + assertTrue(parcMemory_Outstanding() == 0, "Memory imbalance, expected 0 got %u", parcMemory_Outstanding()); +} + +LONGBOW_TEST_CASE(Local, _isPowerOfTwo) +{ + struct test_struct { + uint32_t value; + bool isPow2; + } test_vector[] = { { 0, false }, { 1, true }, { 2, true }, { 15, false }, { 16, true }, { 32, true }, { UINT32_MAX, true } }; + + for (int i = 0; test_vector[i].value != UINT32_MAX; i++) { + bool test = _isPowerOfTwo(test_vector[i].value); + assertTrue(test == test_vector[i].isPow2, "Got wrong result for value %u, got %d expected %d\n", test_vector[i].value, test, test_vector[i].isPow2); + } +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_RingBuffer_1x1); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/concurrent/test/test_parc_RingBuffer_NxM.c b/libparc/parc/concurrent/test/test_parc_RingBuffer_NxM.c new file mode 100755 index 00000000..974da053 --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_RingBuffer_NxM.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../parc_RingBuffer_NxM.c" + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(parc_RingBuffer_NxM) +{ + // 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(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_RingBuffer_NxM) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_RingBuffer_NxM) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("Tests leak memory by %d allocations\n", outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _destroy); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +static void +_testDestoryer(void **ptr) +{ + parcBuffer_Release((PARCBuffer **) ptr); +} + +LONGBOW_TEST_CASE(Local, _destroy) +{ + // put something in the ring and don't remove it. Make sure the destroyer catches it. + + uint32_t capacity = 128; + PARCRingBufferNxM *ring = parcRingBufferNxM_Create(capacity, _testDestoryer); + + PARCBuffer *buffer = parcBuffer_Allocate(5); + parcRingBufferNxM_Put(ring, buffer); + + parcRingBufferNxM_Release(&ring); + assertTrue(parcMemory_Outstanding() == 0, "Memory imbalance, expected 0 got %u", parcMemory_Outstanding()); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_RingBuffer_NxM); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/concurrent/test/test_parc_ScheduledTask.c b/libparc/parc/concurrent/test/test_parc_ScheduledTask.c new file mode 100644 index 00000000..3655be93 --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_ScheduledTask.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + */ +#include "../parc_ScheduledTask.c" + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_ScheduledTask) +{ + // 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(Object); + LONGBOW_RUN_TEST_FIXTURE(Specialization); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_ScheduledTask) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_ScheduledTask) +{ + 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; +} + +void * +_function(PARCFutureTask *task, void *parameter) +{ + return parameter; +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, CreateRelease) +{ + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + PARCScheduledTask *instance = parcScheduledTask_Create(task, 0); + assertNotNull(instance, "Expected non-null result from parcScheduledTask_Create();"); + + parcObjectTesting_AssertAcquireReleaseContract(parcScheduledTask_Acquire, instance); + + parcScheduledTask_Release(&instance); + assertNull(instance, "Expected null result from parcScheduledTask_Release();"); + parcFutureTask_Release(&task); +} + +LONGBOW_TEST_FIXTURE(Object) +{ + LONGBOW_RUN_TEST_CASE(Object, parcScheduledTask_Compare); + LONGBOW_RUN_TEST_CASE(Object, parcScheduledTask_Copy); + LONGBOW_RUN_TEST_CASE(Object, parcScheduledTask_Display); + LONGBOW_RUN_TEST_CASE(Object, parcScheduledTask_Equals); + LONGBOW_RUN_TEST_CASE(Object, parcScheduledTask_HashCode); + LONGBOW_RUN_TEST_CASE(Object, parcScheduledTask_IsValid); + LONGBOW_RUN_TEST_CASE(Object, parcScheduledTask_ToJSON); + LONGBOW_RUN_TEST_CASE(Object, parcScheduledTask_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Object) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Object) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Object, parcScheduledTask_Compare) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Object, parcScheduledTask_Copy) +{ + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + PARCScheduledTask *instance = parcScheduledTask_Create(task, 0); + PARCScheduledTask *copy = parcScheduledTask_Copy(instance); + assertTrue(parcScheduledTask_Equals(instance, copy), "Expected the copy to be equal to the original"); + + parcScheduledTask_Release(&instance); + parcScheduledTask_Release(©); + parcFutureTask_Release(&task); +} + +LONGBOW_TEST_CASE(Object, parcScheduledTask_Display) +{ + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + PARCScheduledTask *instance = parcScheduledTask_Create(task, 0); + parcScheduledTask_Display(instance, 0); + parcScheduledTask_Release(&instance); + parcFutureTask_Release(&task); +} + +LONGBOW_TEST_CASE(Object, parcScheduledTask_Equals) +{ + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + PARCScheduledTask *x = parcScheduledTask_Create(task, 0); + PARCScheduledTask *y = parcScheduledTask_Create(task, 0); + PARCScheduledTask *z = parcScheduledTask_Create(task, 0); + + parcObjectTesting_AssertEquals(x, y, z, NULL); + + parcScheduledTask_Release(&x); + parcScheduledTask_Release(&y); + parcScheduledTask_Release(&z); + parcFutureTask_Release(&task); +} + +LONGBOW_TEST_CASE(Object, parcScheduledTask_HashCode) +{ + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + PARCScheduledTask *x = parcScheduledTask_Create(task, 0); + PARCScheduledTask *y = parcScheduledTask_Create(task, 0); + + parcObjectTesting_AssertHashCode(x, y); + + parcScheduledTask_Release(&x); + parcScheduledTask_Release(&y); + parcFutureTask_Release(&task); +} + +LONGBOW_TEST_CASE(Object, parcScheduledTask_IsValid) +{ + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + PARCScheduledTask *instance = parcScheduledTask_Create(task, 0); + assertTrue(parcScheduledTask_IsValid(instance), "Expected parcScheduledTask_Create to result in a valid instance."); + + parcScheduledTask_Release(&instance); + assertFalse(parcScheduledTask_IsValid(instance), "Expected parcScheduledTask_Release to result in an invalid instance."); + + parcFutureTask_Release(&task); +} + +LONGBOW_TEST_CASE(Object, parcScheduledTask_ToJSON) +{ + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + PARCScheduledTask *instance = parcScheduledTask_Create(task, 0); + + PARCJSON *json = parcScheduledTask_ToJSON(instance); + + parcJSON_Release(&json); + + parcScheduledTask_Release(&instance); + parcFutureTask_Release(&task); +} + +LONGBOW_TEST_CASE(Object, parcScheduledTask_ToString) +{ + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + PARCScheduledTask *instance = parcScheduledTask_Create(task, 0); + + char *string = parcScheduledTask_ToString(instance); + + assertNotNull(string, "Expected non-NULL result from parcScheduledTask_ToString"); + + parcMemory_Deallocate((void **) &string); + parcScheduledTask_Release(&instance); + parcFutureTask_Release(&task); +} + +LONGBOW_TEST_FIXTURE(Specialization) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Specialization) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Specialization) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_ScheduledTask); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} + + diff --git a/libparc/parc/concurrent/test/test_parc_ScheduledThreadPool.c b/libparc/parc/concurrent/test/test_parc_ScheduledThreadPool.c new file mode 100644 index 00000000..f466d72a --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_ScheduledThreadPool.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + */ +#include "../parc_ScheduledThreadPool.c" + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_ScheduledThreadPool) +{ + // 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(Object); + LONGBOW_RUN_TEST_FIXTURE(Specialization); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_ScheduledThreadPool) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_ScheduledThreadPool) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(CreateAcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, CreateRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(CreateAcquireRelease) +{ + longBowTestCase_SetInt(testCase, "initalAllocations", parcMemory_Outstanding()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(CreateAcquireRelease) +{ + int initialAllocations = longBowTestCase_GetInt(testCase, "initalAllocations"); + if (!parcMemoryTesting_ExpectedOutstanding(initialAllocations, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + parcSafeMemory_ReportAllocation(1); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, CreateRelease) +{ + PARCScheduledThreadPool *instance = parcScheduledThreadPool_Create(3); + assertNotNull(instance, "Expected non-null result from parcScheduledThreadPool_Create();"); + + parcObjectTesting_AssertAcquireReleaseContract(parcScheduledThreadPool_Acquire, instance); + parcScheduledThreadPool_ShutdownNow(instance); + + assertTrue(parcObject_GetReferenceCount(instance) == 1, "Expected 1 reference count. Actual %llu", parcObject_GetReferenceCount(instance)); + + parcScheduledThreadPool_Release(&instance); + assertNull(instance, "Expected null result from parcScheduledThreadPool_Release();"); +} + +LONGBOW_TEST_FIXTURE(Object) +{ + LONGBOW_RUN_TEST_CASE(Object, parcScheduledThreadPool_Compare); + LONGBOW_RUN_TEST_CASE(Object, parcScheduledThreadPool_Copy); + LONGBOW_RUN_TEST_CASE(Object, parcScheduledThreadPool_Display); + LONGBOW_RUN_TEST_CASE(Object, parcScheduledThreadPool_Equals); + LONGBOW_RUN_TEST_CASE(Object, parcScheduledThreadPool_HashCode); + LONGBOW_RUN_TEST_CASE(Object, parcScheduledThreadPool_IsValid); + LONGBOW_RUN_TEST_CASE(Object, parcScheduledThreadPool_ToJSON); + LONGBOW_RUN_TEST_CASE(Object, parcScheduledThreadPool_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Object) +{ + longBowTestCase_SetInt(testCase, "initalAllocations", parcMemory_Outstanding()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Object) +{ + int initialAllocations = longBowTestCase_GetInt(testCase, "initalAllocations"); + if (!parcMemoryTesting_ExpectedOutstanding(initialAllocations, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + parcSafeMemory_ReportAllocation(1); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Object, parcScheduledThreadPool_Compare) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Object, parcScheduledThreadPool_Copy) +{ + PARCScheduledThreadPool *instance = parcScheduledThreadPool_Create(3); + PARCScheduledThreadPool *copy = parcScheduledThreadPool_Copy(instance); + assertTrue(parcScheduledThreadPool_Equals(instance, copy), "Expected the copy to be equal to the original"); + + parcScheduledThreadPool_ShutdownNow(instance); + parcScheduledThreadPool_ShutdownNow(copy); + + parcScheduledThreadPool_Release(&instance); + parcScheduledThreadPool_Release(©); +} + +LONGBOW_TEST_CASE(Object, parcScheduledThreadPool_Display) +{ + PARCScheduledThreadPool *instance = parcScheduledThreadPool_Create(2); + parcScheduledThreadPool_Display(instance, 0); + parcScheduledThreadPool_ShutdownNow(instance); + parcScheduledThreadPool_Release(&instance); +} + +LONGBOW_TEST_CASE(Object, parcScheduledThreadPool_Equals) +{ + PARCScheduledThreadPool *x = parcScheduledThreadPool_Create(2); + PARCScheduledThreadPool *y = parcScheduledThreadPool_Create(2); + PARCScheduledThreadPool *z = parcScheduledThreadPool_Create(2); + PARCScheduledThreadPool *u1 = parcScheduledThreadPool_Create(3); + + parcObjectTesting_AssertEquals(x, y, z, u1, NULL); + + parcScheduledThreadPool_ShutdownNow(x); + parcScheduledThreadPool_ShutdownNow(y); + parcScheduledThreadPool_ShutdownNow(z); + parcScheduledThreadPool_ShutdownNow(u1); + + parcScheduledThreadPool_Release(&x); + parcScheduledThreadPool_Release(&y); + parcScheduledThreadPool_Release(&z); + parcScheduledThreadPool_Release(&u1); +} + +LONGBOW_TEST_CASE(Object, parcScheduledThreadPool_HashCode) +{ + PARCScheduledThreadPool *x = parcScheduledThreadPool_Create(2); + PARCScheduledThreadPool *y = parcScheduledThreadPool_Create(2); + + parcObjectTesting_AssertHashCode(x, y); + + parcScheduledThreadPool_ShutdownNow(x); + parcScheduledThreadPool_ShutdownNow(y); + + parcScheduledThreadPool_Release(&x); + parcScheduledThreadPool_Release(&y); +} + +LONGBOW_TEST_CASE(Object, parcScheduledThreadPool_IsValid) +{ + PARCScheduledThreadPool *instance = parcScheduledThreadPool_Create(2); + assertTrue(parcScheduledThreadPool_IsValid(instance), "Expected parcScheduledThreadPool_Create to result in a valid instance."); + + parcScheduledThreadPool_ShutdownNow(instance); + + parcScheduledThreadPool_Release(&instance); + assertFalse(parcScheduledThreadPool_IsValid(instance), "Expected parcScheduledThreadPool_Release to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Object, parcScheduledThreadPool_ToJSON) +{ + PARCScheduledThreadPool *instance = parcScheduledThreadPool_Create(2); + + PARCJSON *json = parcScheduledThreadPool_ToJSON(instance); + + parcJSON_Release(&json); + + parcScheduledThreadPool_ShutdownNow(instance); + parcScheduledThreadPool_Release(&instance); +} + +LONGBOW_TEST_CASE(Object, parcScheduledThreadPool_ToString) +{ + PARCScheduledThreadPool *instance = parcScheduledThreadPool_Create(2); + + char *string = parcScheduledThreadPool_ToString(instance); + + assertNotNull(string, "Expected non-NULL result from parcScheduledThreadPool_ToString"); + + parcMemory_Deallocate((void **) &string); + parcScheduledThreadPool_ShutdownNow(instance); + parcScheduledThreadPool_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Specialization) +{ + LONGBOW_RUN_TEST_CASE(Specialization, OneJob); + LONGBOW_RUN_TEST_CASE(Specialization, Idle); + LONGBOW_RUN_TEST_CASE(Specialization, parcScheduledThreadPool_Schedule); +} + +LONGBOW_TEST_FIXTURE_SETUP(Specialization) +{ + longBowTestCase_SetInt(testCase, "initalAllocations", parcMemory_Outstanding()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Specialization) +{ + int initialAllocations = longBowTestCase_GetInt(testCase, "initalAllocations"); + if (!parcMemoryTesting_ExpectedOutstanding(initialAllocations, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + parcSafeMemory_ReportAllocation(1); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Specialization, Idle) +{ + PARCScheduledThreadPool *pool = parcScheduledThreadPool_Create(3); + + sleep(2); + + parcScheduledThreadPool_ShutdownNow(pool); + + parcScheduledThreadPool_Release(&pool); +} + +static void * +_function(PARCFutureTask *task, void *parameter) +{ + printf("Hello World\n"); + return parameter; +} + +LONGBOW_TEST_CASE(Specialization, OneJob) +{ + PARCScheduledThreadPool *pool = parcScheduledThreadPool_Create(3); + + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + parcScheduledThreadPool_Schedule(pool, task, parcTimeout_MilliSeconds(2000)); + printf("references %lld\n", parcObject_GetReferenceCount(task)); + parcFutureTask_Release(&task); + + sleep(5); + + parcScheduledThreadPool_ShutdownNow(pool); + + parcScheduledThreadPool_Release(&pool); +} + +LONGBOW_TEST_CASE(Specialization, parcScheduledThreadPool_Schedule) +{ + PARCScheduledThreadPool *pool = parcScheduledThreadPool_Create(3); + + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + + parcScheduledThreadPool_Schedule(pool, task, parcTimeout_MilliSeconds(2000)); + + parcFutureTask_Release(&task); + + parcScheduledThreadPool_Shutdown(pool); +// parcScheduledThreadPool_AwaitTermination(pool, PARCTimeout_Never); + +// uint64_t count = parcScheduledThreadPool_GetCompletedTaskCount(pool); +// assertTrue(count == 5, "Expected 5, actual %lld", count); + + parcScheduledThreadPool_ShutdownNow(pool); + + parcScheduledThreadPool_Release(&pool); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_ScheduledThreadPool); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libparc/parc/concurrent/test/test_parc_Synchronizer.c b/libparc/parc/concurrent/test/test_parc_Synchronizer.c new file mode 100755 index 00000000..34d9c711 --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_Synchronizer.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include "../parc_Synchronizer.c" + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_Synchronizer) +{ + // 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(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Synchronizer) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Synchronizer) +{ + 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) +{ + PARCSynchronizer *instance = parcSynchronizer_Create(); + assertNotNull(instance, "Expeced non-null result from parcSynchronizer_Create();"); + + parcObjectTesting_AssertAcquireReleaseContract(parcSynchronizer_Acquire, instance); + + parcSynchronizer_Release(&instance); + assertNull(instance, "Expeced null result from parcSynchronizer_Release();"); +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, parcSynchronizer_Display); + LONGBOW_RUN_TEST_CASE(Global, parcSynchronizer_IsValid); + + LONGBOW_RUN_TEST_CASE(Global, parcSynchronizer_TryLock); + LONGBOW_RUN_TEST_CASE(Global, parcSynchronizer_LockUnlock); + LONGBOW_RUN_TEST_CASE(Global, parcSynchronizer_TryLock_Fail); + LONGBOW_RUN_TEST_CASE(Global, parcSynchronizer_IsLocked); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, parcSynchronizer_Display) +{ + PARCSynchronizer *instance = parcSynchronizer_Create(); + assertTrue(parcSynchronizer_IsValid(instance), "Expected parcSynchronizer_Create to result in a valid instance."); + + parcSynchronizer_Display(instance, 0); + parcSynchronizer_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcSynchronizer_IsValid) +{ + PARCSynchronizer *instance = parcSynchronizer_Create(); + assertTrue(parcSynchronizer_IsValid(instance), "Expected parcSynchronizer_Create to result in a valid instance."); + + parcSynchronizer_Release(&instance); + assertFalse(parcSynchronizer_IsValid(instance), "Expected parcSynchronizer_Create to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Global, parcSynchronizer_TryLock) +{ + PARCSynchronizer *instance = parcSynchronizer_Create(); + + bool actual = parcSynchronizer_TryLock(instance); + assertTrue(actual, "Expected parcSynchronizer_TryLock to be successful."); + + parcSynchronizer_Unlock(instance); + parcSynchronizer_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcSynchronizer_TryLock_Fail) +{ + PARCSynchronizer *instance = parcSynchronizer_Create(); + + parcSynchronizer_Lock(instance); + bool actual = parcSynchronizer_TryLock(instance); + assertFalse(actual, "Expected parcSynchronizer_TryLock to be unsuccessful."); + + parcSynchronizer_Unlock(instance); + parcSynchronizer_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcSynchronizer_LockUnlock) +{ + PARCSynchronizer *instance = parcSynchronizer_Create(); + + parcSynchronizer_Lock(instance); + parcSynchronizer_Unlock(instance); + + parcSynchronizer_Release(&instance); +} + +LONGBOW_TEST_CASE(Global, parcSynchronizer_IsLocked) +{ + PARCSynchronizer *instance = parcSynchronizer_Create(); + assertFalse(parcSynchronizer_IsLocked(instance), "Expected the synchronizer to be unlocked."); + + parcSynchronizer_Lock(instance); + assertTrue(parcSynchronizer_IsLocked(instance), "Expected the synchronizer to be unlocked."); + + parcSynchronizer_Unlock(instance); + assertFalse(parcSynchronizer_IsLocked(instance), "Expected the synchronizer to be unlocked."); + + parcSynchronizer_Release(&instance); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Synchronizer); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} + + diff --git a/libparc/parc/concurrent/test/test_parc_Thread.c b/libparc/parc/concurrent/test/test_parc_Thread.c new file mode 100644 index 00000000..22ca31bf --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_Thread.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + */ +#include "../parc_ThreadPool.c" + +#include <stdio.h> + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_Thread) +{ + // 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(Object); + LONGBOW_RUN_TEST_FIXTURE(Specialization); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Thread) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Thread) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(CreateAcquireRelease) +{ + LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, CreateRelease); +} + +LONGBOW_TEST_FIXTURE_SETUP(CreateAcquireRelease) +{ + longBowTestCase_SetInt(testCase, "initialAllocations", parcMemory_Outstanding()); + + PARCBuffer *buffer = parcBuffer_Allocate(10); + longBowTestCase_Set(testCase, "object", buffer); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(CreateAcquireRelease) +{ + PARCBuffer *buffer = longBowTestCase_Get(testCase, "object"); + parcBuffer_Release(&buffer); + + uint32_t initialAllocations = (uint32_t) longBowTestCase_Get(testCase, "initialAllocations"); + if (!parcMemoryTesting_ExpectedOutstanding(initialAllocations, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + parcSafeMemory_ReportAllocation(1); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +static void * +_function(PARCThread *thread, void *parameter) +{ + return NULL; +} + +LONGBOW_TEST_CASE(CreateAcquireRelease, CreateRelease) +{ + PARCBuffer *buffer = longBowTestCase_Get(testCase, "object"); + PARCThread *thread = parcThread_Create(_function, buffer); + + assertNotNull(thread, "Expected non-null result from parcThread_Create();"); + + parcObjectTesting_AssertAcquireReleaseContract(parcThread_Acquire, thread); + + parcThread_Release(&thread); + assertNull(thread, "Expected null result from parcThread_Release();"); +} + +LONGBOW_TEST_FIXTURE(Object) +{ + LONGBOW_RUN_TEST_CASE(Object, parcThread_Compare); + LONGBOW_RUN_TEST_CASE(Object, parcThread_Copy); + LONGBOW_RUN_TEST_CASE(Object, parcThread_Display); + LONGBOW_RUN_TEST_CASE(Object, parcThread_Equals); + LONGBOW_RUN_TEST_CASE(Object, parcThread_HashCode); + LONGBOW_RUN_TEST_CASE(Object, parcThread_IsValid); + LONGBOW_RUN_TEST_CASE(Object, parcThread_ToJSON); + LONGBOW_RUN_TEST_CASE(Object, parcThread_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Object) +{ + longBowTestCase_SetInt(testCase, "initialAllocations", parcMemory_Outstanding()); + PARCBuffer *buffer = parcBuffer_Allocate(10); + longBowTestCase_Set(testCase, "object", buffer); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Object) +{ + PARCBuffer *buffer = longBowTestCase_Get(testCase, "object"); + parcBuffer_Release(&buffer); + + uint32_t initialAllocations = (uint32_t) longBowTestCase_Get(testCase, "initialAllocations"); + if (!parcMemoryTesting_ExpectedOutstanding(initialAllocations, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + parcSafeMemory_ReportAllocation(1); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Object, parcThread_Compare) +{ +// testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Object, parcThread_Copy) +{ + PARCBuffer *buffer = longBowTestCase_Get(testCase, "object"); + PARCThread *instance = parcThread_Create(_function, buffer); + PARCThread *copy = parcThread_Copy(instance); + + assertTrue(parcThread_Equals(instance, copy), "Expected the copy to be equal to the original"); + + parcThread_Release(&instance); + parcThread_Release(©); +} + +LONGBOW_TEST_CASE(Object, parcThread_Display) +{ + PARCBuffer *buffer = longBowTestCase_Get(testCase, "object"); + PARCThread *instance = parcThread_Create(_function, buffer); + parcThread_Display(instance, 0); + + + parcThread_Release(&instance); +} + +LONGBOW_TEST_CASE(Object, parcThread_Equals) +{ + PARCBuffer *buffer = longBowTestCase_Get(testCase, "object"); + PARCThread *x = parcThread_Create(_function, buffer); + PARCThread *y = parcThread_Create(_function, buffer); + PARCThread *z = parcThread_Create(_function, buffer); + + parcObjectTesting_AssertEquals(x, y, z, NULL); + + parcThread_Release(&x); + parcThread_Release(&y); + parcThread_Release(&z); +} + +LONGBOW_TEST_CASE(Object, parcThread_HashCode) +{ + PARCBuffer *buffer = longBowTestCase_Get(testCase, "object"); + PARCThread *x = parcThread_Create(_function, buffer); + PARCThread *y = parcThread_Create(_function, buffer); + + parcObjectTesting_AssertHashCode(x, y); + + parcThread_Release(&x); + parcThread_Release(&y); +} + +LONGBOW_TEST_CASE(Object, parcThread_IsValid) +{ + PARCBuffer *buffer = longBowTestCase_Get(testCase, "object"); + PARCThread *instance = parcThread_Create(_function, buffer); + assertTrue(parcThread_IsValid(instance), "Expected parcThread_Create to result in a valid instance."); + + + parcThread_Release(&instance); + assertFalse(parcThread_IsValid(instance), "Expected parcThread_Release to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Object, parcThread_ToJSON) +{ + PARCBuffer *buffer = longBowTestCase_Get(testCase, "object"); + PARCThread *instance = parcThread_Create(_function, buffer); + + PARCJSON *json = parcThread_ToJSON(instance); + + parcJSON_Release(&json); + + + parcThread_Release(&instance); +} + +LONGBOW_TEST_CASE(Object, parcThread_ToString) +{ + PARCBuffer *buffer = longBowTestCase_Get(testCase, "object"); + PARCThread *instance = parcThread_Create(_function, buffer); + + char *string = parcThread_ToString(instance); + + assertNotNull(string, "Expected non-NULL result from parcThread_ToString"); + + parcMemory_Deallocate((void **) &string); + + parcThread_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Specialization) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + LONGBOW_RUN_TEST_CASE(Object, parcThread_Execute); +} + +LONGBOW_TEST_FIXTURE_SETUP(Specialization) +{ + longBowTestCase_SetInt(testCase, "initialAllocations", parcMemory_Outstanding()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Specialization) +{ + uint32_t initialAllocations = (uint32_t) longBowTestCase_Get(testCase, "initialAllocations"); + if (!parcMemoryTesting_ExpectedOutstanding(initialAllocations, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) { + parcSafeMemory_ReportAllocation(1); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Object, parcThread_Execute) +{ +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Thread); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} + + diff --git a/libparc/parc/concurrent/test/test_parc_ThreadPool.c b/libparc/parc/concurrent/test/test_parc_ThreadPool.c new file mode 100644 index 00000000..6ed6e4d3 --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_ThreadPool.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + */ +#include "../parc_ThreadPool.c" + +#include <stdio.h> + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_ThreadPool) +{ + // 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(Object); + LONGBOW_RUN_TEST_FIXTURE(Specialization); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_ThreadPool) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_ThreadPool) +{ + 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) +{ + PARCThreadPool *pool = parcThreadPool_Create(6); + assertNotNull(pool, "Expected non-null result from parcThreadPool_Create();"); + + parcObjectTesting_AssertAcquireReleaseContract(parcThreadPool_Acquire, pool); + + parcThreadPool_ShutdownNow(pool); + + parcThreadPool_Release(&pool); + assertNull(pool, "Expected null result from parcThreadPool_Release();"); +} + +LONGBOW_TEST_FIXTURE(Object) +{ + LONGBOW_RUN_TEST_CASE(Object, parcThreadPool_Compare); + LONGBOW_RUN_TEST_CASE(Object, parcThreadPool_Copy); + LONGBOW_RUN_TEST_CASE(Object, parcThreadPool_Display); + LONGBOW_RUN_TEST_CASE(Object, parcThreadPool_Equals); + LONGBOW_RUN_TEST_CASE(Object, parcThreadPool_HashCode); + LONGBOW_RUN_TEST_CASE(Object, parcThreadPool_IsValid); + LONGBOW_RUN_TEST_CASE(Object, parcThreadPool_ToJSON); + LONGBOW_RUN_TEST_CASE(Object, parcThreadPool_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Object) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Object) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Object, parcThreadPool_Compare) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Object, parcThreadPool_Copy) +{ + PARCThreadPool *instance = parcThreadPool_Create(6); + PARCThreadPool *copy = parcThreadPool_Copy(instance); + assertTrue(parcThreadPool_Equals(instance, copy), "Expected the copy to be equal to the original"); + + parcThreadPool_ShutdownNow(instance); + parcThreadPool_ShutdownNow(copy); + + parcThreadPool_Release(&instance); + parcThreadPool_Release(©); +} + +LONGBOW_TEST_CASE(Object, parcThreadPool_Display) +{ + PARCThreadPool *instance = parcThreadPool_Create(6); + parcThreadPool_Display(instance, 0); + + parcThreadPool_ShutdownNow(instance); + + parcThreadPool_Release(&instance); +} + +LONGBOW_TEST_CASE(Object, parcThreadPool_Equals) +{ + PARCThreadPool *x = parcThreadPool_Create(6); + PARCThreadPool *y = parcThreadPool_Create(6); + PARCThreadPool *z = parcThreadPool_Create(6); + + parcObjectTesting_AssertEquals(x, y, z, NULL); + + parcThreadPool_ShutdownNow(x); + parcThreadPool_ShutdownNow(y); + parcThreadPool_ShutdownNow(z); + + parcThreadPool_Release(&x); + parcThreadPool_Release(&y); + parcThreadPool_Release(&z); +} + +LONGBOW_TEST_CASE(Object, parcThreadPool_HashCode) +{ + PARCThreadPool *x = parcThreadPool_Create(6); + PARCThreadPool *y = parcThreadPool_Create(6); + + parcObjectTesting_AssertHashCode(x, y); + + parcThreadPool_ShutdownNow(x); + parcThreadPool_ShutdownNow(y); + + parcThreadPool_Release(&x); + parcThreadPool_Release(&y); +} + +LONGBOW_TEST_CASE(Object, parcThreadPool_IsValid) +{ + PARCThreadPool *instance = parcThreadPool_Create(6); + assertTrue(parcThreadPool_IsValid(instance), "Expected parcThreadPool_Create to result in a valid instance."); + + parcThreadPool_ShutdownNow(instance); + + parcThreadPool_Release(&instance); + assertFalse(parcThreadPool_IsValid(instance), "Expected parcThreadPool_Release to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Object, parcThreadPool_ToJSON) +{ + PARCThreadPool *instance = parcThreadPool_Create(6); + + PARCJSON *json = parcThreadPool_ToJSON(instance); + + parcJSON_Release(&json); + + parcThreadPool_ShutdownNow(instance); + + parcThreadPool_Release(&instance); +} + +LONGBOW_TEST_CASE(Object, parcThreadPool_ToString) +{ + PARCThreadPool *instance = parcThreadPool_Create(6); + + char *string = parcThreadPool_ToString(instance); + + assertNotNull(string, "Expected non-NULL result from parcThreadPool_ToString"); + + parcMemory_Deallocate((void **) &string); + + parcThreadPool_ShutdownNow(instance); + + parcThreadPool_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Specialization) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + LONGBOW_RUN_TEST_CASE(Object, parcThreadPool_Execute); +} + +LONGBOW_TEST_FIXTURE_SETUP(Specialization) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Specialization) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + parcSafeMemory_ReportAllocation(1); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +void * +_function(PARCFutureTask *task, void *parameter) +{ + printf("Hello World\n"); + return parameter; +} + +LONGBOW_TEST_CASE(Object, parcThreadPool_Execute) +{ + dprintf(1, "-----\n"); + PARCThreadPool *pool = parcThreadPool_Create(6); + + PARCFutureTask *task = parcFutureTask_Create(_function, _function); + parcThreadPool_Execute(pool, task); + parcThreadPool_Execute(pool, task); + parcThreadPool_Execute(pool, task); + parcThreadPool_Execute(pool, task); + parcThreadPool_Execute(pool, task); + parcFutureTask_Release(&task); + + parcThreadPool_Shutdown(pool); + bool shutdownSuccess = parcThreadPool_AwaitTermination(pool, PARCTimeout_Never); + + assertTrue(shutdownSuccess, "parcThreadPool_AwaitTermination timed-out"); + + uint64_t count = parcThreadPool_GetCompletedTaskCount(pool); + assertTrue(count == 5, "Expected 5, actual %lld", count); + + + parcThreadPool_Release(&pool); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_ThreadPool); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} + + diff --git a/libparc/parc/concurrent/test/test_parc_Timer.c b/libparc/parc/concurrent/test/test_parc_Timer.c new file mode 100644 index 00000000..5bc7ae6c --- /dev/null +++ b/libparc/parc/concurrent/test/test_parc_Timer.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + */ +#include "../parc_Timer.c" + +#include <LongBow/testing.h> +#include <LongBow/debugging.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <parc/testing/parc_MemoryTesting.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(parc_Timer) +{ + // 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(Object); + LONGBOW_RUN_TEST_FIXTURE(Specialization); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Timer) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Timer) +{ + 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) +{ + PARCTimer *instance = parcTimer_Create(); + assertNotNull(instance, "Expected non-null result from parcTimer_Create();"); + + parcObjectTesting_AssertAcquireReleaseContract(parcTimer_Acquire, instance); + + parcTimer_Release(&instance); + assertNull(instance, "Expected null result from parcTimer_Release();"); +} + +LONGBOW_TEST_FIXTURE(Object) +{ + LONGBOW_RUN_TEST_CASE(Object, parcTimer_Compare); + LONGBOW_RUN_TEST_CASE(Object, parcTimer_Copy); + LONGBOW_RUN_TEST_CASE(Object, parcTimer_Display); + LONGBOW_RUN_TEST_CASE(Object, parcTimer_Equals); + LONGBOW_RUN_TEST_CASE(Object, parcTimer_HashCode); + LONGBOW_RUN_TEST_CASE(Object, parcTimer_IsValid); + LONGBOW_RUN_TEST_CASE(Object, parcTimer_ToJSON); + LONGBOW_RUN_TEST_CASE(Object, parcTimer_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Object) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Object) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Object, parcTimer_Compare) +{ + testUnimplemented(""); +} + +LONGBOW_TEST_CASE(Object, parcTimer_Copy) +{ + PARCTimer *instance = parcTimer_Create(); + PARCTimer *copy = parcTimer_Copy(instance); + assertTrue(parcTimer_Equals(instance, copy), "Expected the copy to be equal to the original"); + + parcTimer_Release(&instance); + parcTimer_Release(©); +} + +LONGBOW_TEST_CASE(Object, parcTimer_Display) +{ + PARCTimer *instance = parcTimer_Create(); + parcTimer_Display(instance, 0); + parcTimer_Release(&instance); +} + +LONGBOW_TEST_CASE(Object, parcTimer_Equals) +{ + PARCTimer *x = parcTimer_Create(); + PARCTimer *y = parcTimer_Create(); + PARCTimer *z = parcTimer_Create(); + + parcObjectTesting_AssertEquals(x, y, z, NULL); + + parcTimer_Release(&x); + parcTimer_Release(&y); + parcTimer_Release(&z); +} + +LONGBOW_TEST_CASE(Object, parcTimer_HashCode) +{ + PARCTimer *x = parcTimer_Create(); + PARCTimer *y = parcTimer_Create(); + + parcObjectTesting_AssertHashCode(x, y); + + parcTimer_Release(&x); + parcTimer_Release(&y); +} + +LONGBOW_TEST_CASE(Object, parcTimer_IsValid) +{ + PARCTimer *instance = parcTimer_Create(); + assertTrue(parcTimer_IsValid(instance), "Expected parcTimer_Create to result in a valid instance."); + + parcTimer_Release(&instance); + assertFalse(parcTimer_IsValid(instance), "Expected parcTimer_Release to result in an invalid instance."); +} + +LONGBOW_TEST_CASE(Object, parcTimer_ToJSON) +{ + PARCTimer *instance = parcTimer_Create(); + + PARCJSON *json = parcTimer_ToJSON(instance); + + parcJSON_Release(&json); + + parcTimer_Release(&instance); +} + +LONGBOW_TEST_CASE(Object, parcTimer_ToString) +{ + PARCTimer *instance = parcTimer_Create(); + + char *string = parcTimer_ToString(instance); + + assertNotNull(string, "Expected non-NULL result from parcTimer_ToString"); + + parcMemory_Deallocate((void **) &string); + parcTimer_Release(&instance); +} + +LONGBOW_TEST_FIXTURE(Specialization) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Specialization) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Specialization) +{ + if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) { + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Timer); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} + + |