On 07/30/2012 09:43 AM, Jakub Filak wrote:
* concurrent run of post-create event may mark all processed problems
as
duplicates of each other and delete all of them
* abrtd accepts notifications and pushes a problem directory to a queue
* after processing of one problem abrtd pops a directory from the queue
and continues of processing with next directory
Signed-off-by: Jakub Filak <jfilak(a)redhat.com>
---
src/daemon/abrtd.c | 66 ++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 54 insertions(+), 12 deletions(-)
diff --git a/src/daemon/abrtd.c b/src/daemon/abrtd.c
index 1112591..c664a66 100644
--- a/src/daemon/abrtd.c
+++ b/src/daemon/abrtd.c
@@ -74,6 +74,24 @@ static int child_count = 0;
*/
static GList *s_pid_list;
+/*
+ * The post-create event cannot be run concurrently for more problem
+ * directories. The problem is in searching for duplicates process in case when
+ * two concurrently processed directories are duplicates of each other. Both of
+ * the directories are marked as duplicates of each other and are deleted.
+ *
+ * This queue is used for serialization of processing of created problem
+ * directories.
+ *
+ * The GIO notify handler pushes new directories to this queue and if the queue was
+ * empty before push then starts processing of the pushed directory.
+ *
+ * Directory processing code pops the processed directory from the queue at the
+ * end of processing. If the queue is not empty starts processing of the next
+ * directory which is on the top of the queue.
+ */
+static GList *s_dir_queue;
+
enum {
EVTYPE_POST_CREATE,
EVTYPE_NOTIFY,
@@ -291,6 +309,25 @@ static gboolean handle_signal_cb(GIOChannel *gio, GIOCondition
condition, gpoint
return TRUE; /* "please don't remove this event" */
}
+static gboolean handle_event_output_cb(GIOChannel *gio, GIOCondition condition, gpointer
ptr);
+
+/* Runs post-create event asynchronously. Uses GIOChanel for communication with event
process. */
+static void run_post_create_on_dir_async(const char *name)
+{
+ struct event_processing_state *state = new_event_processing_state();
+ state->event_type = EVTYPE_POST_CREATE;
+ state->dirname = concat_path_file(g_settings_dump_location, name);
+
+ state->child_pid = spawn_event_handler_child(state->dirname,
"post-create", &state->child_stdout_fd);
+ s_pid_list = g_list_prepend(s_pid_list, state);
+ ndelay_on(state->child_stdout_fd);
+
+ GIOChannel *channel_event_output =
my_io_channel_unix_new(state->child_stdout_fd);
+ /*uint channel_id_event_output =*/ g_io_add_watch(channel_event_output,
+ G_IO_IN | G_IO_PRI | G_IO_HUP,
+ handle_event_output_cb,
+ state);
+}
/* Event-processing child output handler (such as "post-create" event) */
static gboolean handle_event_output_cb(GIOChannel *gio, GIOCondition condition, gpointer
ptr)
@@ -435,11 +472,21 @@ static gboolean handle_event_output_cb(GIOChannel *gio,
GIOCondition condition,
/* We stop using this channel */
g_io_channel_unref(gio);
+
+ /* pop the currently processed directory from the incoming queue */
+ char *name = s_dir_queue->data;
+ s_dir_queue = g_list_remove(s_dir_queue, name);
+ free(name);
+
+ /* if the queue is not empty process directory from */
+ /* the top of the incoming queue */
+ if (s_dir_queue)
+ run_post_create_on_dir_async(s_dir_queue->data);
+
return FALSE; /* "glib, please remove this events source!" */
/* Removing will also drop the last ref to this gio, closing/freeing it */
}
-
/* Inotify handler */
static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition condition, gpointer
ptr_unused)
@@ -550,19 +597,14 @@ static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition
condition, gpoin
}
}
- struct event_processing_state *state = new_event_processing_state();
- state->event_type = EVTYPE_POST_CREATE;
- state->dirname = concat_path_file(g_settings_dump_location, name);
+ const bool empty_queue = s_dir_queue == NULL;
+ /* push the new directory to the end of the incoming queue */
+ s_dir_queue = g_list_append(s_dir_queue, xstrdup(name));
- state->child_pid = spawn_event_handler_child(state->dirname,
"post-create", &state->child_stdout_fd);
- s_pid_list = g_list_prepend(s_pid_list, state);
- ndelay_on(state->child_stdout_fd);
+ /* if the queue was empty process the current directory */
+ if (empty_queue)
+ run_post_create_on_dir_async(name);
- GIOChannel *channel_event_output =
my_io_channel_unix_new(state->child_stdout_fd);
- /*uint channel_id_event_output =*/ g_io_add_watch(channel_event_output,
- G_IO_IN | G_IO_PRI | G_IO_HUP,
- handle_event_output_cb,
- state);
} /* while */
free(buf);
Looks good to me.