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