[pypy-commit] stmgc default: stm_clear_on_abort(): move the thread-local variables into the
arigo
noreply at buildbot.pypy.org
Sat Sep 7 18:12:27 CEST 2013
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r523:4c80cba2b8ce
Date: 2013-09-07 17:19 +0200
http://bitbucket.org/pypy/stmgc/changeset/4c80cba2b8ce/
Log: stm_clear_on_abort(): move the thread-local variables into the
tx_descriptor structure.
Add stm_call_on_abort() for invoking callbacks (like free()) if the
current transaction is aborted.
diff --git a/c4/et.c b/c4/et.c
--- a/c4/et.c
+++ b/c4/et.c
@@ -986,8 +986,12 @@
spinlock_release(d->public_descriptor->collection_lock);
/* clear memory registered by stm_clear_on_abort */
- if (stm_to_clear_on_abort)
- memset(stm_to_clear_on_abort, 0, stm_bytes_to_clear_on_abort);
+ if (d->mem_clear_on_abort)
+ memset(d->mem_clear_on_abort, 0, d->mem_bytes_to_clear_on_abort);
+
+ /* invoke the callbacks registered by stm_call_on_abort */
+ stm_invoke_callbacks_on_abort(d);
+ stm_clear_callbacks_on_abort(d);
dprintf(("\n"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
@@ -1482,6 +1486,10 @@
d->num_commits++;
d->active = 0;
stm_stop_sharedlock();
+
+ /* clear the list of callbacks that would have been called
+ on abort */
+ stm_clear_callbacks_on_abort(d);
}
/************************************************************/
diff --git a/c4/et.h b/c4/et.h
--- a/c4/et.h
+++ b/c4/et.h
@@ -183,6 +183,9 @@
struct tx_descriptor *tx_prev, *tx_next;
int tcolor;
pthread_t pthreadid;
+ void *mem_clear_on_abort;
+ size_t mem_bytes_to_clear_on_abort;
+ struct G2L callbacks_on_abort;
};
extern __thread struct tx_descriptor *thread_descriptor;
diff --git a/c4/extra.c b/c4/extra.c
--- a/c4/extra.c
+++ b/c4/extra.c
@@ -14,13 +14,46 @@
}
-__thread void *stm_to_clear_on_abort = NULL;
-__thread size_t stm_bytes_to_clear_on_abort;
-
void stm_clear_on_abort(void *start, size_t bytes)
{
- stm_to_clear_on_abort = start;
- stm_bytes_to_clear_on_abort = bytes;
+ struct tx_descriptor *d = thread_descriptor;
+ assert(d != NULL);
+ d->mem_clear_on_abort = start;
+ d->mem_bytes_to_clear_on_abort = bytes;
+}
+
+void stm_call_on_abort(void *key, void callback(void *))
+{
+ struct tx_descriptor *d = thread_descriptor;
+ if (callback == NULL) {
+ /* ignore the return value: unregistered keys can be
+ "deleted" again */
+ g2l_delete_item(&d->callbacks_on_abort, (gcptr)key);
+ }
+ else {
+ /* double-registering the same key will crash */
+ g2l_insert(&d->callbacks_on_abort, (gcptr)key, (gcptr)callback);
+ }
+}
+
+void stm_clear_callbacks_on_abort(struct tx_descriptor *d)
+{
+ if (g2l_any_entry(&d->callbacks_on_abort))
+ g2l_clear(&d->callbacks_on_abort);
+}
+
+void stm_invoke_callbacks_on_abort(struct tx_descriptor *d)
+{
+ wlog_t *item;
+ G2L_LOOP_FORWARD(d->callbacks_on_abort, item) {
+ void *key = (void *)item->addr;
+ void (*callback)(void *) = (void(*)(void *))item->val;
+ assert(key != NULL);
+ assert(callback != NULL);
+
+ callback(key);
+
+ } G2L_LOOP_END;
}
diff --git a/c4/extra.h b/c4/extra.h
--- a/c4/extra.h
+++ b/c4/extra.h
@@ -17,5 +17,7 @@
size_t stm_decode_abort_info(struct tx_descriptor *d, long long elapsed_time,
int abort_reason, struct tx_abort_info *output);
void stm_visit_abort_info(struct tx_descriptor *d, void (*visit)(gcptr *));
+void stm_clear_callbacks_on_abort(struct tx_descriptor *d);
+void stm_invoke_callbacks_on_abort(struct tx_descriptor *d);
#endif
diff --git a/c4/gcpage.c b/c4/gcpage.c
--- a/c4/gcpage.c
+++ b/c4/gcpage.c
@@ -207,7 +207,8 @@
//stm_dbgmem_not_used(obj, size_class * WORD, 0);
}
else {
- g2l_delete_item(&gcp->nonsmall_objects, obj);
+ int deleted = g2l_delete_item(&gcp->nonsmall_objects, obj);
+ assert(deleted);
stm_free(obj);
}
}
@@ -234,7 +235,8 @@
assert(obj->h_tid & GCFLAG_PUBLIC);
stmgcpage_acquire_global_lock();
- g2l_delete_item(®istered_stubs, obj);
+ int deleted = g2l_delete_item(®istered_stubs, obj);
+ assert(deleted);
stmgcpage_release_global_lock();
dprintf(("unregistered %p\n", obj));
}
diff --git a/c4/lists.c b/c4/lists.c
--- a/c4/lists.c
+++ b/c4/lists.c
@@ -132,15 +132,15 @@
*(char **)p = (char *)wlog;
}
-void g2l_delete_item(struct G2L *g2l, gcptr addr)
+int g2l_delete_item(struct G2L *g2l, gcptr addr)
{
wlog_t *entry;
G2L_FIND(*g2l, addr, entry, goto missing);
entry->addr = NULL;
- return;
+ return 1;
missing:
- stm_fatalerror("g2l_delete_item: item %p not in dict", addr);
+ return 0;
}
/************************************************************/
diff --git a/c4/lists.h b/c4/lists.h
--- a/c4/lists.h
+++ b/c4/lists.h
@@ -113,7 +113,7 @@
wlog_t *_g2l_find(char *entry, gcptr addr);
void _g2l_compress(struct G2L *g2l);
void g2l_insert(struct G2L *g2l, gcptr addr, gcptr val);
-void g2l_delete_item(struct G2L *g2l, gcptr addr);
+int g2l_delete_item(struct G2L *g2l, gcptr addr);
static inline int g2l_contains(struct G2L *g2l, gcptr addr)
{
diff --git a/c4/stmgc.h b/c4/stmgc.h
--- a/c4/stmgc.h
+++ b/c4/stmgc.h
@@ -175,10 +175,14 @@
/* Clear some memory when aborting a transaction in the current
thread. This is a provisional API. The information is stored
- thread-locally and belongs to the current thread. */
+ in the current tx_descriptor. */
void stm_clear_on_abort(void *start, size_t bytes);
-extern __thread void *stm_to_clear_on_abort;
-extern __thread size_t stm_bytes_to_clear_on_abort;
+
+/* 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. */
+void stm_call_on_abort(void *key, void callback(void *));
/* only user currently is stm_allocate_public_integer_address() */
void stm_register_integer_address(intptr_t);
diff --git a/c4/test/support.py b/c4/test/support.py
--- a/c4/test/support.py
+++ b/c4/test/support.py
@@ -128,6 +128,7 @@
void stm_initialize_tests(int max_aborts);
void stm_clear_on_abort(void *start, size_t bytes);
+ void stm_call_on_abort(void *key, void callback(void *));
/* some constants normally private that are useful in the tests */
#define WORD ...
diff --git a/c4/test/test_extra.py b/c4/test/test_extra.py
--- a/c4/test/test_extra.py
+++ b/c4/test/test_extra.py
@@ -269,3 +269,42 @@
assert p[2] == 'l'
assert p[3] == 'l'
assert p[4] == 'o'
+
+def test_call_on_abort():
+ p0 = ffi.new("char[]", "aaa")
+ p1 = ffi.new("char[]", "hello")
+ p2 = ffi.new("char[]", "removed")
+ p3 = ffi.new("char[]", "world")
+ #
+ @ffi.callback("void(void *)")
+ def clear_me(p):
+ p = ffi.cast("char *", p)
+ p[0] = chr(ord(p[0]) + 1)
+ #
+ lib.stm_call_on_abort(p0, clear_me)
+ # the registered callbacks are removed on
+ # successful commit
+ lib.stm_commit_transaction()
+ lib.stm_begin_inevitable_transaction()
+ #
+ @perform_transaction
+ def run(retry_counter):
+ if retry_counter == 0:
+ 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(p2) == "removed"
+ if retry_counter == 0:
+ assert ffi.string(p1) == "hello"
+ assert ffi.string(p3) == "world"
+ abort_and_retry()
+ else:
+ assert ffi.string(p1) == "iello"
+ assert ffi.string(p3) == "xorld"
+ if retry_counter == 1:
+ # the registered callbacks are removed
+ # on abort
+ abort_and_retry()
More information about the pypy-commit
mailing list