Signed-off-by: Jakub Filak <[email protected]>
---
 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

Reply via email to