Move addrarange's initialization code into new function
addrarange_locked, which addrarange calls while holding
dwfl->load_lock.

Also move arangecu's CU initialization code into new function
arangecu_locked, which arangecu calls while holding dwfl->load_lock.

Atomics are used to acquire initialized aranges and their CUs without
needing to obtain the load_lock.

Signed-off-by: Aaron Merey <[email protected]>
---
 libdwfl/cu.c | 128 +++++++++++++++++++++++++++++++++------------------
 1 file changed, 84 insertions(+), 44 deletions(-)

diff --git a/libdwfl/cu.c b/libdwfl/cu.c
index 416773b3..2087ca46 100644
--- a/libdwfl/cu.c
+++ b/libdwfl/cu.c
@@ -43,52 +43,72 @@ dwar (Dwfl_Module *mod, unsigned int idx)
 }
 
 
+static Dwfl_Error
+addrarange_locked (Dwfl_Module *mod, struct dwfl_arange **result)
+{
+  struct dwfl_arange *aranges = NULL;
+  Dwarf_Aranges *dwaranges = NULL;
+  size_t naranges;
+  if (__libdw_getdieranges (mod->dw, &dwaranges, &naranges) != 0)
+    return DWFL_E_LIBDW;
+
+  /* If the module has no aranges (when no code is included) we
+     allocate nothing.  */
+  if (naranges != 0)
+    {
+      aranges = malloc (naranges * sizeof *aranges);
+      if (unlikely (aranges == NULL))
+       return DWFL_E_NOMEM;
+
+      /* libdw has sorted its list by address, which is how we want it.
+        But the sorted list is full of not-quite-contiguous runs pointing
+        to the same CU.  We don't care about the little gaps inside the
+        module, we'll consider them part of the surrounding CU anyway.
+        Collect our own array with just one record for each run of ranges
+        pointing to one CU.  */
+
+      naranges = 0;
+      Dwarf_Off lastcu = 0;
+      for (size_t i = 0; i < dwaranges->naranges; ++i)
+       if (i == 0 || dwaranges->info[i].offset != lastcu)
+         {
+           aranges[naranges].arange = i;
+           aranges[naranges].cu = NULL;
+           ++naranges;
+           lastcu = dwaranges->info[i].offset;
+         }
+    }
+
+  /* Store the final array, which is probably much smaller than before.  */
+  mod->naranges = naranges;
+  if (naranges > 0)
+    *result = (realloc (aranges, naranges * sizeof aranges[0]) ?: aranges);
+  else if (aranges != NULL)
+    free (aranges);
+  mod->lazycu += naranges;
+
+  return DWFL_E_NOERROR;
+}
+
 static Dwfl_Error
 addrarange (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_arange **arange)
 {
-  if (mod->aranges == NULL)
+  if (unlikely (atomic_load_acquire (&mod->aranges) == NULL))
     {
       struct dwfl_arange *aranges = NULL;
-      Dwarf_Aranges *dwaranges = NULL;
-      size_t naranges;
-      if (__libdw_getdieranges (mod->dw, &dwaranges, &naranges) != 0)
-       return DWFL_E_LIBDW;
+      Dwfl_Error error = DWFL_E_NOERROR;
 
-      /* If the module has no aranges (when no code is included) we
-        allocate nothing.  */
-      if (naranges != 0)
+      mutex_lock (mod->dwfl->load_lock);
+      if (mod->aranges == NULL)
        {
-         aranges = malloc (naranges * sizeof *aranges);
-         if (unlikely (aranges == NULL))
-           return DWFL_E_NOMEM;
-
-         /* libdw has sorted its list by address, which is how we want it.
-            But the sorted list is full of not-quite-contiguous runs pointing
-            to the same CU.  We don't care about the little gaps inside the
-            module, we'll consider them part of the surrounding CU anyway.
-            Collect our own array with just one record for each run of ranges
-            pointing to one CU.  */
-
-         naranges = 0;
-         Dwarf_Off lastcu = 0;
-         for (size_t i = 0; i < dwaranges->naranges; ++i)
-           if (i == 0 || dwaranges->info[i].offset != lastcu)
-             {
-               aranges[naranges].arange = i;
-               aranges[naranges].cu = NULL;
-               ++naranges;
-               lastcu = dwaranges->info[i].offset;
-             }
+         error = addrarange_locked (mod, &aranges);
+         if (error == DWFL_E_NOERROR)
+           atomic_store_release (&mod->aranges, aranges);
        }
 
-      /* Store the final array, which is probably much smaller than before.  */
-      mod->naranges = naranges;
-      if (naranges > 0)
-        mod->aranges = (realloc (aranges, naranges * sizeof aranges[0])
-                       ?: aranges);
-      else if (aranges != NULL)
-       free (aranges);
-      mod->lazycu += naranges;
+      mutex_unlock (mod->dwfl->load_lock);
+      if (error != DWFL_E_NOERROR)
+       return error;
     }
 
   /* The address must be inside the module to begin with.  */
@@ -288,20 +308,40 @@ __libdwfl_nextcu (Dwfl_Module *mod, struct dwfl_cu 
*lastcu,
   return DWFL_E_NOERROR;
 }
 
+static Dwfl_Error
+arangecu_locked (Dwfl_Module *mod, struct dwfl_arange *arange,
+                struct dwfl_cu **cu)
+{
+  const Dwarf_Arange *dwarange = &mod->dw->dieranges->info[arange->arange];
+  Dwfl_Error result = intern_cu (mod, dwarange->offset, cu);
+  if (result != DWFL_E_NOERROR)
+    return result;
+  assert (*cu != NULL && *cu != (void *) -1l);
+  less_lazy (mod);             /* Each arange with null ->cu counts once.  */
+
+  return DWFL_E_NOERROR;
+}
 
 /* Intern the CU arange points to, if necessary.  */
 
 static Dwfl_Error
 arangecu (Dwfl_Module *mod, struct dwfl_arange *arange, struct dwfl_cu **cu)
 {
-  if (arange->cu == NULL)
+  if (unlikely (atomic_load_acquire (&arange->cu) == NULL))
     {
-      const Dwarf_Arange *dwarange = &mod->dw->dieranges->info[arange->arange];
-      Dwfl_Error result = intern_cu (mod, dwarange->offset, &arange->cu);
-      if (result != DWFL_E_NOERROR)
-       return result;
-      assert (arange->cu != NULL && arange->cu != (void *) -1l);
-      less_lazy (mod);         /* Each arange with null ->cu counts once.  */
+      Dwfl_Error error = DWFL_E_NOERROR;
+      mutex_lock (mod->dwfl->load_lock);
+      if (arange->cu == NULL)
+       {
+         struct dwfl_cu *newcu = NULL;
+         error = arangecu_locked (mod, arange, &newcu);
+         if (error == DWFL_E_NOERROR)
+           atomic_store_release (&arange->cu, newcu);
+       }
+
+      mutex_unlock (mod->dwfl->load_lock);
+      if (error != DWFL_E_NOERROR)
+       return error;
     }
 
   *cu = arange->cu;
-- 
2.56.0

Reply via email to