[pypy-commit] stmgc default: hg merge rewind_setjmp
arigo
noreply at buildbot.pypy.org
Sun Aug 10 18:00:35 CEST 2014
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r1295:bdc151305c79
Date: 2014-08-10 18:00 +0200
http://bitbucket.org/pypy/stmgc/changeset/bdc151305c79/
Log: hg merge rewind_setjmp
Add a custom layer around setjmp(), removing the restriction that we
can only longjmp() if the function calling setjmp() didn't return
yet.
diff --git a/c7/TODO b/c7/TODO
--- a/c7/TODO
+++ b/c7/TODO
@@ -24,3 +24,6 @@
- increase the memory limit, currently 2.5GB; this requires, apparently,
more fighting against LLVM bugs
+
+- avoid __builtin_frame_address(0) in precisely the performance-critical
+ functions like the interpreter main loop
diff --git a/c7/demo/demo2.c b/c7/demo/demo2.c
--- a/c7/demo/demo2.c
+++ b/c7/demo/demo2.c
@@ -71,9 +71,8 @@
{
nodeptr_t r_n;
long prev, sum;
- stm_jmpbuf_t here;
- STM_START_TRANSACTION(&stm_thread_local, here);
+ stm_start_transaction(&stm_thread_local);
stm_read((objptr_t)global_chained_list);
r_n = global_chained_list;
@@ -101,11 +100,9 @@
nodeptr_t swap_nodes(nodeptr_t initial)
{
- stm_jmpbuf_t here;
-
assert(initial != NULL);
- STM_START_TRANSACTION(&stm_thread_local, here);
+ stm_start_transaction(&stm_thread_local);
if (stm_thread_local.longest_marker_state != 0) {
fprintf(stderr, "[%p] marker %d for %.6f seconds:\n",
@@ -202,7 +199,7 @@
stm_commit_transaction();
- stm_start_inevitable_transaction(&stm_thread_local);
+ stm_start_transaction(&stm_thread_local);
STM_POP_ROOT(stm_thread_local, global_chained_list); /* update value */
assert(global_chained_list->value == -1);
STM_PUSH_ROOT(stm_thread_local, global_chained_list); /* remains forever in the shadow stack */
@@ -224,7 +221,9 @@
void *demo2(void *arg)
{
int status;
+ rewind_jmp_buf rjbuf;
stm_register_thread_local(&stm_thread_local);
+ stm_rewind_jmp_enterframe(&stm_thread_local, &rjbuf);
char *org = (char *)stm_thread_local.shadowstack;
STM_PUSH_ROOT(stm_thread_local, global_chained_list); /* remains forever in the shadow stack */
@@ -244,6 +243,7 @@
STM_POP_ROOT(stm_thread_local, global_chained_list);
OPT_ASSERT(org == (char *)stm_thread_local.shadowstack);
+ stm_rewind_jmp_leaveframe(&stm_thread_local, &rjbuf);
unregister_thread_local();
status = sem_post(&done); assert(status == 0);
return NULL;
@@ -280,11 +280,13 @@
int main(void)
{
int status, i;
+ rewind_jmp_buf rjbuf;
status = sem_init(&done, 0, 0); assert(status == 0);
stm_setup();
stm_register_thread_local(&stm_thread_local);
+ stm_rewind_jmp_enterframe(&stm_thread_local, &rjbuf);
stmcb_expand_marker = expand_marker;
@@ -302,6 +304,7 @@
final_check();
+ stm_rewind_jmp_leaveframe(&stm_thread_local, &rjbuf);
unregister_thread_local();
//stm_teardown();
diff --git a/c7/demo/demo_largemalloc.c b/c7/demo/demo_largemalloc.c
--- a/c7/demo/demo_largemalloc.c
+++ b/c7/demo/demo_largemalloc.c
@@ -25,6 +25,15 @@
void stmcb_commit_soon() {}
+void stmcb_trace_cards(struct object_s *obj, void cb(object_t **),
+ uintptr_t start, uintptr_t stop) {
+ abort();
+}
+void stmcb_get_card_base_itemsize(struct object_s *obj,
+ uintptr_t offset_itemsize[2]) {
+ abort();
+}
+
/************************************************************/
#define ARENA_SIZE (1024*1024*1024)
@@ -67,7 +76,7 @@
int i;
arena_data = malloc(ARENA_SIZE);
assert(arena_data != NULL);
- _stm_mutex_pages_lock();
+ //_stm_mutex_pages_lock();
for (i = 0; i < 25; i++)
timing(i);
return 0;
diff --git a/c7/demo/demo_random.c b/c7/demo/demo_random.c
--- a/c7/demo/demo_random.c
+++ b/c7/demo/demo_random.c
@@ -332,15 +332,15 @@
void *demo_random(void *arg)
{
int status;
+ rewind_jmp_buf rjbuf;
stm_register_thread_local(&stm_thread_local);
+ stm_rewind_jmp_enterframe(&stm_thread_local, &rjbuf);
setup_thread();
objptr_t p;
- stm_jmpbuf_t here;
- volatile int call_fork = (arg != NULL);
- STM_START_TRANSACTION(&stm_thread_local, here);
+ stm_start_transaction(&stm_thread_local);
assert(td.num_roots >= td.num_roots_at_transaction_start);
td.num_roots = td.num_roots_at_transaction_start;
p = NULL;
@@ -358,11 +358,12 @@
if (p == (objptr_t)-1) {
push_roots();
+ long call_fork = (arg != NULL && *(long *)arg);
if (call_fork == 0) { /* common case */
stm_commit_transaction();
td.num_roots_at_transaction_start = td.num_roots;
if (get_rand(100) < 98) {
- STM_START_TRANSACTION(&stm_thread_local, here);
+ stm_start_transaction(&stm_thread_local);
} else {
stm_start_inevitable_transaction(&stm_thread_local);
}
@@ -374,7 +375,7 @@
else {
/* run a fork() inside the transaction */
printf("========== FORK =========\n");
- call_fork = 0;
+ *(long*)arg = 0;
pid_t child = fork();
printf("=== in process %d thread %lx, fork() returned %d\n",
(int)getpid(), (long)pthread_self(), (int)child);
@@ -394,6 +395,7 @@
}
stm_commit_transaction();
+ stm_rewind_jmp_leaveframe(&stm_thread_local, &rjbuf);
stm_unregister_thread_local(&stm_thread_local);
status = sem_post(&done); assert(status == 0);
@@ -442,6 +444,7 @@
int main(void)
{
int i, status;
+ rewind_jmp_buf rjbuf;
/* pick a random seed from the time in seconds.
A bit pointless for now... because the interleaving of the
@@ -455,6 +458,7 @@
stm_setup();
stm_register_thread_local(&stm_thread_local);
+ stm_rewind_jmp_enterframe(&stm_thread_local, &rjbuf);
setup_globals();
@@ -472,7 +476,7 @@
long forkbase = NUMTHREADS * THREAD_STARTS / (FORKS + 1);
long _fork = (thread_starts % forkbase) == 0;
thread_starts--;
- newthread(demo_random, (void *)_fork);
+ newthread(demo_random, &_fork);
}
}
@@ -492,6 +496,7 @@
printf("Test OK!\n");
+ stm_rewind_jmp_leaveframe(&stm_thread_local, &rjbuf);
stm_unregister_thread_local(&stm_thread_local);
stm_teardown();
diff --git a/c7/demo/demo_simple.c b/c7/demo/demo_simple.c
--- a/c7/demo/demo_simple.c
+++ b/c7/demo/demo_simple.c
@@ -41,12 +41,20 @@
void stmcb_commit_soon() {}
+void stmcb_trace_cards(struct object_s *obj, void cb(object_t **),
+ uintptr_t start, uintptr_t stop) {
+ abort();
+}
+void stmcb_get_card_base_itemsize(struct object_s *obj,
+ uintptr_t offset_itemsize[2]) {
+ abort();
+}
static sem_t done;
static __thread int tl_counter = 0;
-static int gl_counter = 0;
+//static int gl_counter = 0;
void *demo2(void *arg)
{
diff --git a/c7/stm/core.c b/c7/stm/core.c
--- a/c7/stm/core.c
+++ b/c7/stm/core.c
@@ -324,14 +324,14 @@
STM_SEGMENT->transaction_read_version = 1;
}
-void _stm_start_transaction(stm_thread_local_t *tl, stm_jmpbuf_t *jmpbuf)
+static void _stm_start_transaction(stm_thread_local_t *tl, bool inevitable)
{
assert(!_stm_in_transaction(tl));
s_mutex_lock();
retry:
- if (jmpbuf == NULL) {
+ if (inevitable) {
wait_for_end_of_inevitable_transaction(tl);
}
@@ -346,11 +346,9 @@
STM_PSEGMENT->signalled_to_commit_soon = false;
STM_PSEGMENT->safe_point = SP_RUNNING;
STM_PSEGMENT->marker_inev[1] = 0;
- if (jmpbuf == NULL)
+ if (inevitable)
marker_fetch_inev();
- STM_PSEGMENT->transaction_state = (jmpbuf != NULL ? TS_REGULAR
- : TS_INEVITABLE);
- STM_SEGMENT->jmpbuf_ptr = jmpbuf;
+ STM_PSEGMENT->transaction_state = (inevitable ? TS_INEVITABLE : TS_REGULAR);
#ifndef NDEBUG
STM_PSEGMENT->running_pthread = pthread_self();
#endif
@@ -390,6 +388,22 @@
check_nursery_at_transaction_start();
}
+long stm_start_transaction(stm_thread_local_t *tl)
+{
+#ifdef STM_NO_AUTOMATIC_SETJMP
+ long repeat_count = 0; /* test/support.py */
+#else
+ long repeat_count = rewind_jmp_setjmp(&tl->rjthread);
+#endif
+ _stm_start_transaction(tl, false);
+ return repeat_count;
+}
+
+void stm_start_inevitable_transaction(stm_thread_local_t *tl)
+{
+ _stm_start_transaction(tl, true);
+}
+
/************************************************************/
@@ -814,7 +828,7 @@
dprintf(("commit_transaction\n"));
assert(STM_SEGMENT->nursery_end == NURSERY_END);
- STM_SEGMENT->jmpbuf_ptr = NULL;
+ rewind_jmp_forget(&STM_SEGMENT->running_thread->rjthread);
/* if a major collection is required, do it here */
if (is_major_collection_requested()) {
@@ -987,6 +1001,18 @@
#pragma pop_macro("STM_PSEGMENT")
}
+#ifdef STM_NO_AUTOMATIC_SETJMP
+void _test_run_abort(stm_thread_local_t *tl) __attribute__((noreturn));
+int stm_is_inevitable(void)
+{
+ switch (STM_PSEGMENT->transaction_state) {
+ case TS_REGULAR: return 0;
+ case TS_INEVITABLE: return 1;
+ default: abort();
+ }
+}
+#endif
+
static void abort_with_mutex(void)
{
assert(_has_mutex());
@@ -996,10 +1022,9 @@
abort_data_structures_from_segment_num(STM_SEGMENT->segment_num);
- stm_jmpbuf_t *jmpbuf_ptr = STM_SEGMENT->jmpbuf_ptr;
+ stm_thread_local_t *tl = STM_SEGMENT->running_thread;
/* clear memory registered on the thread-local */
- stm_thread_local_t *tl = STM_SEGMENT->running_thread;
if (tl->mem_clear_on_abort)
memset(tl->mem_clear_on_abort, 0, tl->mem_bytes_to_clear_on_abort);
@@ -1035,9 +1060,11 @@
*/
usleep(1);
- assert(jmpbuf_ptr != NULL);
- assert(jmpbuf_ptr != (stm_jmpbuf_t *)-1); /* for tests only */
- __builtin_longjmp(*jmpbuf_ptr, 1);
+#ifdef STM_NO_AUTOMATIC_SETJMP
+ _test_run_abort(tl);
+#else
+ rewind_jmp_longjmp(&tl->rjthread);
+#endif
}
void _stm_become_inevitable(const char *msg)
@@ -1051,12 +1078,11 @@
marker_fetch_inev();
wait_for_end_of_inevitable_transaction(NULL);
STM_PSEGMENT->transaction_state = TS_INEVITABLE;
- STM_SEGMENT->jmpbuf_ptr = NULL;
+ rewind_jmp_forget(&STM_SEGMENT->running_thread->rjthread);
clear_callbacks_on_abort();
}
else {
assert(STM_PSEGMENT->transaction_state == TS_INEVITABLE);
- assert(STM_SEGMENT->jmpbuf_ptr == NULL);
}
s_mutex_unlock();
diff --git a/c7/stm/forksupport.c b/c7/stm/forksupport.c
--- a/c7/stm/forksupport.c
+++ b/c7/stm/forksupport.c
@@ -176,14 +176,15 @@
static void fork_abort_thread(long i)
{
struct stm_priv_segment_info_s *pr = get_priv_segment(i);
+ stm_thread_local_t *tl = pr->pub.running_thread;
dprintf(("forksupport_child: abort in seg%ld\n", i));
- assert(pr->pub.running_thread->associated_segment_num == i);
+ assert(tl->associated_segment_num == i);
assert(pr->transaction_state == TS_REGULAR);
set_gs_register(get_segment_base(i));
- stm_jmpbuf_t jmpbuf;
- if (__builtin_setjmp(jmpbuf) == 0) {
- pr->pub.jmpbuf_ptr = &jmpbuf;
+ rewind_jmp_buf rjbuf;
+ stm_rewind_jmp_enterframe(tl, &rjbuf);
+ if (rewind_jmp_setjmp(&tl->rjthread) == 0) {
#ifndef NDEBUG
pr->running_pthread = pthread_self();
#endif
@@ -192,6 +193,8 @@
strcpy(pr->marker_self, "fork");
stm_abort_transaction();
}
+ rewind_jmp_forget(&tl->rjthread);
+ stm_rewind_jmp_leaveframe(tl, &rjbuf);
}
static void forksupport_child(void)
diff --git a/c7/stm/rewind_setjmp.c b/c7/stm/rewind_setjmp.c
new file mode 100644
--- /dev/null
+++ b/c7/stm/rewind_setjmp.c
@@ -0,0 +1,112 @@
+#include "rewind_setjmp.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <alloca.h>
+
+
+struct _rewind_jmp_moved_s {
+ struct _rewind_jmp_moved_s *next;
+ size_t size;
+};
+#define RJM_HEADER sizeof(struct _rewind_jmp_moved_s)
+
+#ifndef RJBUF_CUSTOM_MALLOC
+#define rj_malloc malloc
+#define rj_free free
+#else
+void *rj_malloc(size_t);
+void rj_free(void *);
+#endif
+
+
+static void copy_stack(rewind_jmp_thread *rjthread, char *base)
+{
+ assert(rjthread->head != NULL);
+ char *stop = rjthread->head->frame_base;
+ assert(stop > base);
+ struct _rewind_jmp_moved_s *next = (struct _rewind_jmp_moved_s *)
+ rj_malloc(RJM_HEADER + (stop - base));
+ assert(next != NULL); /* XXX out of memory */
+ next->next = rjthread->moved_off;
+ next->size = stop - base;
+ memcpy(((char *)next) + RJM_HEADER, base, stop - base);
+
+ rjthread->moved_off_base = stop;
+ rjthread->moved_off = next;
+}
+
+__attribute__((noinline))
+long rewind_jmp_setjmp(rewind_jmp_thread *rjthread)
+{
+ if (rjthread->moved_off) {
+ _rewind_jmp_free_stack_slices(rjthread);
+ }
+ rewind_jmp_thread *volatile rjthread1 = rjthread;
+ int result;
+ if (__builtin_setjmp(rjthread->jmpbuf) == 0) {
+ rjthread = rjthread1;
+ rjthread->initial_head = rjthread->head;
+ result = 0;
+ }
+ else {
+ rjthread = rjthread1;
+ rjthread->head = rjthread->initial_head;
+ result = rjthread->repeat_count + 1;
+ }
+ rjthread->repeat_count = result;
+ copy_stack(rjthread, (char *)&rjthread1);
+ return result;
+}
+
+__attribute__((noinline, noreturn))
+static void do_longjmp(rewind_jmp_thread *rjthread, char *stack_free)
+{
+ assert(rjthread->moved_off_base != NULL);
+
+ while (rjthread->moved_off) {
+ struct _rewind_jmp_moved_s *p = rjthread->moved_off;
+ char *target = rjthread->moved_off_base;
+ target -= p->size;
+ if (target < stack_free) {
+ /* need more stack space! */
+ do_longjmp(rjthread, alloca(stack_free - target));
+ }
+ memcpy(target, ((char *)p) + RJM_HEADER, p->size);
+ rjthread->moved_off_base = target;
+ rjthread->moved_off = p->next;
+ rj_free(p);
+ }
+ __builtin_longjmp(rjthread->jmpbuf, 1);
+}
+
+__attribute__((noreturn))
+void rewind_jmp_longjmp(rewind_jmp_thread *rjthread)
+{
+ char _rewind_jmp_marker;
+ do_longjmp(rjthread, &_rewind_jmp_marker);
+}
+
+__attribute__((noinline))
+void _rewind_jmp_copy_stack_slice(rewind_jmp_thread *rjthread)
+{
+ if (rjthread->head == NULL) {
+ _rewind_jmp_free_stack_slices(rjthread);
+ return;
+ }
+ assert(rjthread->moved_off_base < (char *)rjthread->head);
+ copy_stack(rjthread, rjthread->moved_off_base);
+}
+
+void _rewind_jmp_free_stack_slices(rewind_jmp_thread *rjthread)
+{
+ struct _rewind_jmp_moved_s *p = rjthread->moved_off;
+ struct _rewind_jmp_moved_s *pnext;
+ while (p) {
+ pnext = p->next;
+ rj_free(p);
+ p = pnext;
+ }
+ rjthread->moved_off = NULL;
+ rjthread->moved_off_base = NULL;
+}
diff --git a/c7/stm/rewind_setjmp.h b/c7/stm/rewind_setjmp.h
new file mode 100644
--- /dev/null
+++ b/c7/stm/rewind_setjmp.h
@@ -0,0 +1,82 @@
+#ifndef _REWIND_SETJMP_H_
+#define _REWIND_SETJMP_H_
+
+/************************************************************
+
+ : : ^^^^^
+ |-------------------| older frames in the stack
+ | prev=0 |
+ ,---> | rewind_jmp_buf |
+ | |-------------------|
+ | | |
+ | : :
+ | : :
+ | | |
+ | |-------------------|
+ `---------prev |
+ ,----> | rewind_jmp_buf |
+ | +-------------------|
+ | | |
+ | : :
+ | | |
+ | |-------------------|
+ `----------prev |
+ ,---> | rewind_jmp_buf | <--------------- MOVED_OFF_BASE
+ | |---------------- +-------------+
+ | | | STACK COPY |
+ | | : :
+ | : | size |
+ | | | next | <---- MOVED_OFF
+ | | +---|------ +-------------+
+ | | | | | STACK COPY |
+ | |-------------------| | : (SEQUEL) :
+ `---------prev | | : :
+HEAD-----> | rewind_jmp_buf | | | |
+ |-------------------| | | size |
+ `------> | next=0 |
+ +-------------+
+
+
+************************************************************/
+
+typedef struct _rewind_jmp_buf {
+ char *frame_base;
+ struct _rewind_jmp_buf *prev;
+} rewind_jmp_buf;
+
+typedef struct {
+ rewind_jmp_buf *head;
+ rewind_jmp_buf *initial_head;
+ char *moved_off_base;
+ struct _rewind_jmp_moved_s *moved_off;
+ void *jmpbuf[5];
+ long repeat_count;
+} rewind_jmp_thread;
+
+
+#define rewind_jmp_enterframe(rjthread, rjbuf) do { \
+ (rjbuf)->frame_base = __builtin_frame_address(0); \
+ (rjbuf)->prev = (rjthread)->head; \
+ (rjthread)->head = (rjbuf); \
+} while (0)
+
+#define rewind_jmp_leaveframe(rjthread, rjbuf) do { \
+ (rjthread)->head = (rjbuf)->prev; \
+ if ((rjbuf)->frame_base == (rjthread)->moved_off_base) \
+ _rewind_jmp_copy_stack_slice(rjthread); \
+} while (0)
+
+long rewind_jmp_setjmp(rewind_jmp_thread *rjthread);
+void rewind_jmp_longjmp(rewind_jmp_thread *rjthread) __attribute__((noreturn));
+
+#define rewind_jmp_forget(rjthread) do { \
+ if ((rjthread)->moved_off) _rewind_jmp_free_stack_slices(rjthread); \
+ (rjthread)->moved_off_base = 0; \
+} while (0)
+
+void _rewind_jmp_copy_stack_slice(rewind_jmp_thread *);
+void _rewind_jmp_free_stack_slices(rewind_jmp_thread *);
+
+#define rewind_jmp_armed(rjthread) ((rjthread)->moved_off_base != 0)
+
+#endif
diff --git a/c7/stmgc.c b/c7/stmgc.c
--- a/c7/stmgc.c
+++ b/c7/stmgc.c
@@ -36,3 +36,4 @@
#include "stm/weakref.c"
#include "stm/timing.c"
#include "stm/marker.c"
+#include "stm/rewind_setjmp.c"
diff --git a/c7/stmgc.h b/c7/stmgc.h
--- a/c7/stmgc.h
+++ b/c7/stmgc.h
@@ -13,6 +13,8 @@
#include <limits.h>
#include <unistd.h>
+#include "stm/rewind_setjmp.h"
+
#if LONG_MAX == 2147483647
# error "Requires a 64-bit environment"
#endif
@@ -25,7 +27,6 @@
typedef TLPREFIX struct stm_read_marker_s stm_read_marker_t;
typedef TLPREFIX struct stm_creation_marker_s stm_creation_marker_t;
typedef TLPREFIX char stm_char;
-typedef void* stm_jmpbuf_t[5]; /* for use with __builtin_setjmp() */
struct stm_read_marker_s {
/* In every segment, every object has a corresponding read marker.
@@ -44,7 +45,6 @@
stm_char *nursery_current;
uintptr_t nursery_end;
struct stm_thread_local_s *running_thread;
- stm_jmpbuf_t *jmpbuf_ptr;
};
#define STM_SEGMENT ((stm_segment_info_t *)4352)
@@ -79,6 +79,8 @@
typedef struct stm_thread_local_s {
/* every thread should handle the shadow stack itself */
struct stm_shadowentry_s *shadowstack, *shadowstack_base;
+ /* rewind_setjmp's interface */
+ rewind_jmp_thread rjthread;
/* a generic optional thread-local object */
object_t *thread_local_obj;
/* in case this thread runs a transaction that aborts,
@@ -114,7 +116,6 @@
object_t *_stm_allocate_slowpath(ssize_t);
object_t *_stm_allocate_external(ssize_t);
void _stm_become_inevitable(const char*);
-void _stm_start_transaction(stm_thread_local_t *, stm_jmpbuf_t *);
void _stm_collectable_safe_point(void);
/* for tests, but also used in duhton: */
@@ -326,40 +327,42 @@
void stm_register_thread_local(stm_thread_local_t *tl);
void stm_unregister_thread_local(stm_thread_local_t *tl);
+/* At some key places, like the entry point of the thread and in the
+ function with the interpreter's dispatch loop, you need to declare
+ a local variable of type 'rewind_jmp_buf' and call these macros. */
+#define stm_rewind_jmp_enterframe(tl, rjbuf) \
+ rewind_jmp_enterframe(&(tl)->rjthread, rjbuf)
+#define stm_rewind_jmp_leaveframe(tl, rjbuf) \
+ rewind_jmp_leaveframe(&(tl)->rjthread, rjbuf)
+
/* Starting and ending transactions. stm_read(), stm_write() and
stm_allocate() should only be called from within a transaction.
- Use the macro STM_START_TRANSACTION() to start a transaction that
- can be restarted using the 'jmpbuf' (a local variable of type
- stm_jmpbuf_t). */
-#define STM_START_TRANSACTION(tl, jmpbuf) ({ \
- while (__builtin_setjmp(jmpbuf) == 1) { /*redo setjmp*/ } \
- _stm_start_transaction(tl, &jmpbuf); \
-})
-
-/* Start an inevitable transaction, if it's going to return from the
- current function immediately. */
-static inline void stm_start_inevitable_transaction(stm_thread_local_t *tl) {
- _stm_start_transaction(tl, NULL);
-}
-
-/* Commit a transaction. */
+ The stm_start_transaction() call returns the number of times it
+ returned, starting at 0. If it is > 0, then the transaction was
+ aborted and restarted this number of times. */
+long stm_start_transaction(stm_thread_local_t *tl);
+void stm_start_inevitable_transaction(stm_thread_local_t *tl);
void stm_commit_transaction(void);
-/* Abort the currently running transaction. */
+/* Abort the currently running transaction. This function never
+ returns: it jumps back to the stm_start_transaction(). */
void stm_abort_transaction(void) __attribute__((noreturn));
-/* Turn the current transaction inevitable. The 'jmpbuf' passed to
- STM_START_TRANSACTION() is not going to be used any more after
- this call (but the stm_become_inevitable() itself may still abort). */
+/* Turn the current transaction inevitable.
+ The stm_become_inevitable() itself may still abort. */
+#ifdef STM_NO_AUTOMATIC_SETJMP
+int stm_is_inevitable(void);
+#else
+static inline int stm_is_inevitable(void) {
+ return !rewind_jmp_armed(&STM_SEGMENT->running_thread->rjthread);
+}
+#endif
static inline void stm_become_inevitable(stm_thread_local_t *tl,
const char* msg) {
assert(STM_SEGMENT->running_thread == tl);
- if (STM_SEGMENT->jmpbuf_ptr != NULL)
+ if (!stm_is_inevitable())
_stm_become_inevitable(msg);
}
-static inline int stm_is_inevitable(void) {
- return (STM_SEGMENT->jmpbuf_ptr == NULL);
-}
/* Forces a safe-point if needed. Normally not needed: this is
automatic if you call stm_allocate(). */
diff --git a/c7/test/support.py b/c7/test/support.py
--- a/c7/test/support.py
+++ b/c7/test/support.py
@@ -7,7 +7,6 @@
ffi = cffi.FFI()
ffi.cdef("""
typedef ... object_t;
-typedef ... stm_jmpbuf_t;
#define SIZEOF_MYOBJ ...
#define STM_NB_SEGMENTS ...
#define _STM_FAST_ALLOC ...
@@ -64,7 +63,8 @@
uintptr_t _stm_get_private_page(uintptr_t pagenum);
int _stm_get_flags(object_t *obj);
-void _stm_start_transaction(stm_thread_local_t *tl, stm_jmpbuf_t *jmpbuf);
+void clear_jmpbuf(stm_thread_local_t *tl);
+long stm_start_transaction(stm_thread_local_t *tl);
bool _check_commit_transaction(void);
bool _check_abort_transaction(void);
bool _check_become_inevitable(stm_thread_local_t *tl);
@@ -148,7 +148,7 @@
GC_N_SMALL_REQUESTS = 36 # from gcpage.c
LARGE_MALLOC_OVERHEAD = 16 # from largemalloc.h
-lib = ffi.verify('''
+lib = ffi.verify(r'''
#include <stdlib.h>
#include <string.h>
#include <assert.h>
@@ -167,23 +167,26 @@
return obj->stm_flags;
}
+void clear_jmpbuf(stm_thread_local_t *tl) {
+ memset(&tl->rjthread, 0, sizeof(rewind_jmp_thread));
+}
+
+__attribute__((noreturn))
+void _test_run_abort(stm_thread_local_t *tl) {
+ void **jmpbuf = tl->rjthread.jmpbuf;
+ fprintf(stderr, "~~~~~ ABORT ~~~~~\n");
+ __builtin_longjmp(jmpbuf, 1);
+}
+
#define CHECKED(CALL) \
- stm_jmpbuf_t here; \
- stm_segment_info_t *segment = STM_SEGMENT; \
- if (__builtin_setjmp(here) == 0) { /* returned directly */ \
- if (segment->jmpbuf_ptr != NULL) { \
- assert(segment->jmpbuf_ptr == (stm_jmpbuf_t *)-1); \
- segment->jmpbuf_ptr = &here; \
- } \
+ stm_thread_local_t *_tl = STM_SEGMENT->running_thread; \
+ void **jmpbuf = _tl->rjthread.jmpbuf; \
+ if (__builtin_setjmp(jmpbuf) == 0) { /* returned directly */\
CALL; \
- if (segment->jmpbuf_ptr != NULL) { \
- segment->jmpbuf_ptr = (stm_jmpbuf_t *)-1; \
- } \
+ clear_jmpbuf(_tl); \
return 0; \
} \
- if (segment->jmpbuf_ptr != NULL) { \
- segment->jmpbuf_ptr = (stm_jmpbuf_t *)-1; \
- } \
+ clear_jmpbuf(_tl); \
return 1
bool _checked_stm_write(object_t *object) {
@@ -350,6 +353,7 @@
}
''', sources=source_files,
define_macros=[('STM_TESTS', '1'),
+ ('STM_NO_AUTOMATIC_SETJMP', '1'),
('STM_LARGEMALLOC_TEST', '1'),
('STM_NO_COND_WAIT', '1'),
('STM_DEBUGPRINT', '1'),
@@ -559,7 +563,9 @@
def start_transaction(self):
tl = self.tls[self.current_thread]
assert not lib._stm_in_transaction(tl)
- lib._stm_start_transaction(tl, ffi.cast("stm_jmpbuf_t *", -1))
+ res = lib.stm_start_transaction(tl)
+ assert res == 0
+ lib.clear_jmpbuf(tl)
assert lib._stm_in_transaction(tl)
#
seen = set()
diff --git a/c7/test/test_basic.py b/c7/test/test_basic.py
--- a/c7/test/test_basic.py
+++ b/c7/test/test_basic.py
@@ -369,6 +369,7 @@
def test_inevitable_transaction_has_priority(self):
self.start_transaction()
+ assert lib.stm_is_inevitable() == 0
lp1 = stm_allocate(16)
stm_set_char(lp1, 'a')
self.push_root(lp1)
diff --git a/c7/test/test_rewind.c b/c7/test/test_rewind.c
new file mode 100644
--- /dev/null
+++ b/c7/test/test_rewind.c
@@ -0,0 +1,255 @@
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "rewind_setjmp.h"
+
+
+rewind_jmp_thread gthread;
+int gevents[1000];
+int num_gevents = 0;
+
+void gevent(int num)
+{
+ assert(num_gevents <= sizeof(gevents) / sizeof(int));
+ gevents[num_gevents++] = num;
+}
+
+void check_gevents(int expected[], int expected_size)
+{
+ int i;
+ int expected_count = expected_size / sizeof(int);
+ for (i = 0; i < expected_count && i < num_gevents; i++) {
+ assert(gevents[i] == expected[i]);
+ }
+ assert(num_gevents == expected_count);
+}
+
+#define CHECK(expected) check_gevents(expected, sizeof(expected))
+
+/************************************************************/
+
+__attribute__((noinline))
+void f1(int x)
+{
+ gevent(1);
+ if (x < 10) {
+ rewind_jmp_longjmp(>hread);
+ }
+}
+
+static int test1_x;
+
+void test1(void)
+{
+ rewind_jmp_buf buf;
+ rewind_jmp_enterframe(>hread, &buf);
+
+ test1_x = 0;
+ rewind_jmp_setjmp(>hread);
+
+ test1_x++;
+ f1(test1_x);
+
+ assert(test1_x == 10);
+ int expected[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+ CHECK(expected);
+
+ assert(rewind_jmp_armed(>hread));
+ rewind_jmp_forget(>hread);
+ assert(!rewind_jmp_armed(>hread));
+
+ rewind_jmp_leaveframe(>hread, &buf);
+}
+
+/************************************************************/
+
+static int test2_x;
+
+__attribute__((noinline))
+int f2(void)
+{
+ rewind_jmp_buf buf;
+ rewind_jmp_enterframe(>hread, &buf);
+ test2_x = 0;
+ rewind_jmp_setjmp(>hread);
+ rewind_jmp_leaveframe(>hread, &buf);
+ return ++test2_x;
+}
+
+void test2(void)
+{
+ rewind_jmp_buf buf;
+ rewind_jmp_enterframe(>hread, &buf);
+ int x = f2();
+ gevent(x);
+ if (x < 10)
+ rewind_jmp_longjmp(>hread);
+ rewind_jmp_leaveframe(>hread, &buf);
+ int expected[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ CHECK(expected);
+}
+
+/************************************************************/
+
+__attribute__((noinline))
+int f3(int rec)
+{
+ if (rec > 0)
+ return f3(rec - 1);
+ else
+ return f2();
+}
+
+void test3(void)
+{
+ rewind_jmp_buf buf;
+ rewind_jmp_enterframe(>hread, &buf);
+ int x = f3(50);
+ gevent(x);
+ if (x < 10)
+ rewind_jmp_longjmp(>hread);
+ rewind_jmp_leaveframe(>hread, &buf);
+ int expected[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ CHECK(expected);
+}
+
+/************************************************************/
+
+__attribute__((noinline))
+int f4(int rec)
+{
+ rewind_jmp_buf buf;
+ rewind_jmp_enterframe(>hread, &buf);
+ int res;
+ if (rec > 0)
+ res = f4(rec - 1);
+ else
+ res = f2();
+ rewind_jmp_leaveframe(>hread, &buf);
+ return res;
+}
+
+void test4(void)
+{
+ rewind_jmp_buf buf;
+ rewind_jmp_enterframe(>hread, &buf);
+ int x = f4(5);
+ gevent(x);
+ if (x < 10)
+ rewind_jmp_longjmp(>hread);
+ rewind_jmp_leaveframe(>hread, &buf);
+ int expected[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ CHECK(expected);
+}
+
+/************************************************************/
+
+void test5(void)
+{
+ struct { int a; rewind_jmp_buf buf; int b; } sbuf;
+ rewind_jmp_enterframe(>hread, &sbuf.buf);
+ sbuf.a = 42;
+ sbuf.b = -42;
+ test2_x = 0;
+ rewind_jmp_setjmp(>hread);
+ sbuf.a++;
+ sbuf.b--;
+ gevent(sbuf.a);
+ gevent(sbuf.b);
+ if (test2_x == 0) {
+ test2_x++;
+ rewind_jmp_longjmp(>hread);
+ }
+ int expected[] = {43, -43, 43, -43};
+ CHECK(expected);
+ rewind_jmp_leaveframe(>hread, &sbuf.buf);
+}
+
+/************************************************************/
+
+static int test6_x;
+
+__attribute__((noinline))
+void foo(int *x) { ++*x; }
+
+__attribute__((noinline))
+void f6(int a1, int a2, int a3, int a4, int a5, int a6, int a7,
+ int a8, int a9, int a10, int a11, int a12, int a13)
+{
+ rewind_jmp_buf buf;
+ rewind_jmp_enterframe(>hread, &buf);
+
+ rewind_jmp_setjmp(>hread);
+ gevent(a1); gevent(a2); gevent(a3); gevent(a4);
+ gevent(a5); gevent(a6); gevent(a7); gevent(a8);
+ gevent(a9); gevent(a10); gevent(a11); gevent(a12);
+ gevent(a13);
+ if (++test6_x < 4) {
+ foo(&a1);
+ foo(&a2);
+ foo(&a3);
+ foo(&a4);
+ foo(&a5);
+ foo(&a6);
+ foo(&a7);
+ foo(&a8);
+ foo(&a9);
+ foo(&a10);
+ foo(&a11);
+ foo(&a12);
+ foo(&a13);
+ rewind_jmp_longjmp(>hread);
+ }
+ rewind_jmp_leaveframe(>hread, &buf);
+}
+
+void test6(void)
+{
+ rewind_jmp_buf buf;
+ rewind_jmp_enterframe(>hread, &buf);
+ test6_x = 0;
+ f6(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
+ rewind_jmp_leaveframe(>hread, &buf);
+ int expected[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
+ CHECK(expected);
+}
+
+/************************************************************/
+
+int rj_malloc_count = 0;
+
+void *rj_malloc(size_t size)
+{
+ rj_malloc_count++;
+ void *ptr = malloc(size);
+ fprintf(stderr, "malloc(%ld) -> %p\n", (long)size, ptr);
+ return ptr;
+}
+
+void rj_free(void *ptr)
+{
+ if (ptr)
+ rj_malloc_count--;
+ fprintf(stderr, "free(%p)\n", ptr);
+ free(ptr);
+}
+
+
+int main(int argc, char *argv[])
+{
+ assert(argc > 1);
+ if (!strcmp(argv[1], "1")) test1();
+ else if (!strcmp(argv[1], "2")) test2();
+ else if (!strcmp(argv[1], "3")) test3();
+ else if (!strcmp(argv[1], "4")) test4();
+ else if (!strcmp(argv[1], "5")) test5();
+ else if (!strcmp(argv[1], "6")) test6();
+ else
+ assert(!"bad argv[1]");
+ assert(rj_malloc_count == 0);
+ return 0;
+}
diff --git a/c7/test/test_rewind.py b/c7/test/test_rewind.py
new file mode 100644
--- /dev/null
+++ b/c7/test/test_rewind.py
@@ -0,0 +1,19 @@
+import os
+
+def run_test(opt):
+ err = os.system("clang -g -O%d -Werror -DRJBUF_CUSTOM_MALLOC -I../stm"
+ " -o test_rewind_O%d test_rewind.c ../stm/rewind_setjmp.c"
+ % (opt, opt))
+ if err != 0:
+ raise OSError("clang failed on test_rewind.c")
+ for testnum in [1, 2, 3, 4, 5, 6]:
+ print '=== O%d: RUNNING TEST %d ===' % (opt, testnum)
+ err = os.system("./test_rewind_O%d %d" % (opt, testnum))
+ if err != 0:
+ raise OSError("'test_rewind_O%d %d' failed" % (opt, testnum))
+ os.unlink("./test_rewind_O%d" % (opt,))
+
+def test_O0(): run_test(0)
+def test_O1(): run_test(1)
+def test_O2(): run_test(2)
+def test_O3(): run_test(3)
diff --git a/duhton/transaction.c b/duhton/transaction.c
--- a/duhton/transaction.c
+++ b/duhton/transaction.c
@@ -162,8 +162,9 @@
void *run_thread(void *thread_id)
{
- stm_jmpbuf_t here;
+ rewind_jmp_buf rjbuf;
stm_register_thread_local(&stm_thread_local);
+ stm_rewind_jmp_enterframe(&stm_thread_local, &rjbuf);
TLOBJ = NULL;
@@ -176,7 +177,7 @@
TLOBJ = cell;
stm_commit_transaction(); /* inevitable */
- STM_START_TRANSACTION(&stm_thread_local, here);
+ stm_start_transaction(&stm_thread_local);
cell = TLOBJ;
TLOBJ = NULL;
@@ -187,6 +188,7 @@
}
stm_flush_timing(&stm_thread_local, 1);
+ stm_rewind_jmp_leaveframe(&stm_thread_local, &rjbuf);
stm_unregister_thread_local(&stm_thread_local);
return NULL;
More information about the pypy-commit
mailing list