Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r972:d912ca6f0d53
Date: 2014-03-11 10:04 +0100
http://bitbucket.org/pypy/stmgc/changeset/d912ca6f0d53/

Log:    Implementation and test for stm_call_on_abort().

diff --git a/c7/stm/core.c b/c7/stm/core.c
--- a/c7/stm/core.c
+++ b/c7/stm/core.c
@@ -180,6 +180,7 @@
     assert(list_is_empty(STM_PSEGMENT->modified_old_objects));
     assert(tree_is_cleared(STM_PSEGMENT->young_outside_nursery));
     assert(tree_is_cleared(STM_PSEGMENT->nursery_objects_shadows));
+    assert(tree_is_cleared(STM_PSEGMENT->callbacks_on_abort));
     assert(STM_PSEGMENT->objects_pointing_to_nursery == NULL);
     assert(STM_PSEGMENT->large_overflow_objects == NULL);
 
@@ -378,6 +379,8 @@
         STM_PSEGMENT->overflow_number_has_been_used = false;
     }
 
+    clear_callbacks_on_abort();
+
     /* send what is hopefully the correct signals */
     if (STM_PSEGMENT->transaction_state == TS_INEVITABLE) {
         /* wake up one thread in wait_for_end_of_inevitable_transaction() */
@@ -503,6 +506,9 @@
     if (tl->mem_clear_on_abort)
         memset(tl->mem_clear_on_abort, 0, tl->mem_bytes_to_clear_on_abort);
 
+    /* invoke the callbacks */
+    invoke_and_clear_callbacks_on_abort();
+
     if (STM_SEGMENT->nursery_end == NSE_SIGABORT)
         STM_SEGMENT->nursery_end = NURSERY_END;   /* done aborting */
 
@@ -542,6 +548,7 @@
         wait_for_end_of_inevitable_transaction(true);
         STM_PSEGMENT->transaction_state = TS_INEVITABLE;
         STM_SEGMENT->jmpbuf_ptr = NULL;
+        clear_callbacks_on_abort();
     }
 
     s_mutex_unlock();
diff --git a/c7/stm/core.h b/c7/stm/core.h
--- a/c7/stm/core.h
+++ b/c7/stm/core.h
@@ -105,6 +105,9 @@
        next minor collection. */
     struct tree_s *nursery_objects_shadows;
 
+    /* Tree of 'key->callback' associations from stm_call_on_abort() */
+    struct tree_s *callbacks_on_abort;
+
     /* Start time: to know approximately for how long a transaction has
        been running, in contention management */
     uint64_t start_time;
diff --git a/c7/stm/extra.c b/c7/stm/extra.c
new file mode 100644
--- /dev/null
+++ b/c7/stm/extra.c
@@ -0,0 +1,55 @@
+#ifndef _STM_CORE_H_
+# error "must be compiled via stmgc.c"
+#endif
+
+
+void stm_call_on_abort(void *key, void callback(void *))
+{
+    assert(_running_transaction());
+
+    if (STM_PSEGMENT->transaction_state == TS_INEVITABLE) {
+        /* ignore callbacks if we're in an inevitable transaction
+           (which cannot abort) */
+        return;
+    }
+
+    if (callback == NULL) {
+        /* ignore the return value: unregistered keys can be
+           "deleted" again */
+        tree_delete_item(STM_PSEGMENT->callbacks_on_abort, (uintptr_t)key);
+    }
+    else {
+        /* double-registering the same key will crash */
+        tree_insert(STM_PSEGMENT->callbacks_on_abort,
+                    (uintptr_t)key, (uintptr_t)callback);
+    }
+}
+
+static void clear_callbacks_on_abort(void)
+{
+    if (!tree_is_cleared(STM_PSEGMENT->callbacks_on_abort))
+        tree_clear(STM_PSEGMENT->callbacks_on_abort);
+}
+
+static void invoke_and_clear_callbacks_on_abort(void)
+{
+    wlog_t *item;
+    struct tree_s *callbacks = STM_PSEGMENT->callbacks_on_abort;
+    if (tree_is_cleared(callbacks))
+        return;
+    STM_PSEGMENT->callbacks_on_abort = tree_create();
+
+    TREE_LOOP_FORWARD(*callbacks, item) {
+        void *key = (void *)item->addr;
+        void (*callback)(void *) = (void(*)(void *))item->val;
+        assert(key != NULL);
+        assert(callback != NULL);
+
+        /* The callback may call stm_call_on_abort(key, NULL).  It is
+           ignored, because 'callbacks_on_abort' was cleared already. */
+        callback(key);
+
+    } TREE_LOOP_END;
+
+    tree_free(callbacks);
+}
diff --git a/c7/stm/extra.h b/c7/stm/extra.h
new file mode 100644
--- /dev/null
+++ b/c7/stm/extra.h
@@ -0,0 +1,3 @@
+
+static void clear_callbacks_on_abort(void);
+static void invoke_and_clear_callbacks_on_abort(void);
diff --git a/c7/stm/setup.c b/c7/stm/setup.c
--- a/c7/stm/setup.c
+++ b/c7/stm/setup.c
@@ -59,6 +59,7 @@
         pr->modified_old_objects = list_create();
         pr->young_outside_nursery = tree_create();
         pr->nursery_objects_shadows = tree_create();
