Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r1620:756a54dd920b
Date: 2015-02-13 18:40 +0100
http://bitbucket.org/pypy/stmgc/changeset/756a54dd920b/

Log:    Found out how to cancel a become_globally_unique_transaction() while
        still keeping the current transaction inevitable --- by making it
        the next inevitable transaction atomically.

diff --git a/c7/stm/core.c b/c7/stm/core.c
--- a/c7/stm/core.c
+++ b/c7/stm/core.c
@@ -330,10 +330,11 @@
 
 static void _stm_start_transaction(stm_thread_local_t *tl)
 {
-    assert(!_stm_in_transaction(tl));
-
-    while (!acquire_thread_segment(tl))
-        ;
+    if (!will_start_inevitable) {
+        assert(!_stm_in_transaction(tl));
+        while (!acquire_thread_segment(tl))
+            ;
+    }
     /* GS invalid before this point! */
 
     assert(STM_PSEGMENT->safe_point == SP_NO_TRANSACTION);
@@ -350,10 +351,12 @@
     STM_PSEGMENT->shadowstack_at_start_of_transaction = tl->shadowstack;
     STM_PSEGMENT->threadlocal_at_start_of_transaction = tl->thread_local_obj;
 
-    enter_safe_point_if_requested();
-    dprintf(("start_transaction\n"));
+    if (!will_start_inevitable) {
+        enter_safe_point_if_requested();
+        dprintf(("start_transaction\n"));
 
-    s_mutex_unlock();
+        s_mutex_unlock();
+    }
 
     /* Now running the SP_RUNNING start.  We can set our
        'transaction_read_version' after releasing the mutex,
@@ -827,7 +830,8 @@
     stm_thread_local_t *tl = STM_SEGMENT->running_thread;
     timing_event(tl, event);
 
-    release_thread_segment(tl);
+    if (!will_start_inevitable)
+        release_thread_segment(tl);
     /* cannot access STM_SEGMENT or STM_PSEGMENT from here ! */
 }
 
@@ -847,6 +851,11 @@
 
     s_mutex_lock();
 
+    if (will_start_inevitable) {
+        assert(will_start_inevitable == 1);
+        assert(will_start_inevitable = 2);   /* 1 -> 2, only if !NDEBUG */
+    }
+
  restart:
     /* force all other threads to be paused.  They will unpause
        automatically when we are done here, i.e. at mutex_unlock().
@@ -905,6 +914,9 @@
     _finish_transaction(STM_TRANSACTION_COMMIT);
     /* cannot access STM_SEGMENT or STM_PSEGMENT from here ! */
 
+    if (will_start_inevitable)
+        return;    /* hack: return with the mutex still held */
+
     s_mutex_unlock();
 
     invoke_general_finalizers(tl);
@@ -1160,3 +1172,29 @@
     synchronize_all_threads(STOP_OTHERS_AND_BECOME_GLOBALLY_UNIQUE);
     s_mutex_unlock();
 }
