Refactor `instance.lock` to `instance.use` to reduce the ambiguity regarding a lock named "lock".
Remove the `id` off of the instance.
This is not needed and removing it reduces the need for some locking.
The thread itself is now the "id" so `id_thread` is now renamed to just `thread`.
The `controller_instance_find()` function can now just get the instance pointer.
Add a "time" controller lock check flag.
Use this to designate expecting to return `F_time` on time outs.
Add additional documentation regarding locks.
Reduce uses of locking where possible by re-organizing code.
The `controller_instance_prepare()` function, in particular, has the the code changed to reduce how long the lock is held.
This is possible because the `controller_instance_find()` now gets the instance pointer rather than an identifier.
The `controller_instance_prepare()` function allocates the instance pointer independent of the array.
This allows for the write lock on instance to be only used when assigning to the array.
The instance must therefore be immediately freed on any error because it is not assigned to an array and cannot be auto-deallocated.
Add missing locks to areas where the locks should be, such as a read lock while reading the dependency rule status.
Make sure `instance.active` lock is set before calling `instance.use` locks (there may be more work needed regarding this).
Try to reduce how long `instance.use` are held (there may be more work needed regarding this).
* controller_lock_check_flag_*_d:
* - no: Do not perform any checks; only return on success (or initial parameter errors before calling locks or waits).
* - yes: Perform all checks and return (error check and enabled/disable check).
- * - error: Only perform error check and return.
+ * - error: Only perform error check and return error.
+ * - time: Perform error check and return error, otherwise if time out then return F_time.
*/
#ifndef _di_controller_lock_check_flag_d_
#define controller_lock_check_flag_no_d 0x0
#define controller_lock_check_flag_yes_d 0x1
#define controller_lock_check_flag_error_d 0x2
+ #define controller_lock_check_flag_time_d 0x4
#endif // _di_controller_lock_check_flag_d_
/**
/**
* Instance options.
*
+ * @todo this should probably be defines rather than enums.
+ *
* controller_instance_option_*_e:
* - asynchronous: The Instance runs asynchronously.
* - require: The Instance is required.
*
* controller_instance_state_*_e:
* - idle: No instance is running for this Rule.
- * - busy: A instance is actively using this, and is running synchronously.
* - active: A instance is actively using this, and is running asynchronously.
+ * - busy: A instance is actively using this, and is running synchronously.
* - done: A instance has finished running on this and there is a thread that needs to be cleaned up.
*/
#ifndef _di_controller_instance_state_e_
enum {
controller_instance_state_idle_e = 1,
- controller_instance_state_busy_e,
controller_instance_state_active_e,
+ controller_instance_state_busy_e,
controller_instance_state_done_e,
}; // enum
#endif // _di_controller_instance_state_e_
if (!instance) return;
- if (instance->id_thread) {
- f_thread_signal_write(instance->id_thread, F_signal_kill);
- f_thread_join(instance->id_thread, 0);
+ if (instance->thread) {
+ f_thread_signal_write(instance->thread, F_signal_kill);
+ f_thread_join(instance->thread, 0);
- instance->id_thread = 0;
+ instance->thread = 0;
}
f_thread_condition_delete(&instance->wait_condition);
- f_thread_lock_delete(&instance->lock);
+ f_thread_lock_delete(&instance->use);
f_thread_lock_delete(&instance->active);
f_thread_mutex_full_delete(&instance->wait);
f_status_t status = f_memory_new(1, sizeof(controller_instance_t), (void **) instance);
if (F_status_is_error_not(status)) {
- status = f_thread_lock_create(0, &(*instance)->lock);
+ status = f_thread_lock_create(0, &(*instance)->use);
}
if (F_status_is_error_not(status)) {
*
* The typedef for this is located in the defs.h header.
*
- * The "active" lock is used for asynchronous operations.
- * This can be used to check if anything is asynchronously operating the rule.
+ * The "active" lock is used for asynchronous execution of a rule.
+ * This can be used to check if anything is asynchronously operating the rule and designates that something is actively using the instance.
+ * A write lock on "active" should be used to prevent activation.
*
* Properties:
- * - id: The ID of this process relative to the processes array.
- * - id_thread: The thread ID, a valid ID when state is "active", and an invalid ID when the state is "busy".
+ * - thread: The thread ID, a valid ID when state is "active", and an invalid ID when the state is "busy".
*
- * - action: The Action being performed.
- * - options: Configuration options for this thread.
+ * - action: The Action being performed (should not need locking to read or write to).
+ * - options: Configuration options for this thread (should not need locking to read or write to).
* - state: The state of the process.
- * - type: The currently active process type (from the controller_instance_type_*_e).
+ * - type: The currently active process type (from the controller_instance_type_*_e) (should not need locking to read or write to).
* - result: The last return code from an execution of a process.
*
* - active: A read/write lock representing that something is currently using this (read locks = in use, write lock = begin deleting).
- * - lock: A read/write lock on the structure.
+ * - use: A read/write lock on the structure, except for specially noted properties.
* - wait: A mutex lock for working with "wait".
* - wait_condition: A thread condition to tell a process waiting process that the Rule has is done being processed.
*
* - child: The process id of a child process, if one is running (when forking to execute a child process).
* - path_pids: An array of paths representing PID files.
- * - environment: Environment data used when executing the instance.
+ * - environment: Environment data used when executing the instance (should not need locking to read or write to).
* - stack: A stack used to represent dependencies as Rule ID's to avoid circular Rule dependencies (If Rule A waits on Rule B, then Rule B must not wait on Rule A).
*
- * - rule: A copy of the Rule actively being executed.
- * - cache: The cache used by this Instance.
+ * - rule: A copy of the Rule actively being executed (should not need locking to read or write to).
+ * - cache: The cache used by this Instance (should not need locking to read or write to).
* - main: The main program data.
*/
#ifndef _di_controller_instance_t_
struct controller_instance_t_ {
- f_number_unsigned_t id;
- f_thread_id_t id_thread;
+ f_thread_id_t thread;
uint8_t action;
uint8_t options;
int result;
f_thread_lock_t active;
- f_thread_lock_t lock;
+ f_thread_lock_t use;
f_thread_mutex_full_t wait;
f_thread_condition_t wait_condition;
};
#define controller_instance_t_initialize { \
- 0, \
f_thread_id_t_initialize, \
0, \
0, \
cache->action.line_item = cache_line_item;
if (status_lock != F_okay) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
+ if (F_status_set_fine(status_lock) != F_interrupt) {
+ controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
+ }
break;
}
#endif
#ifndef _di_controller_instance_find_
- f_status_t controller_instance_find(const f_number_unsigned_t action, const f_string_static_t alias, const controller_instances_t instances, f_number_unsigned_t * const at) {
+ f_status_t controller_instance_find(const f_number_unsigned_t action, const f_string_static_t alias, const controller_instances_t instances, controller_instance_t ** const instance) {
if (!alias.used) return F_okay;
if (!instances.used) return F_false;
for (f_number_unsigned_t i = 0; i < instances.used; ++i) {
if (instances.array[i] && instances.array[i]->action == action && f_compare_dynamic(alias, instances.array[i]->rule.alias) == F_equal_to) {
- if (at) *at = i;
+ *instance = instances.array[i];
return F_true;
}
* The Rule alias to find.
* @param instances
* The array of instancees to.
- * @param at
- * (optional) The location within instances the id was found.
+ * @param instance
+ * The found instance.
*
- * Set to NULL to disable.
+ * Must not be NULL.
*
* @return
* F_okay if not given a valid id to search.
* F_true if there is a instance found (address is stored in "at").
*/
#ifndef _di_controller_instance_find_
- f_status_t controller_instance_find(const f_number_unsigned_t action, const f_string_static_t alias, const controller_instances_t instances, f_number_unsigned_t * const at);
+ f_status_t controller_instance_find(const f_number_unsigned_t action, const f_string_static_t alias, const controller_instances_t instances, controller_instance_t ** const instance);
#endif // _di_controller_instance_find_
#ifdef __cplusplus
#endif
#ifndef _di_controller_instance_prepare_
- f_status_t controller_instance_prepare(controller_t * const main, const uint8_t is_normal, const uint8_t action, const f_string_static_t alias, f_number_unsigned_t * const id) {
+ f_status_t controller_instance_prepare(controller_t * const main, const uint8_t is_normal, const uint8_t action, const f_string_static_t alias, controller_instance_t ** const instance) {
if (!main) return F_status_set_error(F_parameter);
- f_status_t status = F_okay;
+ if (!controller_thread_enable_is_normal(&main->thread, is_normal)) return F_status_set_error(F_interrupt);
- if (controller_instance_find(action, alias, main->thread.instances, id) == F_false) {
- f_thread_unlock(&main->thread.lock.instance);
+ f_status_t status = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &main->thread.lock.instance);
- status = controller_lock_write_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &main->thread.lock.instance);
-
- if (status != F_okay) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_false);
- }
- else {
- status = f_memory_array_increase(controller_allocation_small_d, sizeof(controller_instance_t), (void **) &main->thread.instances.array, &main->thread.instances.used, &main->thread.instances.size);
+ if (status != F_okay) {
+ controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_true);
- // The Instances array has instance as a double-pointer.
- if (F_status_is_error_not(status) && !main->thread.instances.array[main->thread.instances.used]) {
- status = controller_instance_initialize(&main->thread.instances.array[main->thread.instances.used]);
- }
+ return status;
+ }
- if (F_status_is_error_not(status)) {
+ status = controller_instance_find(action, alias, main->thread.instances, instance);
- // The Instances array has instance as a double-pointer.
- status = controller_instance_initialize(&main->thread.instances.array[main->thread.instances.used]);
+ f_thread_unlock(&main->thread.lock.instance);
- controller_instance_t * const instance = F_status_is_error_not(status) ? main->thread.instances.array[main->thread.instances.used] : 0;
+ if (status == F_true) return F_found;
+ if (status == F_okay) return F_found_not;
- if (status == F_okay) {
- status = controller_lock_write_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance->lock);
- }
+ // Instantiate the instance pointer off of the instances array to avoid needing to lock anything on the new instance.
+ status = controller_instance_initialize(instance);
+ if (F_status_is_error(status)) return status;
- if (F_status_is_error(status)) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_false);
- }
- else {
- instance->action = action;
- instance->rule.alias.used = 0;
- instance->main = main;
+ (*instance)->action = action;
+ (*instance)->rule.alias.used = 0;
+ (*instance)->main = main;
- status = f_string_dynamic_append(alias, &instance->rule.alias);
+ status = f_string_dynamic_append(alias, &(*instance)->rule.alias);
- if (F_status_is_error_not(status)) {
- instance->id = main->thread.instances.used++;
- status = F_okay;
+ if (F_status_is_error_not(status)) {
+ status = controller_lock_write_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &main->thread.lock.instance);
- if (id) {
- *id = instance->id;
- }
- }
+ if (F_status_is_error(status)) {
+ controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_false);
+ }
+ else {
+ if (F_status_is_error_not(status)) {
+ status = f_memory_array_increase(controller_allocation_small_d, sizeof(controller_instance_t), (void **) &main->thread.instances.array, &main->thread.instances.used, &main->thread.instances.size);
- f_thread_unlock(&instance->lock);
+ // The Instances array has instance as a double-pointer.
+ if (F_status_is_error_not(status)) {
+ main->thread.instances.array[main->thread.instances.used++] = *instance;
}
}
f_thread_unlock(&main->thread.lock.instance);
}
-
- // The read lock must be restored on return.
- const f_status_t status_restore = F_status_is_error(controller_lock_read_standard(is_normal, controller_lock_check_flag_no_d, &main->thread, &main->thread.lock.instance))
- ? F_status_set_error(F_lock_read)
- : F_okay;
-
- if (F_status_is_fine(status)) {
- status = status_restore;
- }
}
- else {
- status = F_found;
+
+ // De-allocate instance on error because it is not yet assigned to the array.
+ if (F_status_is_error(status)) {
+ controller_instance_delete(*instance);
+ *instance = 0;
}
return status;
#endif // _di_controller_instance_prepare_
#ifndef _di_controller_instance_prepare_instance_type_
- f_status_t controller_instance_prepare_instance_type(controller_t * const main, const uint8_t type, const uint8_t action, const f_string_static_t alias, f_number_unsigned_t *id) {
+ f_status_t controller_instance_prepare_instance_type(controller_t * const main, const uint8_t type, const uint8_t action, const f_string_static_t alias, controller_instance_t ** const instance) {
- return controller_instance_prepare(main, type != controller_instance_type_exit_e, action, alias, id);
+ return controller_instance_prepare(main, type != controller_instance_type_exit_e, action, alias, instance);
}
#endif // _di_controller_instance_prepare_instance_type_
*
* If a instance by the given Rule alias and Rule Action already exists, then nothing is done.
*
- * This requires that a main.thread.lock.instance lock be set on instance.lock before being called.
- *
* @param main
* The main program data.
*
* The Rule Action to use.
* @param alias
* The Rule alias to use.
- * @param id
- * (optional) The instance ID when found or created.
+ * @param instance
+ * The memory address of the prepared instance is assigned to this on success.
*
- * Set to NULL to not use.
+ * Must not be NULL.
*
* @return
* F_okay on success.
- * F_found on success, but nothing was done because an existing instance was found.
+ * F_found on an existing instance being found.
+ * F_found_not no instance found because there is no alias to find.
*
- * F_lock_read (with error bit) if failed to re-establish read lock on main.thread.lock.instance while returning.
* F_parameter (with error bit) if a parameter is invalid.
*
* Errors (with error bit) from: f_string_dynamic_append().
*
- * Errors (with error bit) from: controller_lock_read().
- * Errors (with error bit) from: controller_lock_write().
+ * Errors (with error bit) from: controller_lock_read_standard().
+ * Errors (with error bit) from: controller_lock_write_standard().
*
* @see f_string_dynamic_append()
- * @see controller_lock_read()
- * @see controller_lock_write()
+ * @see controller_lock_write_standard()
*/
#ifndef _di_controller_instance_prepare_
- extern f_status_t controller_instance_prepare(controller_t * const main, const uint8_t is_normal, const uint8_t action, const f_string_static_t alias, f_number_unsigned_t *id);
+ extern f_status_t controller_instance_prepare(controller_t * const main, const uint8_t is_normal, const uint8_t action, const f_string_static_t alias, controller_instance_t ** const instance);
#endif // _di_controller_instance_prepare_
/**
*
* If a instance by the given Rule alias and Rule Action already exists, then nothing is done.
*
- * This requires that a main.thread.lock.instance lock be set on instance->lock before being called.
- *
* @param main
* The main program data.
*
* The Rule Action to use.
* @param alias
* The Rule alias to use.
- * @param id
- * (optional) The instance ID when found or created.
+ * @param instance
+ * The memory address of the prepared instance is assigned to this on success.
*
- * Set to NULL to not use.
+ * Must not be NULL.
*
* @return
* Success from: controller_instance_prepare()
* @see controller_instance_prepare()
*/
#ifndef _di_controller_instance_prepare_instance_type_
- extern f_status_t controller_instance_prepare_instance_type(controller_t * const main, const uint8_t type, const uint8_t action, const f_string_static_t alias, f_number_unsigned_t * const id);
+ extern f_status_t controller_instance_prepare_instance_type(controller_t * const main, const uint8_t type, const uint8_t action, const f_string_static_t alias, controller_instance_t ** const instance);
#endif // _di_controller_instance_prepare_instance_type_
#ifdef __cplusplus
do {
status = controller_mutex_lock_instance(instance, &instance->wait);
- if (!controller_thread_is_enabled_instance(instance)) return F_status_set_error(F_interrupt);
if (F_status_is_error(status)) break;
+ if (!controller_thread_is_enabled_instance(instance)) {
+ f_thread_mutex_unlock(&instance->wait.mutex);
+
+ return F_status_set_error(F_interrupt);
+ }
+
if (count < controller_thread_timeout_wait_1_before_d) {
controller_time_now(controller_thread_timeout_wait_1_seconds_d, controller_thread_timeout_wait_1_nanoseconds_d, &time);
}
if (!controller_thread_is_enabled_instance(instance)) return F_status_set_error(F_interrupt);
if (F_status_is_error(status)) break;
- status_lock = controller_lock_read_instance(instance, &instance->lock);
+ status_lock = controller_lock_read_instance(instance, &instance->use);
if (F_status_is_error(status_lock)) {
controller_print_error_lock_critical(&instance->main->program.error, F_status_set_fine(status_lock), F_true);
}
if (!controller_rule_status_is_available(instance->action, instance->rule) && !(instance->state == controller_instance_state_active_e || instance->state == controller_instance_state_busy_e)) {
- f_thread_unlock(&instance->lock);
+ f_thread_unlock(&instance->use);
return F_okay;
}
+ f_thread_unlock(&instance->use);
+
if (status != F_time) {
// Move up the wait timer after a trigger was received.
}
}
- f_thread_unlock(&instance->lock);
-
if (count < controller_thread_timeout_wait_3_before_d) {
++count;
}
* @param instance
* The instance to wait on.
*
+ * The instance.active lock must be set before calling this.
+ * The instance.use lock must not be set before calling this.
+ *
* Must not be NULL.
*
* @return
if (status == F_time) {
if (check) {
- if (check == controller_lock_check_flag_error_d) {
+ if (check == controller_lock_check_flag_error_d || check == controller_lock_check_flag_time_d) {
if (F_status_is_error(status)) return status;
+ if (check == controller_lock_check_flag_time_d) return F_time;
}
else if (!controller_thread_enable_is_normal(thread, is_normal)) return F_status_set_error(F_interrupt);
}
if (status == F_time) {
if (check) {
- if (check == controller_lock_check_flag_error_d) {
+ if (check == controller_lock_check_flag_error_d || check == controller_lock_check_flag_time_d) {
if (F_status_is_error(status)) return status;
+ if (check == controller_lock_check_flag_time_d) return F_time;
}
else if (!controller_thread_enable_is_normal(thread, is_normal)) return F_status_set_error(F_interrupt);
}
* IF controller_lock_check_flag_error_d, then this is is also TRUE, but it does not call controller_thread_enable_is_normal() and returns on any lock error.
* If controller_lock_check_flag_no_d, then do not check if the state is enabled and keep looping.
* If controller_lock_check_flag_yes_d, then check if the state is enabled and if it is not then abort.
+ * If controller_lock_check_flag_time_d, then identical to controller_lock_check_flag_error_d but returns F_time on time out.
* @param seconds
* The seconds to add to current time.
* @param nanoseconds
* IF controller_lock_check_flag_error_d, then this is is also TRUE, but it does not call controller_thread_enable_is_normal() and returns on any lock error.
* If controller_lock_check_flag_no_d, then do not check if the state is enabled and keep looping.
* If controller_lock_check_flag_yes_d, then check if the state is enabled and if it is not then abort.
+ * If controller_lock_check_flag_time_d, then identical to controller_lock_check_flag_error_d but returns F_time on time out.
* @param thread
* The thread data used to determine if the main thread is disabled or not.
*
* IF controller_lock_check_flag_error_d, then this is is also TRUE, but it does not call controller_thread_enable_is_normal() and returns on any lock error.
* If controller_lock_check_flag_no_d, then do not check if the state is enabled and keep looping.
* If controller_lock_check_flag_yes_d, then check if the state is enabled and if it is not then abort.
+ * If controller_lock_check_flag_time_d, then identical to controller_lock_check_flag_error_d but returns F_time on time out.
* @param seconds
* The seconds to add to current time.
* @param nanoseconds
* @param check
* IF controller_lock_check_flag_error_d, then this is is also TRUE, but it does not call controller_thread_enable_is_normal() and returns on any lock error.
* If controller_lock_check_flag_no_d, then do not check if the state is enabled and keep looping.
+ * If controller_lock_check_flag_time_d, then identical to controller_lock_check_flag_error_d but returns F_time on time out.
* If controller_lock_check_flag_yes_d, then check if the state is enabled and if it is not then abort.
* @param thread
* The thread data used to determine if the main thread is disabled or not.
if (status == F_time) {
if (check) {
- if (check == controller_lock_check_flag_error_d) {
+ if (check == controller_lock_check_flag_error_d || check == controller_lock_check_flag_time_d) {
if (F_status_is_error(status)) return status;
+ if (check == controller_lock_check_flag_time_d) return F_time;
}
else if (!controller_thread_enable_is_normal(thread, is_normal)) return F_status_set_error(F_interrupt);
}
* Given a mutex lock, periodically check to see if main thread is disabled while waiting.
*
* @param is_normal
+ * If TRUE, then perform as if this operates during a normal operation (Entry and Control).
+ * If FALSE, then perform as if this operates during a an Exit operation.
+ * @param check
* IF controller_lock_check_flag_error_d, then this is is also TRUE, but it does not call controller_thread_enable_is_normal() and returns on any lock error.
* If controller_lock_check_flag_no_d, then do not check if the state is enabled and keep looping.
* If controller_lock_check_flag_yes_d, then check if the state is enabled and if it is not then abort.
- * @param check
- * If TRUE, then check if the state is enabled and if it is not then abort.
- * If FALSE, then do not check if the state is enabled and keep looping.
+ * If controller_lock_check_flag_time_d, then identical to controller_lock_check_flag_error_d but returns F_time on time out.
* @param seconds
* The seconds to add to current time.
* @param nanoseconds
*
* @return
* F_okay on success.
+ * F_time on out of time, but only if check is controller_lock_check_flag_time_d.
*
* F_interrupt (with error bit set) on (exit) signal received, lock will not be set when this is returned.
* F_parameter (with error bit) if a parameter is invalid.
* Given a mutex lock, periodically check to see if main thread is disabled while waiting.
*
* @param is_normal
+ * If TRUE, then perform as if this operates during a normal operation (Entry and Control).
+ * If FALSE, then perform as if this operates during a an Exit operation.
+ * @param check
* IF controller_lock_check_flag_error_d, then this is is also TRUE, but it does not call controller_thread_enable_is_normal() and returns on any lock error.
* If controller_lock_check_flag_no_d, then do not check if the state is enabled and keep looping.
* If controller_lock_check_flag_yes_d, then check if the state is enabled and if it is not then abort.
- * @param check
- * If TRUE, then check if the state is enabled and if it is not then abort.
- * If FALSE, then do not check if the state is enabled and keep looping.
+ * If controller_lock_check_flag_time_d, then identical to controller_lock_check_flag_error_d but returns F_time on time out.
* @param thread
* The thread data used to determine if the main thread is disabled or not.
*
controller_lock_print(print->to, &main->thread);
- fl_print_format("%r%[%QThe pid file '%]", print->to, f_string_eol_s, print->context, print->prefix, print->context);
- fl_print_format("%['Critical failure while attempting to establish '%]", print->to, print->context, print->context);
+ fl_print_format("%r%[Critical failure while attempting to establish '%]", print->to, f_string_eol_s, print->context, print->context);
fl_print_format("%[%r lock%]", print->to, print->notable, is_read ? f_file_operation_read_s : f_file_operation_write_s, print->notable);
if (status != F_failure) {
- fl_print_format(" %['due to%] ", print->to, print->context, print->context);
+ fl_print_format("%[' due to '%]", print->to, print->context, print->context);
if (status == F_parameter) {
fl_print_format("%[Invalid Parameter%]", print->to, print->notable, print->notable);
controller_print_error_status(&main->program.error, macro_controller_f(f_thread_create), status);
}
else {
- controller_thread_join(&main->thread.id_entry);
+ if (main->thread.id_entry) {
+ controller_thread_join(&main->thread.id_entry);
+ }
status = main->thread.status;
- main->thread.id_entry = 0;
}
}
}
if (!(main->setting.flag & controller_main_flag_validate_d)) {
// Wait for the Entry thread to complete before starting the Rule thread.
- controller_thread_join(&main->thread.id_rule);
+ if (main->thread.id_rule) {
+ controller_thread_join(&main->thread.id_rule);
+ }
if (controller_thread_enable_get(&main->thread) > controller_thread_enable_not_e && main->process.mode == controller_process_mode_service_e) {
status = f_thread_create(0, &main->thread.id_rule, &controller_thread_rule, (void *) main);
if (F_status_is_error(status)) {
main->thread.id_rule = 0;
- }
- if (F_status_is_error(status)) {
controller_print_error_status(&main->program.error, macro_controller_f(f_thread_create), status);
}
}
if (F_status_is_error_not(status) && status != F_failure && !(main->setting.flag & controller_main_flag_validate_d) && controller_thread_enable_is_normal(&main->thread, F_true)) {
if (main->process.mode == controller_process_mode_service_e) {
- controller_thread_join(&main->thread.id_signal);
+ if (main->thread.id_signal) {
+ controller_thread_join(&main->thread.id_signal);
+ }
}
else if (main->process.mode == controller_process_mode_helper_e || main->process.mode == controller_process_mode_program_e) {
status = controller_rule_wait_all(main, F_true, F_false);
} // for
// Lock failed, attempt to re-establish lock before returning.
- if (F_status_set_fine(status) == F_lock || F_status_set_fine(status) == F_lock_read) {
+ if (F_status_set_fine(status) == F_lock || F_status_set_fine(status) == F_lock_read || F_status_set_fine(status) == F_lock_write) {
success = F_false;
- if (F_status_set_fine(status) == F_lock_read) {
- status = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->lock);
- if (status != F_okay) return F_status_set_error(F_lock_read);
-
- status = F_status_set_error(F_lock);
- }
+ return status;
}
if (success == false && !instance->rule.items.used) {
f_status_t status = F_okay;
f_execute_result_t result = f_execute_result_t_initialize;
- // @fixme Lock the instance.childs.used with write access. (active should already have a read lock)
- status = controller_lock_write_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->lock);
+ status = controller_lock_write_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->use);
if (status != F_okay) return F_status_set_error(F_lock_read);
status = f_memory_array_increase(controller_allocation_small_d, sizeof(pid_t), (void **) &instance->childs.array, &instance->childs.used, &instance->childs.size);
if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_interrupt) return status;
+
controller_print_error_status(&main->program.error, macro_controller_f(f_memory_array_increase), F_status_set_fine(status));
return status;
}
- pid_t * const process_child_id = controller_rule_execute_next_child(instance); // @fixme should be included in write lock.
+ pid_t * const process_child_id = controller_rule_execute_next_child(instance);
+
+ f_thread_unlock(&instance->use);
if (options & controller_instance_option_simulate_e) {
controller_print_entry_output_execute_simulate(&main->program.output, instance, program, arguments);
// Have the parent wait for the child instance to finish.
waitpid(id_child, &result.status, 0);
- f_thread_unlock(&instance->lock);
-
- f_status_t status_lock = controller_lock_write_instance(instance, &instance->lock);
+ f_status_t status_lock = controller_lock_write_instance(instance, &instance->use);
if (F_status_is_error(status_lock)) {
controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
- status_lock = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &instance->main->thread, &instance->lock);
- if (status_lock != F_okay) return F_status_set_error(F_lock_read);
-
- return F_status_set_error(F_lock);
+ return status_lock;
}
instance->result = result.status;
// Remove the pid now that waidpid() has returned.
*process_child_id = 0;
- f_thread_unlock(&instance->lock);
-
- status_lock = controller_lock_read_instance(instance, &instance->lock);
-
- if (status_lock != F_okay) {
-
- // Try again, after the first interrupt.
- if (F_status_set_fine(status_lock) == F_interrupt) {
- status_lock = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &instance->main->thread, &instance->lock);
- }
-
- if (status_lock != F_okay) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
-
- return F_status_set_error(F_lock_read);
- }
- }
+ f_thread_unlock(&instance->use);
status = WIFEXITED(result.status) && WEXITSTATUS(result.status) ? F_status_set_error(F_failure) : F_okay;
}
if (F_status_is_error(status)) {
status = F_status_set_fine(status);
+ {
+ const f_status_t status_lock = controller_lock_read_instance(instance, &instance->use);
+
+ if (F_status_is_error(status_lock)) {
+ controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
+
+ return status_lock;
+ }
+ }
+
if ((WIFEXITED(instance->result) && WEXITSTATUS(instance->result)) || status == F_control_group || status == F_failure || status == F_limit || status == F_processor || status == F_schedule) {
controller_print_error_rule_item_execute(&instance->main->program.error, type == controller_rule_item_type_script_e, program.used ? program : arguments.array[0], status, instance->result);
}
controller_print_error_status(&instance->main->program.error, macro_controller_f(fll_execute_program), F_status_set_fine(status));
}
+ f_thread_unlock(&instance->use);
+
status = F_status_set_error(status);
}
f_status_t status = F_okay;
f_execute_result_t result = f_execute_result_t_initialize;
- // @fixme Lock the instance.childs.used with write access. (active should already have a read lock)
- status = controller_lock_write_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->lock);
- if (status != F_okay) return F_status_set_error(F_lock_read);
+ status = controller_lock_write_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->use);
+ if (F_status_is_error(status)) return status;
status = f_memory_array_increase(controller_allocation_small_d, sizeof(pid_t), (void **) &instance->childs.array, &instance->childs.used, &instance->childs.size);
}
if (F_status_is_error(status)) {
+ f_thread_unlock(&instance->use);
+
controller_print_error_status(&main->program.error, macro_controller_f(f_memory_array_increase), F_status_set_fine(status));
return status;
}
- pid_t * const process_child_id = controller_rule_execute_next_child(instance); // @fixme should be included in write lock.
+ pid_t * const process_child_id = controller_rule_execute_next_child(instance);
f_string_dynamic_t * const process_child_pid_file = controller_rule_execute_next_pid_path(instance);
+ f_thread_unlock(&instance->use);
+
status = f_file_exists(pid_file, F_true);
if (F_status_is_error(status) || status == F_true) {
// Have the parent wait for the child instance to finish.
waitpid(id_child, &result.status, 0);
- f_thread_unlock(&instance->lock);
-
- f_status_t status_lock = controller_lock_write_instance(instance, &instance->lock);
+ f_status_t status_lock = controller_lock_write_instance(instance, &instance->use);
if (status_lock != F_okay) {
controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
- status_lock = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &instance->main->thread, &instance->lock);
- if (status_lock != F_okay) return F_status_set_error(F_lock_read);
-
- return F_status_set_error(F_lock);
+ return status_lock;
}
// Assign the child instance id to allow for the cancel instance to send appropriate termination signals to the child instance.
*process_child_id = id_child;
- // The child instance should perform the change into background, therefore it is safe to wait for the child to Exit (another instance is spawned).
- if (F_status_set_fine(status_lock) != F_interrupt) {
- waitpid(id_child, &result.status, 0);
- }
-
- if (!controller_thread_is_enabled_instance(instance)) return status_lock == F_okay ? F_status_set_error(F_interrupt) : F_status_set_error(F_lock);
+ f_thread_unlock(&instance->use);
- if (status_lock == F_okay) {
- f_thread_unlock(&instance->lock);
- }
+ // The child instance should perform the change into background, therefore it is safe to wait for the child to Exit (another instance is spawned).
+ waitpid(id_child, &result.status, 0);
- status_lock = controller_lock_write_instance(instance, &instance->lock);
+ status_lock = controller_lock_write_instance(instance, &instance->use);
if (status_lock != F_okay) {
controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
- status_lock = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &instance->main->thread, &instance->lock);
- if (status_lock != F_okay) return F_status_set_error(F_lock_read);
-
- return F_status_set_error(F_lock);
+ return F_status_set_error(F_lock_write);
}
instance->result = result.status;
// Remove the pid now that waidpid() has returned.
*process_child_id = 0;
- f_thread_unlock(&instance->lock);
-
- status_lock = controller_lock_read_instance(instance, &instance->lock);
-
- if (F_status_is_error(status_lock)) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
-
- return F_status_set_error(F_lock);
- }
+ f_thread_unlock(&instance->use);
status = WIFEXITED(result.status) && WEXITSTATUS(result.status) ? F_status_set_error(F_failure) : F_okay;
}
else {
main->program.child = result.status;
-
- if (!controller_thread_is_enabled_instance(instance)) return F_status_set_error(F_interrupt);
}
+ if (!controller_thread_is_enabled_instance(instance)) return F_status_set_error(F_interrupt);
+
if (F_status_is_error(status)) {
status = F_status_set_fine(status);
if (F_status_is_error(status)) {
status = F_status_set_fine(status);
+ {
+ const f_status_t status_lock = controller_lock_read_instance(instance, &instance->use);
+
+ if (F_status_is_error(status_lock)) {
+ controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
+
+ return status_lock;
+ }
+ }
+
if ((WIFEXITED(instance->result) && WEXITSTATUS(instance->result)) || status == F_control_group || status == F_failure || status == F_limit || status == F_processor || status == F_schedule) {
controller_print_error_rule_item_execute(&instance->main->program.error, type == controller_rule_item_type_utility_e, program.used ? program : arguments.array[0], status, instance->result);
}
controller_print_error_status(&instance->main->program.error, macro_controller_f(fll_execute_program), F_status_set_fine(status));
}
+ f_thread_unlock(&instance->use);
+
return F_status_set_error(status);
}
/**
* Perform an execution of the given Rule.
*
- * This requires that a read lock be set on instance->lock before being called.
+ * This requires that a read lock be set on instance.use before being called.
*
* @param main
* The main program data.
*
* F_failure (with error bit) if failed to execute.
* F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
- * F_lock (with error bit) if failed to establish a lock, and the instance->lock read lock is already restored.
- * F_lock_read (with error bit) if failed to re-establish read lock on instance->lock while returning.
+ * F_lock (with error bit) if failed to establish a lock, and the instance.use read lock is already restored.
+ * F_lock_read (with error bit) if failed to re-establish read lock on instance.use while returning.
*
* On success and the Rule is run synchronously, then the individual status for the Rule is set to F_complete.
* On success and the Rule is run asynchronously, then the individual status for the Rule is set to F_busy.
/**
* Perform an execution of the given Rule in the foreground.
*
- * This requires that a read lock be set on instance->lock before being called.
+ * This requires that a read lock be set on instance.use before being called.
*
* @param instance
* The instance data.
* F_child on child process exiting.
*
* F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
- * F_lock (with error bit) if failed to establish a lock, and the instance->lock read lock is already restored.
- * F_lock_read (with error bit) if failed to re-establish read lock on instance->lock while returning.
+ * F_lock (with error bit) if failed to establish a lock, and the instance.use read lock is already restored.
+ * F_lock_read (with error bit) if failed to re-establish read lock on instance.use while returning.
*
* Errors (with error bit) from: fll_execute_program().
*
/**
* Find the next available child slot and return a pointer to that location.
*
+ * This does not lock the instance.use lock and does access instance.childs.
+ * The caller must ensure the instance.use is write locked before calling this.
+ *
* @param instance
* The instance data.
*
/**
* Perform an execution of the given Rule in the foreground or background and creating a PID file.
*
- * This requires that a read lock be set on instance->lock before being called.
+ * This requires that a read lock be set on instance.use before being called.
*
* 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.
*
* F_file_found (with error bit) if the PID file already exists.
* F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
- * F_lock (with error bit) if failed to establish a lock, and the instance->lock read lock is already restored.
- * F_lock_read (with error bit) if failed to re-establish read lock on instance->lock while returning.
+ * F_lock (with error bit) if failed to establish a lock, and the instance.use read lock is already restored.
+ * F_lock_read (with error bit) if failed to re-establish read lock on instance.use while returning.
*
* Errors (with error bit) from: fll_execute_program().
*
{
f_number_unsigned_t j = 0;
f_number_unsigned_t id_rule = 0;
- f_number_unsigned_t id_dependency = 0;
controller_instance_t *dependency = 0;
for (j = 0; j < dynamics[i]->used && controller_thread_is_enabled_instance(instance); ++j) {
- id_dependency = 0;
dependency = 0;
found = F_false;
- status_lock = controller_lock_read_instance(instance, &main->thread.lock.instance);
+ status = controller_instance_prepare_instance_type(main, instance->type, instance->action, dynamics[i]->array[j], &dependency);
- if (status_lock != F_okay) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
- }
- else {
- status = controller_instance_prepare_instance_type(main, instance->type, instance->action, dynamics[i]->array[j], &id_dependency);
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_lock) {
+ if (!controller_thread_is_enabled_instance_type(&main->thread, instance->type)) return F_status_set_error(F_interrupt);
+ }
- if (F_status_is_error(status)) {
- if (F_status_set_fine(status) == F_lock) {
- if (!controller_thread_is_enabled_instance_type(&main->thread, instance->type)) return F_status_set_error(F_interrupt);
- }
+ controller_print_error_rule_item_rule_not_loaded(&main->program.error, &instance->cache.action, dynamics[i]->array[j]);
- controller_print_error_rule_item_rule_not_loaded(&main->program.error, &instance->cache.action, dynamics[i]->array[j]);
+ return status;
+ }
- return status;
- }
+ status = found = F_true;
+
+ status_lock = controller_lock_read_instance(instance, &dependency->active);
- status = found = F_true;
- dependency = main->thread.instances.array[id_dependency];
+ if (F_status_is_error(status_lock)) {
+ controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
- status_lock = controller_lock_read_instance(instance, &dependency->active);
+ status = F_false;
+ dependency = 0;
+ }
+ else {
+ status_lock = controller_lock_read_instance(instance, &main->thread.lock.rule);
if (F_status_is_error(status_lock)) {
controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
status = F_false;
- dependency = 0;
-
- f_thread_unlock(&main->thread.lock.instance);
}
else {
- f_thread_unlock(&main->thread.lock.instance);
-
- status_lock = controller_lock_read_instance(instance, &main->thread.lock.rule);
-
- if (F_status_is_error(status_lock)) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
-
- status = F_false;
- }
- else {
- status = controller_rule_find(dynamics[i]->array[j], main->process.rules, &id_rule);
+ status = controller_rule_find(dynamics[i]->array[j], main->process.rules, &id_rule);
- f_thread_unlock(&main->thread.lock.rule);
- }
+ f_thread_unlock(&main->thread.lock.rule);
}
}
f_thread_unlock(&main->thread.lock.rule);
- status_lock = controller_lock_read_instance(instance, &dependency->lock);
+ status_lock = controller_lock_read_instance(instance, &dependency->use);
if (F_status_is_error(status_lock)) {
controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
status = status_lock;
}
else if (dependency->state == controller_instance_state_active_e || dependency->state == controller_instance_state_busy_e) {
- f_thread_unlock(&dependency->lock);
+ f_thread_unlock(&dependency->use);
status = controller_instance_wait(dependency);
if (F_status_is_error(status) && !(instance->options & controller_instance_option_simulate_e)) break;
- status = dependency->rule.status[instance->action];
+ status_lock = controller_lock_read_instance(instance, &dependency->use);
+
+ if (F_status_is_error(status_lock)) {
+ controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
+
+ status = status_lock;
+ }
+ else {
+ status = dependency->rule.status[instance->action];
+
+ f_thread_unlock(&dependency->use);
+ }
}
else {
+ f_thread_unlock(&dependency->use);
+
status_lock = controller_lock_read_instance(instance, &main->thread.lock.rule);
if (F_status_is_error(status_lock)) {
controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
- f_thread_unlock(&dependency->lock);
-
status = status_lock;
}
else if (controller_rule_status_is_available(instance->action, main->process.rules.array[id_rule])) {
f_thread_unlock(&main->thread.lock.rule);
- f_thread_unlock(&dependency->lock);
options_instance = 0;
status = main->process.rules.array[id_rule].status[instance->action];
f_thread_unlock(&main->thread.lock.rule);
- f_thread_unlock(&dependency->lock);
}
}
if (F_status_is_error(status_lock)) {
status = status_lock;
- controller_print_error_rule_instance_need_want_wish(&main->program.error, instance, strings[i], alias_other_buffer, "due to lock failure");
+ if (F_status_set_fine(status_lock) != F_interrupt) {
+ controller_print_error_rule_instance_need_want_wish(&main->program.error, instance, strings[i], alias_other_buffer, "due to lock failure");
+ }
}
else if (controller_rule_status_is_error(instance->action, main->process.rules.array[id_rule])) {
f_thread_unlock(&main->thread.lock.rule);
f_number_unsigned_t id_rule = 0;
- f_thread_unlock(&instance->lock);
-
- status_lock = controller_lock_write_instance(instance, &instance->lock);
+ status_lock = controller_lock_write_instance(instance, &instance->use);
if (F_status_is_error(status_lock)) {
controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
- status_lock = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->lock);
- if (status_lock != F_okay) return F_status_set_error(F_lock_read);
-
- return F_status_set_error(F_lock);
+ return status_lock;
}
if (F_status_is_error(status)) {
status_lock = controller_lock_write_instance(instance, &main->thread.lock.rule);
if (F_status_is_error(status_lock)) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
-
- // Remove the write lock and restore the read lock.
- f_thread_unlock(&instance->lock);
+ f_thread_unlock(&instance->use);
- status_lock = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->lock);
- if (status_lock != F_okay) return F_status_set_error(F_lock_read);
+ controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
- return F_status_set_error(F_lock);
+ return F_status_set_error(status_lock);
}
// Update the Rule status, which is stored separately from the Rule status for this instance.
}
f_thread_unlock(&main->thread.lock.rule);
- f_thread_unlock(&instance->lock);
-
- status_lock = controller_lock_read_instance(instance, &instance->lock);
-
- if (F_status_is_error(status_lock)) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
-
- status_lock = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->lock);
- if (status_lock != F_okay) return F_status_set_error(F_lock_read);
-
- return F_status_set_error(F_lock);
- }
+ f_thread_unlock(&instance->use);
return instance->rule.status[instance->action];
}
if (!main || !cache) return F_status_set_error(F_parameter);
if (!controller_thread_is_enabled_instance_type(&main->thread, type)) return F_status_set_error(F_interrupt);
- f_status_t status = controller_lock_read_standard(type != controller_instance_type_exit_e, controller_lock_check_flag_yes_d, &main->thread, &main->thread.lock.instance);
-
- if (status != F_okay) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_true);
-
- return status;
- }
-
- f_number_unsigned_t at = 0;
+ controller_instance_t *instance = 0;
- status = controller_instance_prepare(main, type != controller_instance_type_exit_e, action, alias_rule, &at);
+ f_status_t status = controller_instance_prepare(main, type != controller_instance_type_exit_e, action, alias_rule, &instance);
if (F_status_is_error(status)) {
- f_thread_unlock(&main->thread.lock.instance);
-
controller_print_error_rule_item_rule_not_loaded(&main->program.error, &cache->action, alias_rule);
return status;
}
- controller_instance_t * const instance = main->thread.instances.array[at];
-
status = controller_lock_read_standard(type != controller_instance_type_exit_e, controller_lock_check_flag_yes_d, &main->thread, &instance->active);
if (status != F_okay) {
controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_true);
controller_print_error_rule_item(&main->program.error, &cache->action, F_false, F_status_set_fine(status));
- f_thread_unlock(&main->thread.lock.instance);
-
return status;
}
- f_status_t status_lock = controller_lock_write_instance(instance, &instance->lock);
-
- if (F_status_is_error(status_lock)) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
+ status = controller_lock_write_instance(instance, &instance->use);
+ if (F_status_is_error(status)) {
f_thread_unlock(&instance->active);
- f_thread_unlock(&main->thread.lock.instance);
- return status_lock;
- }
+ controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_false);
- // Once a write lock on the instance is achieved, it is safe to unlock the instance read lock.
- f_thread_unlock(&main->thread.lock.instance);
+ return status;
+ }
// If the instance is already running, then there is nothing to do.
if (instance->state == controller_instance_state_active_e || instance->state == controller_instance_state_busy_e) {
- f_thread_unlock(&instance->lock);
+ f_thread_unlock(&instance->use);
f_thread_unlock(&instance->active);
return F_busy;
}
+ status = F_okay;
+
// The thread is done, so close the thread.
if (instance->state == controller_instance_state_done_e) {
- controller_thread_join(&instance->id_thread);
-
- f_thread_condition_signal_all(&instance->wait_condition);
- }
-
- instance->id = at;
-
- f_thread_unlock(&instance->lock);
-
- status_lock = controller_lock_write_instance(instance, &instance->lock);
-
- if (F_status_is_error(status_lock)) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
+ if (instance->thread) {
+ status = controller_thread_join(&instance->thread);
+ }
- f_thread_unlock(&instance->active);
+ // Always set to idle and signal the wait condition because the thread is about to be replaced.
+ instance->state = controller_instance_state_idle_e;
- return status_lock;
+ f_thread_condition_signal_all(&instance->wait_condition);
}
instance->state = controller_instance_state_active_e;
}
}
- f_thread_unlock(&instance->lock);
+ {
+ const uint8_t instance_action = instance->action;
- if (F_status_is_error_not(status)) {
- if (instance->action && (options_force & controller_instance_option_asynchronous_e)) {
- if (instance->type == controller_instance_type_exit_e) {
- status = f_thread_create(0, &instance->id_thread, controller_thread_instance_other, (void *) instance);
+ f_thread_unlock(&instance->use);
+
+ if (F_status_is_error_not(status)) {
+ if (instance_action && (options_force & controller_instance_option_asynchronous_e)) {
+ if (instance->type == controller_instance_type_exit_e) {
+ status = f_thread_create(0, &instance->thread, controller_thread_instance_other, (void *) instance);
+ }
+ else {
+ status = f_thread_create(0, &instance->thread, controller_thread_instance_normal, (void *) instance);
+ }
+
+ if (F_status_is_error(status)) {
+ controller_print_error_status(&main->program.error, macro_controller_f(f_thread_create), F_status_set_fine(status));
+ }
}
else {
- status = f_thread_create(0, &instance->id_thread, controller_thread_instance_normal, (void *) instance);
- }
+ status = controller_rule_instance_perform(instance, options_force);
- if (F_status_is_error(status)) {
- controller_print_error_status(&main->program.error, macro_controller_f(f_thread_create), F_status_set_fine(status));
+ if (status == F_child || F_status_set_fine(status) == F_interrupt) {
+ f_thread_unlock(&instance->active);
+
+ return status;
+ }
}
}
- else {
- status = controller_rule_instance_perform(instance, options_force);
+ }
- if (status == F_child || F_status_set_fine(status) == F_interrupt) {
- f_thread_unlock(&instance->active);
+ status = controller_lock_read_instance(instance, &instance->use);
- return status;
- }
- }
+ if (F_status_is_error(status)) {
+ f_thread_unlock(&instance->active);
+
+ controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_true);
+
+ return status;
}
- if (!action || F_status_is_error(status) && (instance->state == controller_instance_state_active_e || instance->state == controller_instance_state_busy_e)) {
- {
- status_lock = controller_lock_write_instance(instance, &instance->lock);
+ {
+ const uint8_t instance_state = instance->state;
+
+ if (!action || F_status_is_error(status) && (instance_state == controller_instance_state_active_e || instance_state == controller_instance_state_busy_e)) {
+ f_thread_unlock(&instance->use);
- if (F_status_is_error(status_lock)) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
+ status = controller_lock_write_instance(instance, &instance->use);
+ if (F_status_is_error(status)) {
f_thread_unlock(&instance->active);
- return status_lock;
+ controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_false);
+
+ return status;
}
- }
- if (!action || (options_force & controller_instance_option_asynchronous_e)) {
- instance->state = controller_instance_state_done_e;
- }
- else {
- instance->state = controller_instance_state_idle_e;
- }
+ if (!action || (options_force & controller_instance_option_asynchronous_e)) {
+ instance->state = controller_instance_state_done_e;
+ }
+ else {
+ instance->state = controller_instance_state_idle_e;
+ }
- f_thread_condition_signal_all(&instance->wait_condition);
+ f_thread_condition_signal_all(&instance->wait_condition);
+ }
- f_thread_unlock(&instance->lock);
+ f_thread_unlock(&instance->use);
}
f_thread_unlock(&instance->active);
return status_lock;
}
}
-
- status_lock = controller_lock_read_instance(instance, &instance->lock);
-
- if (F_status_is_error(status_lock)) {
- controller_print_error_lock_critical(&instance->main->program.error, F_status_set_fine(status_lock), F_true);
-
- if (options_force & controller_instance_option_asynchronous_e) {
- f_thread_unlock(&instance->active);
- }
-
- return status_lock;
- }
}
- uint8_t flag = 0x2;
+ uint8_t flag = 0x0;
const f_status_t status = controller_rule_instance_perform_details(instance, options_force, &flag);
- if (flag & 0x2) {
- f_thread_unlock(&instance->lock);
+ if (flag & 0x4) {
+ f_thread_unlock(&instance->main->thread.lock.rule);
}
if (options_force & controller_instance_option_asynchronous_e) {
}
if (flag & 0x1) {
- controller_print_error_lock_critical(&instance->main->program.error, F_status_set_fine(status), !(flag & 0x4));
+ controller_print_error_lock_critical(&instance->main->program.error, F_status_set_fine(status), !(flag & 0x2));
}
return status;
if (!instance || !instance->main) return F_status_set_error(F_parameter);
- f_status_t status = F_okay;
- f_number_unsigned_t id_rule = 0;
-
- const f_number_unsigned_t used_original_stack = instance->stack.used;
-
- f_status_t status_lock = controller_lock_read_instance(instance, &instance->main->thread.lock.rule); // @fixme Wrong lock function?
+ f_status_t status_lock = controller_lock_read_instance(instance, &instance->use);
if (F_status_is_error(status_lock)) {
*flag |= 0x1;
return status_lock;
}
- if (controller_rule_find(instance->rule.alias, instance->main->process.rules, &id_rule) == F_true) {
- f_thread_unlock(&instance->lock);
+ f_status_t status = F_okay;
+ f_number_unsigned_t id_rule = 0;
+
+ const f_number_unsigned_t used_original_stack = instance->stack.used;
+
+ f_thread_unlock(&instance->use);
- *flag &= ~0x2;
+ status_lock = controller_lock_read_instance(instance, &instance->main->thread.lock.rule);
+ if (F_status_is_error(status_lock)) return status_lock;
- status_lock = controller_lock_write_instance(instance, &instance->lock);
+ *flag |= 0x4;
+
+ if (controller_rule_find(instance->rule.alias, instance->main->process.rules, &id_rule) == F_true) {
+ status_lock = controller_lock_write_instance(instance, &instance->use);
if (F_status_is_error(status_lock)) {
- *flag |= 0x5;
+ *flag |= 0x3;
return status_lock;
}
- *flag |= 0x2;
-
controller_rule_delete(&instance->rule);
status = controller_rule_copy(instance->main->process.rules.array[id_rule], &instance->rule);
- f_thread_unlock(&instance->lock);
-
- *flag &= ~0x2;
-
- status_lock = controller_lock_read_instance(instance, &instance->lock);
-
+ f_thread_unlock(&instance->use);
f_thread_unlock(&instance->main->thread.lock.rule);
- if (F_status_is_error(status_lock)) {
- *flag |= 0x1;
-
- return status_lock;
- }
-
- *flag |= 0x2;
+ *flag &= ~0x4;
if (F_status_is_error(status)) {
controller_print_error_status(&instance->main->program.error, macro_controller_f(controller_rule_copy), F_status_set_fine(status));
return F_process_not;
}
else {
+ status_lock = controller_lock_read_instance(instance, &instance->use);
+
+ if (F_status_is_error(status_lock)) {
+ *flag |= 0x1;
+
+ return status_lock;
+ }
+
for (f_number_unsigned_t i = 0; i < instance->stack.used && controller_thread_is_enabled_instance(instance); ++i) {
if (instance->stack.array[i] == id_rule) {
}
} // for
+ f_thread_unlock(&instance->use);
+
if (!controller_thread_is_enabled_instance(instance)) return F_status_set_error(F_interrupt);
if (F_status_is_error_not(status)) {
+ status_lock = controller_lock_write_instance(instance, &instance->use);
+
+ if (F_status_is_error(status_lock)) {
+ *flag |= 0x3;
+
+ return status_lock;
+ }
+
status = f_memory_array_increase(controller_allocation_small_d, sizeof(f_number_unsigned_t), (void **) &instance->stack.array, &instance->stack.used, &instance->stack.size);
if (F_status_is_error(status)) {
controller_print_error_status(&instance->main->program.error, macro_controller_f(f_memory_array_increase), F_status_set_fine(status));
}
else {
- f_thread_unlock(&instance->lock);
-
- *flag &= ~0x2;
-
- status_lock = controller_lock_write_instance(instance, &instance->lock);
-
- if (F_status_is_error(status_lock)) {
- *flag |= 0x5;
-
- return status_lock;
- }
-
instance->stack.array[instance->stack.used++] = id_rule;
-
- f_thread_unlock(&instance->lock);
-
- status_lock = controller_lock_read_instance(instance, &instance->lock);
-
- if (F_status_is_error(status_lock)) {
- *flag |= 0x1;
-
- return status_lock;
- }
-
- *flag |= 0x2;
}
+
+ f_thread_unlock(&instance->use);
}
}
}
}
else {
- f_thread_unlock(&instance->main->thread.lock.rule);
-
status = F_status_set_error(F_found_not);
controller_print_error_rule_item_rule_not_loaded(&instance->main->program.error, &instance->cache.action, instance->rule.alias);
+
+ f_thread_unlock(&instance->main->thread.lock.rule);
+
+ *flag &= ~0x4;
}
if (status == F_child) return status;
status_lock = controller_lock_write_instance(instance, &instance->main->thread.lock.rule);
if (F_status_is_error(status_lock)) {
- *flag |= 0x5;
-
- if (F_status_set_fine(status) == F_lock) {
- *flag &= ~0x2;
- }
+ *flag |= 0x3;
return status_lock;
}
+ *flag |= 0x4;
+
if (F_status_set_fine(status) == F_lock) {
if (controller_rule_find(instance->rule.alias, instance->main->process.rules, &id_rule) == F_true) {
instance->main->process.rules.array[id_rule].status[instance->action] = status;
f_thread_unlock(&instance->main->thread.lock.rule);
- if (F_status_set_fine(status) == F_lock) {
- *flag &= ~0x2;
- }
+ *flag &= ~0x4;
- if (F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock && !controller_thread_is_enabled_instance(instance)) {
+ if (F_status_set_fine(status) == F_interrupt || (F_status_set_fine(status) == F_lock || F_status_set_fine(status) == F_lock_read || F_status_set_fine(status) == F_lock_write) && !controller_thread_is_enabled_instance(instance)) {
return F_status_set_error(F_interrupt);
}
- status_lock = controller_lock_write_instance(instance, &instance->lock);
+ status_lock = controller_lock_write_instance(instance, &instance->use);
if (F_status_is_error(status_lock)) {
- *flag |= 0x5;
+ *flag |= 0x3;
return status_lock;
}
instance->state = (options_force & controller_instance_option_asynchronous_e) ? controller_instance_state_done_e : controller_instance_state_idle_e;
instance->stack.used = used_original_stack;
+ f_thread_unlock(&instance->use);
+
f_thread_condition_signal_all(&instance->wait_condition);
return controller_thread_is_enabled_instance(instance) ? status : F_status_set_error(F_interrupt);
* Any dependent rules are processed and executed as per "need", "want", and "wish" Rule settings.
* All dependent rules must be already loaded, this function will not load any rules.
*
- * This requires that a read lock be set on instance->lock before being called.
+ * This requires that a read lock be set on instance.use before being called.
*
* This function is recursively called for each "need", "want", and "wish", and has a max recursion length of the max size of the f_number_unsigneds_t array.
*
* F_failure on execution failure.
*
* F_interrupt (with error bit) on receiving a instance signal, such as an interrupt signal.
- * F_lock (with error bit) if failed to establish a lock, and the instance->lock read lock is already restored.
- * F_lock_read (with error bit) if failed to re-establish read lock on instance->lock while returning.
+ * F_lock (with error bit) if failed to establish a lock, and the instance.use read lock is already restored.
+ * F_lock_read (with error bit) if failed to re-establish read lock on instance.use while returning.
*
* Errors (with error bit) from: controller_lock_read_standard().
* Errors (with error bit) from: controller_lock_write_instance().
* This does all the preparation work that needs to be synchronously performed within the same thread.
* This will copy the Rule by the alias to the instance structure.
*
+ * The "active" lock may be locked before calling this (must be locked if asynchronous).
+ * The "lock" lock must be locked before calling this.
+ *
* @param instance
* The instance data.
*
* @param flag
* Designate lock states:
* - 0x1: Locking error.
- * - 0x2: The instance lock is set.
- * - 0x4: Locking error is a write lock.
+ * - 0x2: Locking error is a write lock.
+ * - 0x4: The rule lock is still set.
*
* Must not be NULL.
*
if (!main) return F_status_set_error(F_parameter);
+ if (!controller_thread_enable_is_normal(&main->thread, is_normal)) return F_status_set_error(F_interrupt);
+
f_status_t status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_no_d, &main->thread, &main->thread.lock.instance);
if (status_lock != F_okay) {
}
f_status_t status = F_okay;
+ f_status_t status_join = F_okay;
+
uint8_t required_not_run = F_false;
uint8_t skip = F_false;
+ uint8_t locked = 0x0; // 0x1 = active (read), 0x2 = lock (read), 0x4 lock (write), 0x8 = continue loop.
f_number_unsigned_t i = 0;
f_number_unsigned_t j = 0;
const f_number_unsigned_t instance_total = main->thread.instances.used;
controller_instance_t *instance_list[instance_total];
- for (; i < instance_total; ++i) {
- instance_list[i] = main->thread.instances.array[i];
- } // for
+ memcpy((void *) instance_list, (void *) main->thread.instances.array, sizeof(controller_instance_t *) * instance_total);
f_thread_unlock(&main->thread.lock.instance);
- for (i = 0; i < instance_total; ++i) {
+ for (; i < instance_total; ++i) {
if (!controller_thread_enable_is_normal(&main->thread, is_normal)) break;
-
- // Re-establish instance read lock to wait for or protect from the cleanup thread while checking the read instance.
- status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &main->thread.lock.instance);
- if (status_lock != F_okay) break;
-
- if (!instance_list[i]) {
- f_thread_unlock(&main->thread.lock.instance);
-
- continue;
- }
+ if (!instance_list[i]) continue;
status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->active);
+ if (status_lock != F_okay) break;
- if (status_lock != F_okay) {
- f_thread_unlock(&main->thread.lock.instance);
-
- break;
- }
-
- // Once the active lock is obtained, then the main instance read lock can be safely released.
- f_thread_unlock(&main->thread.lock.instance);
-
- status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->lock);
-
- if (status_lock != F_okay) {
- f_thread_unlock(&instance_list[i]->active);
+ locked = 0x1;
- break;
- }
+ status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->use);
+ if (status_lock != F_okay) break;
- if (required) {
- if (!(instance_list[i]->options & controller_instance_option_require_e)) {
- f_thread_unlock(&instance_list[i]->lock);
- f_thread_unlock(&instance_list[i]->active);
+ locked |= 0x2;
- continue;
- }
+ // If the instance thread has been cleaned up and joined or if instance is not required when only processing "required", then skip.
+ if (!instance_list[i]->thread || required && !(instance_list[i]->options & controller_instance_option_require_e)) {
+ locked |= 0x8;
}
- if (!instance_list[i]->state || instance_list[i]->state == controller_instance_state_idle_e || instance_list[i]->state == controller_instance_state_done_e) {
-
+ if (!(locked & 0x8) && (!instance_list[i]->state || instance_list[i]->state == controller_instance_state_idle_e || instance_list[i]->state == controller_instance_state_done_e)) {
if (instance_list[i]->state == controller_instance_state_done_e) {
- f_thread_unlock(&instance_list[i]->lock);
- status_lock = controller_lock_write_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->lock);
+ // Release use lock before waiting for thread to join.
+ f_thread_unlock(&instance_list[i]->use);
- if (status_lock != F_okay) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
+ locked &= ~0x2;
+ status_join = F_data_not;
- f_thread_unlock(&instance_list[i]->active);
-
- return status_lock;
+ if (instance_list[i]->thread) {
+ status_join = controller_thread_join(&instance_list[i]->thread);
}
- if (instance_list[i]->state == controller_instance_state_done_e) {
- f_thread_unlock(&instance_list[i]->active);
-
- if (f_thread_lock_write_try(&instance_list[i]->active) == F_okay) {
- controller_thread_join(&instance_list[i]->id_thread);
+ if (F_status_is_error_not(status) || F_status_set_fine(status) == F_found_not) {
+ status_lock = controller_lock_write_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->use);
+ if (status_lock != F_okay) break;
- instance_list[i]->state = controller_instance_state_idle_e;
+ locked |= 0x4;
- f_thread_unlock(&instance_list[i]->active);
- f_thread_condition_signal_all(&instance_list[i]->wait_condition);
- }
+ instance_list[i]->state = controller_instance_state_idle_e;
- status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->active);
+ f_thread_unlock(&instance_list[i]->use);
- if (status_lock != F_okay) {
- f_thread_unlock(&instance_list[i]->lock);
+ locked &= ~0x4;
- break;
- }
+ f_thread_condition_signal_all(&instance_list[i]->wait_condition);
}
- f_thread_unlock(&instance_list[i]->lock);
-
- status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->lock);
+ status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->use);
if (status_lock != F_okay) break;
+
+ locked |= 0x2;
}
if (instance_list[i]->options & controller_instance_option_require_e) {
if (controller_rule_status_is_error(instance_list[i]->action, instance_list[i]->rule)) {
status = F_status_set_error(F_require);
- f_thread_unlock(&instance_list[i]->lock);
- f_thread_unlock(&instance_list[i]->active);
-
break;
}
else if (controller_rule_status_is_available(instance_list[i]->action, instance_list[i]->rule)) {
}
}
- f_thread_unlock(&instance_list[i]->lock);
- f_thread_unlock(&instance_list[i]->active);
-
+ // Break on required and continue otherwise.
if (F_status_set_fine(status) == F_require) break;
- continue;
+ locked |= 0x8;
}
- if (!controller_rule_status_is_error(instance_list[i]->action, instance_list[i]->rule) && (instance_list[i]->state == controller_instance_state_active_e || instance_list[i]->state == controller_instance_state_busy_e)) {
- f_thread_unlock(&instance_list[i]->lock);
+ if (!(locked & 0x8) && (!controller_rule_status_is_error(instance_list[i]->action, instance_list[i]->rule) && (instance_list[i]->state == controller_instance_state_active_e || instance_list[i]->state == controller_instance_state_busy_e))) {
- status = controller_instance_wait(instance_list[i]);
-
- if (F_status_set_fine(status) == F_interrupt) {
- f_thread_unlock(&instance_list[i]->active);
+ // Release use lock before waiting for instance to complete.
+ f_thread_unlock(&instance_list[i]->use);
- break;
- }
+ locked &= ~0x2;
- status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->lock);
+ status = controller_instance_wait(instance_list[i]);
+ if (F_status_set_fine(status) == F_interrupt) break;
- if (status_lock != F_okay) {
- f_thread_unlock(&instance_list[i]->active);
+ status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->use);
+ if (status_lock != F_okay) break;
- break;
- }
-
- if ((instance_list[i]->options & controller_instance_option_require_e)) {
- f_thread_unlock(&instance_list[i]->lock);
+ locked |= 0x2;
+ if (instance_list[i]->options & controller_instance_option_require_e) {
if (controller_rule_status_is_error(instance_list[i]->action, instance_list[i]->rule)) {
status = F_status_set_error(F_require);
- f_thread_unlock(&instance_list[i]->active);
-
break;
}
}
- else {
- f_thread_unlock(&instance_list[i]->lock);
- }
- }
- else {
- f_thread_unlock(&instance_list[i]->lock);
}
- f_thread_unlock(&instance_list[i]->active);
-
if (F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_require) break;
+ if (locked & 0x6) f_thread_unlock(&instance_list[i]->use);
+ if (locked & 0x1) f_thread_unlock(&instance_list[i]->active);
+
+ locked = 0;
} // for
+ if (i < instance_total) {
+ if (locked & 0x6) f_thread_unlock(&instance_list[i]->use);
+ if (locked & 0x1) f_thread_unlock(&instance_list[i]->active);
+ }
+
if (F_status_is_error(status_lock)) {
- controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
+ controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), !(locked & 0x4));
return status_lock;
}
break;
}
- if (f_thread_lock_write_try(&main->thread.lock.instance) == F_okay) {
- controller_instance_t *instance = 0;
+ {
+ f_time_spec_t time;
- f_number_unsigned_t i = 0;
+ if (controller_thread_enable_get(&main->thread) != controller_thread_enable_e) {
+ status = F_enable_not;
- for (status = F_okay; i < main->thread.instances.used && controller_thread_enable_get(&main->thread) == controller_thread_enable_e; ++i) {
+ break;
+ }
- if (!main->thread.instances.array[i]) continue;
-
- instance = main->thread.instances.array[i];
+ memset((void *) &time, 0, sizeof(struct timespec));
- {
- f_time_spec_t time;
+ controller_time_now(controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &time);
- do {
- if (controller_thread_enable_get(&main->thread) != controller_thread_enable_e) {
- status = F_enable_not;
-
- break;
- }
+ status = f_thread_mutex_lock_timed(&time, &main->thread.lock.cancel.mutex);
+ if (status != F_okay) continue;
+ }
- memset((void *) &time, 0, sizeof(struct timespec));
+ if (f_thread_lock_write_try(&main->thread.lock.instance) == F_okay) {
+ controller_instance_t *instance = 0;
- controller_time_now(controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &time);
+ f_number_unsigned_t i = 0;
- status = f_thread_mutex_lock_timed(&time, &main->thread.lock.cancel.mutex);
- if (F_status_is_error(status)) break;
+ for (status = F_okay; i < main->thread.instances.used && controller_thread_enable_get(&main->thread) == controller_thread_enable_e; ++i) {
- } while (status != F_okay);
+ if (!main->thread.instances.array[i]) continue;
- if (status == F_enable_not) break;
- if (F_status_is_error(status)) continue;
- }
+ instance = main->thread.instances.array[i];
// If "active" has a read or write lock, then do not attempt to clean it.
- if (f_thread_lock_write_try(&instance->active) != F_okay) {
- f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
-
- continue;
- }
-
- // If "lock" has a read or write lock, then do not attempt to clean it.
- if (f_thread_lock_write_try(&instance->lock) != F_okay) {
- f_thread_unlock(&instance->active);
- f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
-
- continue;
- }
+ // Once a write lock is achieved on "active", then there should only ever be one process operating on it.
+ // Therefore, the instance.use lock does not need to be used in any way.
+ if (f_thread_lock_write_try(&instance->active) != F_okay) continue;
// If instance is active or busy, then do not attempt to clean it.
if (instance->state == controller_instance_state_active_e || instance->state == controller_instance_state_busy_e || controller_thread_enable_get(&main->thread) != controller_thread_enable_e) {
- f_thread_unlock(&instance->lock);
f_thread_unlock(&instance->active);
- f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
if (controller_thread_enable_get(&main->thread) == controller_thread_enable_e) continue;
} // for
if (j < instance->path_pids.used || controller_thread_enable_get(&main->thread) != controller_thread_enable_e) {
- f_thread_unlock(&instance->lock);
f_thread_unlock(&instance->active);
- f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
if (controller_thread_enable_get(&main->thread) == controller_thread_enable_e) continue;
}
// Close any still open thread.
- if (instance->id_thread) {
- status = f_thread_join(instance->id_thread, 0);
+ if (instance->thread) {
+ status = controller_thread_join(&instance->thread);
if (F_status_is_error_not(status) || F_status_set_fine(status) == F_found_not) {
instance->state = controller_instance_state_idle_e;
- instance->id_thread = 0;
f_thread_condition_signal_all(&instance->wait_condition);
}
else {
- f_thread_unlock(&instance->lock);
f_thread_unlock(&instance->active);
- f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
continue;
}
} // for
if (controller_thread_enable_get(&main->thread) != controller_thread_enable_e) {
- f_thread_unlock(&instance->lock);
f_thread_unlock(&instance->active);
- f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
break;
}
controller_rule_delete(&instance->rule);
}
- f_thread_unlock(&instance->lock);
f_thread_unlock(&instance->active);
- f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
} // for
f_thread_unlock(&main->thread.lock.instance);
}
+
+ f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
} // while
return 0;
if (!main) return;
- f_status_t status = controller_mutex_lock(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.lock.cancel);
+ f_status_t status = controller_mutex_lock(is_normal, controller_lock_check_flag_time_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.lock.cancel);
if (F_status_is_error(status)) return;
// Only cancel when enabled.
time.tv_nsec = interval_nanoseconds;
if (main->process.mode == controller_process_mode_helper_e && !(main->setting.flag & controller_main_flag_validate_d)) {
- controller_lock_read(is_normal, controller_lock_check_flag_no_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.lock.instance);
+ if (F_status_is_error_not(controller_lock_read(is_normal, controller_lock_check_flag_no_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.lock.instance))) {
+ for (i = 0; i < main->thread.instances.used; ++i) {
- for (i = 0; i < main->thread.instances.used; ++i) {
+ if (!main->thread.instances.array[i]) continue;
- if (!main->thread.instances.array[i]) continue;
-
- instance = main->thread.instances.array[i];
+ instance = main->thread.instances.array[i];
- if (!instance->id_thread) continue;
+ if (!instance->thread) continue;
- controller_thread_detach(&instance->id_thread);
+ controller_thread_detach(&instance->thread);
- instance->id_thread = 0;
- } // for
+ instance->thread = 0;
+ } // for
- f_thread_unlock(&main->thread.lock.instance);
+ f_thread_unlock(&main->thread.lock.instance);
+ }
}
{
return;
}
+ // Ensure that the instances cannot be changed during the cancellation process by establishing a read lock.
status = controller_lock_read(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.lock.instance);
if (F_status_is_error(status)) {
continue;
}
+ status = controller_lock_read(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->active);
+ if (F_status_is_error(status)) continue;
+
+ status = controller_lock_read(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->use);
+
+ if (F_status_is_error(status)) {
+ f_thread_unlock(&instance->active);
+
+ continue;
+ }
+
for (j = 0; j < instance->childs.used; ++j) {
if (instance->childs.array[j] > 0) {
}
}
} // for
- } // for
- f_thread_unlock(&main->thread.lock.instance);
+ f_thread_unlock(&instance->use);
+ f_thread_unlock(&instance->active);
+ } // for
if (entry->timeout_exit && !(entry->flag & controller_entry_flag_timeout_exit_no_e)) {
f_number_unsigned_t lapsed = 0;
- controller_lock_read(is_normal, controller_lock_check_flag_no_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.lock.instance);
-
for (i = 0; i < main->thread.instances.used && lapsed < entry->timeout_exit; ++i) {
if (!main->thread.instances.array[i]) continue;
continue;
}
+ status = controller_lock_read(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->active);
+ if (F_status_is_error(status)) continue;
+
+ status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->use);
+
+ if (F_status_is_error(status)) {
+ f_thread_unlock(&instance->active);
+
+ continue;
+ }
+
for (j = 0; j < instance->childs.used && lapsed < entry->timeout_exit; ++j) {
while (instance->childs.array[j] > 0 && lapsed < entry->timeout_exit) {
}
}
} // for
- } // for
-
- f_thread_unlock(&main->thread.lock.instance);
- }
-
- status = controller_lock_read(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.lock.instance);
-
- if (F_status_is_error(status)) {
- f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
- return;
+ f_thread_unlock(&instance->use);
+ f_thread_unlock(&instance->active);
+ } // for
}
for (i = 0; i < main->thread.instances.size; ++i) {
if (!main->thread.instances.array[i]) continue;
- status = controller_lock_read(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.instances.array[i]->active);
- if (F_status_is_error(status)) continue;
-
instance = main->thread.instances.array[i];
+ status = controller_lock_read(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->active);
+ if (F_status_is_error(status)) continue;
+
// Do not kill Exit Instances, when not performing "execute" during exit.
if (instance->type == controller_instance_type_exit_e && controller_thread_enable_get(&main->thread) != controller_thread_enable_exit_execute_e) {
f_thread_unlock(&instance->active);
continue;
}
- if (instance->id_thread) {
+ if (instance->thread) {
+ status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->use);
+
+ if (F_status_is_error(status)) {
+ f_thread_unlock(&instance->active);
+
+ continue;
+ }
+
if (instance->childs.used) {
for (j = 0; j < instance->childs.used; ++j) {
f_time_sleep_spec(time, 0);
}
- f_thread_join(instance->id_thread, 0);
+ f_thread_unlock(&instance->use);
+ f_thread_join(instance->thread, 0);
- instance->id_thread = 0;
+ instance->thread = 0;
}
if (!(entry->flag & controller_entry_flag_timeout_exit_no_e)) {
+ status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->use);
+
+ if (F_status_is_error(status)) {
+ f_thread_unlock(&instance->active);
+
+ continue;
+ }
+
for (j = 0; j < instance->childs.size; ++j) {
// Do not kill Exit processes, when not performing "execute" during exit.
f_signal_send(F_signal_kill, instance->childs.array[j]);
}
- status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->lock);
-
- instance->childs.array[j] = 0;
-
- if (F_status_is_error_not(status)) f_thread_unlock(&instance->lock);
+ if (F_status_is_error_not(status)) {
+ instance->childs.array[j] = 0;
+ }
}
} // for
+
+ f_thread_unlock(&instance->use);
}
if (!(entry->flag & controller_entry_flag_timeout_exit_no_e)) {
+ status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->use);
+
+ if (F_status_is_error(status)) {
+ f_thread_unlock(&instance->active);
+
+ continue;
+ }
+
for (j = 0; j < instance->path_pids.used; ++j) {
// Do not kill Exit processes, when not performing "execute" during exit.
f_signal_send(F_signal_kill, pid);
}
- status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->lock);
-
- f_file_remove(instance->path_pids.array[j]);
- instance->path_pids.array[j].used = 0;
+ if (F_status_is_error_not(status)) {
+ f_file_remove(instance->path_pids.array[j]);
- if (F_status_is_error_not(status)) f_thread_unlock(&instance->lock);
+ instance->path_pids.array[j].used = 0;
+ }
}
} // for
+
+ f_thread_unlock(&instance->use);
}
- status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->lock);
+ status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->use);
if (F_status_is_error_not(status)) {
--instance->path_pids.used;
} // while
- f_thread_unlock(&instance->lock);
+ f_thread_unlock(&instance->use);
}
f_thread_unlock(&instance->active);