Support sized delete.
authorTorvald Riegel <triegel@redhat.com>
Mon, 9 Nov 2015 17:30:24 +0000 (17:30 +0000)
committerTorvald Riegel <torvald@gcc.gnu.org>
Mon, 9 Nov 2015 17:30:24 +0000 (17:30 +0000)
This adds transactional clones of the sized version of operator delete.

From-SVN: r230036

libitm/ChangeLog
libitm/alloc.cc
libitm/alloc_cpp.cc
libitm/libitm.map
libitm/libitm_i.h
libitm/testsuite/libitm.c++/newdelete.C [new file with mode: 0644]

index 69492d813a433396d3ddc91cdac8c861a84d2622..0564345903bd3b703a192237b0c44a8d8f5d788d 100644 (file)
@@ -1,3 +1,17 @@
+2015-11-09  Torvald Riegel  <triegel@redhat.com>
+
+       * alloc_cpp.cc (_ZdlPvX, _ZdlPvXRKSt9nothrow_t, _ZGTtdlPvX,
+       _ZGTtdlPvXRKSt9nothrow_t, delsz_opnt): New.
+       * libitm.map: Add _ZGTtdlPvX and _ZGTtdlPvXRKSt9nothrow_t.
+       * libitm_i.h (gtm_alloc_action): Add free_fn_sz and sz.  Add comments.
+       (gtm_thread::forget_allocations): New overload with size_t argument.
+       * alloc.c (gtm_thread::forget_allocation): Define new overload and
+       adapt existing one.
+       (gtm_thread::record_allocation): Adapt.
+       (gtm_thread::commit_allocations_1): Adapt.
+       (gtm_thread::commit_allocations_2): Adapt.
+       * testsuite/libitm.c++/newdelete.C: New.
+
 2015-10-27  Daniel Jacobowitz  <dan@codesourcery.com>
            Joseph Myers  <joseph@codesourcery.com>
            Mark Shinwell  <shinwell@codesourcery.com>
index bb292da88dde2a4707730a00a2b2696379eaa1ac..7b8786c5ec537b23cbb610b5f38200342d3753bb 100644 (file)
@@ -29,26 +29,38 @@ namespace GTM HIDDEN {
 void
 gtm_thread::record_allocation (void *ptr, void (*free_fn)(void *))
 {
-  uintptr_t iptr = (uintptr_t) ptr;
-
-  gtm_alloc_action *a = this->alloc_actions.find(iptr);
-  if (a == 0)
-    a = this->alloc_actions.insert(iptr);
+  // We do not deallocate before outermost commit, so we should never have
+  // an existing log entry for a new allocation.
+  gtm_alloc_action *a = this->alloc_actions.insert((uintptr_t) ptr);
 
   a->free_fn = free_fn;
+  a->free_fn_sz = 0;
   a->allocated = true;
 }
 
 void
 gtm_thread::forget_allocation (void *ptr, void (*free_fn)(void *))
 {
-  uintptr_t iptr = (uintptr_t) ptr;
-
-  gtm_alloc_action *a = this->alloc_actions.find(iptr);
-  if (a == 0)
-    a = this->alloc_actions.insert(iptr);
-
+  // We do not deallocate before outermost commit, so we should never have
+  // an existing log entry for a deallocation at the same address.  We may
+  // have an existing entry for a matching allocation, but this is handled
+  // correctly because both are complementary in that only one of these will
+  // cause an action at commit or abort.
+  gtm_alloc_action *a = this->alloc_actions.insert((uintptr_t) ptr);
   a->free_fn = free_fn;
+  a->free_fn_sz = 0;
+  a->allocated = false;
+}
+
+void
+gtm_thread::forget_allocation (void *ptr, size_t sz,
+                              void (*free_fn_sz)(void *, size_t))
+{
+  // Same as forget_allocation but with a size.
+  gtm_alloc_action *a = this->alloc_actions.insert((uintptr_t) ptr);
+  a->free_fn = 0;
+  a->free_fn_sz = free_fn_sz;
+  a->sz = sz;
   a->allocated = false;
 }
 
@@ -67,31 +79,27 @@ commit_allocations_2 (uintptr_t key, gtm_alloc_action *a, void *data)
 
   if (cb_data->revert_p)
     {
-      // Roll back nested allocations.
+      // Roll back nested allocations, discard deallocations.
       if (a->allocated)
-       a->free_fn (ptr);
+       {
+         if (a->free_fn_sz != 0)
+           a->free_fn_sz (ptr, a->sz);
+         else
+           a->free_fn (ptr);
+       }
     }
   else
     {
-      if (a->allocated)
-       {
-         // Add nested allocations to parent transaction.
-         gtm_alloc_action* a_parent = cb_data->parent->insert(key);
-         *a_parent = *a;
-       }
-      else
-       {
-         // ??? We could eliminate a parent allocation that matches this
-         // memory release, if we had support for removing all accesses
-         // to this allocation from the transaction's undo and redo logs
-         // (otherwise, the parent transaction's undo or redo might write to
-         // data that is already shared again because of calling free()).
-         // We don't have this support currently, and the benefit of this
-         // optimization is unknown, so just add it to the parent.
-         gtm_alloc_action* a_parent;
-         a_parent = cb_data->parent->insert(key);
-         *a_parent = *a;
-       }
+      // Add allocations and deallocations to parent.
+      // ??? We could eliminate a (parent) allocation that matches this
+      // a deallocation, if we had support for removing all accesses
+      // to this allocation from the transaction's undo and redo logs
+      // (otherwise, the parent transaction's undo or redo might write to
+      // data that is already shared again because of calling free()).
+      // We don't have this support currently, and the benefit of this
+      // optimization is unknown, so just add it to the parent.
+      gtm_alloc_action* a_parent = cb_data->parent->insert(key);
+      *a_parent = *a;
     }
 }
 
