Signed-off-by: Jakub Filak <jfilak(a)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));
+}
--
1.7.10.2