atomicqueue: fix subtle race
authorWim Taymans <wim.taymans@collabora.co.uk>
Fri, 24 Feb 2012 11:51:24 +0000 (12:51 +0100)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Sat, 7 Apr 2012 14:24:53 +0000 (15:24 +0100)
Fix a race where the reader would see the updated the tail pointer before the
write could write the data into the queue. Fix this by having a separate reader
tail pointer that is only incremented after the writer wrote the data.

gst/gstatomicqueue.c

index 5b21be6..1ec9a4d 100644 (file)
@@ -57,6 +57,7 @@ struct _GstAQueueMem
   gpointer *array;
   volatile gint head;
   volatile gint tail;
+  volatile gint tail_read;
   GstAQueueMem *next;
   GstAQueueMem *free;
 };
@@ -84,6 +85,7 @@ new_queue_mem (guint size, gint pos)
   mem->array = g_new0 (gpointer, mem->size + 1);
   mem->head = pos;
   mem->tail = pos;
+  mem->tail_read = pos;
   mem->next = NULL;
   mem->free = NULL;
 
@@ -234,7 +236,7 @@ gst_atomic_queue_peek (GstAtomicQueue * queue)
     head_mem = g_atomic_pointer_get (&queue->head_mem);
 
     head = g_atomic_int_get (&head_mem->head);
-    tail = g_atomic_int_get (&head_mem->tail);
+    tail = g_atomic_int_get (&head_mem->tail_read);
     size = head_mem->size;
 
     /* when we are not empty, we can continue */
@@ -291,7 +293,7 @@ gst_atomic_queue_pop (GstAtomicQueue * queue)
       head_mem = g_atomic_pointer_get (&queue->head_mem);
 
       head = g_atomic_int_get (&head_mem->head);
-      tail = g_atomic_int_get (&head_mem->tail);
+      tail = g_atomic_int_get (&head_mem->tail_read);
       size = head_mem->size;
 
       /* when we are not empty, we can continue */
@@ -380,6 +382,9 @@ gst_atomic_queue_push (GstAtomicQueue * queue, gpointer data)
           tail + 1));
 
   tail_mem->array[tail & size] = data;
+
+  /* and now the readers can read */
+  g_atomic_int_inc (&tail_mem->tail_read);
 }
 
 /**
@@ -408,7 +413,7 @@ gst_atomic_queue_length (GstAtomicQueue * queue)
   head = g_atomic_int_get (&head_mem->head);
 
   tail_mem = g_atomic_pointer_get (&queue->tail_mem);
-  tail = g_atomic_int_get (&tail_mem->tail);
+  tail = g_atomic_int_get (&tail_mem->tail_read);
 
 #ifdef LOW_MEM
   if (g_atomic_int_dec_and_test (&queue->num_readers))