Author: Armin Rigo <[email protected]>
Branch:
Changeset: r323:d97c6375a312
Date: 2013-06-30 19:36 +0200
http://bitbucket.org/pypy/stmgc/changeset/d97c6375a312/
Log: Tests for should_break_transaction() and set_transaction_length().
diff --git a/c4/et.c b/c4/et.c
--- a/c4/et.c
+++ b/c4/et.c
@@ -86,6 +86,8 @@
gcptr P = G;
revision_t v;
+ d->count_reads++;
+
restart_all:
if (P->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)
{
@@ -213,7 +215,6 @@
/* The risks are that the following assert fails, because the flag was
added just now by a parallel thread during stealing... */
/*assert(!(P->h_tid & GCFLAG_NURSERY_MOVED));*/
- d->count_reads++;
fxcache_add(&d->recent_reads_cache, P);
return P;
@@ -601,7 +602,6 @@
record_write_barrier(W);
}
- d->count_reads++;
spinlock_release(d->public_descriptor->collection_lock);
dprintf(("write_barrier: %p -> %p -> %p\n", P, R, W));
diff --git a/c4/stmgc.h b/c4/stmgc.h
--- a/c4/stmgc.h
+++ b/c4/stmgc.h
@@ -57,7 +57,8 @@
/* start a new transaction, calls callback(), and when it returns
finish that transaction. callback() is called with the 'arg'
provided, and with a retry_counter number. Must save roots around
- this call. */
+ this call. The callback() is called repeatedly as long as it
+ returns a value > 0. */
void stm_perform_transaction(gcptr arg, int (*callback)(gcptr, int));
/* finish the current transaction, start a new one, or turn the current
@@ -70,8 +71,11 @@
/* debugging: check if we're currently running a transaction or not. */
int stm_in_transaction(void);
-/* change the default transaction length */
+/* change the default transaction length, and ask if now would be a good
+ time to break the transaction (by returning from the 'callback' above
+ with a positive value). */
void stm_set_transaction_length(long length_max);
+_Bool stm_should_break_transaction(void);
/* callback: get the size of an object */
extern size_t stmcb_size(gcptr);
diff --git a/c4/stmsync.c b/c4/stmsync.c
--- a/c4/stmsync.c
+++ b/c4/stmsync.c
@@ -6,6 +6,7 @@
__thread gcptr *stm_shadowstack;
static unsigned long stm_regular_length_limit = 10000;
+static revision_t sync_required = 0;
void stm_set_transaction_length(long length_max)
{
@@ -16,6 +17,38 @@
stm_regular_length_limit = length_max;
}
+_Bool stm_should_break_transaction(void)
+{
+ struct tx_descriptor *d = thread_descriptor;
+
+ /* a single comparison to handle all cases:
+
+ - first, if sync_required == -1, this should return True.
+
+ - if d->atomic, then we should return False. This is done by
+ forcing reads_size_limit to ULONG_MAX as soon as atomic > 0.
+
+ - otherwise, if is_inevitable(), then we should return True.
+ This is done by forcing both reads_size_limit and
+ reads_size_limit_nonatomic to 0 in that case.
+
+ - finally, the default case: return True if d->count_reads is
+ greater than reads_size_limit == reads_size_limit_nonatomic.
+ */
+#ifdef _GC_DEBUG
+ /* reads_size_limit is ULONG_MAX if d->atomic, or else it is equal to
+ reads_size_limit_nonatomic. */
+ assert(d->reads_size_limit == (d->atomic ? ULONG_MAX :
+ d->reads_size_limit_nonatomic));
+ /* if is_inevitable(), reads_size_limit_nonatomic should be 0
+ (and thus reads_size_limit too, if !d->atomic.) */
+ if (d->active == 2)
+ assert(d->reads_size_limit_nonatomic == 0);
+#endif
+
+ return (sync_required | d->count_reads) >= d->reads_size_limit;
+}
+
static void init_shadowstack(void)
{
struct tx_descriptor *d = thread_descriptor;
@@ -67,8 +100,6 @@
/************************************************************/
-static revision_t sync_required = 0;
-
void stm_perform_transaction(gcptr arg, int (*callback)(gcptr, int))
{ /* must save roots around this call */
jmp_buf _jmpbuf;
@@ -235,7 +266,7 @@
which prevents any other thread from running in a transaction.
Warning, may block waiting for rwlock_in_transaction while another
thread runs a major GC itself! */
- ACCESS_ONCE(sync_required) = 1;
+ ACCESS_ONCE(sync_required) = -1;
stm_stop_sharedlock();
start_exclusivelock();
ACCESS_ONCE(sync_required) = 0;
diff --git a/c4/test/support.py b/c4/test/support.py
--- a/c4/test/support.py
+++ b/c4/test/support.py
@@ -61,6 +61,7 @@
void stm_commit_transaction(void);
void stm_begin_inevitable_transaction(void);
void stm_set_transaction_length(long length_max);
+ _Bool stm_should_break_transaction(void);
/* extra non-public code */
void printfcolor(char *msg);
@@ -665,5 +666,7 @@
assert (r % 4) == 0
return ffi.cast("gcptr", r)
+should_break_transaction = lib.stm_should_break_transaction
+
nrb_protected = ffi.cast("gcptr", -1)
diff --git a/c4/test/test_atomic.py b/c4/test/test_atomic.py
new file mode 100644
--- /dev/null
+++ b/c4/test/test_atomic.py
@@ -0,0 +1,33 @@
+import py
+from support import *
+
+
+def setup_function(f):
+ lib.stm_clear_between_tests()
+ lib.stm_initialize_tests(getattr(f, 'max_aborts', 0))
+
+def teardown_function(_):
+ lib.stm_finalize()
+
+
+def test_should_break_transaction():
+ # we're inevitable here, so:
+ assert should_break_transaction() == True
+ #
+ @perform_transaction
+ def run(retry_counter):
+ assert should_break_transaction() == False
+
+def test_set_transaction_length():
+ lib.stm_set_transaction_length(5) # breaks after 4 read-or-writes
+ plist = [palloc(HDR) for i in range(6)]
+ should_br = ['?'] * (len(plist) + 1)
+ #
+ @perform_transaction
+ def run(retry_counter):
+ should_br[0] = should_break_transaction()
+ for i in range(len(plist)):
+ lib.stm_write_barrier(plist[i])
+ should_br[i + 1] = should_break_transaction()
+ #
+ assert should_br == [False, False, False, False, True, True, True]
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit