I forgot all about needing to do this so I am considering this a bug.
Each process may execute multiple Actions.
Each Action has its own PID.
In the case of foreground (synchronous) execution, having only a single PID and PID file path on the Process structure is not a problem.
When with PID file (asynchronous) execution operates, multiple PIDs (and respective PID files) may exist for any single Process structure.
Use an array of PIDs and PID file paths.
}
#endif // _di_controller_lock_write_process_type_
+#ifndef _di_controller_pids_increase_
+ f_status_t controller_pids_increase(controller_pids_t *pids) {
+
+ if (pids->used + 1 > pids->size) {
+ f_array_length_t size = pids->used + controller_default_allocation_step;
+
+ if (size > f_array_length_t_size) {
+ if (pids->used + 1 > f_array_length_t_size) {
+ return F_status_set_error(F_array_too_large);
+ }
+
+ size = f_array_length_t_size;
+ }
+
+ return controller_pids_resize(size, pids);
+ }
+
+ return F_data_not;
+ }
+#endif // _di_controller_pids_increase_
+
+#ifndef _di_controller_pids_resize_
+ f_status_t controller_pids_resize(const f_array_length_t length, controller_pids_t *pids) {
+
+ f_status_t status = F_none;
+
+ status = f_memory_resize(pids->size, length, sizeof(controller_rule_t), (void **) & pids->array);
+
+ if (F_status_is_error_not(status)) {
+ pids->size = length;
+
+ if (pids->used > pids->size) {
+ pids->used = length;
+ }
+ }
+
+ return status;
+ }
+#endif // _di_controller_pids_resize_
+
#ifndef _di_controller_print_unlock_flush_
void controller_print_unlock_flush(FILE * const stream, f_thread_mutex_t *mutex) {
controller_lock_delete_mutex(&process->wait_lock);
controller_cache_delete_simple(&process->cache);
+ controller_pids_resize(0, &process->childs);
controller_rule_delete_simple(&process->rule);
- f_string_dynamic_resize(0, &process->path_pid);
+ f_string_dynamics_resize(0, &process->path_pids);
f_macro_array_lengths_t_delete_simple(process->stack)
}
#endif // _di_controller_with_defines_
/**
+ * An array of PIDs.
+ *
+ * array: An array of rule PIDs.
+ * size: Total amount of allocated space.
+ * used: Total number of allocated spaces used.
+ */
+#ifndef _di_controller_pids_t_
+ typedef struct {
+ pid_t *array;
+
+ f_array_length_t size;
+ f_array_length_t used;
+ } controller_pids_t;
+
+ #define controller_pids_t_initialize { \
+ 0, \
+ 0, \
+ 0, \
+ }
+#endif // _di_controller_pids_t_
+
+/**
* A Rule Process.
*
* This refers to "process" as in the processing of a single rule for the given Rule ID and does not refer to "process" as in a CPU Process.
uint8_t action;
uint8_t options;
uint8_t type;
- pid_t child;
f_thread_id_t id_thread;
f_thread_lock_t lock;
controller_cache_t cache;
f_array_lengths_t stack;
- f_string_dynamic_t path_pid;
+ f_string_dynamics_t path_pids;
+ controller_pids_t childs;
controller_rule_t rule;
void *main_data;
0, \
0, \
0, \
- 0, \
f_thread_id_t_initialize, \
f_thread_lock_t_initialize, \
f_thread_lock_t_initialize, \
f_thread_condition_t_initialize, \
controller_cache_t_initialize, \
f_array_lengths_t_initialize, \
- f_string_dynamic_t_initialize, \
+ f_string_dynamics_t_initialize, \
+ controller_pids_t_initialize, \
controller_rule_t_initialize, \
0, \
0, \
#endif // _di_controller_lock_write_process_type_
/**
+ * Increase the size of the pid 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 pids
+ * The pid array to resize.
+ *
+ * @return
+ * F_none on success.
+ * F_data_not on success, but there is no reason to increase size (used + controller_default_allocation_step <= 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.
+ *
+ * @see controller_pids_resize()
+ */
+#ifndef _di_controller_pids_increase_
+ extern f_status_t controller_pids_increase(controller_pids_t *pids) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_increase_
+
+/**
+ * Resize the pid array.
+ *
+ * @param length
+ * The new size to use.
+ * @param pids
+ * The pid 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.
+ *
+ * @see f_memory_resize()
+ */
+#ifndef _di_controller_pids_resize_
+ extern f_status_t controller_pids_resize(const f_array_length_t length, controller_pids_t *pids) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_pids_resize_
+
+/**
* Flush the stream buffer and then unlock the mutex.
*
* Weird behavior was observed when piping data from this program.
int result = 0;
pid_t id_child = 0;
+ status = controller_pids_increase(&process->childs);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(main.data->error, F_status_set_fine(status), "controller_pids_increase", F_true);
+
+ return status;
+ }
+
+ pid_t *child = 0;
+ f_string_dynamic_t *child_pid_file = 0;
+
+ {
+ f_array_length_t i = 0;
+
+ for (; i < process->childs.used && process->childs.array[i]; ++i) {
+ // do nothing
+ } // for
+
+ child = &process->childs.array[i];
+
+ if (i == process->childs.used) {
+ ++process->childs.used;
+ }
+ }
+
if (options & controller_process_option_simulate) {
if (main.data->error.verbosity != f_console_verbosity_quiet) {
f_thread_mutex_lock(&main.thread->lock.print);
}
// assign the child process id to allow for the cancel process to send appropriate termination signals to the child process.
- process->child = id_child;
+ *child = id_child;
f_thread_unlock(&process->lock);
}
// remove the pid now that waidpid() has returned.
- process->child = 0;
+ *child = 0;
f_thread_unlock(&process->lock);
int result = 0;
pid_t id_child = 0;
- process->path_pid.used = 0;
+ status = controller_pids_increase(&process->childs);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(main.data->error, F_status_set_fine(status), "controller_pids_increase", F_true);
+
+ return status;
+ }
+
+ status = f_string_dynamics_increase(&process->path_pids);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(main.data->error, F_status_set_fine(status), "f_string_dynamics_increase", F_true);
+
+ return status;
+ }
+
+ pid_t *child = 0;
+ f_string_dynamic_t *child_pid_file = 0;
+
+ {
+ f_array_length_t i = 0;
+
+ for (; i < process->childs.used && process->childs.array[i]; ++i) {
+ // do nothing
+ } // for
+
+ child = &process->childs.array[i];
+
+ if (i == process->childs.used) {
+ ++process->childs.used;
+ }
+
+ for (i = 0; i < process->path_pids.used && process->path_pids.array[i].used; ++i) {
+ // do nothing
+ } // for
+
+ child_pid_file = &process->path_pids.array[i];
+
+ if (i == process->path_pids.used) {
+ ++process->path_pids.used;
+ }
+ }
status = f_file_exists(pid_file.string);
}
if (status == F_true) {
- fll_error_file_print(main.data->error, F_file_found, "f_file_exists", F_true, pid_file.string, "create PID", fll_error_file_type_file);
+ fll_error_file_print(main.data->error, F_file_found, "f_file_exists", F_true, pid_file.string, "find", fll_error_file_type_file);
return F_status_set_error(F_file_found);
}
- status = controller_string_dynamic_append_terminated(pid_file, &process->path_pid);
+ status = controller_string_dynamic_append_terminated(pid_file, child_pid_file);
if (F_status_is_error(status)) {
fll_error_print(main.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
}
// assign the child process id to allow for the cancel process to send appropriate termination signals to the child process.
- process->child = id_child;
+ *child = id_child;
f_thread_unlock(&process->lock);
}
// remove the pid now that waidpid() has returned.
- process->child = 0;
+ *child = 0;
f_thread_unlock(&process->lock);
continue;
}
- // if process has a pid file, then it is running in the background, only cleanup if the pid file no longer exists.
- if (process->path_pid.used && f_file_exists(process->path_pid.string) == F_true) {
- f_thread_unlock(&process->active);
- f_thread_unlock(&process->lock);
+ // if process has a PID file, then it is running in the background, only cleanup if the PID file no longer exists.
+ if (process->path_pids.used) {
+ f_array_length_t j = 0;
- continue;
+ for (; j < process->path_pids.used; ++j) {
+ if (process->path_pids.array[j].used && f_file_exists(process->path_pids.array[j].string) == F_true) break;
+ } // for
+
+ if (j < process->path_pids.used) {
+ f_thread_unlock(&process->active);
+ f_thread_unlock(&process->lock);
+
+ continue;
+ }
}
f_thread_unlock(&process->lock);
controller_cache_delete_simple(&process->cache);
f_type_array_lengths_resize(0, &process->stack);
- // deallocate the pid file.
- if (process->path_pid.used) {
- process->path_pid.used = 0;
- f_string_dynamic_resize(0, &process->path_pid);
+ // shrink the childs array.
+ if (process->childs.used) {
+ for (; process->childs.used; --process->childs.used) {
+ if (process->childs.array[process->childs.used]) break;
+ } // for
+
+ if (process->childs.used < process->childs.size) {
+ controller_pids_resize(process->childs.used, &process->childs);
+ }
+ }
+
+ // deallocate the PID files.
+ if (process->path_pids.used) {
+ process->path_pids.used = 0;
+ f_string_dynamics_resize(0, &process->path_pids);
}
// deallocate any rules in the space that is declared to be unused.
controller_process_t *process = 0;
f_array_length_t i = 0;
+ f_array_length_t j = 0;
pid_t pid = 0;
// the sleep() function that is run inside the cleanup function must be interrupted via the f_thread_cancel().
// do not cancel exit processes, when not performing "execute" during exit.
if (process->type == controller_process_type_exit && main->thread->enabled != controller_thread_enabled_exit_execute) continue;
- if (process->child > 0) {
- f_signal_send(F_signal_termination, process->child);
- }
-
- if (process->path_pid.used && f_file_exists(process->path_pid.string) == F_true) {
- status = controller_file_pid_read(process->path_pid, &pid);
+ for (j = 0; j < process->childs.used; ++j) {
- if (pid) {
- f_signal_send(F_signal_termination, pid);
+ if (process->childs.array[j] > 0) {
+ f_signal_send(F_signal_termination, process->childs.array[j]);
}
- }
- } // for
-
- for (i = 0; i < main->thread->processs.used; ++i) {
-
- if (!main->thread->processs.array[i]) continue;
- if (caller && i == caller->id) continue;
-
- process = main->thread->processs.array[i];
-
- // do not cancel exit processes, when not performing "execute" during exit.
- if (process->type == controller_process_type_exit && main->thread->enabled != controller_thread_enabled_exit_execute) continue;
+ } // for
- if (process->id_thread) {
- controller_time(0, controller_thread_exit_process_cancel_wait, &time);
+ for (j = 0; j < process->path_pids.used; ++j) {
- status = f_thread_join_timed(process->id_thread, time, 0);
+ if (process->path_pids.array[j].used && f_file_exists(process->path_pids.array[j].string) == F_true) {
+ status = controller_file_pid_read(process->path_pids.array[j], &pid);
- if (status == F_none) {
- process->child = 0;
- process->id_thread = 0;
+ if (pid) {
+ f_signal_send(F_signal_termination, pid);
+ }
}
- }
+ } // for
} // for
for (i = 0; i < main->thread->processs.size && spent < controller_thread_exit_process_cancel_total; ++i) {
status = f_thread_join_timed(process->id_thread, time, 0);
if (status == F_none) {
- process->child = 0;
+ for (j = 0; j < process->childs.size; ++j) {
+ process->childs.array[j] = 0;
+ } // for
+
+ process->childs.used = 0;
process->id_thread = 0;
}
} while (status == F_time && spent < controller_thread_exit_process_cancel_total);
- if (process->path_pid.used) {
- for (; spent < controller_thread_exit_process_cancel_total; ++spent) {
+ if (process->path_pids.used) {
+ for (j = 0; j < process->path_pids.used; ++j) {
- if (f_file_exists(process->path_pid.string) == F_true) {
- status = controller_file_pid_read(process->path_pid, &pid);
+ for (; spent < controller_thread_exit_process_cancel_total; ++spent) {
- if (pid) {
+ if (process->path_pids.array[j].used && f_file_exists(process->path_pids.array[j].string) == F_true) {
+ status = controller_file_pid_read(process->path_pids.array[j], &pid);
- // a hackish way to determine if the pid exists while waiting.
- if (getpgid(pid) >= 0) {
- time.tv_sec = 0;
- time.tv_nsec = controller_thread_exit_process_cancel_wait;
+ if (pid) {
- nanosleep(&time, 0);
- }
- else {
- f_file_remove(process->path_pid.string);
- process->path_pid.used = 0;
+ // a hackish way to determine if the pid exists while waiting.
+ if (getpgid(pid) >= 0) {
+ time.tv_sec = 0;
+ time.tv_nsec = controller_thread_exit_process_cancel_wait;
- break;
+ nanosleep(&time, 0);
+ continue;
+ }
+ else {
+ f_file_remove(process->path_pids.array[j].string);
+ process->path_pids.array[j].used = 0;
+ }
}
}
- else {
- break;
- }
- }
- else {
+
break;
- }
+ } // for
} // for
}
} // for
if (process->type == controller_process_type_exit && main->thread->enabled != controller_thread_enabled_exit_execute) continue;
if (process->id_thread) {
+ if (process->childs.used) {
+ for (j = 0; j < process->childs.used; ++j) {
- if (process->child > 0) {
- f_signal_send(F_signal_kill, process->child);
+ if (process->childs.array[j] > 0) {
+ f_signal_send(F_signal_kill, process->childs.array[j]);
- time.tv_sec = 0;
- time.tv_nsec = controller_thread_exit_process_cancel_wait;
+ time.tv_sec = 0;
+ time.tv_nsec = controller_thread_exit_process_cancel_wait;
+
+ process->childs.array[j] = 0;
+ }
+ } // for
nanosleep(&time, 0);
}
f_thread_join(process->id_thread, 0);
- process->child = 0;
process->id_thread = 0;
}
- if (process->path_pid.used) {
- if (f_file_exists(process->path_pid.string) == F_true) {
- status = controller_file_pid_read(process->path_pid, &pid);
+ for (j = 0; j < process->childs.size; ++j) {
+ process->childs.array[j] = 0;
+ } // for
+
+ process->childs.used = 0;
+
+ for (j = 0; j < process->path_pids.used; ++j) {
+
+ if (f_file_exists(process->path_pids.array[j].string) == F_true) {
+ status = controller_file_pid_read(process->path_pids.array[j], &pid);
if (pid) {
f_signal_send(F_signal_kill, pid);
}
- f_file_remove(process->path_pid.string);
- process->path_pid.used = 0;
+ f_file_remove(process->path_pids.array[j].string);
+ process->path_pids.array[j].used = 0;
}
- }
+ } // for
+
+ process->path_pids.used = 0;
} // for
}
#endif // _di_controller_thread_process_cancel_
start utility sleeper_1
start utility sleeper_2
+ start utility sleeper_3
explode:
start maintenance boom
main() {
if [[ -f /tmp/sleeper_2.pid ]] ; then
- echo "Failure: pid file '/tmp/sleeper_1.pid' already exists."
+ echo "Failure: pid file '/tmp/sleeper_2.pid' already exists."
return 1
fi
--- /dev/null
+# fss-000d
+# sleeper rule whose program creates its own PID file, runs in the background, sleep for a while, removes PID file, and returns.
+
+setting:
+ name "Sleeper #2"
+ nice 10
+
+utility:
+ pid_file /tmp/sleeper_3.1.pid
+ start {
+ \#!/bin/bash
+
+ main() {
+ if [[ -f /tmp/sleeper_3.1.pid ]] ; then
+ echo "Failure: pid file '/tmp/sleeper_3.1.pid' already exists."
+ return 1
+ fi
+
+ echo "$BASHPID" > /tmp/sleeper_3.1.pid
+
+ echo "Sleeper 3.1, now sleeping."
+ sleep 15
+
+ echo "Sleeper 3.1, done sleeping."
+ rm -f /tmp/sleeper_3.1.pid
+ return 0
+ \}
+
+ main &
+ }
+
+utility:
+ pid_file /tmp/sleeper_3.2.pid
+ start {
+ \#!/bin/bash
+
+ main() {
+ if [[ -f /tmp/sleeper_3.2.pid ]] ; then
+ echo "Failure: pid file '/tmp/sleeper_3.2.pid' already exists."
+ return 1
+ fi
+
+ echo "$BASHPID" > /tmp/sleeper_3.2.pid
+
+ echo "Sleeper 3.2, now sleeping."
+ sleep 20
+
+ echo "Sleeper 3.2, done sleeping."
+ rm -f /tmp/sleeper_3.2.pid
+ return 0
+ \}
+
+ main &
+ }