+
+void stm_commit_and_start_inevitable(stm_thread_local_t *tl, const char *msg)
+{
+    stm_become_inevitable(tl, msg);   /* may still abort */
+
+    /* cannot abort any more from here */
+    will_start_inevitable = 1;
+
+    /* as long as 'will_start_inevitable' is true, we cannot release the
+       mutex_lock or do a cond_wait.  We must go through uninterrupted
+       with all the steps below.
+    */
+    stm_commit_transaction();
+    assert(will_start_inevitable == 2);
+    assert(_has_mutex());
+
+    _stm_start_transaction(tl);
+    assert(_has_mutex());
+
+    /* this line should be the only step from _stm_become_inevitable() we
+       must do in this case */
+    STM_PSEGMENT->transaction_state = TS_INEVITABLE;
+
+    will_start_inevitable = 0;
+    s_mutex_unlock();
+}
diff --git a/c7/stm/sync.c b/c7/stm/sync.c
--- a/c7/stm/sync.c
+++ b/c7/stm/sync.c
@@ -91,6 +91,7 @@
 static inline void s_mutex_unlock(void)
 {
     assert(_has_mutex_here);
+    assert(!will_start_inevitable);
     if (UNLIKELY(pthread_mutex_unlock(&sync_ctl.global_mutex) != 0))
         stm_fatalerror("pthread_mutex_unlock: %m");
     assert((_has_mutex_here = false, 1));
@@ -103,6 +104,7 @@
 #endif
 
     assert(_has_mutex_here);
+    assert(will_start_inevitable < 2);
     if (UNLIKELY(pthread_cond_wait(&sync_ctl.cond[ctype],
                                    &sync_ctl.global_mutex) != 0))
         stm_fatalerror("pthread_cond_wait/%d: %m", (int)ctype);
diff --git a/c7/stm/sync.h b/c7/stm/sync.h
--- a/c7/stm/sync.h
+++ b/c7/stm/sync.h
@@ -38,5 +38,6 @@
 static void committed_globally_unique_transaction(void);
 
 static bool pause_signalled, globally_unique_transaction;
+static uint8_t will_start_inevitable;
 
 void signal_other_to_commit_soon(struct stm_priv_segment_info_s *other_pseg);
diff --git a/c7/stmgc.h b/c7/stmgc.h
--- a/c7/stmgc.h
+++ b/c7/stmgc.h
@@ -408,6 +408,15 @@
 void stm_become_globally_unique_transaction(stm_thread_local_t *tl,
                                             const char *msg);
 
+/* Commit and start the next transaction as inevitable.  Provided the
+   commit succeeds, the next transaction is immediately made
+   inevitable: no other transaction can commit (or turn inevitable)
+   between the two steps.  The only reason to use this function
+   instead of stm_become_inevitable() is that the forced commit ends a
+   globally unique transaction.  If there is already another inevitable
+   transaction, this function will come stm_become_inevitable() first. */
+void stm_commit_and_start_inevitable(stm_thread_local_t *tl, const char *msg);
+
 
 /* Profiling events.  In the comments: content of the markers, if any */
 enum stm_event_e {
diff --git a/c7/test/support.py b/c7/test/support.py
--- a/c7/test/support.py
+++ b/c7/test/support.py
@@ -62,6 +62,7 @@
 bool _check_abort_transaction(void);
 bool _check_become_inevitable(stm_thread_local_t *tl);
 bool _check_become_globally_unique_transaction(stm_thread_local_t *tl);
+bool _check_commit_and_start_inevitable(stm_thread_local_t *tl);
 int stm_is_inevitable(void);
 long current_segment_num(void);
 
@@ -254,13 +255,17 @@
 }
 
 bool _check_become_inevitable(stm_thread_local_t *tl) {
-    CHECKED(stm_become_inevitable(tl, "TEST"));
+    CHECKED(stm_become_inevitable(tl, "TESTINEV"));
 }
 
 bool _check_become_globally_unique_transaction(stm_thread_local_t *tl) {
     CHECKED(stm_become_globally_unique_transaction(tl, "TESTGUT"));
 }
 
+bool _check_commit_and_start_inevitable(stm_thread_local_t *tl) {
+    CHECKED(stm_commit_and_start_inevitable(tl, "TESTCSI"));
+}
+
 object_t *hashtable_read_result;
 
 bool _check_hashtable_read(object_t *hobj, stm_hashtable_t *h, uintptr_t key)
@@ -781,3 +786,8 @@
         tl = self.tls[self.current_thread]
         if lib._check_become_globally_unique_transaction(tl):
             raise Conflict()
+
+    def commit_and_start_inevitable(self):
+        tl = self.tls[self.current_thread]
+        if lib._check_commit_and_start_inevitable(tl):
+            raise Conflict()
diff --git a/c7/test/test_random.py b/c7/test/test_random.py
--- a/c7/test/test_random.py
+++ b/c7/test/test_random.py
@@ -314,10 +314,14 @@
 ###################################################################
 
 
-def op_start_transaction(ex, global_state, thread_state):
+def op_start_transaction(ex, global_state, thread_state,
+                         commit_and_start_inevitable=False):
     thread_state.start_transaction(ex.thread_num)
     #
-    ex.do('self.start_transaction()')
+    if commit_and_start_inevitable:
+        ex.do('self.commit_and_start_inevitable()')
+    else:
+        ex.do('self.start_transaction()')
     thread_state.reload_roots(ex)
     #
     # assert that everything known is old:
@@ -326,7 +330,8 @@
         ex.do("assert not is_in_nursery(%s)" % o)
 
 
-def op_commit_transaction(ex, global_state, thread_state):
+def op_commit_transaction(ex, global_state, thread_state,
+                          commit_and_start_inevitable=False):
     #
     # push all new roots
     ex.do("# push new objs before commit:")
@@ -335,7 +340,8 @@
     #
     if aborts:
         thread_state.abort_transaction()
-    ex.do(raising_call(aborts, "self.commit_transaction"))
+    if not commit_and_start_inevitable:
+        ex.do(raising_call(aborts, "self.commit_transaction"))
 
 def op_abort_transaction(ex, global_state, thread_state):
     trs = thread_state.transaction_state
@@ -345,13 +351,17 @@
     thread_state.abort_transaction()
     ex.do('self.abort_transaction()')
 
-def op_become_inevitable(ex, global_state, thread_state):
+def op_become_inevitable(ex, global_state, thread_state,
+                         commit_and_start_inevitable=False):
     trs = thread_state.transaction_state
     global_state.check_if_can_become_inevitable(trs)
 
     thread_state.push_roots(ex)
-    ex.do(raising_call(trs.check_must_abort(),
-                       "self.become_inevitable"))
+    if commit_and_start_inevitable:
+        assert not trs.check_must_abort()
+    else:
+        ex.do(raising_call(trs.check_must_abort(),
+                        "self.become_inevitable"))
     if trs.check_must_abort():
         thread_state.abort_transaction()
     else:
@@ -359,6 +369,16 @@
         thread_state.pop_roots(ex)
         thread_state.reload_roots(ex)
 
+def op_commit_and_start_inevitable(ex, global_state, thread_state):
+    op_become_inevitable(ex, global_state, thread_state)
+    if thread_state.transaction_state is not None:
+        op_commit_transaction(ex, global_state, thread_state,
+                              commit_and_start_inevitable=True)
+        op_start_transaction(ex, global_state, thread_state,
+                             commit_and_start_inevitable=True)
+        op_become_inevitable(ex, global_state, thread_state,
+                             commit_and_start_inevitable=True)
+        assert thread_state.transaction_state is not None
 
 def op_allocate(ex, global_state, thread_state):
     size = global_state.rnd.choice([
@@ -584,6 +604,7 @@
             op_abort_transaction,
             op_forget_root,
             op_become_inevitable,
+            op_commit_and_start_inevitable,
             op_assert_size,
             op_assert_modified,
             op_minor_collect,
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to