+        pr->callbacks_on_abort = tree_create();
         pr->overflow_number = GCFLAG_OVERFLOW_NUMBER_bit0 * (i + 1);
         highest_overflow_number = pr->overflow_number;
     }
@@ -96,6 +97,7 @@
         list_free(pr->modified_old_objects);
         tree_free(pr->young_outside_nursery);
         tree_free(pr->nursery_objects_shadows);
+        tree_free(pr->callbacks_on_abort);
     }
 
     munmap(stm_object_pages, TOTAL_MEMORY);
diff --git a/c7/stmgc.c b/c7/stmgc.c
--- a/c7/stmgc.c
+++ b/c7/stmgc.c
@@ -10,6 +10,7 @@
 #include "stm/largemalloc.h"
 #include "stm/nursery.h"
 #include "stm/contention.h"
+#include "stm/extra.h"
 #include "stm/fprintcolor.h"
 
 #include "stm/misc.c"
@@ -25,4 +26,5 @@
 #include "stm/hash_id.c"
 #include "stm/core.c"
 #include "stm/contention.c"
+#include "stm/extra.c"
 #include "stm/fprintcolor.c"
diff --git a/c7/stmgc.h b/c7/stmgc.h
--- a/c7/stmgc.h
+++ b/c7/stmgc.h
@@ -277,6 +277,13 @@
    otherwise.  After a minor collection no object can move any more. */
 long stm_can_move(object_t *);
 
+/* If the current transaction aborts later, invoke 'callback(key)'.  If
+   the current transaction commits, then the callback is forgotten.  You
+   can only register one callback per key.  You can call
+   'stm_call_on_abort(key, NULL)' to cancel an existing callback.
+   Note: 'key' must be aligned to a multiple of 8 bytes. */
+void stm_call_on_abort(void *key, void callback(void *));
+
 
 /* ==================== END ==================== */
 
diff --git a/c7/test/support.py b/c7/test/support.py
--- a/c7/test/support.py
+++ b/c7/test/support.py
@@ -81,6 +81,7 @@
 void stm_set_prebuilt_identityhash(object_t *obj, uint64_t hash);
 
 int stm_can_move(object_t *);
+void stm_call_on_abort(void *key, void callback(void *));
 """)
 
 
diff --git a/c7/test/test_extra.py b/c7/test/test_extra.py
--- a/c7/test/test_extra.py
+++ b/c7/test/test_extra.py
@@ -1,6 +1,15 @@
 from support import *
 import py
 
+def ffi_new_aligned(string):
+    ALIGN = ffi.sizeof("void *")
+    p1 = ffi.new("void *[]", (len(string) + ALIGN) // ALIGN)
+    p2 = ffi.gc(ffi.cast("char *", p1), lambda p2: p1)
+    p2[0:len(string)+1] = string + '\x00'
+    assert ffi.string(p2) == string
+    return p2
+
+
 class TestExtra(BaseTest):
 
     def test_clear_on_abort(self):
@@ -17,3 +26,45 @@
         assert p[2] == 'l'
         assert p[3] == 'l'
         assert p[4] == 'o'
+
+    def test_call_on_abort(self):
+        p0 = ffi_new_aligned("aaa")
+        p1 = ffi_new_aligned("hello")
+        p2 = ffi_new_aligned("removed")
+        p3 = ffi_new_aligned("world")
+        #
+        @ffi.callback("void(void *)")
+        def clear_me(p):
+            p = ffi.cast("char *", p)
+            p[0] = chr(ord(p[0]) + 1)
+        #
+        self.start_transaction()
+        lib.stm_call_on_abort(p0, clear_me)
+        # the registered callbacks are removed on
+        # successful commit
+        self.commit_transaction()
+        assert ffi.string(p0) == "aaa"
+        #
+        self.start_transaction()
+        lib.stm_call_on_abort(p1, clear_me)
+        lib.stm_call_on_abort(p2, clear_me)
+        lib.stm_call_on_abort(p3, clear_me)
+        lib.stm_call_on_abort(p2, ffi.NULL)
+        assert ffi.string(p0) == "aaa"
+        assert ffi.string(p1) == "hello"
+        assert ffi.string(p2) == "removed"
+        assert ffi.string(p3) == "world"
+        self.abort_transaction()
+        #
+        assert ffi.string(p0) == "aaa"
+        assert ffi.string(p1) == "iello"
+        assert ffi.string(p2) == "removed"
+        assert ffi.string(p3) == "xorld"
+        #
+        # the registered callbacks are removed on abort
+        self.start_transaction()
+        self.abort_transaction()
+        assert ffi.string(p0) == "aaa"
+        assert ffi.string(p1) == "iello"
+        assert ffi.string(p2) == "removed"
+        assert ffi.string(p3) == "xorld"
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to