Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r1319:1d2c771f29c4
Date: 2014-08-19 17:29 +0200
http://bitbucket.org/pypy/stmgc/changeset/1d2c771f29c4/

Log:    Add stm_call_on_commit(), for implementing free() of raw memory from
        non-inevitable transactions.

diff --git a/c7/stm/core.c b/c7/stm/core.c
--- a/c7/stm/core.c
+++ b/c7/stm/core.c
@@ -375,7 +375,8 @@
     assert(list_is_empty(STM_PSEGMENT->young_weakrefs));
     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(tree_is_cleared(STM_PSEGMENT->callbacks_on_commit_and_abort[0]));
+    assert(tree_is_cleared(STM_PSEGMENT->callbacks_on_commit_and_abort[1]));
     assert(STM_PSEGMENT->objects_pointing_to_nursery == NULL);
     assert(STM_PSEGMENT->large_overflow_objects == NULL);
 #ifndef NDEBUG
@@ -850,7 +851,7 @@
         STM_PSEGMENT->overflow_number_has_been_used = false;
     }
 
-    clear_callbacks_on_abort();
+    invoke_and_clear_user_callbacks(0);   /* for commit */
 
     /* send what is hopefully the correct signals */
     if (STM_PSEGMENT->transaction_state == TS_INEVITABLE) {
@@ -1044,7 +1045,7 @@
         memset(tl->mem_clear_on_abort, 0, tl->mem_bytes_to_clear_on_abort);
 
     /* invoke the callbacks */
-    invoke_and_clear_callbacks_on_abort();
+    invoke_and_clear_user_callbacks(1);   /* for abort */
 
     int attribute_to = STM_TIME_RUN_ABORTED_OTHER;
 
@@ -1101,7 +1102,7 @@
         wait_for_end_of_inevitable_transaction(NULL);
         STM_PSEGMENT->transaction_state = TS_INEVITABLE;
         stm_rewind_jmp_forget(STM_SEGMENT->running_thread);
-        clear_callbacks_on_abort();
+        invoke_and_clear_user_callbacks(0);   /* for commit */
     }
     else {
         assert(STM_PSEGMENT->transaction_state == TS_INEVITABLE);
diff --git a/c7/stm/core.h b/c7/stm/core.h
--- a/c7/stm/core.h
+++ b/c7/stm/core.h
@@ -132,8 +132,9 @@
        weakrefs never point to young objects and never contain NULL. */
     struct list_s *old_weakrefs;
 
-    /* Tree of 'key->callback' associations from stm_call_on_abort() */
-    struct tree_s *callbacks_on_abort;
+    /* Tree of 'key->callback' associations from stm_call_on_commit()
+       and stm_call_on_abort() (respectively, array items 0 and 1) */
+    struct tree_s *callbacks_on_commit_and_abort[2];
 
     /* Start time: to know approximately for how long a transaction has
        been running, in contention management */
diff --git a/c7/stm/extra.c b/c7/stm/extra.c
--- a/c7/stm/extra.c
+++ b/c7/stm/extra.c
@@ -3,55 +3,76 @@
 #endif
 
 
-void stm_call_on_abort(stm_thread_local_t *tl,
-                       void *key, void callback(void *))
+static bool register_callbacks(stm_thread_local_t *tl,
+                               void *key, void callback(void *), long index)
 {
     if (!_stm_in_transaction(tl)) {
         /* check that the current thread-local is really running a
            transaction, and do nothing otherwise. */
-        return;
+        return false;
     }
 
     if (STM_PSEGMENT->transaction_state == TS_INEVITABLE) {
         /* ignore callbacks if we're in an inevitable transaction
            (which cannot abort) */
-        return;
+        return false;
     }
 
+    struct tree_s *callbacks;
+    callbacks = STM_PSEGMENT->callbacks_on_commit_and_abort[index];
+
     if (callback == NULL) {
         /* ignore the return value: unregistered keys can be
            "deleted" again */
-        tree_delete_item(STM_PSEGMENT->callbacks_on_abort, (uintptr_t)key);
+        tree_delete_item(callbacks, (uintptr_t)key);
     }
     else {
         /* double-registering the same key will crash */
-        tree_insert(STM_PSEGMENT->callbacks_on_abort,
-                    (uintptr_t)key, (uintptr_t)callback);
+        tree_insert(callbacks, (uintptr_t)key, (uintptr_t)callback);
+    }
+    return true;
+}
+
+void stm_call_on_commit(stm_thread_local_t *tl,
+                       void *key, void callback(void *))
+{
+    if (!register_callbacks(tl, key, callback, 0)) {
+        /* no regular transaction running, invoke the callback
+           immediately */
+        callback(key);
     }
 }
 
-static void clear_callbacks_on_abort(void)
+void stm_call_on_abort(stm_thread_local_t *tl,
+                       void *key, void callback(void *))
 {
-    if (!tree_is_cleared(STM_PSEGMENT->callbacks_on_abort))
-        tree_clear(STM_PSEGMENT->callbacks_on_abort);
+    register_callbacks(tl, key, callback, 1);
 }
 
-static void invoke_and_clear_callbacks_on_abort(void)
+static void invoke_and_clear_user_callbacks(long index)
 {
-    wlog_t *item;
-    struct tree_s *callbacks = STM_PSEGMENT->callbacks_on_abort;
+    struct tree_s *callbacks;
+
+    /* clear the callbacks that we don't want to invoke at all */
+    callbacks = STM_PSEGMENT->callbacks_on_commit_and_abort[1 - index];
+    if (!tree_is_cleared(callbacks))
+        tree_clear(callbacks);
+
+    /* invoke the callbacks from the other group */
+    callbacks = STM_PSEGMENT->callbacks_on_commit_and_abort[index];
     if (tree_is_cleared(callbacks))
         return;
-    STM_PSEGMENT->callbacks_on_abort = tree_create();
+    STM_PSEGMENT->callbacks_on_commit_and_abort[index] = tree_create();
 
+    wlog_t *item;
     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. */
+        /* The callback may call stm_call_on_abort(key, NULL).  It is ignored,
+           because 'callbacks_on_commit_and_abort' was cleared already. */
         callback(key);
 
     } TREE_LOOP_END;
diff --git a/c7/stm/extra.h b/c7/stm/extra.h
--- a/c7/stm/extra.h
+++ b/c7/stm/extra.h
@@ -1,3 +1,3 @@
 
-static void clear_callbacks_on_abort(void);
-static void invoke_and_clear_callbacks_on_abort(void);
+static void invoke_and_clear_user_callbacks(long index);
+/* 0 = for commit, 1 = for abort */
diff --git a/c7/stm/setup.c b/c7/stm/setup.c
--- a/c7/stm/setup.c
+++ b/c7/stm/setup.c
@@ -126,7 +126,8 @@
         pr->old_weakrefs = list_create();
         pr->young_outside_nursery = tree_create();
         pr->nursery_objects_shadows = tree_create();
-        pr->callbacks_on_abort = tree_create();
+        pr->callbacks_on_commit_and_abort[0] = tree_create();
+        pr->callbacks_on_commit_and_abort[1] = tree_create();
         pr->overflow_number = GCFLAG_OVERFLOW_NUMBER_bit0 * i;
         highest_overflow_number = pr->overflow_number;
         pr->pub.transaction_read_version = 0xff;
@@ -166,7 +167,8 @@
         list_free(pr->old_weakrefs);
         tree_free(pr->young_outside_nursery);
         tree_free(pr->nursery_objects_shadows);
-        tree_free(pr->callbacks_on_abort);
+        tree_free(pr->callbacks_on_commit_and_abort[0]);
+        tree_free(pr->callbacks_on_commit_and_abort[1]);
     }
 
     munmap(stm_object_pages, TOTAL_MEMORY);
