From c277fbe36926791f0a21a0717f69ce084e0ebaab Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Tue, 26 Jan 2021 21:47:57 -0600 Subject: [PATCH] Progress: controller program and f_thread. Add more mutex lock protections. The thread->data->child is shared and cannot be used as a per thread child process storage, so comment out and add an @fixme. In the case where the exec() functions return in the child process, the mutexes may not be fully trusted. All the child process should do is find its way to the exit, deallocating along the way. Avoid all mutex-related and printf() related functionality where possible. There were some places where the F_child is not being checked for and returned on but should be checked for and rreturned on. Work towards improving the mutex locking logic when working with the asynchronous processes, caching, and cancelling. There is more work to do in this regard. Sleep interval for the cache cleanup thread now uses the short timer when in test mode and the long timer in non-test mode. Add a list of functions that need to be completed for t_thread. Add f_thread_mutex_attribute_t and f_thread_mutex_attributes_t related functions. Minor cleanups in f_thread. --- level_0/f_thread/c/private-thread.c | 64 ++++++++++ level_0/f_thread/c/private-thread.h | 76 +++++++++++- level_0/f_thread/c/thread-common.h | 18 +-- level_0/f_thread/c/thread.c | 130 +++++++++++++++++++- level_0/f_thread/c/thread.h | 219 +++++++++++++++++++++++++++++++++- level_3/controller/c/private-rule.c | 142 ++++++++++++++-------- level_3/controller/c/private-rule.h | 24 ++-- level_3/controller/c/private-thread.c | 99 ++++++++------- 8 files changed, 651 insertions(+), 121 deletions(-) diff --git a/level_0/f_thread/c/private-thread.c b/level_0/f_thread/c/private-thread.c index da63306..e808662 100644 --- a/level_0/f_thread/c/private-thread.c +++ b/level_0/f_thread/c/private-thread.c @@ -194,6 +194,70 @@ extern "C" { } #endif // !defined(_di_f_thread_conditions_decrease_) || !defined(_di_f_thread_conditions_decrease_by_) || !defined(_di_f_thread_conditions_increase_) || !defined(_di_f_thread_conditions_increase_by_) +#if !defined(_di_f_thread_mutex_attributes_adjust_) || !defined(_di_f_thread_mutex_attributes_decimate_by_) || !defined(_di_f_thread_mutex_attributes_decrease_) || !defined(_di_f_thread_mutex_attributes_decrease_by_) || !defined(_di_f_thread_mutex_attributes_increase_) || !defined(_di_f_thread_mutex_attributes_increase_by_) || !defined(_di_f_thread_mutex_attributes_resize_) + f_status_t private_f_thread_mutex_attribute_delete(f_thread_mutex_attribute_t *attribute) { + + const int error = pthread_mutexattr_destroy(attribute); + + if (error) { + if (error == EBUSY) return F_status_set_error(F_busy); + if (error == EINVAL) return F_status_set_error(F_parameter); + + return F_status_set_error(F_failure); + } + + return F_none; + } +#endif // !defined(_di_f_thread_mutex_attributes_adjust_) || !defined(_di_f_thread_mutex_attributes_decimate_by_) || !defined(_di_f_thread_mutex_attributes_decrease_) || !defined(_di_f_thread_mutex_attributes_decrease_by_) || !defined(_di_f_thread_mutex_attributes_increase_) || !defined(_di_f_thread_mutex_attributes_increase_by_) || !defined(_di_f_thread_mutex_attributes_resize_) + +#if !defined(_di_f_thread_mutex_attributes_adjust_) || !defined(_di_f_thread_mutex_attributes_decimate_by_) + f_status_t private_f_thread_mutex_attributes_adjust(const f_array_length_t length, f_thread_mutex_attributes_t *attributes) { + f_status_t status = F_none; + + for (f_array_length_t i = length; i < attributes->size; ++i) { + + status = private_f_thread_mutex_attribute_delete(&attributes->array[i]); + if (F_status_is_error(status)) return status; + } // for + + status = f_memory_adjust(attributes->size, length, sizeof(f_thread_mutex_t), (void **) & attributes->array); + + if (F_status_is_error_not(status)) { + attributes->size = length; + + if (attributes->used > attributes->size) { + attributes->used = length; + } + } + + return status; + } +#endif // !defined(_di_f_thread_mutex_attributes_adjust_) || !defined(_di_f_thread_mutex_attributes_decimate_by_) + +#if !defined(_di_f_thread_mutex_attributes_decrease_) || !defined(_di_f_thread_mutex_attributes_decrease_by_) || !defined(_di_f_thread_mutex_attributes_increase_) || !defined(_di_f_thread_mutex_attributes_increase_by_) + f_status_t private_f_thread_mutex_attributes_resize(const f_array_length_t length, f_thread_mutex_attributes_t *attributes) { + f_status_t status = F_none; + + for (f_array_length_t i = length; i < attributes->size; ++i) { + + status = private_f_thread_mutex_attribute_delete(&attributes->array[i]); + if (F_status_is_error(status)) return status; + } // for + + status = f_memory_resize(attributes->size, length, sizeof(f_thread_mutex_t), (void **) & attributes->array); + + if (F_status_is_error_not(status)) { + attributes->size = length; + + if (attributes->used > attributes->size) { + attributes->used = length; + } + } + + return status; + } +#endif // !defined(_di_f_thread_mutex_attributes_decrease_) || !defined(_di_f_thread_mutex_attributes_decrease_by_) || !defined(_di_f_thread_mutex_attributes_increase_) || !defined(_di_f_thread_mutex_attributes_increase_by_) + #if !defined(_di_f_thread_mutexs_adjust_) || !defined(_di_f_thread_mutexs_decimate_by_) || !defined(_di_f_thread_mutexs_decrease_) || !defined(_di_f_thread_mutexs_decrease_by_) || !defined(_di_f_thread_mutexs_increase_) || !defined(_di_f_thread_mutexs_increase_by_) || !defined(_di_f_thread_mutexs_resize_) f_status_t private_f_thread_mutex_delete(f_thread_mutex_t *mutex) { diff --git a/level_0/f_thread/c/private-thread.h b/level_0/f_thread/c/private-thread.h index f34b41b..a7340ee 100644 --- a/level_0/f_thread/c/private-thread.h +++ b/level_0/f_thread/c/private-thread.h @@ -255,10 +255,82 @@ extern "C" { * * Intended to be shared to each of the different implementation variations. * + * @param attribute + * The attribute to delete. + * + * @return + * F_none on success. + * + * F_failure (with error bit) on error. + * + * @see pthread_mutexattr_destroy() + * + * @see f_thread_mutex_attributes_adjust() + * @see f_thread_mutex_attributes_decimate_by() + * @see f_thread_mutex_attributes_decrease() + * @see f_thread_mutex_attributes_decrease_by() + * @see f_thread_mutex_attributes_increase() + * @see f_thread_mutex_attributes_increase_by() + * @see f_thread_mutex_attributes_resize() + */ +#if !defined(_di_f_thread_mutex_attributes_adjust_) || !defined(_di_f_thread_mutex_attributes_decimate_by_) || !defined(_di_f_thread_mutex_attributes_decrease_) || !defined(_di_f_thread_mutex_attributes_decrease_by_) || !defined(_di_f_thread_mutex_attributes_increase_) || !defined(_di_f_thread_mutex_attributes_increase_by_) || !defined(_di_f_thread_mutex_attributes_resize_) + extern f_status_t private_f_thread_mutex_attribute_delete(f_thread_mutex_attribute_t *attribute) f_gcc_attribute_visibility_internal; +#endif // !defined(_di_f_thread_mutex_attributes_adjust_) || !defined(_di_f_thread_mutex_attributes_decimate_by_) || !defined(_di_f_thread_mutex_attributes_decrease_) || !defined(_di_f_thread_mutex_attributes_decrease_by_) || !defined(_di_f_thread_mutex_attributes_increase_) || !defined(_di_f_thread_mutex_attributes_increase_by_) || !defined(_di_f_thread_mutex_attributes_resize_) + +/** + * Private implementation for resizing. + * + * Intended to be shared to each of the different implementation variations. + * * @param length * The new size to use. - * @param mutexs - * The mutexs to adjust. + * @param attributes + * The attributes to adjust. + * + * @return + * F_none on success. + * + * Errors (with error bit) from: f_memory_adjust(). + * + * @see f_memory_adjust() + * @see f_thread_mutex_attributes_adjust() + * @see f_thread_mutex_attributes_decimate_by() + */ +#if !defined(_di_f_thread_mutex_attributes_adjust_) || !defined(_di_f_thread_mutex_attributes_decimate_by_) + extern f_status_t private_f_thread_mutex_attributes_adjust(const f_array_length_t length, f_thread_mutex_attributes_t *attributes) f_gcc_attribute_visibility_internal; +#endif // !defined(_di_f_thread_mutex_attributes_adjust_) || !defined(_di_f_thread_mutex_attributes_decimate_by_) + +/** + * Private implementation for resizing. + * + * Intended to be shared to each of the different implementation variations. + * + * @param length + * The new size to use. + * @param attributes + * The attributes to resize. + * + * @return + * F_none on success. + * + * Errors (with error bit) from: f_memory_resize(). + * + * @see f_memory_resize() + * @see f_thread_mutex_attributes_decrease_by() + * @see f_thread_mutex_attributes_increase() + * @see f_thread_mutex_attributes_increase_by() + */ +#if !defined(_di_f_thread_mutex_attributes_decrease_by_) || !defined(_di_f_thread_mutex_attributes_increase_) || !defined(_di_f_thread_mutex_attributes_increase_by_) + extern f_status_t private_f_thread_mutex_attributes_resize(const f_array_length_t length, f_thread_mutex_attributes_t *attributes) f_gcc_attribute_visibility_internal; +#endif // !defined(_di_f_thread_mutex_attributes_decrease_by_) || !defined(_di_f_thread_mutex_attributes_increase_) || !defined(_di_f_thread_mutex_attributes_increase_by_) + +/** + * Private implementation for deleting (and destroying). + * + * Intended to be shared to each of the different implementation variations. + * + * @param mutex + * The mutexs to delete. * * @return * F_none on success. diff --git a/level_0/f_thread/c/thread-common.h b/level_0/f_thread/c/thread-common.h index 5bfb6fd..7f445d7 100644 --- a/level_0/f_thread/c/thread-common.h +++ b/level_0/f_thread/c/thread-common.h @@ -341,16 +341,18 @@ extern "C" { #define f_thread_mutex_attributes_t_initialize { 0, 0, 0 } - #define f_macro_thread_mutex_attributes_t_resize(status, mutex_attributes, length) f_macro_memory_structure_resize(status, mutex_attributes, f_thread_mutex_attribute_t, length) - #define f_macro_thread_mutex_attributes_t_adjust(status, mutex_attributes, length) f_macro_memory_structure_adjust(status, mutex_attributes, f_thread_mutex_attribute_t, length) + #define f_macro_thread_mutex_attributes_t_clear(attributes) f_macro_memory_structure_clear(attributes) - #define f_macro_thread_mutex_attributes_t_delete_simple(mutex_attributes) f_macro_memory_structure_delete_simple(mutex_attributes, f_thread_mutex_attribute_t) - #define f_macro_thread_mutex_attributes_t_destroy_simple(mutex_attributes) f_macro_memory_structure_destroy_simple(mutex_attributes, f_thread_mutex_attribute_t) + #define f_macro_thread_mutex_attributes_t_resize(status, attributes, length) status = f_thread_mutex_attributes_resize(length, &attributes); + #define f_macro_thread_mutex_attributes_t_adjust(status, attributes, length) status = f_thread_mutex_attributes_adjust(length, &attributes); - #define f_macro_thread_mutex_attributes_t_increase(status, mutex_attributes) f_macro_memory_structure_increase(status, mutex_attributes, f_thread_mutex_attribute_t) - #define f_macro_thread_mutex_attributes_t_increase_by(status, mutex_attributes, amount) f_macro_memory_structure_increase_by(status, mutex_attributes, f_thread_mutex_attribute_t, amount) - #define f_macro_thread_mutex_attributes_t_decrease_by(status, mutex_attributes, amount) f_macro_memory_structure_decrease_by(status, mutex_attributes, f_thread_mutex_attribute_t, amount) - #define f_macro_thread_mutex_attributes_t_decimate_by(status, mutex_attributes, amount) f_macro_memory_structure_decimate_by(status, mutex_attributes, f_thread_mutex_attribute_t, amount) + #define f_macro_thread_mutex_attributes_t_delete_simple(attributes) f_thread_mutex_attributes_resize(0, &attributes); + #define f_macro_thread_mutex_attributes_t_destroy_simple(attributes) f_thread_mutex_attributes_adjust(0, &attributes); + + #define f_macro_thread_mutex_attributes_t_increase(status, attributes) status = f_thread_mutex_attributes_increase(attributes); + #define f_macro_thread_mutex_attributes_t_increase_by(status, attributes, amount) status = f_thread_mutex_attributes_increase_by(amount, attributes); + #define f_macro_thread_mutex_attributes_t_decrease_by(status, attributes, amount) status = f_thread_mutex_attributes_decrease_by(amount, attributes); + #define f_macro_thread_mutex_attributes_t_decimate_by(status, attributes, amount) status = f_thread_mutex_attributes_decimate_by(amount, attributes); #endif // _di_f_thread_mutex_attributes_t_ /** diff --git a/level_0/f_thread/c/thread.c b/level_0/f_thread/c/thread.c index fda017d..0295b95 100644 --- a/level_0/f_thread/c/thread.c +++ b/level_0/f_thread/c/thread.c @@ -933,7 +933,7 @@ extern "C" { #endif // _di_f_thread_join_ #ifndef _di_f_thread_join_try_ - f_status_t f_thread_try(const f_thread_id_t id, void **result) { + f_status_t f_thread_join_try(const f_thread_id_t id, void **result) { const int error = pthread_tryjoin_np(id, result); @@ -952,7 +952,7 @@ extern "C" { #endif // _di_f_thread_join_try_ #ifndef _di_f_thread_join_timed_ - f_status_t f_thread_timed(const f_thread_id_t id, const struct timespec wait, void **result) { + f_status_t f_thread_join_timed(const f_thread_id_t id, const struct timespec wait, void **result) { const int error = pthread_timedjoin_np(id, result, &wait); @@ -1061,6 +1061,132 @@ extern "C" { } #endif // _di_f_thread_lock_try_ +#ifndef _di_f_thread_mutex_attributes_adjust_ + f_status_t f_thread_mutex_attributes_adjust(const f_array_length_t length, f_thread_mutex_attributes_t *attributes) { + #ifndef _di_level_0_parameter_checking_ + if (!attributes) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + return private_f_thread_mutex_attributes_adjust(length, attributes); + } +#endif // _di_f_thread_mutex_attributes_adjust_ + +#ifndef _di_f_thread_mutex_attributes_decimate_by_ + f_status_t f_thread_mutex_attributes_decimate_by(const f_array_length_t amount, f_thread_mutex_attributes_t *attributes) { + #ifndef _di_level_0_parameter_checking_ + if (!amount) return F_status_set_error(F_parameter); + if (!attributes) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + if (attributes->size - amount > 0) { + return private_f_thread_mutex_attributes_adjust(attributes->size - amount, attributes); + } + + return private_f_thread_mutex_attributes_adjust(0, attributes); + } +#endif // _di_f_thread_mutex_attributes_decimate_by_ + +#ifndef _di_f_thread_mutex_attributes_decrease_by_ + f_status_t f_thread_mutex_attributes_decrease_by(const f_array_length_t amount, f_thread_mutex_attributes_t *attributes) { + #ifndef _di_level_0_parameter_checking_ + if (!amount) return F_status_set_error(F_parameter); + if (!attributes) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + if (attributes->size - amount > 0) { + return private_f_thread_mutex_attributes_resize(attributes->size - amount, attributes); + } + + return private_f_thread_mutex_attributes_resize(0, attributes); + } +#endif // _di_f_thread_mutex_attributes_decrease_by_ + +#ifndef _di_f_thread_mutex_attribute_create_ + f_status_t f_thread_mutex_attribute_create(f_thread_mutex_attribute_t *attribute) { + #ifndef _di_level_0_parameter_checking_ + if (!attribute) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + const int error = pthread_mutexattr_init(attribute); + + // @todo figure out the error codes and update accordingly. + if (error) { + if (error == EAGAIN) return F_status_set_error(F_resource_not); + if (error == EBUSY) return F_status_set_error(F_busy); + if (error == EINVAL) return F_status_set_error(F_parameter); + if (error == ENOMEM) return F_status_set_error(F_memory_not); + if (error == EPERM) return F_status_set_error(F_prohibited); + + return F_status_set_error(F_failure); + } + + return F_none; + } +#endif // _di_f_thread_mutex_attribute_create_ + +#ifndef _di_f_thread_mutex_attribute_delete_ + f_status_t f_thread_mutex_attribute_delete(f_thread_mutex_attribute_t *attribute) { + #ifndef _di_level_0_parameter_checking_ + if (!attribute) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + return private_f_thread_mutex_attribute_delete(attribute); + } +#endif // _di_f_thread_mutex_attribute_delete_ + +#ifndef _di_f_thread_mutex_attributes_increase_ + f_status_t f_thread_mutex_attributes_increase(f_thread_mutex_attributes_t *attributes) { + #ifndef _di_level_0_parameter_checking_ + if (!attributes) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + if (attributes->used + 1 > attributes->size) { + f_array_length_t size = attributes->used + f_memory_default_allocation_step; + + if (size > f_array_length_t_size) { + if (attributes->used + 1 > f_array_length_t_size) { + return F_status_set_error(F_array_too_large); + } + + size = f_array_length_t_size; + } + + return private_f_thread_mutex_attributes_resize(size, attributes); + } + + return F_data_not; + } +#endif // _di_f_thread_mutex_attributes_increase_ + +#ifndef _di_f_thread_mutex_attributes_increase_by_ + f_status_t f_thread_mutex_attributes_increase_by(const f_array_length_t amount, f_thread_mutex_attributes_t *attributes) { + #ifndef _di_level_0_parameter_checking_ + if (!amount) return F_status_set_error(F_parameter); + if (!attributes) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + if (attributes->used + amount > attributes->size) { + if (attributes->used + amount > f_array_length_t_size) { + return F_status_set_error(F_array_too_large); + } + + return private_f_thread_mutex_attributes_resize(attributes->used + amount, attributes); + } + + return F_data_not; + } +#endif // _di_f_thread_mutex_attributes_increase_by_ + +#ifndef _di_f_thread_mutex_attributes_resize_ + f_status_t f_thread_mutex_attributes_resize(const f_array_length_t length, f_thread_mutex_attributes_t *attributes) { + #ifndef _di_level_0_parameter_checking_ + if (!attributes) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + return private_f_thread_mutex_attributes_resize(length, attributes); + } +#endif // _di_f_thread_mutex_attributes_resize_ + #ifndef _di_f_thread_mutex_create_ f_status_t f_thread_mutex_create(f_thread_mutex_attribute_t * const attribute, f_thread_mutex_t *mutex) { #ifndef _di_level_0_parameter_checking_ diff --git a/level_0/f_thread/c/thread.h b/level_0/f_thread/c/thread.h index 13e8e0d..35a9c06 100644 --- a/level_0/f_thread/c/thread.h +++ b/level_0/f_thread/c/thread.h @@ -10,6 +10,47 @@ #ifndef _F_thread_h #define _F_thread_h +// @todo pthread_attr_getstackaddr() +// @todo pthread_attr_getstacksize() +// @todo pthread_attr_setstackaddr() +// @todo pthread_attr_setstacksize() +// @todo pthread_barrier_destroy +// @todo pthread_barrier_init +// @todo pthread_barrier_wait +// @todo pthread_barrierattr_destroy +// @todo pthread_barrierattr_getpshared +// @todo pthread_barrierattr_init +// @todo pthread_barrierattr_setpshared +// @todo pthread_condattr_getclock +// @todo pthread_condattr_getpshared +// @todo pthread_condattr_setclock +// @todo pthread_condattr_setpshared +// @todo pthread_getconcurrency +// @todo pthread_getschedparam +// @todo pthread_key_delete +// @todo pthread_mutex_getprioceiling +// @todo pthread_mutex_setprioceiling +// @todo pthread_mutex_timedlock +// @todo pthread_mutexattr_getprioceiling +// @todo pthread_mutexattr_getprotocol +// @todo pthread_mutexattr_getpshared +// @todo pthread_mutexattr_gettype +// @todo pthread_mutexattr_setprioceiling +// @todo pthread_mutexattr_setprotocol +// @todo pthread_mutexattr_setpshared +// @todo pthread_mutexattr_settype +// @todo pthread_rwlock_destroy +// @todo pthread_rwlock_init +// @todo pthread_rwlock_timedrdlock +// @todo pthread_rwlock_timedwrlock +// @todo pthread_rwlock_trywrlock +// @todo pthread_rwlock_unlock +// @todo pthread_rwlock_wrlock +// @todo pthread_rwlockattr_destroy +// @todo pthread_rwlockattr_getpshared +// @todo pthread_rwlockattr_init +// @todo pthread_rwlockattr_setpshared + // include pre-requirements #define _GNU_SOURCE @@ -696,7 +737,7 @@ extern "C" { * F_supported_not (with error bit) if per-CPU clocks are not supported by the OS. * F_failure (with error bit) on any other error. * - * @see pthread_equal() + * @see pthread_getcpuclockid() */ #ifndef _di_f_thread_clock_get_id_ extern f_status_t f_thread_clock_get_id(const f_thread_id_t id_thread, clockid_t *id_clock); @@ -753,7 +794,7 @@ extern "C" { * F_parameter (with error bit) if a parameter is invalid. * F_failure (with error bit) on any other error. * - * @see pthread_cond_destroy() + * @see pthread_condattr_destroy() */ #ifndef _di_f_thread_condition_attribute_delete_ extern f_status_t f_thread_condition_attribute_delete(f_thread_condition_attribute_t *attribute); @@ -1263,7 +1304,7 @@ extern "C" { * @see pthread_tryjoin_np() */ #ifndef _di_f_thread_join_try_ - extern f_status_t f_thread_try(const f_thread_id_t id, void **result); + extern f_status_t f_thread_join_try(const f_thread_id_t id, void **result); #endif // _di_f_thread_join_try_ /** @@ -1296,7 +1337,7 @@ extern "C" { * @see pthread_timedjoin_np() */ #ifndef _di_f_thread_join_timed_ - extern f_status_t f_thread_timed(const f_thread_id_t id, const struct timespec wait, void **result); + extern f_status_t f_thread_join_timed(const f_thread_id_t id, const struct timespec wait, void **result); #endif // _di_f_thread_join_timed_ /** @@ -1398,6 +1439,176 @@ extern "C" { #endif // _di_f_thread_lock_try_ /** + * Resize the string attributes array. + * + * @param length + * The new size to use. + * @param attributes + * The string attributes array to resize. + * + * @return + * F_none on success. + * + * F_memory_not (with error bit) on out of memory. + * F_parameter (with error bit) if a parameter is invalid. + */ +#ifndef _di_f_thread_mutex_attributes_adjust_ + extern f_status_t f_thread_mutex_attributes_adjust(const f_array_length_t length, f_thread_mutex_attributes_t *attributes); +#endif // _di_f_thread_mutex_attributes_adjust_ + +/** + * Resize the string attributes array to a smaller size. + * + * This will resize making the array smaller based on (size - given length). + * If the given length is too small, then the resize will fail. + * This will not shrink the size to less than 0. + * + * @param amount + * A positive number representing how much to decimate the size by. + * @param attributes + * The string attributes array to resize. + * + * @return + * F_none on success. + * + * F_memory_not (with error bit) on out of memory. + * F_parameter (with error bit) if a parameter is invalid. + */ +#ifndef _di_f_thread_mutex_attributes_decimate_by_ + extern f_status_t f_thread_mutex_attributes_decimate_by(const f_array_length_t amount, f_thread_mutex_attributes_t *attributes); +#endif // _di_f_thread_mutex_attributes_decimate_by_ + +/** + * Resize the string attributes array to a smaller size. + * + * This will resize making the array smaller based on (size - given length). + * If the given length is too small, then the resize will fail. + * This will not shrink the size to less than 0. + * + * @param amount + * A positive number representing how much to decrease the size by. + * @param attributes + * The string attributes array to resize. + * + * @return + * F_none on success. + * + * F_memory_not (with error bit) on out of memory. + * F_parameter (with error bit) if a parameter is invalid. + */ +#ifndef _di_f_thread_mutex_attributes_decrease_by_ + extern f_status_t f_thread_mutex_attributes_decrease_by(const f_array_length_t amount, f_thread_mutex_attributes_t *attributes); +#endif // _di_f_thread_mutex_attributes_decrease_by_ + +/** + * Create a thread mutex attribute. + * + * @param attribute + * The mutex attributes to create. + * + * @return + * F_none on success. + * + * F_busy (with error bit) if the mutex is busy. + * F_memory_not (with error bit) if out of memory. + * F_parameter (with error bit) if a parameter is invalid. + * F_prohibited (with error bit) if not allowed to perform the operation. + * F_resource_not (with error bit) if max mutexes is reached. + * F_failure (with error bit) on any other error. + * + * @see pthread_mutexattr_init() + */ +#ifndef _di_f_thread_mutex_attribute_create_ + extern f_status_t f_thread_mutex_attribute_create(f_thread_mutex_attribute_t *attribute); +#endif // _di_f_thread_mutex_attribute_create_ + +/** + * Delete a thread mutex attribute. + * + * The pthread_mutexattr_destroy() function has no distinction like the *_destroy() and the *_delete() used by the FLL project. + * Therefore there is only this function for both deleting and destroying. + * + * @param attribute + * The attribute to delete. + * + * @return + * F_none on success. + * + * F_busy (with error bit) if the mutex is busy. + * F_parameter (with error bit) if a parameter is invalid. + * F_failure (with error bit) on any other error. + * + * @see pthread_mutexattr_destroy() + */ +#ifndef _di_f_thread_mutex_attribute_delete_ + extern f_status_t f_thread_mutex_attribute_delete(f_thread_mutex_attribute_t *attribute); +#endif // _di_f_thread_mutex_attribute_delete_ + +/** + * Increase the size of the string attributes array, but only if necessary. + * + * If the given length is too large for the buffer, then attempt to set max buffer size (f_array_length_t_size). + * If already set to the maximum buffer size, then the resize will fail. + * + * @param attributes + * The string attributes array to resize. + * + * @return + * F_none on success. + * F_data_not on success, but there is no reason to increase size (used + 1 <= size). + * + * F_array_too_large (with error bit) if the new array length is too large. + * F_memory_not (with error bit) on out of memory. + * F_parameter (with error bit) if a parameter is invalid. + */ +#ifndef _di_f_thread_mutex_attributes_increase_ + extern f_status_t f_thread_mutex_attributes_increase(f_thread_mutex_attributes_t *attributes); +#endif // _di_f_thread_mutex_attributes_increase_ + +/** + * Resize the string attributes array to a larger size. + * + * This will resize making the string larger based on the given length. + * If the given length is too large for the buffer, then attempt to set max buffer size (f_array_length_t_size). + * If already set to the maximum buffer size, then the resize will fail. + * + * @param amount + * A positive number representing how much to increase the size by. + * @param attributes + * The string attributes array to resize. + * + * @return + * F_none on success. + * F_data_not on success, but there is no reason to increase size (used + amount <= size). + * + * F_memory_not (with error bit) on out of memory. + * F_parameter (with error bit) if a parameter is invalid. + * F_array_too_large (with error bit) if the new array length is too large. + */ +#ifndef _di_f_thread_mutex_attributes_increase_by_ + extern f_status_t f_thread_mutex_attributes_increase_by(const f_array_length_t amount, f_thread_mutex_attributes_t *attributes); +#endif // _di_f_thread_mutex_attributes_increase_by_ + +/** + * Resize the string attributes array. + * + * @param length + * The new size to use. + * @param attributes + * The string attributes array to adjust. + * + * @return + * F_none on success. + * + * F_memory_not (with error bit) on out of memory. + * F_parameter (with error bit) if a parameter is invalid. + */ +#ifndef _di_f_thread_mutex_attributes_resize_ + extern f_status_t f_thread_mutex_attributes_resize(const f_array_length_t length, f_thread_mutex_attributes_t *attributes); +#endif // _di_f_thread_mutex_attributes_resize_ + + +/** * Create a thread mutex. * * @param attribute diff --git a/level_3/controller/c/private-rule.c b/level_3/controller/c/private-rule.c index d6caac2..0cb532c 100644 --- a/level_3/controller/c/private-rule.c +++ b/level_3/controller/c/private-rule.c @@ -623,9 +623,7 @@ extern "C" { status = controller_rule_execute_foreground(index, item->type, *action, 0, action->parameters, options, &execute_set, thread); - if (status == F_child) { - break; - } + if (status == F_child) break; if (F_status_is_error(status)) { action->status = F_status_set_error(F_failure); @@ -644,9 +642,7 @@ extern "C" { status = controller_rule_execute_foreground(index, item->type, *action, rule->script.used ? rule->script.string : controller_default_program_script, arguments_none, options, &execute_set, thread); - if (status == F_child) { - break; - } + if (status == F_child) break; if (F_status_is_error(status)) { action->status = F_status_set_error(F_failure); @@ -664,6 +660,8 @@ extern "C" { status = controller_rule_execute_pid_with(index, item->type, *action, 0, action->parameters, options, &execute_set, thread); + if (status == F_child) break; + if (F_status_is_error(status)) { action->status = F_status_set_error(F_failure); @@ -693,11 +691,11 @@ extern "C" { continue; } - - if (status == F_child || status == F_signal) break; } // for - if (status == F_child || status == F_signal || F_status_is_error(status) && !(options & controller_rule_option_simulate)) break; + if (status == F_child || status == F_signal || F_status_is_error(status) && !(options & controller_rule_option_simulate)){ + break; + } } // for f_macro_string_maps_t_delete_simple(environment); @@ -720,19 +718,12 @@ extern "C" { } #endif // _di_controller_rule_execute_ -#ifndef _di_controller_rule_execute_pid_with_ - f_status_t controller_rule_execute_pid_with(const f_array_length_t index, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_t *thread) { +#ifndef _di_controller_rule_execute_foreground_ + f_status_t controller_rule_execute_foreground(const f_array_length_t index, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_t *thread) { f_status_t status = F_none; int result = 0; - // @todo check to see if pid file exists. - - // @todo this needs to support/use an option to designate that the process automatically forks in the background. - // in which case fll_execute_program() is called. - // otherwise this needs to call an asynchronous execute process. - // until then, this controller_rule_execute_pid_with() function is not correct and only represents a process that forks to the background. - if (options & controller_rule_option_simulate) { if (thread->data->error.verbosity != f_console_verbosity_quiet) { @@ -771,13 +762,25 @@ extern "C" { controller_asynchronous_t *asynchronous = (controller_asynchronous_t *) thread->setting->rules.array[index].asynchronous; // assign the child process id to the asynchronous thread to allow for the cancel process to send appropriate termination signals to the child process. - asynchronous->child = result; + if (thread->asynchronouss.enabled) { + f_thread_mutex_lock(&thread->mutex->asynchronous); + + asynchronous->child = result; + + f_thread_mutex_unlock(&thread->mutex->asynchronous); + } - // have the parent wait for the child process to finish. (@todo see comments above about forking into the background, this code block will need to change.) + // have the parent wait for the child process to finish. waitpid(asynchronous->child, &result, WUNTRACED | WCONTINUED); // remove the pid now that waidpid() has returned. - asynchronous->child = 0; + if (thread->asynchronouss.enabled) { + f_thread_mutex_lock(&thread->mutex->asynchronous); + + asynchronous->child = 0; + + f_thread_mutex_unlock(&thread->mutex->asynchronous); + } // this must explicitly check for 0 (as opposed to checking (!result)). if (!WIFEXITED(result)) { @@ -797,7 +800,7 @@ extern "C" { } if (status == F_child) { - thread->data->child = result; + //thread->data->child = result; // @fixme cant do this! return F_child; } @@ -823,25 +826,28 @@ extern "C" { f_thread_mutex_unlock(&thread->mutex->print); - thread->data->child = 0; - - return F_status_set_error(status); + status = F_status_set_error(status); } - // @todo wait for pid file or timeout. - - thread->data->child = 0; + // thread->data->child = 0; // @fixme cant do this! return status; } -#endif // _di_controller_rule_execute_pid_with_ +#endif // _di_controller_rule_execute_foreground_ -#ifndef _di_controller_rule_execute_foreground_ - f_status_t controller_rule_execute_foreground(const f_array_length_t index, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_t *thread) { +#ifndef _di_controller_rule_execute_pid_with_ + f_status_t controller_rule_execute_pid_with(const f_array_length_t index, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_t *thread) { f_status_t status = F_none; int result = 0; + // @todo check to see if pid file exists. + + // @todo this needs to support/use an option to designate that the process automatically forks in the background. + // in which case fll_execute_program() is called. + // otherwise this needs to call an asynchronous execute process. + // until then, this controller_rule_execute_pid_with() function is not correct and only represents a process that forks to the background. + if (options & controller_rule_option_simulate) { if (thread->data->error.verbosity != f_console_verbosity_quiet) { @@ -879,14 +885,28 @@ extern "C" { if (status == F_parent) { controller_asynchronous_t *asynchronous = (controller_asynchronous_t *) thread->setting->rules.array[index].asynchronous; + result = 0; + // assign the child process id to the asynchronous thread to allow for the cancel process to send appropriate termination signals to the child process. - asynchronous->child = result; + if (thread->asynchronouss.enabled) { + f_thread_mutex_lock(&thread->mutex->asynchronous); - // have the parent wait for the child process to finish. (@todo see comments above about forking into the background, this code block will need to change.) - waitpid(asynchronous->child, &result, WUNTRACED | WCONTINUED); + asynchronous->child = result; + + f_thread_mutex_unlock(&thread->mutex->asynchronous); + + // have the parent wait for the child process to finish. (@todo see comments above about forking into the background, this code block will need to change, maybe pass WNOHANG?.) + waitpid(asynchronous->child, &result, WUNTRACED | WCONTINUED); + } // remove the pid now that waidpid() has returned. - asynchronous->child = 0; + if (thread->asynchronouss.enabled) { + f_thread_mutex_lock(&thread->mutex->asynchronous); + + asynchronous->child = 0; + + f_thread_mutex_unlock(&thread->mutex->asynchronous); + } // this must explicitly check for 0 (as opposed to checking (!result)). if (!WIFEXITED(result)) { @@ -906,7 +926,7 @@ extern "C" { } if (status == F_child) { - thread->data->child = result; + //thread->data->child = result; // @fixme cant do this! return F_child; } @@ -932,14 +952,18 @@ extern "C" { f_thread_mutex_unlock(&thread->mutex->print); - status = F_status_set_error(status); + //thread->data->child = 0; // @fixme cant do this! + + return F_status_set_error(status); } - thread->data->child = 0; + // @todo wait for pid file or timeout. + + //thread->data->child = 0; // @fixme cant do this! return status; } -#endif // _di_controller_rule_execute_foreground_ +#endif // _di_controller_rule_execute_pid_with_ #ifndef _di_controller_rule_find_loaded_ f_array_length_t controller_rule_find_loaded(const controller_data_t data, const controller_setting_t setting, const f_string_static_t rule_id) { @@ -1628,6 +1652,10 @@ extern "C" { } // for } + if (status == F_child || status == F_signal) { + return status; + } + if (!(options & controller_rule_option_wait) && F_status_is_error_not(status)) { controller_rule_wait_all(thread); } @@ -1671,13 +1699,13 @@ extern "C" { if (F_status_is_error_not(status)) { status = controller_rule_execute(index, action, options, thread); - if (F_status_is_error(status)) { - controller_rule_error_print(thread->data->error, *thread->cache_action, F_true); - } - if (status == F_child) { return F_child; } + + if (F_status_is_error(status)) { + controller_rule_error_print(thread->data->error, *thread->cache_action, F_true); + } } } @@ -1707,6 +1735,7 @@ extern "C" { if (F_status_is_error(status)) { f_thread_mutex_unlock(&thread->mutex->asynchronous); + return status; } @@ -3681,7 +3710,7 @@ extern "C" { return; } - controller_rule_t * const rule = &setting->rules.array[index]; + const controller_rule_t *rule = &setting->rules.array[index]; f_array_length_t i = 0; f_array_length_t j = 0; @@ -3976,30 +4005,35 @@ extern "C" { } controller_rule_t *rule = &thread->setting->rules.array[index]; + controller_asynchronous_t *asynchronous = (controller_asynchronous_t *) rule->asynchronous; if (!rule->asynchronous) { return; } + // do not need to wait when state is 0. + if (!asynchronous->state) { + return; + } + if (f_thread_mutex_lock_try(&rule->wait) == F_none) { - controller_asynchronous_t *asynchronous = (controller_asynchronous_t *) rule->asynchronous; - if (asynchronous->state == controller_asynchronous_state_done) { + if (asynchronous->state != controller_asynchronous_state_joined) { f_thread_join(asynchronous->id, 0); } if (thread->asynchronouss.enabled) { - f_thread_mutex_lock(&thread->mutex->asynchronous); + if (f_thread_mutex_lock_try(&thread->mutex->asynchronous) == F_none) { + if (asynchronous->state) { + if (asynchronous->state == controller_asynchronous_state_done) { + asynchronous->state = controller_asynchronous_state_joined; + } - if (asynchronous->state) { - if (asynchronous->state == controller_asynchronous_state_done) { - asynchronous->state = controller_asynchronous_state_joined; + controller_macro_cache_action_t_clear(asynchronous->cache); } - controller_macro_cache_action_t_clear(asynchronous->cache); + f_thread_mutex_unlock(&thread->mutex->asynchronous); } - - f_thread_mutex_unlock(&thread->mutex->asynchronous); } f_thread_mutex_unlock(&rule->wait); @@ -4009,8 +4043,10 @@ extern "C" { // a wait lock is already in place, which will also be responsible for thread joining. // this can therefore immediately unlock and return. f_thread_mutex_lock(&rule->wait); + f_thread_mutex_unlock(&rule->wait); } + } #endif // _di_controller_rule_wait_for_ diff --git a/level_3/controller/c/private-rule.h b/level_3/controller/c/private-rule.h index 7730b4c..0de920e 100644 --- a/level_3/controller/c/private-rule.h +++ b/level_3/controller/c/private-rule.h @@ -259,10 +259,7 @@ extern "C" { #endif // _di_controller_rule_execute_ /** - * Perform an execution of the given rule in the foreground or background and creating a PID file. - * - * When this is synchronous, this will wait for the PID file to be generated before continuing. - * When this is asynchronous, this will continue on adding the rule id and action to the asynchronous list. + * Perform an execution of the given rule in the foreground. * * @param index * The index location in the rules running this action. @@ -291,7 +288,6 @@ extern "C" { * * @return * F_none on success. - * F_busy on successful execute in asynchronous mode (executed process may or may not fail later on). * F_child on child process exiting. * F_signal on (exit) signal received. * @@ -299,12 +295,15 @@ extern "C" { * * @see fll_execute_program() */ -#ifndef _di_controller_rule_execute_pid_with_ - extern f_status_t controller_rule_execute_pid_with(const f_array_length_t index, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_t *thread) f_gcc_attribute_visibility_internal; -#endif // _di_controller_rule_execute_pid_with_ +#ifndef _di_controller_rule_execute_foreground_ + extern f_status_t controller_rule_execute_foreground(const f_array_length_t index, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_t *thread) f_gcc_attribute_visibility_internal; +#endif // _di_controller_rule_execute_foreground_ /** - * Perform an execution of the given rule in the foreground. + * Perform an execution of the given rule in the foreground or background and creating a PID file. + * + * When this is synchronous, this will wait for the PID file to be generated before continuing. + * When this is asynchronous, this will continue on adding the rule id and action to the asynchronous list. * * @param index * The index location in the rules running this action. @@ -333,6 +332,7 @@ extern "C" { * * @return * F_none on success. + * F_busy on successful execute in asynchronous mode (executed process may or may not fail later on). * F_child on child process exiting. * F_signal on (exit) signal received. * @@ -340,9 +340,9 @@ extern "C" { * * @see fll_execute_program() */ -#ifndef _di_controller_rule_execute_foreground_ - extern f_status_t controller_rule_execute_foreground(const f_array_length_t index, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_t *thread) f_gcc_attribute_visibility_internal; -#endif // _di_controller_rule_execute_foreground_ +#ifndef _di_controller_rule_execute_pid_with_ + extern f_status_t controller_rule_execute_pid_with(const f_array_length_t index, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_t *thread) f_gcc_attribute_visibility_internal; +#endif // _di_controller_rule_execute_pid_with_ /** * Search the already loaded rules to see if one is found. diff --git a/level_3/controller/c/private-thread.c b/level_3/controller/c/private-thread.c index c852788..ba985c9 100644 --- a/level_3/controller/c/private-thread.c +++ b/level_3/controller/c/private-thread.c @@ -13,28 +13,35 @@ extern "C" { void * controller_thread_asynchronous(void *arguments) { controller_asynchronous_t *asynchronous = (controller_asynchronous_t *) arguments; - controller_thread_t thread = controller_thread_t_initialize; + controller_thread_t *thread_main = (controller_thread_t *) asynchronous->thread; - { - controller_thread_t *thread_main = (controller_thread_t *) asynchronous->thread; + if (!thread_main->asynchronouss.enabled) { + return 0; + } - if (!thread_main->asynchronouss.enabled) { - return 0; - } + controller_thread_t thread = controller_thread_t_initialize; - f_thread_mutex_lock(&thread_main->setting->rules.array[asynchronous->index].lock); + f_thread_mutex_lock(&thread_main->setting->rules.array[asynchronous->index].lock); - thread.cache_main = thread_main->cache_main; - thread.cache_action = &asynchronous->cache; - thread.data = thread_main->data; - thread.mutex = thread_main->mutex; - thread.setting = thread_main->setting; - thread.stack = &asynchronous->stack; + thread.cache_main = thread_main->cache_main; + thread.cache_action = &asynchronous->cache; + thread.data = thread_main->data; + thread.mutex = thread_main->mutex; + thread.setting = thread_main->setting; + thread.stack = &asynchronous->stack; + + if (controller_rule_process(asynchronous->index, asynchronous->action, asynchronous->options, &thread) == F_child) { + // @todo consider returning 1 to designate that this is a child process exiting. + return 0; } - controller_rule_process(asynchronous->index, asynchronous->action, asynchronous->options, &thread); + if (thread.asynchronouss.enabled) { + //f_thread_mutex_lock(&thread_main->mutex->asynchronous); + + asynchronous->state = controller_asynchronous_state_done; - asynchronous->state = controller_asynchronous_state_done; + //f_thread_mutex_unlock(&thread_main->mutex->asynchronous); + } f_thread_mutex_unlock(&thread.setting->rules.array[asynchronous->index].lock); @@ -47,20 +54,26 @@ extern "C" { thread->asynchronouss.enabled = F_false; - f_thread_mutex_lock(&thread->mutex->asynchronous); - f_array_length_t i = 0; + f_thread_mutex_lock(&thread->mutex->asynchronous); + for (; i < thread->asynchronouss.used; ++i) { if (!thread->asynchronouss.array[i].state) continue; - if (thread->asynchronouss.array[i].child > 0) { - f_signal_send(F_signal_termination, thread->asynchronouss.array[i].child); + if (thread->asynchronouss.array[i].state == controller_asynchronous_state_active) { + if (thread->asynchronouss.array[i].child > 0) { + f_signal_send(F_signal_termination, thread->asynchronouss.array[i].child); + } + + thread->asynchronouss.array[i].state = controller_asynchronous_state_done; } - // @todo perhaps a timed join here where if it takes to long, try sending a kill signal to the child process. - f_thread_join(thread->asynchronouss.array[i].id, 0); + if (thread->asynchronouss.array[i].state == controller_asynchronous_state_done) { + // @todo perhaps a timed join here where if it takes to long, try sending a kill signal to the child process. + f_thread_join(thread->asynchronouss.array[i].id, 0); + } thread->asynchronouss.array[i].state = 0; @@ -69,11 +82,6 @@ extern "C" { thread->asynchronouss.used = 0; - for (i = 0; i < thread->setting->rules.used; ++i) { - f_thread_mutex_unlock(&thread->setting->rules.array[i].lock); - f_thread_mutex_unlock(&thread->setting->rules.array[i].wait); - } // for - f_thread_mutex_unlock(&thread->mutex->print); f_thread_mutex_unlock(&thread->mutex->cache); f_thread_mutex_unlock(&thread->mutex->rule); @@ -85,12 +93,13 @@ extern "C" { void * controller_thread_cache(void *arguments) { controller_thread_t *thread = (controller_thread_t *) arguments; - controller_rule_t *rule = 0; + + const unsigned int interval = thread->data->parameters[controller_parameter_test].result == f_console_result_found ? controller_thread_cache_cleanup_interval_short : controller_thread_cache_cleanup_interval_long; f_array_length_t i = 0; for (;;) { - sleep(controller_thread_cache_cleanup_interval_long); + sleep(interval); if (f_thread_mutex_lock_try(&thread->mutex->cache) == F_none) { controller_macro_cache_t_delete_simple((*thread->cache_main)); @@ -101,18 +110,23 @@ extern "C" { if (thread->asynchronouss.used) { for (i = 0; i < thread->asynchronouss.used; ++i) { - if (thread->asynchronouss.array[i].state == controller_asynchronous_state_done) { - f_thread_join(thread->asynchronouss.array[i].id, 0); - thread->asynchronouss.array[i].state = controller_asynchronous_state_joined; - } + if (!thread->asynchronouss.array[i].state) continue; - if (thread->asynchronouss.array[i].state == controller_asynchronous_state_joined) { - controller_macro_asynchronous_t_delete_simple(thread->asynchronouss.array[i]); + if (f_thread_mutex_lock_try(&thread->setting->rules.array[thread->asynchronouss.array[i].index].lock) == F_none) { - thread->asynchronouss.array[i].state = 0; - } + if (thread->asynchronouss.array[i].state == controller_asynchronous_state_done) { + f_thread_join(thread->asynchronouss.array[i].id, 0); + thread->asynchronouss.array[i].state = controller_asynchronous_state_joined; + } - if (thread->asynchronouss.array[i].state) break; + if (thread->asynchronouss.array[i].state == controller_asynchronous_state_joined) { + controller_macro_asynchronous_t_delete_simple(thread->asynchronouss.array[i]); + + thread->asynchronouss.array[i].state = 0; + } + + f_thread_mutex_unlock(&thread->setting->rules.array[thread->asynchronouss.array[i].index].lock); + } } // for for (i = thread->asynchronouss.used - 1; thread->asynchronouss.used; --i, --thread->asynchronouss.used) { @@ -122,8 +136,9 @@ extern "C" { thread->asynchronouss.array[i].state = 0; } - - if (thread->asynchronouss.array[i].state) break; + else if (thread->asynchronouss.array[i].state) { + break; + } } // for } @@ -178,7 +193,7 @@ extern "C" { if (f_file_exists(thread->setting->path_pid.string) == F_true) { if (thread->data->error.verbosity != f_console_verbosity_quiet) { - if (thread->asynchronouss.enabled) f_thread_mutex_lock(&thread->mutex->print); + f_thread_mutex_lock(&thread->mutex->print); fprintf(thread->data->error.to.stream, "%c", f_string_eol_s[0]); fprintf(thread->data->error.to.stream, "%s%sThe pid file '", thread->data->error.context.before->string, thread->data->error.prefix ? thread->data->error.prefix : f_string_empty_s); @@ -238,6 +253,10 @@ extern "C" { } } + if (status == F_child) { + return F_child; + } + f_thread_mutex_unlock(&thread->mutex->cache); } } -- 1.8.3.1