Signed-off-by: Jakub Filak jfilak@redhat.com --- libreport.spec.in | 1 + src/include/Makefile.am | 3 +- src/include/run_event_list_thread.h | 174 +++++++++++++++++++++ src/lib/Makefile.am | 3 +- src/lib/run_event_list_thread.c | 284 +++++++++++++++++++++++++++++++++++ 5 files changed, 463 insertions(+), 2 deletions(-) create mode 100644 src/include/run_event_list_thread.h create mode 100644 src/lib/run_event_list_thread.c
diff --git a/libreport.spec.in b/libreport.spec.in index ea51431..6f628cd 100644 --- a/libreport.spec.in +++ b/libreport.spec.in @@ -298,6 +298,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %{_includedir}/libreport/run_event.h %{_includedir}/libreport/event_usability.h %{_includedir}/libreport/run_event_list.h +%{_includedir}/libreport/run_event_list_thread.h # Private api headers: %{_includedir}/libreport/internal_abrt_dbus.h %{_includedir}/libreport/internal_libreport.h diff --git a/src/include/Makefile.am b/src/include/Makefile.am index bca63dd..4244b6e 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -11,4 +11,5 @@ libreport_include_HEADERS = \ internal_libreport.h \ internal_abrt_dbus.h \ event_usability.h \ - run_event_list.h + run_event_list.h \ + run_event_list_thread.h diff --git a/src/include/run_event_list_thread.h b/src/include/run_event_list_thread.h new file mode 100644 index 0000000..b253a2e --- /dev/null +++ b/src/include/run_event_list_thread.h @@ -0,0 +1,174 @@ +/* + Copyright (C) 2012 ABRT team. + Copyright (C) 2012 RedHat inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#ifndef LIBREPORT_RUN_EVENT_LIST_THREAD_H_ +#define LIBREPORT_RUN_EVENT_LIST_THREAD_H_ + +/* + * This functions allow asynchronous run of event list process. + * + * Example of usage: + * + * struct elp_thread_args *thread_proc = new_elp_thread_args(); + * + * if (elp_thread_init(thread_proc)) + * exit(1); + * + * struct even_list_process *event_proc = new_event_list_process(...); + * + * elp_thread_run(thread_proc, event_proc); + * + * in some event loop call: + * elp_thread_loop_next_step(thread_proc); + * + * after finish call: + * free_event_list_process(elp_thread_get_process(thread_proc)); + * free_elp_thread(thread_proc); + */ + +/* + * Forward declaration + */ +struct event_list_process; + +/* + * Structure that holds state of thread process. + */ +struct elp_thread_args; + +/* + * Callback used by thread process to send signals about process state + * + * @param args a caller + */ +typedef void(* elp_thread_args_callback)(struct elp_thread_args *args); + +/* + * Creates a new thread process state + * + * @return never returns NULL + */ +struct elp_thread_args *new_elp_thread_args(); + +/* + * Sets "on start" signal callback. The process calls this signla before + * the first step of event list process is performed. + * + * Replaces the currently configured callback. + * + * @param args Not NULL pointer to thread process state + * @param cb Callback signal + */ +void elp_thread_args_set_on_start(struct elp_thread_args *args, + elp_thread_args_callback cb); + +/* + * Sets "on step done" signal callback. The process calls this signal + * after each event list process step. + * + * Replaces the currently configured callback. + * + * @param args Not NULL pointer to thread process state + * @param cb Callback signal + */ +void elp_thread_args_set_on_step_done(struct elp_thread_args *args, + elp_thread_args_callback cb); + +/* + * Sets "on finish" signal callback. The process calls this signal after + * last event list process step. + * + * Replaces the currently configured callback. + * + * @param args Not NULL pointer to thread process state + * @param cb Callback signal + */ +void elp_thread_args_set_on_finish(struct elp_thread_args *args, + elp_thread_args_callback cb); + +/* + * Gets an event list process. + * + * @param args Not NULL pointer to thread process state + * @return An event list process + */ +struct event_list_process *elp_thread_args_get_process(const struct elp_thread_args *args); + +/* + * Destroy thread process + * + * @param args A pointer to thread process + * @return An event list process + */ +void free_elp_thread_args(struct elp_thread_args *args); + +/* + * Creates a thread used by a process + * + * @param args Not NULL pointer to thread process state + * @return On success returns non 0, on error, it returns an error number + */ +int elp_thread_init(struct elp_thread_args *args); + +/* + * Interrupts an event list process and kills a process thread. + * A thread process cannot be used after this function call. + * + * @param args Not NULL pointer to thread process state + */ +void elp_thread_kill(struct elp_thread_args *args); + +/* + * Interrupts an event list process and leaves a process thread untouched. + * The process can be rerun after this method finishes. + * + * @param args Not NULL pointer to thread process state + */ +void elp_thread_interrupt(struct elp_thread_args *args); + +/* + * Sets an event list process and stars waiting on next step. + * + * @param args Not NULL pointer to thread process state + * @param process An event list process. Do not take ownership of it's memory. Replaces a currently configured process. + */ +void elp_thread_run(struct elp_thread_args *args, struct event_list_process *process); + +/* + * Gets an state of the inner event list process. + * + * @param args Not NULL pointer to thread process state + * @return non 0 if event process list is under processing; otherwise return 0 + */ +int elp_thread_is_runnig(struct elp_thread_args *args); + +/* + * Notify a thread process to perform next event list process step. + * + * @param args Not NULL pointer to thread process state + */ +void elp_thread_loop_next_step(struct elp_thread_args *args); + +/* + * Kills a currently running event command process + * + * @param args Not NULL pointer to thread process state + */ +void elp_thread_loop_kill_command(struct elp_thread_args *args); + +#endif /* LIBREPORT_RUN_EVENT_LIST_THREAD_H_ */ diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index fd406ce..d7eab1a 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -49,7 +49,8 @@ libreport_la_SOURCES = \ client.c \ utf8.c \ event_usability.c \ - run_event_list.c + run_event_list.c \ + run_event_list_thread.c
libreport_la_CPPFLAGS = \ -Wall -Wwrite-strings -Werror \ diff --git a/src/lib/run_event_list_thread.c b/src/lib/run_event_list_thread.c new file mode 100644 index 0000000..2c230a5 --- /dev/null +++ b/src/lib/run_event_list_thread.c @@ -0,0 +1,284 @@ +/* + Copyright (C) 2012 ABRT team. + Copyright (C) 2012 RedHat inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include <pthread.h> +#include "run_event_list.h" +#include "run_event_list_thread.h" +#include "internal_libreport.h" + +struct elp_thread_args +{ + pthread_t process_tid; + + int proc_ready; + int step_ready; + struct event_list_process *process; + + int killed; + int interrupted; + int runnig; + + pthread_mutex_t sync_mut; + + pthread_cond_t proc_cond; + pthread_cond_t step_cond; + + elp_thread_args_callback start_cb; + elp_thread_args_callback step_done_cb; + elp_thread_args_callback finish_cb; +}; + +struct elp_thread_args *new_elp_thread_args() +{ + struct elp_thread_args *args = xzalloc(sizeof(*args)); + + pthread_mutex_init(&(args->sync_mut), NULL); + + pthread_cond_init(&(args->proc_cond), NULL); + pthread_cond_init(&(args->step_cond), NULL); + + return args; +} + +void elp_thread_args_set_on_start(struct elp_thread_args *args, + elp_thread_args_callback cb) +{ + args->start_cb = cb; +} + +void elp_thread_args_set_on_step_done(struct elp_thread_args *args, + elp_thread_args_callback cb) +{ + args->step_done_cb = cb; +} + +void elp_thread_args_set_on_finish(struct elp_thread_args *args, + elp_thread_args_callback cb) +{ + args->finish_cb = cb; +} + +static void elp_thread_args_set_process(struct elp_thread_args *args, + struct event_list_process *process) +{ + args->process = process; +} + +struct event_list_process *elp_thread_args_get_process(const struct elp_thread_args *args) +{ + return args->process; +} + +static void elp_thread_args_on_start(struct elp_thread_args *args) +{ + if (args->start_cb) + args->start_cb(args); +} + +static void elp_thread_args_on_step_done(struct elp_thread_args *args) +{ + if (args->step_done_cb) + args->step_done_cb(args); +} + +static void elp_thread_args_on_finish(struct elp_thread_args *args) +{ + if (args->finish_cb) + args->finish_cb(args); +} + +void elp_thread_interrupt_internal(struct elp_thread_args *args) +{ + args->step_ready = 0; + args->interrupted = 1; + /* not unset runnig because of waiting on last step */ +} + +static void elp_thread_kill_internal(struct elp_thread_args *args) +{ + args->proc_ready = 0; + args->killed = 1; + args->runnig = 0; + + elp_thread_interrupt_internal(args); +} + +static int elp_thread_wait_on_process(struct elp_thread_args *args) +{ + pthread_mutex_lock(&(args->sync_mut)); + + while (!args->killed && !args->proc_ready) + { + const int error = pthread_cond_wait(&(args->proc_cond), &(args->sync_mut)); + if (error) + { + perror_msg("waiting o next process failed with %d", error); + elp_thread_kill_internal(args); + } + } + + --(args->proc_ready); + + const int killed = args->killed; + + pthread_mutex_unlock(&(args->sync_mut)); + + return !killed; +} + +static int elp_thread_wait_on_step(struct elp_thread_args *args) +{ + pthread_mutex_lock(&(args->sync_mut)); + + while(!args->interrupted && !args->killed && !args->step_ready) + { + const int error = pthread_cond_wait(&(args->step_cond), &(args->sync_mut)); + if (error) + { + perror_msg("waiting o next process failed with %d", error); + elp_thread_kill_internal(args); + } + } + + --(args->step_ready); + + const int interrupted = args->interrupted || args->killed; + + pthread_mutex_unlock(&(args->sync_mut)); + + return !interrupted; +} + +void free_elp_thread_args(struct elp_thread_args *args) +{ + if (!args) + return; + + pthread_cond_destroy(&(args->step_cond)); + pthread_cond_destroy(&(args->proc_cond)); + + pthread_mutex_destroy(&(args->sync_mut)); + + free(args); +} + +void elp_thread_run(struct elp_thread_args *args, struct event_list_process *process) +{ + pthread_mutex_lock(&(args->sync_mut)); + + if (elp_thread_is_runnig(args)) + error_msg_and_die("event list process thread is already runnig"); + + args->runnig = 1; + args->killed = 0; + args->interrupted = 0; + args->proc_ready = 1; + + elp_thread_args_set_process(args, process); + + pthread_cond_signal(&(args->proc_cond)); + pthread_mutex_unlock(&(args->sync_mut)); +} + +int elp_thread_is_runnig(struct elp_thread_args *args) +{ + return args->runnig; +} + +void elp_thread_loop_next_step(struct elp_thread_args *args) +{ + pthread_mutex_lock(&(args->sync_mut)); + + ++(args->step_ready); + + pthread_cond_signal(&(args->step_cond)); + pthread_mutex_unlock(&(args->sync_mut)); +} + +static void *elp_thread(void *args) +{ + struct elp_thread_args *p = (struct elp_thread_args *)args; + + while (elp_thread_wait_on_process(p)) + { + elp_thread_args_on_start(p); + + pthread_mutex_lock(&(p->sync_mut)); + + if (!p->killed) + { + pthread_mutex_unlock(&(p->sync_mut)); + + /* Perform first step outside the loop because we */ + /* want to finish immediately if process is empty */ + elp_thread_wait_on_step(p); + bool cont = elp_next_step(p->process); + + while (elp_thread_wait_on_step(p) && cont) + { + cont = elp_next_step(p->process); + elp_thread_args_on_step_done(p); + } + + pthread_mutex_lock(&(p->sync_mut)); + } + + p->runnig = 0; + + pthread_mutex_unlock(&(p->sync_mut)); + + elp_thread_args_on_finish(p); + } + + return NULL; +} + +int elp_thread_init(struct elp_thread_args *args) +{ + return pthread_create(&(args->process_tid), NULL, elp_thread, args); +} + +void elp_thread_loop_kill_command(struct elp_thread_args *args) +{ + pthread_mutex_lock(&(args->sync_mut)); + + if (args->process) + elp_kill_run(args->process); + + pthread_mutex_unlock(&(args->sync_mut)); +} + +void elp_thread_interrupt(struct elp_thread_args *args) +{ + pthread_mutex_lock(&(args->sync_mut)); + + elp_thread_interrupt_internal(args); + + pthread_cond_signal(&(args->step_cond)); + pthread_mutex_unlock(&(args->sync_mut)); +} + +void elp_thread_kill(struct elp_thread_args *args) +{ + pthread_mutex_lock(&(args->sync_mut)); + + elp_thread_kill_internal(args); + + pthread_cond_signal(&(args->proc_cond)); + pthread_mutex_unlock(&(args->sync_mut)); +}