Author: Remi Meier <[email protected]>
Branch:
Changeset: r1306:d29906f937fc
Date: 2014-08-12 18:16 +0200
http://bitbucket.org/pypy/stmgc/changeset/d29906f937fc/
Log: add demo_random2 that includes returning from frames in normal
transactions
diff --git a/c7/demo/demo_random2.c b/c7/demo/demo_random2.c
new file mode 100644
--- /dev/null
+++ b/c7/demo/demo_random2.c
@@ -0,0 +1,532 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "stmgc.h"
+
+#define NUMTHREADS 3
+#define STEPS_PER_THREAD 500
+#define THREAD_STARTS 1000 // how many restarts of threads
+#define PREBUILT_ROOTS 3
+#define FORKS 3
+
+#define ACTIVE_ROOTS_SET_SIZE 100 // max num of roots created/alive in one
transaction
+
+
+// SUPPORT
+struct node_s;
+typedef TLPREFIX struct node_s node_t;
+typedef node_t* nodeptr_t;
+typedef object_t* objptr_t;
+int num_forked_children = 0;
+
+struct node_s {
+ struct object_s hdr;
+ int sig;
+ long my_size;
+ long my_id;
+ long my_hash;
+ nodeptr_t next;
+};
+
+#define SIGNATURE 0x01234567
+
+
+static sem_t done;
+__thread stm_thread_local_t stm_thread_local;
+__thread void *thread_may_fork;
+
+// global and per-thread-data
+time_t default_seed;
+objptr_t prebuilt_roots[PREBUILT_ROOTS];
+
+struct thread_data {
+ unsigned int thread_seed;
+ int steps_left;
+ objptr_t active_roots_set[ACTIVE_ROOTS_SET_SIZE];
+ int active_roots_num;
+ long roots_on_ss;
+ long roots_on_ss_at_tr_start;
+};
+__thread struct thread_data td;
+
+struct thread_data *_get_td(void)
+{
+ return &td; /* for gdb */
+}
+
+
+ssize_t stmcb_size_rounded_up(struct object_s *ob)
+{
+ return ((struct node_s*)ob)->my_size;
+}
+
+void stmcb_trace(struct object_s *obj, void visit(object_t **))
+{
+ struct node_s *n;
+ n = (struct node_s*)obj;
+
+ /* and the same value at the end: */
+ /* note, ->next may be the same as last_next */
+ nodeptr_t *last_next = (nodeptr_t*)((char*)n + n->my_size - sizeof(void*));
+
+ assert(n->next == *last_next);
+
+ visit((object_t **)&n->next);
+ visit((object_t **)last_next);
+
+ assert(n->next == *last_next);
+}
+
+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();
+}
+
+int get_rand(int max)
+{
+ if (max == 0)
+ return 0;
+ return (int)(rand_r(&td.thread_seed) % (unsigned int)max);
+}
+
+objptr_t get_random_root()
+{
+ /* get some root from shadowstack or active_root_set or prebuilt_roots */
+ int num = get_rand(3);
+ intptr_t ss_size = td.roots_on_ss;
+ if (num == 0 && ss_size > 0) {
+ num = get_rand(ss_size);
+ /* XXX: impl detail: there is already a "-1" on the SS -> +1 */
+ objptr_t r = (objptr_t)stm_thread_local.shadowstack_base[num+1].ss;
+ assert((((uintptr_t)r) & 3) == 0);
+ }
+
+ if (num == 1 && td.active_roots_num > 0) {
+ num = get_rand(td.active_roots_num);
+ return td.active_roots_set[num];
+ } else {
+ num = get_rand(PREBUILT_ROOTS);
+ return prebuilt_roots[num];
+ }
+}
+
+
+long push_roots()
+{
+ int i;
+ long to_push = td.active_roots_num;
+ for (i = to_push - 1; i >= 0; i--) {
+ STM_PUSH_ROOT(stm_thread_local, td.active_roots_set[i]);
+ td.roots_on_ss++;
+ td.active_roots_num--;
+ }
+ return to_push;
+}
+
+void pop_roots(long to_pop)
+{
+ int i;
+ for (i = 0; i < to_pop; i++) {
+ STM_POP_ROOT(stm_thread_local, td.active_roots_set[i]);
+ td.roots_on_ss--;
+ td.active_roots_num++;
+ assert(td.active_roots_num < ACTIVE_ROOTS_SET_SIZE);
+ }
+}
+
+void del_root(int idx)
+{
+ int i;
+
+ for (i = idx; i < td.active_roots_num - 1; i++)
+ td.active_roots_set[i] = td.active_roots_set[i + 1];
+ td.active_roots_num--;
+}
+
+void add_root(objptr_t r)
+{
+ if (r && td.active_roots_num < ACTIVE_ROOTS_SET_SIZE) {
+ td.active_roots_set[td.active_roots_num++] = r;
+ }
+}
+
+
+void read_barrier(objptr_t p)
+{
+ if (p != NULL) {
+ stm_read(p);
+ }
+}
+
+void write_barrier(objptr_t p)
+{
+ if (p != NULL) {
+ stm_write(p);
+ }
+}
+
+void set_next(objptr_t p, objptr_t v)
+{
+ if (p != NULL) {
+ nodeptr_t n = (nodeptr_t)p;
+
+ /* and the same value at the end: */
+ nodeptr_t TLPREFIX *last_next = (nodeptr_t TLPREFIX *)((stm_char*)n +
n->my_size - sizeof(void*));
+ assert(n->next == *last_next);
+ n->next = (nodeptr_t)v;
+ *last_next = (nodeptr_t)v;
+ }
+}
+
+nodeptr_t get_next(objptr_t p)
+{
+ nodeptr_t n = (nodeptr_t)p;
+
+ /* and the same value at the end: */
+ nodeptr_t TLPREFIX *last_next = (nodeptr_t TLPREFIX *)((stm_char*)n +
n->my_size - sizeof(void*));
+ OPT_ASSERT(n->next == *last_next);
+
+ return n->next;
+}
+
+
+objptr_t simple_events(objptr_t p, objptr_t _r)
+{
+ int k = get_rand(10);
+
+ switch (k) {
+ case 0: // remove a root
+ if (td.active_roots_num) {
+ del_root(get_rand(td.active_roots_num));
+ }
+ break;
+ case 1: // add 'p' to roots
+ add_root(p);
+ break;
+ case 2: // set 'p' to point to a root
+ if (_r)
+ p = _r;
+ break;
+ case 3: // allocate fresh 'p'
+ ;
+ long pushed = push_roots();
+ size_t sizes[4] = {sizeof(struct node_s),
+ sizeof(struct node_s) + (get_rand(100000) & ~15),
+ sizeof(struct node_s) + 4096,
+ sizeof(struct node_s) + 4096*70};
+ size_t size = sizes[get_rand(4)];
+ p = stm_allocate(size);
+ ((nodeptr_t)p)->sig = SIGNATURE;
+ ((nodeptr_t)p)->my_size = size;
+ ((nodeptr_t)p)->my_id = 0;
+ ((nodeptr_t)p)->my_hash = 0;
+ pop_roots(pushed);
+ break;
+ case 4: // read and validate 'p'
+ read_barrier(p);
+ break;
+ case 5: // only do a stm_write_barrier
+ write_barrier(p);
+ break;
+ case 6: // follow p->next
+ if (p) {
+ read_barrier(p);
+ p = (objptr_t)(get_next(p));
+ }
+ break;
+ case 7: // set 'p' as *next in one of the roots
+ write_barrier(_r);
+ set_next(_r, p);
+ break;
+ case 8: // id checking
+ if (p) {
+ nodeptr_t n = (nodeptr_t)p;
+ if (n->my_id == 0) {
+ write_barrier(p);
+ n->my_id = stm_id(p);
+ }
+ else {
+ read_barrier(p);
+ assert(n->my_id == stm_id(p));
+ }
+ }
+ break;
+ case 9:
+ if (p) {
+ nodeptr_t n = (nodeptr_t)p;
+ if (n->my_hash == 0) {
+ write_barrier(p);
+ n->my_hash = stm_identityhash(p);
+ }
+ else {
+ read_barrier(p);
+ assert(n->my_hash == stm_identityhash(p));
+ }
+ }
+ break;
+ }
+ return p;
+}
+
+
+void frame_loop();
+objptr_t do_step(objptr_t p)
+{
+ objptr_t _r;
+ int k;
+
+ _r = get_random_root();
+ k = get_rand(12);
+
+ if (k < 10) {
+ p = simple_events(p, _r);
+ } else if (get_rand(20) == 1) {
+ long pushed = push_roots();
+ stm_commit_transaction();
+ td.roots_on_ss_at_tr_start = td.roots_on_ss;
+
+ /* if (get_rand(100) < 98) { */
+ /* stm_start_transaction(&stm_thread_local); */
+ /* } else */{
+ stm_start_inevitable_transaction(&stm_thread_local);
+ }
+ td.roots_on_ss = td.roots_on_ss_at_tr_start;
+ td.active_roots_num = 0;
+ pop_roots(pushed);
+ return NULL;
+ } else if (get_rand(10) == 1) {
+ fprintf(stderr, "R");
+
+ long pushed = push_roots();
+ /* leaving our frame */
+ frame_loop();
+ /* back in our frame */
+ pop_roots(pushed);
+
+ fprintf(stderr, "r");
+ return NULL;
+ } else if (get_rand(20) == 1) {
+ long pushed = push_roots();
+ stm_become_inevitable(&stm_thread_local, "please");
+ assert(stm_is_inevitable());
+ pop_roots(pushed);
+ return NULL;
+ } else if (get_rand(200) == 1) {
+ return (objptr_t)-1; // possibly fork
+ } else if (get_rand(240) == 1) {
+ long pushed = push_roots();
+ stm_become_globally_unique_transaction(&stm_thread_local, "really");
+ fprintf(stderr, "[GUT/%d]", (int)STM_SEGMENT->segment_num);
+ pop_roots(pushed);
+ return NULL;
+ }
+ return p;
+}
+
+void frame_loop()
+{
+ objptr_t p = NULL;
+ rewind_jmp_buf rjbuf;
+
+ stm_rewind_jmp_enterframe(&stm_thread_local, &rjbuf);
+ volatile long roots_on_ss = td.roots_on_ss;
+ /* "interpreter main loop": this is one "application-frame" */
+ while (td.steps_left-->0 && get_rand(10) != 0) {
+ if (td.steps_left % 8 == 0)
+ fprintf(stdout, "#");
+
+ assert(p == NULL || ((nodeptr_t)p)->sig == SIGNATURE);
+
+ p = do_step(p);
+
+ if (p == (objptr_t)-1) {
+ p = NULL;
+
+ /* long call_fork = (thread_may_fork != NULL && *(long
*)thread_may_fork); */
+ /* if (call_fork) { /\* common case *\/ */
+ /* push_roots(); */
+ /* /\* run a fork() inside the transaction *\/ */
+ /* printf("========== FORK =========\n"); */
+ /* *(long*)thread_may_fork = 0; */
+ /* pid_t child = fork(); */
+ /* printf("=== in process %d thread %lx, fork() returned
%d\n", */
+ /* (int)getpid(), (long)pthread_self(), (int)child); */
+ /* if (child == -1) { */
+ /* fprintf(stderr, "fork() error: %m\n"); */
+ /* abort(); */
+ /* } */
+ /* if (child != 0) */
+ /* num_forked_children++; */
+ /* else */
+ /* num_forked_children = 0; */
+
+ /* pop_roots(); */
+ /* } */
+ }
+ }
+ assert(roots_on_ss == td.roots_on_ss);
+
+ stm_rewind_jmp_leaveframe(&stm_thread_local, &rjbuf);
+}
+
+
+
+void setup_thread()
+{
+ memset(&td, 0, sizeof(struct thread_data));
+
+ /* stupid check because gdb shows garbage
+ in td.roots: */
+ int i;
+ for (i = 0; i < ACTIVE_ROOTS_SET_SIZE; i++)
+ assert(td.active_roots_set[i] == NULL);
+
+ td.thread_seed = default_seed++;
+ td.steps_left = STEPS_PER_THREAD;
+ td.active_roots_num = 0;
+ td.roots_on_ss = 0;
+ td.roots_on_ss_at_tr_start = 0;
+}
+
+
+
+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();
+
+ td.roots_on_ss_at_tr_start = 0;
+ stm_start_transaction(&stm_thread_local);
+ td.roots_on_ss = td.roots_on_ss_at_tr_start;
+ td.active_roots_num = 0;
+
+ thread_may_fork = arg;
+ while (td.steps_left-->0) {
+ frame_loop();
+ }
+
+ 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);
+ return NULL;
+}
+
+void newthread(void*(*func)(void*), void *arg)
+{
+ pthread_t th;
+ int status = pthread_create(&th, NULL, func, arg);
+ if (status != 0)
+ abort();
+ pthread_detach(th);
+ printf("started new thread\n");
+}
+
+
+void setup_globals()
+{
+ int i;
+
+ struct node_s prebuilt_template = {
+ .sig = SIGNATURE,
+ .my_size = sizeof(struct node_s),
+ .my_id = 0,
+ .my_hash = 0,
+ .next = NULL
+ };
+
+ stm_start_inevitable_transaction(&stm_thread_local);
+ for (i = 0; i < PREBUILT_ROOTS; i++) {
+ void* new_templ = malloc(sizeof(struct node_s));
+ memcpy(new_templ, &prebuilt_template, sizeof(struct node_s));
+ prebuilt_roots[i] = stm_setup_prebuilt((objptr_t)(long)new_templ);
+
+ if (i % 2 == 0) {
+ int hash = i + 5;
+ stm_set_prebuilt_identityhash(prebuilt_roots[i],
+ hash);
+ ((nodeptr_t)prebuilt_roots[i])->my_hash = hash;
+ }
+ }
+ stm_commit_transaction();
+}
+
+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
+ threads is really random. */
+ default_seed = time(NULL);
+ printf("running with seed=%lld\n", (long long)default_seed);
+
+ 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);
+
+ setup_globals();
+
+ int thread_starts = NUMTHREADS * THREAD_STARTS;
+ for (i = 0; i < NUMTHREADS; i++) {
+ newthread(demo_random, NULL);
+ thread_starts--;
+ }
+
+ for (i=0; i < NUMTHREADS * THREAD_STARTS; i++) {
+ status = sem_wait(&done);
+ assert(status == 0);
+ printf("thread finished\n");
+ if (thread_starts) {
+ long forkbase = NUMTHREADS * THREAD_STARTS / (FORKS + 1);
+ long _fork = (thread_starts % forkbase) == 0;
+ thread_starts--;
+ newthread(demo_random, &_fork);
+ }
+ }
+
+ for (i = 0; i < num_forked_children; i++) {
+ pid_t child = wait(&status);
+ if (child == -1)
+ perror("wait");
+ printf("From %d: child %d terminated with exit status %d\n",
+ (int)getpid(), (int)child, status);
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+ ;
+ else {
+ printf("*** error from the child ***\n");
+ return 1;
+ }
+ }
+
+ printf("Test OK!\n");
+
+ stm_rewind_jmp_leaveframe(&stm_thread_local, &rjbuf);
+ stm_unregister_thread_local(&stm_thread_local);
+ stm_teardown();
+
+ return 0;
+}
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit