This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/add_initialize_check_for_thread_abstraction in repository https://gitbox.apache.org/repos/asf/celix.git
commit 1e76efd8ae976f1029547c7474a435403d35e36e Author: Pepijn Noltes <[email protected]> AuthorDate: Mon May 22 22:27:53 2023 +0200 Add initialized field to thread abstraction and add missing doxygen --- bundles/logging/log_helper/src/log_helper.c | 20 +- libs/framework/src/dm_dependency_manager_impl.c | 4 +- libs/framework/src/dm_dependency_manager_impl.h | 2 +- libs/utils/gtest/src/ThreadsTestSuite.cc | 38 ++- libs/utils/include/celix_threads.h | 400 ++++++++++++++++++++++-- libs/utils/src/celix_err.c | 6 +- libs/utils/src/celix_threads.c | 148 +++++---- 7 files changed, 506 insertions(+), 112 deletions(-) diff --git a/bundles/logging/log_helper/src/log_helper.c b/bundles/logging/log_helper/src/log_helper.c index 677564e4..17d75317 100644 --- a/bundles/logging/log_helper/src/log_helper.c +++ b/bundles/logging/log_helper/src/log_helper.c @@ -107,7 +107,7 @@ celix_status_t logHelper_create(bundle_context_pt context, log_helper_t **loghel (*loghelper)->stdOutFallback = celix_bundleContext_getPropertyAsBool(context, LOGHELPER_ENABLE_STDOUT_FALLBACK_NAME, LOGHELPER_ENABLE_STDOUT_FALLBACK_DEFAULT); (*loghelper)->stdOutFallbackIncludeDebug = celix_bundleContext_getPropertyAsBool(context, LOGHELPER_STDOUT_FALLBACK_INCLUDE_DEBUG_NAME, LOGHELPER_STDOUT_FALLBACK_INCLUDE_DEBUG_DEFAULT); - pthread_mutex_init(&(*loghelper)->logListLock, NULL); + celixThreadMutex_create(&(*loghelper)->logListLock, NULL); arrayList_create(&(*loghelper)->logServices); } @@ -139,9 +139,9 @@ celix_status_t logHelper_logServiceAdded(void *handle, service_reference_pt refe { log_helper_t *loghelper = handle; - pthread_mutex_lock(&loghelper->logListLock); + celixThreadMutex_lock(&loghelper->logListLock); arrayList_add(loghelper->logServices, service); - pthread_mutex_unlock(&loghelper->logListLock); + celixThreadMutex_unlock(&loghelper->logListLock); return CELIX_SUCCESS; } @@ -150,9 +150,9 @@ celix_status_t logHelper_logServiceRemoved(void *handle, service_reference_pt re { log_helper_t *loghelper = handle; - pthread_mutex_lock(&loghelper->logListLock); + celixThreadMutex_lock(&loghelper->logListLock); arrayList_removeElement(loghelper->logServices, service); - pthread_mutex_unlock(&loghelper->logListLock); + celixThreadMutex_unlock(&loghelper->logListLock); return CELIX_SUCCESS; } @@ -173,11 +173,11 @@ celix_status_t logHelper_destroy(log_helper_t **loghelper) { serviceTracker_destroy((*loghelper)->logServiceTracker); } - pthread_mutex_lock(&(*loghelper)->logListLock); + celixThreadMutex_lock(&(*loghelper)->logListLock); arrayList_destroy((*loghelper)->logServices); - pthread_mutex_unlock(&(*loghelper)->logListLock); + celixThreadMutex_unlock(&(*loghelper)->logListLock); - pthread_mutex_destroy(&(*loghelper)->logListLock); + celixThreadMutex_destroy(&(*loghelper)->logListLock); free(*loghelper); *loghelper = NULL; @@ -202,7 +202,7 @@ celix_status_t logHelper_log(log_helper_t *loghelper, log_level_t level, const c va_start(listPointer, message); vsnprintf(msg, 1024, message, listPointer); - pthread_mutex_lock(&loghelper->logListLock); + celixThreadMutex_lock(&loghelper->logListLock); int i = 0; for (; i < arrayList_size(loghelper->logServices); i++) { @@ -218,7 +218,7 @@ celix_status_t logHelper_log(log_helper_t *loghelper, log_level_t level, const c } } - pthread_mutex_unlock(&loghelper->logListLock); + celixThreadMutex_unlock(&loghelper->logListLock); if (!logged && loghelper->stdOutFallback) { char *levelStr = NULL; diff --git a/libs/framework/src/dm_dependency_manager_impl.c b/libs/framework/src/dm_dependency_manager_impl.c index 1de53a89..0a94a528 100644 --- a/libs/framework/src/dm_dependency_manager_impl.c +++ b/libs/framework/src/dm_dependency_manager_impl.c @@ -35,7 +35,7 @@ celix_dependency_manager_t* celix_private_dependencyManager_create(celix_bundle_ if (manager != NULL) { manager->ctx = context; manager->components = celix_arrayList_create(); - pthread_mutex_init(&manager->mutex, NULL); + celixThreadMutex_create(&manager->mutex, NULL); } return manager; } @@ -46,7 +46,7 @@ void celix_private_dependencyManager_destroy(celix_dependency_manager_t *manager celix_arrayList_destroy(manager->components); celixThreadMutex_unlock(&manager->mutex); - pthread_mutex_destroy(&manager->mutex); + celixThreadMutex_destroy(&manager->mutex); free(manager); } } diff --git a/libs/framework/src/dm_dependency_manager_impl.h b/libs/framework/src/dm_dependency_manager_impl.h index aa43b020..ff5558cd 100644 --- a/libs/framework/src/dm_dependency_manager_impl.h +++ b/libs/framework/src/dm_dependency_manager_impl.h @@ -31,7 +31,7 @@ extern "C" { struct celix_dependency_manager { celix_bundle_context_t *ctx; celix_array_list_t *components; - pthread_mutex_t mutex; + celix_thread_mutex_t mutex; }; celix_dependency_manager_t* celix_private_dependencyManager_create(celix_bundle_context_t *context); diff --git a/libs/utils/gtest/src/ThreadsTestSuite.cc b/libs/utils/gtest/src/ThreadsTestSuite.cc index 999415a5..a8efe0df 100644 --- a/libs/utils/gtest/src/ThreadsTestSuite.cc +++ b/libs/utils/gtest/src/ThreadsTestSuite.cc @@ -339,16 +339,48 @@ TEST_F(ThreadsTestSuite, TssTest) { int* value = (int*)malloc(sizeof(int)); *value = 123; - status = celix_tss_set(key, value); + status = celix_tss_set(&key, value); EXPECT_EQ(CELIX_SUCCESS, status); - value = (int*)celix_tss_get(key); + value = (int*)celix_tss_get(&key); EXPECT_EQ(123, *value); - status = celix_tss_delete(key); + status = celix_tss_delete(&key); EXPECT_EQ(CELIX_SUCCESS, status); } +TEST_F(ThreadsTestSuite, UninitializedTssTest) { + celix_tss_key_t key; + key.initialized = false; + + EXPECT_EQ(CELIX_ILLEGAL_STATE, celix_tss_set(&key, (void*)0x42)); + EXPECT_EQ(nullptr, celix_tss_get(&key)); + + //nothing happens when deleting an uninitialized key + EXPECT_EQ(CELIX_SUCCESS, celix_tss_delete(&key)); +} + +TEST_F(ThreadsTestSuite, UninitializedThreadTest) { + celix_thread_t thread; + thread.threadInitialized = false; + EXPECT_EQ(CELIX_SUCCESS, celixThread_join(thread, nullptr)); //ignore join on uninitialized thread + EXPECT_EQ(CELIX_SUCCESS, celixThread_detach(thread)); //ignore join on uninitialized thread + EXPECT_EQ(CELIX_SUCCESS, celixThread_kill(thread, SIGINT)); //ignore join on uninitialized thread + + celix_thread_mutex_t mutex; + mutex.initialized = false; + EXPECT_EQ(CELIX_SUCCESS, celixThreadMutex_destroy(&mutex)); //ignore destroy on uninitialized mutex + + celix_thread_cond_t cond; + cond.initialized = false; + EXPECT_EQ(CELIX_SUCCESS, celixThreadCondition_destroy(&cond)); //ignore destroy on uninitialized cond + + celix_thread_rwlock_t rwlock; + rwlock.initialized = false; + EXPECT_EQ(CELIX_SUCCESS, celixThreadRwlock_destroy(&rwlock)); //ignore destroy on uninitialized rwlock +} + + static void * thread_test_func_create(void * arg) { char ** test_str = (char**) arg; *test_str = strdup("SUCCESS"); diff --git a/libs/utils/include/celix_threads.h b/libs/utils/include/celix_threads.h index fba1e246..8d55721d 100644 --- a/libs/utils/include/celix_threads.h +++ b/libs/utils/include/celix_threads.h @@ -30,14 +30,19 @@ extern "C" { #endif +/** + * @file celix_threads.h + * @brief Abstraction of thread utilities. + * + * This file contains the abstraction of thread utilities. Currently only pthread is supported. + */ + + struct celix_thread { - bool threadInitialized; - pthread_t thread; + bool threadInitialized; /**< Indicates if the thread is initialized. */ + pthread_t thread; /**< The actual thread.*/ }; -typedef pthread_once_t celix_thread_once_t; -#define CELIX_THREAD_ONCE_INIT PTHREAD_ONCE_INIT - typedef struct celix_thread celix_thread_t; typedef pthread_attr_t celix_thread_attr_t; @@ -45,38 +50,135 @@ typedef void *(*celix_thread_start_t)(void *); static const celix_thread_t celix_thread_default = {0, 0}; +/** + * @brief Create a new thread. + * + * Note will not check the initialized field when creating. + * + * @section errors Errors + * If an error occurs, the thread is not created and the function returns an error code. + * + * And if pthread is the underlying implementation: + * - EAGAIN Insufficient resources to create another thread. + * - EAGAIN A system-imposed limit on the number of threads was encountered. + * - EINVAL Invalid settings in attr. + * - EPERM No permission to set the scheduling policy and parameters specified in attr. + * @endsection + * + * @param[in,out] new_thread The created thread. + * @param[in] attr The thread attributes. Can be NULL for default attributes. + * @param[in] func The function to execute in the thread. + * @param[in] data The data passed to the function. + * @return CELIX_SUCCESS if the thread is created. + */ CELIX_UTILS_EXPORT celix_status_t celixThread_create(celix_thread_t *new_thread, const celix_thread_attr_t *attr, celix_thread_start_t func, void *data); + /** - * If supported by the platform sets the name of the thread. + * @brief If supported by the platform sets the name of the thread. */ CELIX_UTILS_EXPORT void celixThread_setName(celix_thread_t *thread, const char *threadName); +/** + * @brief Exit the current thread. + * + * @param[in] exitStatus The exit status of the thread. This is output status for the celixThread_join function. + */ CELIX_UTILS_EXPORT void celixThread_exit(void *exitStatus); +/** + * @brief Detach the thread. + * + * Will silently ignore if the thread is not initialized. + * + * @section errors Errors + * If an error occurs, the function returns an error code. + * + * And if pthread is the underlying implementation: + * - EINVAL thread is not a joinable thread. + * - ESRCH No thread with the ID thread could be found. + * + * @param[in] thread The thread to detach. + * @return CELIX_SUCCESS if the thread is detached. + */ CELIX_UTILS_EXPORT celix_status_t celixThread_detach(celix_thread_t thread); -CELIX_UTILS_EXPORT celix_status_t celixThread_join(celix_thread_t thread, void **status); +/** + * @brief Join the thread. + * + * Will silently ignore if the thread is not initialized. + * + * @section errors Errors + * If an error occurs, the function returns an error code. + * + * And if pthread is the underlying implementation: + * - EDEADLK A deadlock was detected (e.g., two threads tried to join with each other); or thread specifies the calling thread. + * - EINVAL thread is not a joinable thread. + * - EINVAL Another thread is already waiting to join with this thread. + * - ESRCH No thread with the ID thread could be found. + * @endsection + * + * @param[in] thread The thread to join. + * @param[out] status The exit status of the thread. + * @return CELIX_SUCCESS if the thread is joined. + */ +CELIX_UTILS_EXPORT celix_status_t celixThread_join(celix_thread_t thread, void** status); +/** + * @brief Kill the thread. + * + * Will silently ignore if the thread is not initialized. + * + * @section errors Errors + * If an error occurs, the function returns an error code. + * + * And if pthread is the underlying implementation: + * - EINVAL An invalid signal was specified. + * @endsection + * + * @param[in] thread The thread to kill. + * @param[in] sig The signal to send to the thread. + * @return CELIX_SUCCESS if the thread is killed. + */ CELIX_UTILS_EXPORT celix_status_t celixThread_kill(celix_thread_t thread, int sig); +/** + * Return the current thread. + */ CELIX_UTILS_EXPORT celix_thread_t celixThread_self(void); /** - * Return true - as int - if the threads are equals + * @brief Return true - as int - if the threads are equals * @param[in] thread1 * @param[in] thread2 * @return non-zero if the thread IDs t1 and t2 correspond to the same thread, otherwise it will return zero. */ CELIX_UTILS_EXPORT int celixThread_equals(celix_thread_t thread1, celix_thread_t thread2); +/** + * @brief Check if the thread is initialized. + * + * @param[in] thread + * @return true if the thread is initialized. + */ CELIX_UTILS_EXPORT bool celixThread_initialized(celix_thread_t thread); -typedef pthread_mutex_t celix_thread_mutex_t; +typedef pthread_once_t celix_thread_once_t; +#define CELIX_THREAD_ONCE_INIT PTHREAD_ONCE_INIT + +CELIX_UTILS_EXPORT celix_status_t celixThread_once(celix_thread_once_t *once_control, void (*init_routine)(void)); + typedef pthread_mutexattr_t celix_thread_mutexattr_t; +typedef struct celix_thread_mutex { + bool initialized; /**< Indicates if the thread is initialized. */ + pthread_mutex_t pthreadMutex; /**< The actual mutex. */ +} celix_thread_mutex_t; + +#define CELIX_THREAD_MUTEX_INITIALIZER {true, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEXATTR_INITIALIZER} + //MUTEX TYPES enum { CELIX_THREAD_MUTEX_NORMAL, @@ -85,13 +187,103 @@ enum { CELIX_THREAD_MUTEX_DEFAULT }; - +/** + * @brief Create a mutex. + * + * Note will not check the initialized field when creating. + * + * @section errors Errors + * If an error occurs, the function returns an error code. + * + * And if pthread is the underlying implementation: + * - EAGAIN The system lacked the necessary resources (other than memory) to initialize another mutex. + * - ENOMEM Insufficient memory exists to initialize the mutex. + * - EPERM The caller does not have the privilege to perform the operation. + * - EINVAL The attributes object referenced by attr has the robust mutex attribute set without the process-shared + * attribute being set. + * @endsection + * + * @param[out] mutex The created mutex. + * @param[in] attr The mutex attributes. Can be NULL. + * @return CELIX_SUCCESS if the mutex is created. + */ CELIX_UTILS_EXPORT celix_status_t celixThreadMutex_create(celix_thread_mutex_t *mutex, celix_thread_mutexattr_t *attr); +/** + * @brief Destroy a mutex. + * + * Will silently ignore if the mutex is not initialized. + * + * @section errors Errors + * If an error occurs, the function returns an error code. + * + * And if pthread is the underlying implementation: + * - EAGAIN The system lacked the necessary resources (other than memory) to initialize another mutex. + * - ENOMEM Insufficient memory exists to initialize the mutex. + * - EPERM The caller does not have the privilege to perform the operation. + * @endsection + * + * @param[in,out] mutex The mutex to destroy. + * @return CELIX_SUCCESS if the mutex is destroyed. + */ CELIX_UTILS_EXPORT celix_status_t celixThreadMutex_destroy(celix_thread_mutex_t *mutex); +/** + * @brief Check if the mutex is initialized. + */ +CELIX_UTILS_EXPORT bool celixThreadMutex_isInitialized(const celix_thread_mutex_t *mutex); + +/** + * @brief Lock a mutex. + * + * Will not check if the mutex is initialized. + * + * @section errors Errors + * If an error occurs, the function returns an error code. + * + * And if pthread is the underlying implementation: + * - EAGAIN The mutex could not be acquired because the maximum number of recursive locks for mutex has been exceeded. + * - EINVAL The mutex was created with the protocol attribute having the value PTHREAD_PRIO_PROTECT and the calling + * thread's priority is higher than the mutex's current priority ceiling. + * - ENOTRECOVERABLE The state protected by the mutex is not recoverable. + * - EOWNERDEAD The mutex is a robust mutex and the process containing the previous owning thread terminated while + * holding the mutex lock. The mutex lock shall be acquired by the calling thread and it is up to the new owner to + * make the state consistent. + * - EDEADLK The mutex type is PTHREAD_MUTEX_ERRORCHECK and the current thread already owns the mutex. + * - EOWNERDEAD The mutex is a robust mutex and the previous owning thread terminated while holding the mutex lock. + * The mutex lock shall be acquired by the calling thread and it is up to the new owner to make the state consistent. + * - EDEADLK A deadlock condition was detected. + * @endsection + * + * @param[in] mutex The mutex to lock. + * @return CELIX_SUCCESS if the mutex is locked. + */ CELIX_UTILS_EXPORT celix_status_t celixThreadMutex_lock(celix_thread_mutex_t *mutex); +/** + * @brief Unlock a mutex. + * + * Will not check if the mutex is initialized. + * + * @section errors Errors + * If an error occurs, the function returns an error code. + * + * And if pthread is the underlying implementation: + * - EAGAIN The mutex could not be acquired because the maximum number of recursive locks for mutex has been exceeded. + * - EINVAL The mutex was created with the protocol attribute having the value PTHREAD_PRIO_PROTECT and the calling + * thread's priority is higher than the mutex's current priority ceiling. + * - ENOTRECOVERABLE The state protected by the mutex is not recoverable. + * - EOWNERDEAD The mutex is a robust mutex and the process containing the previous owning thread terminated while + * holding the mutex lock. The mutex lock shall be acquired by the calling thread and it is up to the new owner to + * make the state consistent. + * - EPERM The mutex type is PTHREAD_MUTEX_ERRORCHECK or PTHREAD_MUTEX_RECURSIVE, or the mutex is a robust mutex, + * and the current thread does not own the mutex. + * @endsection + * + * @param[in] mutex The mutex to unlock. + * @return CELIX_SUCCESS if the mutex is unlocked. + * + */ CELIX_UTILS_EXPORT celix_status_t celixThreadMutex_unlock(celix_thread_mutex_t *mutex); CELIX_UTILS_EXPORT celix_status_t celixThreadMutexAttr_create(celix_thread_mutexattr_t *attr); @@ -100,44 +292,186 @@ CELIX_UTILS_EXPORT celix_status_t celixThreadMutexAttr_destroy(celix_thread_mute CELIX_UTILS_EXPORT celix_status_t celixThreadMutexAttr_settype(celix_thread_mutexattr_t *attr, int type); -typedef pthread_rwlock_t celix_thread_rwlock_t; typedef pthread_rwlockattr_t celix_thread_rwlockattr_t; +typedef struct celix_thread_rwlock { + bool initialized; + pthread_rwlock_t pthreadRwLock; +} celix_thread_rwlock_t; + +/** + * @brief Create a read-write lock. + * + * Note will not check the initialized field when creating. + * + * @section errors Errors + * If an error occurs, the function returns an error code. + * + * And if pthread is the underlying implementation: + * - EAGAIN The system lacked the necessary resources (other than memory) to initialize another read-write lock. + * - ENOMEM Insufficient memory exists to initialize the read-write lock. + * - EPERM The caller does not have the privilege to perform the operation. + * @endsection + * + * @param[out] lock The created read-write lock. + * @param[in] attr The read-write lock attributes. Can be NULL. + * @return CELIX_SUCCESS if the read-write lock is created. + */ CELIX_UTILS_EXPORT celix_status_t celixThreadRwlock_create(celix_thread_rwlock_t *lock, celix_thread_rwlockattr_t *attr); +/** + * @brief Destroy a read-write lock. + * + * Will silently ignore if the lock is not initialized. + * + * @section errors Errors + * If an error occurs, the function returns an error code. + * - EAGAIN The system lacked the necessary resources (other than memory) to initialize another read-write lock. + * - ENOMEM Insufficient memory exists to initialize the read-write lock. + * - EPERM The caller does not have the privilege to perform the operation + * @endsection + * + * @param[in] lock The read-write lock to destroy. + * @return CELIX_SUCCESS if the read-write lock is destroyed. + */ CELIX_UTILS_EXPORT celix_status_t celixThreadRwlock_destroy(celix_thread_rwlock_t *lock); +/** + * @brief Check if a read-write lock is initialized. + */ +CELIX_UTILS_EXPORT bool celixThreadRwlock_isInitialized(const celix_thread_rwlock_t *lock); + +/** + * @brief Lock a read-write lock for reading. + * + * Will not check if the lock is initialized. + * + * @section errors Errors + * If an error occurs, the function returns an error code. + * - EAGAIN The read lock could not be acquired because the maximum number of read locks for rwlock has been exceeded. + * - EDEADLK A deadlock condition was detected or the current thread already owns the read-write lock for writing. + * @endsection + * + * @param[in] lock The read-write lock to lock. + * @return CELIX_SUCCESS if the read-write lock is locked. + */ CELIX_UTILS_EXPORT celix_status_t celixThreadRwlock_readLock(celix_thread_rwlock_t *lock); +/** + * @brief Lock a read-write lock for writing. + * + * Will not check if the lock is initialized. + * + * @section errors Errors + * If an error occurs, the function returns an error code. + * - EDEADLK A deadlock condition was detected or the current thread already owns the read-write lock for writing + * or reading. + * + * @param[in] lock The read-write lock to lock. + * @return CELIX_SUCCESS if the read-write lock is locked. + */ CELIX_UTILS_EXPORT celix_status_t celixThreadRwlock_writeLock(celix_thread_rwlock_t *lock); +/** + * @brief Unlock a read-write lock. + * + * Will not check if the lock is initialized. + * + * @param[in] lock The read-write lock to unlock. + * @return CELIX_SUCCESS if the read-write lock is unlocked. + */ CELIX_UTILS_EXPORT celix_status_t celixThreadRwlock_unlock(celix_thread_rwlock_t *lock); + CELIX_UTILS_EXPORT celix_status_t celixThreadRwlockAttr_create(celix_thread_rwlockattr_t *attr); CELIX_UTILS_EXPORT celix_status_t celixThreadRwlockAttr_destroy(celix_thread_rwlockattr_t *attr); -//NOTE: No support yet for setting specific rw lock attributes - -typedef pthread_cond_t celix_thread_cond_t; typedef pthread_condattr_t celix_thread_condattr_t; +typedef struct celix_thread_cond { + bool initialized; /**< Indicates if the condition is initialized. */ + pthread_cond_t pthreadCond; /**< The actual condition variable. */ +} celix_thread_cond_t; +/** + * @brief Initialize a condition variable. + * + * Note will not check the initialized field when initializing. + * + * @section errors Errors + * If an error occurs, the function returns an error code. + * + * And if pthread is the underlying implementation: + * - EAGAIN The system lacked the necessary resources (other than memory) to initialize another condition variable. + * - ENOMEM Insufficient memory exists to initialize the condition variable. + * @endsection + * + * @param[in,out] condition The condition variable to initialize. + * @param[in] attr The condition variable attributes to use. Can be NULL. + * @return CELIX_SUCCESS if no errors are encountered. + */ CELIX_UTILS_EXPORT celix_status_t celixThreadCondition_init(celix_thread_cond_t *condition, celix_thread_condattr_t *attr); +/** + * @brief Destroy a condition variable. + * + * Will silently ignore if the condition variable is not initialized. + * + * @oarnam[in] condition The condition variable to destroy. + * @return CELIX_SUCCESS if no errors are encountered. + */ CELIX_UTILS_EXPORT celix_status_t celixThreadCondition_destroy(celix_thread_cond_t *condition); +/** + * @brief Check if a condition variable is initialized. + */ +CELIX_UTILS_EXPORT bool celixThreadCondition_isInitialized(const celix_thread_cond_t *condition); + +/** + * @brief Wait for a condition variable to be signaled. + * + * Will not check if the condition variable is initialized. + * + * @param[in] cond The condition to wait for. + * @param[in] mutex The mutex to use. + * @return CELIX_SUCCESS if no errors are encountered. + */ CELIX_UTILS_EXPORT celix_status_t celixThreadCondition_wait(celix_thread_cond_t *cond, celix_thread_mutex_t *mutex); +/** + * @brief Wait for a condition variable to be signaled or a timeout to occur. + * + * Will not check if the condition variable is initialized. + * + * @param[in] cond The condition to wait for. + * @param[in] mutex The mutex to use. + * @param[in] seconds The seconds to wait. + * @param[in] nanoseconds The nanoseconds to wait. + * @return CELIX_SUCCESS if no errors are encountered. + */ CELIX_UTILS_EXPORT celix_status_t celixThreadCondition_timedwaitRelative(celix_thread_cond_t *cond, celix_thread_mutex_t *mutex, long seconds, long nanoseconds); +/** + * @brief Broadcast a condition. + * + * @param[in] cond The condition to broadcast. + * @return CELIX_SUCCESS if no errors are encountered. + */ CELIX_UTILS_EXPORT celix_status_t celixThreadCondition_broadcast(celix_thread_cond_t *cond); +/** + * @brief Signal a condition. + * + * @param[in] cond The condition to signal. + * @return CELIX_SUCCESS if no errors are encountered. + */ CELIX_UTILS_EXPORT celix_status_t celixThreadCondition_signal(celix_thread_cond_t *cond); -CELIX_UTILS_EXPORT celix_status_t celixThread_once(celix_thread_once_t *once_control, void (*init_routine)(void)); - //Thread Specific Storage (TSS) Abstraction -typedef pthread_key_t celix_tss_key_t; +typedef struct celix_tss_key { + bool initialized; + pthread_key_t pthreadKey; +} celix_tss_key_t; /** * @brief Create a thread specific storage key visible for all threads. @@ -151,39 +485,39 @@ typedef pthread_key_t celix_tss_key_t; * non-NULL destructor pointer, and the thread has a non-NULL value associated with that key, the value of the key is * set to NULL, and then the function pointed to is called with the previously associated value as its sole argument. * The order of destructor calls is unspecified if more than one destructor exists for a thread when it exits. + * + * Note will not check the initialized field when creating. * * @param key The key to create. * @param destroyFunction The function to call when the key is destroyed. - * @return CELIX_SUCCESS if the key is created successfully. - * - * @retval CELIX_ENOMEM if there was insufficient memory for the key creation. - * @retval CELIX_EAGAIN if the system lacked the necessary resources to create another thread specific data key. + * @return CELIX_SUCCESS if the key is created successfully; otherwise, an error code is returned. */ CELIX_UTILS_EXPORT celix_status_t celix_tss_create(celix_tss_key_t* key, void (*destroyFunction)(void*)); +/** + * @brief Check if a thread specific storage key is initialized. + */ +CELIX_UTILS_EXPORT bool celix_tss_isInitialized(const celix_tss_key_t* key); + /** * @brief Delete a thread specific storage key previously created by celix_tss_create. + * + * Will silently ignore if the key is NULL or key is not initialized. * * @param key The key to delete. - * @return CELIX_SUCCESS if the key is deleted successfully. - * - * @retval CELIX_ILLEGAL_ARGUMENT if the key is invalid. - * @retval CELIX_ILLEGAL_STATE if the key is otherwise not deleted successfully. + * @return CELIX_SUCCESS if the key is deleted successfully; otherwise, an error code is returned. */ -CELIX_UTILS_EXPORT celix_status_t celix_tss_delete(celix_tss_key_t key); +CELIX_UTILS_EXPORT celix_status_t celix_tss_delete(celix_tss_key_t* key); /** * @brief Set a thread-specific value for the provide thread specific storage key. * * @param key The key to set the value for. * @param value The thread-specific value to set. - * @return CELIX_SUCCESS if the value is set successfully. - * - * @retval CELIX_ILLEGAL_ARGUMENT if the key is invalid. - * @retval CELIX_ENOMEM if there was insufficient memory to set the value. - * @retval CELIX_ILLEGAL_STATE if the value is not set successfully. + * @return CELIX_SUCCESS if the value is set successfully; otherwise, an error code is returned. + * @retval CELIX_ILLEGAL_STATE if the key is not initialized. */ -CELIX_UTILS_EXPORT celix_status_t celix_tss_set(celix_tss_key_t key, void* value); +CELIX_UTILS_EXPORT celix_status_t celix_tss_set(const celix_tss_key_t* key, void* value); /** * @brief Get the thread-specific value for the provided thread specific storage key. @@ -193,7 +527,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_tss_set(celix_tss_key_t key, void* value * * @retval NULL if the key is invalid or there is no thread-specific value set for the key. */ -CELIX_UTILS_EXPORT void* celix_tss_get(celix_tss_key_t key); +CELIX_UTILS_EXPORT void* celix_tss_get(const celix_tss_key_t* key); #ifdef __cplusplus } diff --git a/libs/utils/src/celix_err.c b/libs/utils/src/celix_err.c index 6f3ad7e8..8c4b4260 100644 --- a/libs/utils/src/celix_err.c +++ b/libs/utils/src/celix_err.c @@ -50,7 +50,7 @@ celix_err_t* celix_err_getTssErr() { return NULL; } - celix_err_t* err = celix_tss_get(celix_err_tssKey); + celix_err_t* err = celix_tss_get(&celix_err_tssKey); if (err) { return err; } @@ -58,7 +58,7 @@ celix_err_t* celix_err_getTssErr() { err = malloc(sizeof(*err)); if (err) { err->pos = 0; //no entry - celix_status_t status = celix_tss_set(celix_err_tssKey, err); + celix_status_t status = celix_tss_set(&celix_err_tssKey, err); if (status != CELIX_SUCCESS) { fprintf(stderr, "Failed to set thread specific storage for celix_err\n"); free(err); @@ -85,7 +85,7 @@ __attribute__((destructor)) void celix_err_deinitThreadSpecificStorageKey() { return; } - celix_status_t status = celix_tss_delete(celix_err_tssKey); + celix_status_t status = celix_tss_delete(&celix_err_tssKey); if (status != CELIX_SUCCESS) { fprintf(stderr,"Failed to delete thread specific storage key for celix_err\n"); } diff --git a/libs/utils/src/celix_threads.c b/libs/utils/src/celix_threads.c index 088f216e..49bd057d 100644 --- a/libs/utils/src/celix_threads.c +++ b/libs/utils/src/celix_threads.c @@ -16,13 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -/** - * celix_threads.c - * - * \date 4 Jun 2014 - * \author <a href="mailto:[email protected]">Apache Celix Project Team</a> - * \copyright Apache License, Version 2.0 - */ #include <stdlib.h> #include <sys/time.h> @@ -32,16 +25,9 @@ #include "celix_utils.h" -celix_status_t celixThread_create(celix_thread_t *new_thread, const celix_thread_attr_t *attr, celix_thread_start_t func, void *data) { - celix_status_t status = CELIX_SUCCESS; - - if (pthread_create(&(*new_thread).thread, attr, func, data) != 0) { - status = CELIX_BUNDLE_EXCEPTION; - } - else { - __atomic_store_n(&(*new_thread).threadInitialized, true, __ATOMIC_RELEASE); - } - +celix_status_t celixThread_create(celix_thread_t *thread, const celix_thread_attr_t *attr, celix_thread_start_t func, void *data) { + celix_status_t status = pthread_create(&thread->thread, attr, func, data); + thread->threadInitialized = (status == CELIX_SUCCESS); return status; } @@ -61,24 +47,24 @@ void celixThread_exit(void *exitStatus) { } celix_status_t celixThread_detach(celix_thread_t thread) { - return pthread_detach(thread.thread); + if (celixThread_initialized(thread)) { + return pthread_detach(thread.thread); + } + return CELIX_SUCCESS; } -celix_status_t celixThread_join(celix_thread_t thread, void **retVal) { - celix_status_t status = CELIX_SUCCESS; - - if (pthread_join(thread.thread, retVal) != 0) { - status = CELIX_BUNDLE_EXCEPTION; +celix_status_t celixThread_join(celix_thread_t thread, void** retVal) { + if (celixThread_initialized(thread)) { + return pthread_join(thread.thread, retVal); } - - // #TODO make thread a pointer? Now this statement has no effect - // thread.threadInitialized = false; - - return status; + return CELIX_SUCCESS; } celix_status_t celixThread_kill(celix_thread_t thread, int sig) { - return pthread_kill(thread.thread, sig); + if (celixThread_initialized(thread)) { + return pthread_kill(thread.thread, sig); + } + return CELIX_SUCCESS; } celix_thread_t celixThread_self() { @@ -100,19 +86,30 @@ bool celixThread_initialized(celix_thread_t thread) { celix_status_t celixThreadMutex_create(celix_thread_mutex_t *mutex, celix_thread_mutexattr_t *attr) { - return pthread_mutex_init(mutex, attr); + celix_status_t status = pthread_mutex_init(&mutex->pthreadMutex, attr); + if (status != CELIX_SUCCESS) { + __atomic_store_n(&mutex->initialized, false, __ATOMIC_RELEASE); + } + return status; } celix_status_t celixThreadMutex_destroy(celix_thread_mutex_t *mutex) { - return pthread_mutex_destroy(mutex); + if (celixThreadMutex_isInitialized(mutex)) { + return pthread_mutex_destroy(&mutex->pthreadMutex); + } + return CELIX_SUCCESS; +} + +bool celixThreadMutex_isInitialized(const celix_thread_mutex_t *mutex) { + return __atomic_load_n(&mutex->initialized, __ATOMIC_ACQUIRE); } celix_status_t celixThreadMutex_lock(celix_thread_mutex_t *mutex) { - return pthread_mutex_lock(mutex); + return pthread_mutex_lock(&mutex->pthreadMutex); } celix_status_t celixThreadMutex_unlock(celix_thread_mutex_t *mutex) { - return pthread_mutex_unlock(mutex); + return pthread_mutex_unlock(&mutex->pthreadMutex); } celix_status_t celixThreadMutexAttr_create(celix_thread_mutexattr_t *attr) { @@ -146,31 +143,38 @@ celix_status_t celixThreadMutexAttr_settype(celix_thread_mutexattr_t *attr, int } celix_status_t celixThreadCondition_init(celix_thread_cond_t *condition, celix_thread_condattr_t *attr) { + celix_status_t status = CELIX_SUCCESS; #ifdef __APPLE__ - return pthread_cond_init(condition, attr); + status = pthread_cond_init(condition, attr); #else - celix_status_t status = CELIX_SUCCESS; - if(attr) { + if (attr) { status = pthread_condattr_setclock(attr, CLOCK_MONOTONIC); - status = CELIX_DO_IF(status, pthread_cond_init(condition, attr)); - } - else { + status = CELIX_DO_IF(status, pthread_cond_init(&condition->pthreadCond, attr)); + } else { celix_thread_condattr_t condattr; (void)pthread_condattr_init(&condattr); // always return 0 status = pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC); - status = CELIX_DO_IF(status, pthread_cond_init(condition, &condattr)); + status = CELIX_DO_IF(status, pthread_cond_init(&condition->pthreadCond, &condattr)); (void)pthread_condattr_destroy(&condattr); // always return 0 } - return status; #endif + condition->initialized = (status == CELIX_SUCCESS); + return status; } -celix_status_t celixThreadCondition_destroy(celix_thread_cond_t *condition) { - return pthread_cond_destroy(condition); +celix_status_t celixThreadCondition_destroy(celix_thread_cond_t* cond) { + if (celixThreadCondition_isInitialized(cond)) { + return pthread_cond_destroy(&cond->pthreadCond); + } + return CELIX_SUCCESS; +} + +bool celixThreadCondition_isInitialized(const celix_thread_cond_t* cond) { + return __atomic_load_n(&cond->initialized, __ATOMIC_ACQUIRE); } -celix_status_t celixThreadCondition_wait(celix_thread_cond_t *cond, celix_thread_mutex_t *mutex) { - return pthread_cond_wait(cond, mutex); +celix_status_t celixThreadCondition_wait(celix_thread_cond_t* cond, celix_thread_mutex_t* mutex) { + return pthread_cond_wait(&cond->pthreadCond, &mutex->pthreadMutex); } #ifdef __APPLE__ @@ -193,36 +197,45 @@ celix_status_t celixThreadCondition_timedwaitRelative(celix_thread_cond_t *cond, time.tv_nsec -= CELIX_NS_IN_SEC; } } - return pthread_cond_timedwait(cond, mutex, &time); + return pthread_cond_timedwait(&cond->pthreadCond, &mutex->pthreadMutex, &time); } #endif celix_status_t celixThreadCondition_broadcast(celix_thread_cond_t *cond) { - return pthread_cond_broadcast(cond); + return pthread_cond_broadcast(&cond->pthreadCond); } celix_status_t celixThreadCondition_signal(celix_thread_cond_t *cond) { - return pthread_cond_signal(cond); + return pthread_cond_signal(&cond->pthreadCond); } celix_status_t celixThreadRwlock_create(celix_thread_rwlock_t *lock, celix_thread_rwlockattr_t *attr) { - return pthread_rwlock_init(lock, attr); + celix_status_t status = pthread_rwlock_init(&lock->pthreadRwLock, attr); + lock->initialized = (status == CELIX_SUCCESS); + return status; } celix_status_t celixThreadRwlock_destroy(celix_thread_rwlock_t *lock) { - return pthread_rwlock_destroy(lock); + if (!celixThreadRwlock_isInitialized(lock)) { + return pthread_rwlock_destroy(&lock->pthreadRwLock); + } + return CELIX_SUCCESS; +} + +bool celixThreadRwlock_isInitialized(const celix_thread_rwlock_t *lock) { + return __atomic_load_n(&lock->initialized, __ATOMIC_ACQUIRE); } celix_status_t celixThreadRwlock_readLock(celix_thread_rwlock_t *lock) { - return pthread_rwlock_rdlock(lock); + return pthread_rwlock_rdlock(&lock->pthreadRwLock); } celix_status_t celixThreadRwlock_writeLock(celix_thread_rwlock_t *lock) { - return pthread_rwlock_wrlock(lock); + return pthread_rwlock_wrlock(&lock->pthreadRwLock); } celix_status_t celixThreadRwlock_unlock(celix_thread_rwlock_t *lock) { - return pthread_rwlock_unlock(lock); + return pthread_rwlock_unlock(&lock->pthreadRwLock); } celix_status_t celixThreadRwlockAttr_create(celix_thread_rwlockattr_t *attr) { @@ -238,17 +251,32 @@ celix_status_t celixThread_once(celix_thread_once_t *once_control, void (*init_r } celix_status_t celix_tss_create(celix_tss_key_t* key, void (*destroyFunction)(void*)) { - return pthread_key_create(key, destroyFunction); + celix_status_t status = pthread_key_create(&key->pthreadKey, destroyFunction); + key->initialized = (status == CELIX_SUCCESS); + return status; +} + +bool celix_tss_isInitialized(const celix_tss_key_t* key) { + return __atomic_load_n(&key->initialized, __ATOMIC_ACQUIRE); } -celix_status_t celix_tss_delete(celix_tss_key_t key) { - return pthread_key_delete(key); +celix_status_t celix_tss_delete(celix_tss_key_t* key) { + if (key && celix_tss_isInitialized(key)) { + return pthread_key_delete(key->pthreadKey); + } + return CELIX_SUCCESS; } -celix_status_t celix_tss_set(celix_tss_key_t key, void* value) { - return pthread_setspecific(key, value); +celix_status_t celix_tss_set(const celix_tss_key_t* key, void* value) { + if (!celix_tss_isInitialized(key)) { + return CELIX_ILLEGAL_STATE; + } + return pthread_setspecific(key->pthreadKey, value); } -void* celix_tss_get(celix_tss_key_t key) { - return pthread_getspecific(key); +void* celix_tss_get(const celix_tss_key_t* key) { + if (!celix_tss_isInitialized(key)) { + return NULL; + } + return pthread_getspecific(key->pthreadKey); }
