Event queue
diff --git a/ChangeLog b/ChangeLog
index 83679ff..26999c9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2011-06-01  Petr Machata  <[email protected]>
+
+	* sysdeps/linux-gnu/events.c (delayed_events, end_delayed_events):
+	New variables for event queue.
+	(enque_event): To add new event into the queue.
+	(next_qd_event, each_qd_event, enum ecb_status): Iteration interfaces.
+	(next_event): Instead of waitpid, pop event que if there are
+	events waiting.
+	* ltrace.h (Event.next): New field to support event chaining.
+
 2011-05-30  Petr Machata  <[email protected]>
 
 	* common.h (enable_breakpoint, disable_breakpoint)
diff --git a/common.h b/common.h
index a53bc83..ab2465e 100644
--- a/common.h
+++ b/common.h
@@ -242,7 +242,6 @@
 	pcb_cont, /* The iteration should continue.  */
 };
 
-extern Event * next_event(void);
 extern Process * pid2proc(pid_t pid);
 extern void add_process(Process * proc);
 extern void remove_process(Process * proc);
@@ -253,6 +252,18 @@
 			  enum pcb_status (* cb)(Process * proc, void * data),
 			  void * data);
 
+/* Events  */
+enum ecb_status {
+	ecb_cont, /* The iteration should continue.  */
+	ecb_yield, /* The iteration should stop, yielding this
+		    * event.  */
+	ecb_deque, /* Like ecb_stop, but the event should be removed
+		    * from the queue.  */
+};
+extern Event * next_event(void);
+extern Event * each_qd_event(enum ecb_status (* cb)(Event * event, void * data),
+			     void * data);
+extern void enque_event(Event * event);
 extern void handle_event(Event * event);
 extern pid_t execute_program(const char * command, char ** argv);
 extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info);
diff --git a/ltrace.h b/ltrace.h
index 5e43ba5..0ff4572 100644
--- a/ltrace.h
+++ b/ltrace.h
@@ -20,6 +20,7 @@
 typedef struct Process Process;
 typedef struct Event Event;
 struct Event {
+	struct Event * next;
 	Process * proc;
 	Event_type type;
 	union {
diff --git a/proc.c b/proc.c
index 0423eeb..fae6595 100644
--- a/proc.c
+++ b/proc.c
@@ -169,6 +169,23 @@
 	return pcb_cont;
 }
 
+static enum ecb_status
+event_for_proc(Event * event, void * data)
+{
+	if (event->proc == data)
+		return ecb_deque;
+	else
+		return ecb_cont;
+}
+
+static void
+delete_events_for(Process * proc)
+{
+	Event * event;
+	while ((event = each_qd_event(&event_for_proc, proc)) != NULL)
+		free(event);
+}
+
 void
 remove_process(Process *proc)
 {
@@ -182,6 +199,7 @@
 	if (list_of_processes == proc) {
 		tmp = list_of_processes;
 		list_of_processes = list_of_processes->next;
+		delete_events_for(tmp);
 		free(tmp);
 		return;
 	}
@@ -190,6 +208,7 @@
 		if (tmp->next == proc) {
 			tmp2 = tmp->next;
 			tmp->next = tmp->next->next;
+			delete_events_for(tmp2);
 			free(tmp2);
 			return;
 		}
diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c
index b944cd6..6068fea 100644
--- a/sysdeps/linux-gnu/events.c
+++ b/sysdeps/linux-gnu/events.c
@@ -8,17 +8,96 @@
 #include <signal.h>
 #include <string.h>
 #include <sys/ptrace.h>
+#include <assert.h>
 
 #include "common.h"
 
 static Event event;
 
+/* A queue of events that we missed while enabling the
+ * breakpoint in one of tasks.  */
+static Event * delayed_events = NULL;
+static Event * end_delayed_events = NULL;
+
 static enum pcb_status
 first (Process * proc, void * data)
 {
 	return pcb_stop;
 }
 
+void
+enque_event(Event * event)
+{
+	debug(DEBUG_FUNCTION, "%d: queuing event %d for later",
+	      event->proc->pid, event->type);
+	Event * ne = malloc(sizeof(*ne));
+	if (ne == NULL) {
+		perror("event will be missed: malloc");
+		return;
+	}
+
+	*ne = *event;
+	ne->next = NULL;
+	if (end_delayed_events == NULL) {
+		assert(delayed_events == NULL);
+		end_delayed_events = delayed_events = ne;
+	}
+	else {
+		assert(delayed_events != NULL);
+		end_delayed_events = end_delayed_events->next = ne;
+	}
+}
+
+Event *
+each_qd_event(enum ecb_status (*pred)(Event *, void *), void * data)
+{
+	Event * prev = delayed_events;
+	Event * event;
+	for (event = prev; event != NULL; ) {
+		switch ((*pred)(event, data)) {
+		case ecb_cont:
+			prev = event;
+			event = event->next;
+			continue;
+
+		case ecb_deque:
+			debug(DEBUG_FUNCTION, "dequeuing event %d for %d",
+			      event->type,
+			      event->proc != NULL ? event->proc->pid : -1);
+			/*
+			printf("dequeuing event %d for %d\n", event->type,
+			       event->proc != NULL ? event->proc->pid : -1) ;
+			*/
+			if (end_delayed_events == event)
+				end_delayed_events = prev;
+			if (delayed_events == event)
+				delayed_events = event->next;
+			else
+				prev->next = event->next;
+			if (delayed_events == NULL)
+				end_delayed_events = NULL;
+			/* fall-through */
+
+		case ecb_yield:
+			return event;
+		}
+	}
+
+	return NULL;
+}
+
+static enum ecb_status
+event_process_not_reenabling(Event * event, void * data)
+{
+	return ecb_deque;
+}
+
+static Event *
+next_qd_event(void)
+{
+	return each_qd_event(&event_process_not_reenabling, NULL);
+}
+
 Event *
 next_event(void)
 {
@@ -28,6 +107,13 @@
 	int stop_signal;
 
 	debug(DEBUG_FUNCTION, "next_event()");
+	Event * ev;
+	if ((ev = next_qd_event()) != NULL) {
+		event = *ev;
+		free(ev);
+		return &event;
+	}
+
 	if (!each_process(NULL, &first, NULL)) {
 		debug(DEBUG_EVENT, "event: No more traced programs: exiting");
 		exit(0);