@@ -99,10 +107,15 @@ static void
 commit_allocations_1 (uintptr_t key, gtm_alloc_action *a, void *cb_data)
 {
   void *ptr = (void *)key;
-  uintptr_t revert_p = (uintptr_t) cb_data;
+  bool revert_p = (bool) (uintptr_t) cb_data;
 
-  if (a->allocated == revert_p)
-    a->free_fn (ptr);
+  if (revert_p == a->allocated)
+    {
+      if (a->free_fn_sz != 0)
+       a->free_fn_sz (ptr, a->sz);
+      else
+       a->free_fn (ptr);
+    }
 }
 
 /* Permanently commit allocated memory during transaction.
index 85146189ff2d443adafa2269b2beba835e3028f6..13185a785d81bedbb33965c7e5c9538c688c145e 100644 (file)
@@ -35,41 +35,50 @@ using namespace GTM;
 
 #define _ZnwX                  S(_Znw,MANGLE_SIZE_T)
 #define _ZnaX                  S(_Zna,MANGLE_SIZE_T)
+#define _ZdlPvX                        S(_ZdlPv,MANGLE_SIZE_T)
 #define _ZnwXRKSt9nothrow_t    S(S(_Znw,MANGLE_SIZE_T),RKSt9nothrow_t)
 #define _ZnaXRKSt9nothrow_t    S(S(_Zna,MANGLE_SIZE_T),RKSt9nothrow_t)
+#define _ZdlPvXRKSt9nothrow_t  S(S(_ZdlPv,MANGLE_SIZE_T),RKSt9nothrow_t)
 
 #define _ZGTtnwX               S(_ZGTtnw,MANGLE_SIZE_T)
 #define _ZGTtnaX               S(_ZGTtna,MANGLE_SIZE_T)
+#define _ZGTtdlPvX             S(_ZGTtdlPv,MANGLE_SIZE_T)
 #define _ZGTtnwXRKSt9nothrow_t S(S(_ZGTtnw,MANGLE_SIZE_T),RKSt9nothrow_t)
 #define _ZGTtnaXRKSt9nothrow_t S(S(_ZGTtna,MANGLE_SIZE_T),RKSt9nothrow_t)
+#define _ZGTtdlPvXRKSt9nothrow_t S(S(_ZGTtdlPv,MANGLE_SIZE_T),RKSt9nothrow_t)
 
 /* Everything from libstdc++ is weak, to avoid requiring that library
    to be linked into plain C applications using libitm.so.  */
 
 extern "C" {
 
-extern void *_ZnwX (size_t) __attribute__((weak));
-extern void _ZdlPv (void *) __attribute__((weak));
-extern void *_ZnaX (size_t) __attribute__((weak));
-extern void _ZdaPv (void *) __attribute__((weak));
+extern void *_ZnwX  (size_t) __attribute__((weak));
+extern void _ZdlPv  (void *) __attribute__((weak));
+extern void _ZdlPvX (void *, size_t) __attribute__((weak));
+extern void *_ZnaX  (size_t) __attribute__((weak));
+extern void _ZdaPv  (void *) __attribute__((weak));
 
 typedef const struct nothrow_t { } *c_nothrow_p;
 
 extern void *_ZnwXRKSt9nothrow_t (size_t, c_nothrow_p) __attribute__((weak));
 extern void _ZdlPvRKSt9nothrow_t (void *, c_nothrow_p) __attribute__((weak));
+extern void _ZdlPvXRKSt9nothrow_t
+(void *, size_t, c_nothrow_p) __attribute__((weak));
 extern void *_ZnaXRKSt9nothrow_t (size_t, c_nothrow_p) __attribute__((weak));
 extern void _ZdaPvRKSt9nothrow_t (void *, c_nothrow_p) __attribute__((weak));
 
 #if !defined (HAVE_ELF_STYLE_WEAKREF) 
-void *_ZnwX (size_t) { return NULL; }
-void _ZdlPv (void *) { return; }
-void *_ZnaX (size_t) { return NULL; }
-void _ZdaPv (void *) { return; }
-
-void *_ZnwXRKSt9nothrow_t (size_t, c_nothrow_p) { return NULL; }
-void _ZdlPvRKSt9nothrow_t (void *, c_nothrow_p) { return; }
-void *_ZnaXRKSt9nothrow_t (size_t, c_nothrow_p) { return NULL; }
-void _ZdaPvRKSt9nothrow_t (void *, c_nothrow_p) { return; }
+void *_ZnwX  (size_t) { return NULL; }
+void _ZdlPv  (void *) { return; }
+void _ZdlPvX (void *, size_t) { return; }
+void *_ZnaX  (size_t) { return NULL; }
+void _ZdaPv  (void *) { return; }
+
+void *_ZnwXRKSt9nothrow_t  (size_t, c_nothrow_p) { return NULL; }
+void _ZdlPvRKSt9nothrow_t  (void *, c_nothrow_p) { return; }
+void _ZdlPvXRKSt9nothrow_t (void *, size_t, c_nothrow_p) { return; }
+void *_ZnaXRKSt9nothrow_t  (size_t, c_nothrow_p) { return NULL; }
+void _ZdaPvRKSt9nothrow_t  (void *, c_nothrow_p) { return; }
 #endif /* HAVE_ELF_STYLE_WEAKREF */
 
 /* Wrap the delete nothrow symbols for usage with a single argument.
@@ -89,6 +98,12 @@ del_opvnt (void *ptr)
   _ZdaPvRKSt9nothrow_t (ptr, NULL);
 }
 
+static void
+delsz_opnt (void *ptr, size_t sz)
+{
+  _ZdlPvXRKSt9nothrow_t (ptr, sz, NULL);
+}
+
 /* Wrap: operator new (std::size_t sz)  */
 void *
 _ZGTtnwX (size_t sz)
@@ -161,4 +176,20 @@ _ZGTtdaPvRKSt9nothrow_t (void *ptr, c_nothrow_p nt UNUSED)
     gtm_thr()->forget_allocation (ptr, del_opvnt);
 }
 
