There is an existing check that prevents the cancellation from being called more than once.
What is not being considered is that if the main thread calls cancellation while another cancellation is active then the controller_thread_process_exit() gets called.
The controller_thread_process_exit() function will begin more forcibly shutting things down.
Avoid this by providing a mutex lock to lock the cancellation.
Only once the first cancellation is complete will the second (or more) then return without doing anything.
void controller_lock_delete_simple(controller_lock_t * const lock) {
controller_lock_delete_mutex(&lock->alert);
+ controller_lock_delete_mutex(&lock->cancel);
controller_lock_delete_mutex(&lock->print);
controller_lock_delete_rw(&lock->process);
* A structure for sharing mutexes globally between different threads.
*
* The alert lock is intended for a generic waiting on alerts operations.
+ * The cancel lock is intended for preventing double cancellation calls (which can happen due to interrupts).
* The print lock is intended to lock any activity printing to stdout/stderr.
* The process lock is intended to lock any activity on the processs structure.
* The rule lock is intended to lock any activity on the rules structure.
*
* alert: The alert mutex lock for waking up on alerts.
+ * cancel: The cancel mutex lock for locking the cancel operation.
* print: The print mutex lock.
* process: The process r/w lock.
* rule: The rule r/w lock.
#ifndef _di_controller_lock_t_
typedef struct {
f_thread_mutex_t alert;
+ f_thread_mutex_t cancel;
f_thread_mutex_t print;
f_thread_lock_t process;
} controller_lock_t;
#define controller_lock_t_initialize { \
+ f_thread_mutex_t_initialize, \
f_thread_mutex_t_initialize, \
f_thread_mutex_t_initialize, \
f_thread_lock_t_initialize, \
f_status_t status = f_thread_mutex_create(0, &lock->alert);
if (F_status_is_error(status)) return status;
+ status = f_thread_mutex_create(0, &lock->cancel);
+ if (F_status_is_error(status)) return status;
+
status = f_thread_mutex_create(0, &lock->print);
if (F_status_is_error(status)) return status;
#ifndef _di_controller_thread_process_cancel_
void controller_thread_process_cancel(const controller_global_t global, const bool is_normal, const uint8_t by, controller_process_t * const caller) {
+ f_thread_mutex_lock(&global.thread->lock.cancel);
+
// Only cancel when enabled.
if (!controller_thread_is_enabled(is_normal, global.thread)) {
+
+ f_thread_mutex_unlock(&global.thread->lock.cancel);
+
return;
}
--process->path_pids.used;
} // while
} // for
+
+ f_thread_mutex_unlock(&global.thread->lock.cancel);
}
#endif // _di_controller_thread_process_cancel_