Description: Don't leak memory on long chains of events

Delete event->depend_events when no longer needed to avoid keeping
the whole dependency tree in memory as long as the final event exists

Author: Rebecca N. Palmer <rebecca_palmer@zoho.com>
Bug-Ubuntu: https://launchpad.net/bugs/1354086
Forwarded: https://lists.freedesktop.org/archives/beignet/2018-July/009209.html

--- a/src/cl_event.c
+++ b/src/cl_event.c
@@ -184,6 +184,25 @@ cl_event_new(cl_context ctx, cl_command_
   return e;
 }
 
+/* This exists to prevent long chains of events from filling up memory (https://bugs.launchpad.net/ubuntu/+source/beignet/+bug/1354086).  Call only after the dependencies are complete, or failed and marked as such in this event's status, or when this event is being destroyed */
+LOCAL void
+cl_event_delete_depslist(cl_event event)
+{
+  CL_OBJECT_LOCK(event);
+  cl_event *old_depend_events = event->depend_events;
+  int depend_count = event->depend_event_num;
+  event->depend_event_num = 0;
+  event->depend_events = NULL;
+  CL_OBJECT_UNLOCK(event);
+  if (old_depend_events) {
+    assert(depend_count);
+    for (int i = 0; i < depend_count; i++) {
+      cl_event_delete(old_depend_events[i]);
+    }
+    cl_free(old_depend_events);
+  }
+}
+
 LOCAL void
 cl_event_delete(cl_event event)
 {
@@ -200,13 +219,7 @@ cl_event_delete(cl_event event)
 
   assert(list_node_out_of_list(&event->enqueue_node));
 
-  if (event->depend_events) {
-    assert(event->depend_event_num);
-    for (i = 0; i < event->depend_event_num; i++) {
-      cl_event_delete(event->depend_events[i]);
-    }
-    cl_free(event->depend_events);
-  }
+  cl_event_delete_depslist(event);
 
   /* Free all the callbacks. Last ref, no need to lock. */
   while (!list_empty(&event->callbacks)) {
@@ -566,8 +579,12 @@ cl_event_exec(cl_event event, cl_int exe
   assert(depend_status <= CL_COMPLETE || ignore_depends || exec_to_status == CL_QUEUED);
   if (depend_status < CL_COMPLETE) { // Error happend, cancel exec.
     ret = cl_event_set_status(event, depend_status);
+    cl_event_delete_depslist(event);
     return depend_status;
   }
+  if (depend_status == CL_COMPLETE) { // Avoid memory leak
+    cl_event_delete_depslist(event);
+  }
 
   if (cur_status <= exec_to_status) {
     return ret;
--- a/src/cl_event.h
+++ b/src/cl_event.h
@@ -44,7 +44,7 @@ typedef struct _cl_event {
   cl_command_type event_type; /* Event type. */
   cl_bool is_barrier;         /* Is this event a barrier */
   cl_int status;              /* The execution status */
-  cl_event *depend_events;    /* The events must complete before this. */
+  cl_event *depend_events;    /* The events must complete before this. May disappear after they have completed - see cl_event_delete_depslist*/
   cl_uint depend_event_num;   /* The depend events number. */
   list_head callbacks;        /* The events The event callback functions */
   list_node enqueue_node;     /* The node in the enqueue list. */
