From 52ccd88c5e93dcc8bac1dfd67d908660932e8195 Mon Sep 17 00:00:00 2001
From: Pedro Gonnet <gonnet@google.com>
Date: Tue, 31 May 2016 22:11:04 +0200
Subject: [PATCH] make the fast scheduler_addunlock safe again.

---
 src/scheduler.c | 43 ++++++++++---------------------------------
 src/scheduler.h |  2 +-
 2 files changed, 11 insertions(+), 34 deletions(-)

diff --git a/src/scheduler.c b/src/scheduler.c
index 5fc2f6b4b1..c4f84bc72f 100644
--- a/src/scheduler.c
+++ b/src/scheduler.c
@@ -54,39 +54,6 @@
  * @param tb The #task that will be unlocked.
  */
 
-void scheduler_addunlock_old(struct scheduler *s, struct task *ta,
-                         struct task *tb) {
-
-  /* Lock the scheduler since re-allocating the unlocks is not
-     thread-safe. */
-  if (lock_lock(&s->lock) != 0) error("Unable to lock scheduler.");
-
-  /* Does the buffer need to be grown? */
-  if (s->nr_unlocks == s->size_unlocks) {
-    struct task **unlocks_new;
-    int *unlock_ind_new;
-    s->size_unlocks *= 2;
-    if ((unlocks_new = (struct task **)malloc(
-             sizeof(struct task *) *s->size_unlocks)) == NULL ||
-        (unlock_ind_new = (int *)malloc(sizeof(int) * s->size_unlocks)) == NULL)
-      error("Failed to re-allocate unlocks.");
-    memcpy(unlocks_new, s->unlocks, sizeof(struct task *) * s->nr_unlocks);
-    memcpy(unlock_ind_new, s->unlock_ind, sizeof(int) * s->nr_unlocks);
-    free(s->unlocks);
-    free(s->unlock_ind);
-    s->unlocks = unlocks_new;
-    s->unlock_ind = unlock_ind_new;
-  }
-
-  /* Write the unlock to the scheduler. */
-  const int ind = atomic_inc(&s->nr_unlocks);
-  s->unlocks[ind] = tb;
-  s->unlock_ind[ind] = ta - s->tasks;
-
-  /* Release the scheduler. */
-  if (lock_unlock(&s->lock) != 0) error("Unable to unlock scheduler.");
-}
-
 void scheduler_addunlock(struct scheduler *s, struct task *ta,
                          struct task *tb) {
   /* Get an index at which to store this unlock. */
@@ -94,6 +61,7 @@ void scheduler_addunlock(struct scheduler *s, struct task *ta,
 
   /* Does the buffer need to be grown? */
   if (ind == s->size_unlocks) {
+    /* Allocate the new buffer. */
     struct task **unlocks_new;
     int *unlock_ind_new;
     const int size_unlocks_new = s->size_unlocks * 2;
@@ -101,12 +69,19 @@ void scheduler_addunlock(struct scheduler *s, struct task *ta,
              sizeof(struct task *) * size_unlocks_new)) == NULL ||
         (unlock_ind_new = (int *)malloc(sizeof(int) * size_unlocks_new)) == NULL)
       error("Failed to re-allocate unlocks.");
+      
+    /* Wait for all writes to the old buffer to complete. */
+    while (s->completed_unlock_writes < ind);
+    
+    /* Copy the buffers. */
     memcpy(unlocks_new, s->unlocks, sizeof(struct task *) * ind);
     memcpy(unlock_ind_new, s->unlock_ind, sizeof(int) * ind);
     free(s->unlocks);
     free(s->unlock_ind);
     s->unlocks = unlocks_new;
     s->unlock_ind = unlock_ind_new;
+    
+    /* Publish the new buffer size. */
     s->size_unlocks = size_unlocks_new;
   }
   
@@ -116,6 +91,7 @@ void scheduler_addunlock(struct scheduler *s, struct task *ta,
   /* Write the unlock to the scheduler. */
   s->unlocks[ind] = tb;
   s->unlock_ind[ind] = ta - s->tasks;
+  atomic_inc(&s->completed_unlock_writes);
 }
 
 /**
@@ -888,6 +864,7 @@ void scheduler_reset(struct scheduler *s, int size) {
   s->mask = 0;
   s->submask = 0;
   s->nr_unlocks = 0;
+  s->completed_unlock_writes = 0;
 
   /* Set the task pointers in the queues. */
   for (int k = 0; k < s->nr_queues; k++) s->queues[k].tasks = s->tasks;
diff --git a/src/scheduler.h b/src/scheduler.h
index 4352ec0447..754bfb8c22 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -87,7 +87,7 @@ struct scheduler {
   /* The task unlocks. */
   struct task **volatile unlocks;
   int *volatile unlock_ind;
-  volatile int nr_unlocks, size_unlocks;
+  volatile int nr_unlocks, size_unlocks, completed_unlock_writes;
 
   /* Lock for this scheduler. */
   lock_type lock;
-- 
GitLab