+/* Wrap: operator delete(void* ptr, std::size_t sz)  */
+void
+_ZGTtdlPvX (void *ptr, size_t sz)
+{
+  if (ptr)
+    gtm_thr()->forget_allocation (ptr, sz, _ZdlPvX);
+}
+
+/* Wrap: operator delete (void *ptr, std::size_t sz, const std::nothrow_t&)  */
+void
+_ZGTtdlPvXRKSt9nothrow_t (void *ptr, size_t sz, c_nothrow_p nt UNUSED)
+{
+  if (ptr)
+    gtm_thr()->forget_allocation (ptr, sz, delsz_opnt);
+}
+
 } // extern "C"
index 21bcfdfcfab943dfd66a54838fd6d046585fdbb6..ac371dedee60c58c8b2d5e3b12e194eb081397e9 100644 (file)
@@ -182,3 +182,8 @@ LIBITM_1.0 {
   local:
        *;
 };
+LIBITM_1.1 {
+  global:
+       _ZGTtdlPv?;
+       _ZGTtdlPv?RKSt9nothrow_t;
+} LIBITM_1.0;
index 0eda01bacc53876c5b6ef07222a1759ca99c8778..bf8d4d1897eeda7663b74af640276f5c0745982f 100644 (file)
@@ -97,11 +97,25 @@ enum gtm_restart_reason
 
 namespace GTM HIDDEN {
 
+// A log of (de)allocation actions.  We defer handling of some actions until
+// a commit of the outermost transaction.  We also rely on potentially having
+// both an allocation and a deallocation for the same piece of memory in the
+// log; the order in which such entries are processed does not matter because
+// the actions are not in conflict (see below).
 // This type is private to alloc.c, but needs to be defined so that
 // the template used inside gtm_thread can instantiate.
 struct gtm_alloc_action
 {
-  void (*free_fn)(void *);
+  // Iff free_fn_sz is nonzero, it must be used instead of free_fn.
+  union
+  {
+    void (*free_fn)(void *);
+    void (*free_fn_sz)(void *, size_t);
+  };
+  size_t sz;
+  // If true, this is an allocation; we discard the log entry on outermost
+  // commit, and deallocate on abort.  If false, this is a deallocation and
+  // we deallocate on outermost commit and discard the log entry on abort.
   bool allocated;
 };
 
@@ -269,6 +283,7 @@ struct gtm_thread
   void commit_allocations (bool, aa_tree<uintptr_t, gtm_alloc_action>*);
   void record_allocation (void *, void (*)(void *));
   void forget_allocation (void *, void (*)(void *));
+  void forget_allocation (void *, size_t, void (*)(void *, size_t));
   void drop_references_allocations (const void *ptr)
   {
     this->alloc_actions.erase((uintptr_t) ptr);
diff --git a/libitm/testsuite/libitm.c++/newdelete.C b/libitm/testsuite/libitm.c++/newdelete.C
new file mode 100644 (file)
index 0000000..10eba4f
--- /dev/null
@@ -0,0 +1,12 @@
+// { dg-do run }
+
+int main ()
+{
+  atomic_commit {
+    int* data = new int;
+    delete data;
+    data = new int[10];
+    delete[] data;
+  }
+  return 0;
+}