diff --git a/c7/stmgc.h b/c7/stmgc.h
--- a/c7/stmgc.h
+++ b/c7/stmgc.h
@@ -416,6 +416,12 @@
    Note: 'key' must be aligned to a multiple of 8 bytes. */
 void stm_call_on_abort(stm_thread_local_t *, void *key, void callback(void *));
 
+/* If the current transaction commits later, invoke 'callback(key)'.  If
+   the current transaction aborts, then the callback is forgotten.  Same
+   restrictions as stm_call_on_abort().  If the transaction is or becomes
+   inevitable, 'callback(key)' is called immediately. */
+void stm_call_on_commit(stm_thread_local_t *, void *key, void callback(void 
*));
+
 
 /* Similar to stm_become_inevitable(), but additionally suspend all
    other threads.  A very heavy-handed way to make sure that no other
diff --git a/c7/test/support.py b/c7/test/support.py
--- a/c7/test/support.py
+++ b/c7/test/support.py
@@ -111,6 +111,7 @@
 
 int stm_can_move(object_t *);
 void stm_call_on_abort(stm_thread_local_t *, void *key, void callback(void *));
+void stm_call_on_commit(stm_thread_local_t *, void *key, void callback(void 
*));
 
 #define STM_TIME_OUTSIDE_TRANSACTION ...
 #define STM_TIME_RUN_CURRENT ...
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
@@ -81,6 +81,85 @@
         self.abort_transaction()
         assert seen == []
 
+    def test_call_on_commit(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_commit(self.get_stm_thread_local(), p0, clear_me)
+        # the registered callbacks are not called on abort
+        self.abort_transaction()
+        assert ffi.string(p0) == "aaa"
+        #
+        self.start_transaction()
+        lib.stm_call_on_commit(self.get_stm_thread_local(), p1, clear_me)
+        lib.stm_call_on_commit(self.get_stm_thread_local(), p2, clear_me)
+        lib.stm_call_on_commit(self.get_stm_thread_local(), p3, clear_me)
+        lib.stm_call_on_commit(self.get_stm_thread_local(), p2, ffi.NULL)
+        assert ffi.string(p0) == "aaa"
+        assert ffi.string(p1) == "hello"
+        assert ffi.string(p2) == "removed"
+        assert ffi.string(p3) == "world"
+        self.commit_transaction()
+        #
+        assert ffi.string(p0) == "aaa"
+        assert ffi.string(p1) == "iello"
+        assert ffi.string(p2) == "removed"
+        assert ffi.string(p3) == "xorld"
+
+    def test_call_on_commit_immediately_if_inevitable(self):
+        p0 = ffi_new_aligned("aaa")
+        self.start_transaction()
+        self.become_inevitable()
+        #
+        @ffi.callback("void(void *)")
+        def clear_me(p):
+            p = ffi.cast("char *", p)
+            p[0] = chr(ord(p[0]) + 1)
+        #
+        lib.stm_call_on_commit(self.get_stm_thread_local(), p0, clear_me)
+        assert ffi.string(p0) == "baa"
+        self.commit_transaction()
+        assert ffi.string(p0) == "baa"
+
+    def test_call_on_commit_as_soon_as_inevitable(self):
+        p0 = ffi_new_aligned("aaa")
+        self.start_transaction()
+        #
+        @ffi.callback("void(void *)")
+        def clear_me(p):
+            p = ffi.cast("char *", p)
+            p[0] = chr(ord(p[0]) + 1)
+        #
+        lib.stm_call_on_commit(self.get_stm_thread_local(), p0, clear_me)
+        assert ffi.string(p0) == "aaa"
+        self.become_inevitable()
+        assert ffi.string(p0) == "baa"
+        self.commit_transaction()
+        assert ffi.string(p0) == "baa"
+
+    def test_call_on_commit_immediately_if_outside_transaction(self):
+        p0 = ffi_new_aligned("aaa")
+        #
+        @ffi.callback("void(void *)")
+        def clear_me(p):
+            p = ffi.cast("char *", p)
+            p[0] = chr(ord(p[0]) + 1)
+        #
+        lib.stm_call_on_commit(self.get_stm_thread_local(), p0, clear_me)
+        assert ffi.string(p0) == "baa"
+        self.start_transaction()
+        assert ffi.string(p0) == "baa"
+        self.commit_transaction()
+        assert ffi.string(p0) == "baa"
+
     def test_stm_become_globally_unique_transaction(self):
         self.start_transaction()
         #
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to