Author: Armin Rigo <[email protected]>
Branch: c5
Changeset: r557:49446e74e137
Date: 2013-12-18 17:09 +0100
http://bitbucket.org/pypy/stmgc/changeset/49446e74e137/
Log: Initial import.
diff --git a/c5/Makefile b/c5/Makefile
new file mode 100644
--- /dev/null
+++ b/c5/Makefile
@@ -0,0 +1,10 @@
+
+H_FILES = core.h pagecopy.h
+C_FILES = core.c pagecopy.c
+
+
+demo1: demo1.c $(C_FILES) $(H_FILES)
+ gcc -o $@ -O2 -g demo1.c $(C_FILES) -Wall
+
+clean:
+ rm -f demo1
diff --git a/c5/core.c b/c5/core.c
new file mode 100644
--- /dev/null
+++ b/c5/core.c
@@ -0,0 +1,537 @@
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+
+#include "core.h"
+#include "pagecopy.h"
+
+
+/* This file only works on 64-bit Linux for now. The logic is based on
+ remapping pages around, which can get a bit confusing. Each "thread"
+ runs in its own process, so that it has its own mapping. The
+ processes share an mmap of length NB_PAGES, which is created shared
+ but anonymous, and passed to subprocesses by forking.
+
+ The mmap's content does not depend on which process is looking at it:
+ it contains what we'll call "mm pages", which is 4096 bytes of data
+ at some file offset (which all processes agree on). The term "pgoff"
+ used below means such an offset. It is a uint32_t expressed in units
+ of 4096 bytes; so the underlying mmap is limited to 2**32 pages or
+ 16TB.
+
+ The mm pages are then mapped in each process at some address, and
+ their content is accessed with regular pointers. We'll call such a
+ page a "local page". The term "local" is used because each process
+ has its own, different mapping. As it turns out, mm pages are
+ initially mapped sequentially as local pages, but this changes over
+ time. To do writes in a transaction, the data containing the object
+ is first duplicated --- so we allocate a fresh new mm page in the
+ mmap file, and copy the contents to it. Then we remap the new mm
+ page over the *same* local page as the original. So from this
+ process' point of view, the object is still at the same address, but
+ writes to it now happen to go to the new mm page instead of the old
+ one.
+
+ The local pages are usually referenced by pointers, but may also be
+ expressed as an index, called the "local index" of the page.
+*/
+
+#ifdef STM_TESTS
+# define NB_PAGES (256*10) // 10MB
+#else
+# define NB_PAGES (256*1024) // 1GB
+#endif
+#define MAP_PAGES_FLAGS (MAP_SHARED|MAP_ANONYMOUS)
+
+#define CACHE_LINE_SIZE 128 // conservatively large value to avoid aliasing
+
+#define PGKIND_NEVER_USED 0
+#define LARGE_OBJECT_WORDS 36 /* range(2, LARGE_OBJECT_WORDS) */
+#define PGKIND_FREED 0xff
+#define PGKIND_WRITE_HISTORY 0xfe
+#define PGKIND_SHARED_DESCRIPTOR 0xfd /* only for the first mm page */
+
+struct page_header_s {
+ /* Every page starts with one such structure */
+ uint16_t version; /* when the data in the page was written */
+ uint8_t modif_head; /* head of a chained list of objects in this
+ page that have modified == this->version */
+ uint8_t kind; /* either PGKIND_xxx or a number in
+ range(2, LARGE_OBJECT_WORDS) */
+ uint32_t pgoff; /* the mm page offset */
+};
+
+struct read_marker_s {
+ /* We associate a single byte to every object, by simply dividing
+ the address of the object by 16. This is the last byte of the
+ last time we have read the object. See stm_read(). */
+ unsigned char c;
+};
+
+struct write_history_s {
+ struct write_history_s *previous_older_transaction;
+ uint16_t transaction_version;
+ uint32_t nb_updates;
+ uint32_t updates[]; /* pairs (local_index, new_pgoff) */
+};
+
+struct shared_descriptor_s {
+ /* There is a single shared descriptor. This regroups all data
+ that needs to be dynamically shared among processes. The
+ first mm page is used for this. */
+ union {
+ struct page_header_s header;
+ char _pad0[CACHE_LINE_SIZE];
+ };
+ union {
+ uint64_t index_page_never_used;
+ char _pad1[CACHE_LINE_SIZE];
+ };
+ union {
+ unsigned int next_transaction_version;
+ char _pad2[CACHE_LINE_SIZE];
+ };
+ union {
+ struct write_history_s *most_recent_committed_transaction;
+ char _pad3[CACHE_LINE_SIZE];
+ };
+};
+
+struct alloc_for_size_s {
+ char *next;
+ char *end;
+};
+
+struct local_data_s {
+ /* This is just a bunch of global variables, but during testing,
+ we save it all away and restore different ones to simulate
+ different forked processes. */
+ char *read_markers;
+ struct read_marker_s *current_read_markers;
+ uint16_t transaction_version;
+ struct write_history_s *base_page_mapping;
+ struct write_history_s *writes_by_this_transaction;
+ struct alloc_for_size_s alloc[LARGE_OBJECT_WORDS];
+};
+
+struct shared_descriptor_s *stm_shared_descriptor;
+struct local_data_s stm_local;
+
+
+void stm_read(struct object_s *object)
+{
+ stm_local.current_read_markers[((uintptr_t)object) >> 4].c =
+ (unsigned char)(uintptr_t)stm_local.current_read_markers;
+}
+
+_Bool _stm_was_read(struct object_s *object)
+{
+ return (stm_local.current_read_markers[((uintptr_t)object) >> 4].c ==
+ (unsigned char)(uintptr_t)stm_local.current_read_markers);
+}
+
+void _stm_write_slowpath(struct object_s *);
+
+void stm_write(struct object_s *object)
+{
+ if (__builtin_expect(object->modified != stm_local.transaction_version,
+ 0))
+ _stm_write_slowpath(object);
+}
+
+_Bool _stm_was_written(struct object_s *object)
+{
+ return (object->modified == stm_local.transaction_version);
+}
+
+
+struct page_header_s *_stm_reserve_page(void)
+{
+ /* Grab a free mm page, and map it into the address space.
+ Return a pointer to it. It has kind == PGKIND_FREED. */
+
+ // XXX look in some free list first
+
+ /* Return the index'th mm page, which is so far NEVER_USED. It
+ should never have been accessed so far, and be already mapped
+ as the index'th local page. */
+ struct shared_descriptor_s *d = stm_shared_descriptor;
+ uint64_t index = __sync_fetch_and_add(&d->index_page_never_used, 1);
+ if (index >= NB_PAGES) {
+ fprintf(stderr, "Out of mmap'ed memory!\n");
+ abort();
+ }
+ struct page_header_s *result = (struct page_header_s *)
+ (((char *)stm_shared_descriptor) + index * 4096);
+ assert(result->kind == PGKIND_NEVER_USED);
+ result->kind = PGKIND_FREED;
+ result->pgoff = index;
+ return result;
+}
+
+
+static uint32_t get_pgoff(struct page_header_s *page)
+{
+ assert(page->pgoff > 0);
+ assert(page->pgoff < NB_PAGES);
+ return page->pgoff;
+}
+
+static uint32_t get_local_index(struct page_header_s *page)
+{
+ uint64_t index = ((char *)page) - (char *)stm_shared_descriptor;
+ assert((index & 4095) == 0);
+ index /= 4096;
+ assert(0 < index && index < NB_PAGES);
+ return index;
+}
+
+static struct page_header_s *get_page_by_local_index(uint32_t index)
+{
+ assert(0 < index && index < NB_PAGES);
+ uint64_t ofs = ((uint64_t)index) * 4096;
+ return (struct page_header_s *)(((char *)stm_shared_descriptor) + ofs);
+}
+
+void _stm_write_slowpath(struct object_s * object)
+{
+ stm_read(object);
+
+ struct page_header_s *page;
+ page = (struct page_header_s *)(((uintptr_t)object) & ~4095);
+ assert(2 <= page->kind && page->kind < LARGE_OBJECT_WORDS);
+
+ if (page->version != stm_local.transaction_version) {
+ struct page_header_s *newpage = _stm_reserve_page();
+ uint32_t old_pgoff = get_pgoff(page);
+ uint32_t new_pgoff = get_pgoff(newpage);
+
+ pagecopy(newpage, page);
+ newpage->version = stm_local.transaction_version;
+ newpage->modif_head = 0xff;
+ newpage->pgoff = new_pgoff;
+ assert(page->version != stm_local.transaction_version);
+ assert(page->pgoff == old_pgoff);
+
+ remap_file_pages((void *)page, 4096, 0, new_pgoff, MAP_PAGES_FLAGS);
+
+ assert(page->version == stm_local.transaction_version);
+ assert(page->pgoff == new_pgoff);
+
+ struct write_history_s *cur = stm_local.writes_by_this_transaction;
+ uint64_t i = cur->nb_updates++;
+ size_t history_size_max = 4096 - (((uintptr_t)cur) & 4095);
+ assert(sizeof(*cur) + cur->nb_updates * 8 <= history_size_max);
+ cur->updates[i * 2 + 0] = get_local_index(page);
+ cur->updates[i * 2 + 1] = new_pgoff;
+ }
+ object->modified = stm_local.transaction_version;
+ object->modif_next = page->modif_head;
+ page->modif_head = (uint8_t)(((uintptr_t)object) >> 4);
+ assert(page->modif_head != 0xff);
+}
+
+char *_stm_alloc_next_page(size_t i)
+{
+ struct page_header_s *newpage = _stm_reserve_page();
+ newpage->modif_head = 0xff;
+ newpage->kind = i; /* object size in words */
+ newpage->version = stm_local.transaction_version;
+ stm_local.alloc[i].next = ((char *)(newpage + 1)) + (i * 8);
+ stm_local.alloc[i].end = ((char *)newpage) + 4096;
+ assert(stm_local.alloc[i].next <= stm_local.alloc[i].end);
+ return (char *)(newpage + 1);
+}
+
+struct object_s *stm_allocate(size_t size)
+{
+ assert(size % 8 == 0);
+ size_t i = size / 8;
+ assert(2 <= i && i < LARGE_OBJECT_WORDS);
+ struct alloc_for_size_s *alloc = &stm_local.alloc[i];
+
+ char *p = alloc->next;
+ alloc->next += size;
+ if (alloc->next > alloc->end)
+ p = _stm_alloc_next_page(i);
+
+ struct object_s *result = (struct object_s *)p;
+ result->modified = stm_local.transaction_version;
+ /*result->modif_next is uninitialized*/
+ result->flags = 0x42; /* for debugging */
+ return result;
+}
+
+
+unsigned char stm_get_read_marker_number(void)
+{
+ return (unsigned char)(uintptr_t)stm_local.current_read_markers;
+}
+
+void stm_set_read_marker_number(uint8_t num)
+{
+ char *stm_pages = ((char *)stm_shared_descriptor) + 4096;
+ uintptr_t delta = ((uintptr_t)stm_pages) >> 4;
+ struct read_marker_s *crm = (struct read_marker_s *)stm_local.read_markers;
+ stm_local.current_read_markers = crm - delta;
+ assert(stm_get_read_marker_number() == 0);
+ stm_local.current_read_markers += num;
+}
+
+void stm_setup(void)
+{
+ if (sizeof(char *) != 8) {
+ fprintf(stderr, "Only works on 64-bit Linux systems for now!\n");
+ abort();
+ }
+ if (NB_PAGES > (1ull << 32)) {
+ fprintf(stderr, "Cannot use more than 1<<32 pages of memory");
+ abort();
+ }
+ char *stm_pages = mmap(NULL, NB_PAGES*4096, PROT_READ|PROT_WRITE,
+ MAP_PAGES_FLAGS, -1, 0);
+ if (stm_pages == MAP_FAILED) {
+ perror("mmap stm_pages failed");
+ abort();
+ }
+ assert(sizeof(struct shared_descriptor_s) <= 4096);
+ stm_shared_descriptor = (struct shared_descriptor_s *)stm_pages;
+ stm_shared_descriptor->header.kind = PGKIND_SHARED_DESCRIPTOR;
+ /* the page at index 0 contains the '*stm_shared_descriptor' structure */
+ /* the page at index 1 is reserved for history_fast_forward() */
+ stm_shared_descriptor->index_page_never_used = 2;
+ stm_shared_descriptor->next_transaction_version = 1;
+}
+
+void _stm_teardown(void)
+{
+ munmap((void *)stm_shared_descriptor, NB_PAGES*4096);
+ stm_shared_descriptor = NULL;
+}
+
+void stm_setup_process(void)
+{
+ memset(&stm_local, 0, sizeof(stm_local));
+ stm_local.read_markers = mmap(NULL, NB_PAGES*(4096 >> 4) + 1,
+ PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS,
+ -1, 0);
+ if (stm_local.read_markers == MAP_FAILED) {
+ perror("mmap stm_read_markers failed");
+ abort();
+ }
+
+ stm_set_read_marker_number(42);
+ assert(stm_get_read_marker_number() == 42);
+ stm_set_read_marker_number(1);
+}
+
+void _stm_teardown_process(void)
+{
+ munmap((void *)stm_local.read_markers, NB_PAGES*(4096 >> 4) + 1);
+ memset(&stm_local, 0, sizeof(stm_local));
+}
+
+static size_t get_obj_size_in_words(struct page_header_s *page)
+{
+ size_t result = page->kind;
+ assert(2 <= result && result < LARGE_OBJECT_WORDS);
+ return result;
+}
+
+static
+struct object_s *get_object_in_page(struct page_header_s *page, size_t index)
+{
+ /* Slight complication here, because objects are aligned to 8 bytes,
+ but we divides their page offset by 16 to fit a byte (4096/16 =
+ 256) and reduce memory overhead of the read markers. Objects are
+ at least 16 bytes in size, so there is no ambiguity. Example for
+ objects of 24 bytes of the organization inside a page (each word
+ of the first line is 8 bytes):
+
+ [HDR][OBJ.ECT.24][OBJ.ECT.24][OBJ.ECT.24][OBJ.ECT.24][..
+ 0 (16) 32 48 (64) 80 96
+
+ The second line is all possible offsets, which are multiples of
+ 16. They are the rounded-down version of the real offsets.
+ object and round it down to a mutiple of 16. For objects of size
+ 24, the numbers in parenthesis above are not reachable this way.
+ The number 255 is never reachable. To go from the number to the
+ object address, we have to add either 0 or 8.
+ */
+ size_t obj_size_in_words = get_obj_size_in_words(page);
+ size_t offset = (index << 4) +
+ ((index << 1) % obj_size_in_words == 0 ? 8 : 0);
+ return (struct object_s *)(((char *)page) + offset);
+}
+
+static int history_fast_forward(struct write_history_s *new, int conflict)
+{
+ /* XXX do a non-recursive version, which also should avoid repeated
+ remap_file_pages() on the same local-index-ed page */
+ if (stm_local.base_page_mapping != new->previous_older_transaction) {
+ conflict = history_fast_forward(new->previous_older_transaction,
+ conflict);
+ }
+ assert(stm_local.base_page_mapping == new->previous_older_transaction);
+
+ uint64_t i, nb_updates = new->nb_updates;
+ for (i = 0; i < nb_updates; i++) {
+ retry:;
+ /* new->updates[] is an array of pairs (local_index, new_pgoff) */
+ uint32_t local_index = new->updates[i * 2 + 0];
+ uint32_t new_pgoff = new->updates[i * 2 + 1];
+ struct page_header_s *page = get_page_by_local_index(local_index);
+ struct page_header_s *mypage = page;
+
+ if (!conflict && page->version == stm_local.transaction_version) {
+ /* If we have also modified this page, then we must merge our
+ changes with the ones done at 'new_pgoff'. In this case
+ we map 'new_pgoff' at the local index 1. */
+ page = get_page_by_local_index(1);
+ }
+
+ remap_file_pages((void *)page, 4096, 0, new_pgoff, MAP_PAGES_FLAGS);
+ assert(page->pgoff == new_pgoff);
+
+ if (conflict)
+ continue;
+
+ /* look for read-from-me, write-from-others conflicts */
+ if (mypage == page) {
+ /* only look for conflicts: for every object modified by the
+ other transaction, check that it was not read by us. */
+ size_t modif_index = page->modif_head;
+ while (modif_index != 0xff) {
+ struct object_s *obj = get_object_in_page(page, modif_index);
+ assert(obj->flags == 0x42);
+ if (_stm_was_read(obj)) {
+ fprintf(stderr, "# conflict: %p\n", obj);
+ conflict = 1;
+ break;
+ }
+ modif_index = obj->modif_next;
+ }
+ }
+ else {
+ /* Merge two versions of the page: for every object modified
+ by the other transaction, check that it was not read by us,
+ and then copy it over into our own page at 'mypage'. */
+ size_t obj_size = get_obj_size_in_words(page) << 3;
+ uint64_t diff_to_mypage = ((char *)mypage) - (char *)page;
+ size_t modif_index = page->modif_head;
+ while (modif_index != 0xff) {
+ struct object_s *obj = get_object_in_page(page, modif_index);
+ struct object_s *myobj = (struct object_s *)
+ (((char *)obj) + diff_to_mypage);
+ assert(obj->flags == 0x42);
+ assert(myobj->flags == 0x42); // || myobj->flags == 0);
+ if (_stm_was_read(myobj)) {
+ fprintf(stderr, "# conflict: %p\n", myobj);
+ conflict = 1;
+ goto retry;
+ }
+ memcpy(myobj, obj, obj_size);
+ modif_index = obj->modif_next;
+ }
+ }
+ }
+ stm_local.base_page_mapping = new;
+ return conflict;
+}
+
+void stm_start_transaction(void)
+{
+ struct shared_descriptor_s *d = stm_shared_descriptor;
+ stm_local.transaction_version =
+ __sync_fetch_and_add(&d->next_transaction_version, 1u);
+ assert(stm_local.transaction_version <= 0xffff);
+
+ struct page_header_s *newpage = _stm_reserve_page();
+ newpage->kind = PGKIND_WRITE_HISTORY;
+
+ struct write_history_s *cur = (struct write_history_s *)(newpage + 1);
+ cur->previous_older_transaction = NULL;
+ cur->transaction_version = stm_local.transaction_version;
+ cur->nb_updates = 0;
+ assert(stm_local.writes_by_this_transaction == NULL);
+ stm_local.writes_by_this_transaction = cur;
+
+ struct write_history_s *hist = d->most_recent_committed_transaction;
+ if (hist != stm_local.base_page_mapping) {
+ history_fast_forward(hist, 1);
+ }
+}
+
+_Bool stm_stop_transaction(void)
+{
+ struct shared_descriptor_s *d = stm_shared_descriptor;
+ assert(stm_local.writes_by_this_transaction != NULL);
+ int conflict = 0;
+ //fprintf(stderr, "stm_stop_transaction\n");
+
+ while (1) {
+ struct write_history_s *hist = d->most_recent_committed_transaction;
+ if (hist != stm_local.base_page_mapping) {
+ conflict = history_fast_forward(hist, 0);
+ if (conflict)
+ break;
+ else
+ continue; /* retry from the start of the loop */
+ }
+ struct write_history_s *cur = stm_local.writes_by_this_transaction;
+ cur->previous_older_transaction = hist;
+ if (__sync_bool_compare_and_swap(&d->most_recent_committed_transaction,
+ hist, cur))
+ break;
+ }
+ stm_local.writes_by_this_transaction = NULL;
+
+ assert(stm_get_read_marker_number() < 0xff);
+ stm_local.current_read_markers++;
+ return !conflict;
+}
+
+#ifdef STM_TESTS
+struct local_data_s *_stm_save_local_state(void)
+{
+ uint64_t i, page_count = stm_shared_descriptor->index_page_never_used;
+ uint32_t *pgoffs;
+ struct local_data_s *p = malloc(sizeof(struct local_data_s) +
+ page_count * sizeof(uint32_t));
+ assert(p != NULL);
+ memcpy(p, &stm_local, sizeof(stm_local));
+
+ pgoffs = (uint32_t *)(p + 1);
+ pgoffs[0] = page_count;
+ for (i = 2; i < page_count; i++) {
+ pgoffs[i] = get_pgoff(get_page_by_local_index(i));
+ }
+
+ return p;
+}
+
+void _stm_restore_local_state(struct local_data_s *p)
+{
+ uint64_t i, page_count;
+ uint32_t *pgoffs;
+
+ remap_file_pages((void *)stm_shared_descriptor, 4096 * NB_PAGES,
+ 0, 0, MAP_PAGES_FLAGS);
+
+ pgoffs = (uint32_t *)(p + 1);
+ page_count = pgoffs[0];
+ for (i = 2; i < page_count; i++) {
+ struct page_header_s *page = get_page_by_local_index(i);
+ remap_file_pages((void *)page, 4096, 0, pgoffs[i], MAP_PAGES_FLAGS);
+ assert(get_pgoff(page) == pgoffs[i]);
+ }
+
+ memcpy(&stm_local, p, sizeof(struct local_data_s));
+ free(p);
+}
+#endif
diff --git a/c5/core.h b/c5/core.h
new file mode 100644
--- /dev/null
+++ b/c5/core.h
@@ -0,0 +1,30 @@
+#ifndef _STM_CORE_H
+#define _STM_CORE_H
+
+#include <stdint.h>
+
+struct object_s {
+ /* Every objects starts with one such structure */
+ uint16_t modified;
+ uint8_t modif_next;
+ uint8_t flags;
+};
+
+void stm_setup(void);
+void stm_setup_process(void);
+
+void stm_start_transaction(void);
+_Bool stm_stop_transaction(void);
+struct object_s *stm_allocate(size_t size);
+
+void stm_read(struct object_s *object);
+void stm_write(struct object_s *object);
+_Bool _stm_was_read(struct object_s *object);
+_Bool _stm_was_written(struct object_s *object);
+
+struct local_data_s *_stm_save_local_state(void);
+void _stm_restore_local_state(struct local_data_s *p);
+void _stm_teardown(void);
+void _stm_teardown_process(void);
+
+#endif
diff --git a/c5/demo1.c b/c5/demo1.c
new file mode 100644
--- /dev/null
+++ b/c5/demo1.c
@@ -0,0 +1,100 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "core.h"
+
+
+#define NUM_THREADS 4
+
+
+typedef struct {
+ struct object_s header;
+ int val1, val2;
+} obj_t;
+
+void do_run_in_thread(int i)
+{
+ stm_start_transaction();
+ obj_t *ob1 = (obj_t *)stm_allocate(16);
+ obj_t *ob2 = (obj_t *)stm_allocate(16);
+
+ assert(!_stm_was_read(&ob1->header));
+ assert(!_stm_was_read(&ob2->header));
+ stm_read(&ob1->header);
+ stm_read(&ob2->header);
+ assert(_stm_was_read(&ob1->header));
+ assert(_stm_was_read(&ob2->header));
+ assert(_stm_was_written(&ob1->header));
+ assert(_stm_was_written(&ob2->header));
+ stm_write(&ob1->header);
+ stm_write(&ob2->header);
+ assert(_stm_was_written(&ob1->header));
+ assert(_stm_was_written(&ob2->header));
+ ob1->val1 = 100;
+ ob1->val2 = 200;
+ ob2->val1 = 300;
+ ob2->val2 = 400;
+
+ stm_stop_transaction();
+
+ int j;
+ for (j=0; j<2; j++) {
+ stm_start_transaction();
+
+ assert(!_stm_was_read(&ob1->header));
+ assert(!_stm_was_read(&ob2->header));
+ assert(!_stm_was_written(&ob1->header));
+ assert(!_stm_was_written(&ob2->header));
+ stm_read(&ob1->header);
+ printf("thread %d: ob1.val2=%d\n", i, ob1->val2);
+
+ stm_write(&ob1->header);
+ assert(_stm_was_written(&ob1->header));
+ assert(!_stm_was_written(&ob2->header));
+
+ stm_stop_transaction();
+ }
+
+ printf("thread %d: %p, %p\n", i, ob1, ob2);
+}
+
+void do_test(void)
+{
+ int i;
+ pid_t child_pids[NUM_THREADS];
+
+ for (i = 0; i < NUM_THREADS; i++) {
+ child_pids[i] = fork();
+ if (child_pids[i] == -1) {
+ perror("fork");
+ abort();
+ }
+ if (child_pids[i] == 0) {
+ stm_setup_process();
+ do_run_in_thread(i);
+ exit(0);
+ }
+ }
+
+ for (i = 0; i < NUM_THREADS; i++) {
+ int status;
+ if (waitpid(child_pids[i], &status, 0) == -1) {
+ perror("waitpid");
+ abort();
+ }
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ stm_setup();
+
+ do_test();
+
+ return 0;
+}
diff --git a/c5/pagecopy.c b/c5/pagecopy.c
new file mode 100644
--- /dev/null
+++ b/c5/pagecopy.c
@@ -0,0 +1,60 @@
+
+void pagecopy(void *dest, const void *src)
+{
+ asm volatile("0:\n"
+ "movdqa (%0), %%xmm0\n"
+ "movdqa 16(%0), %%xmm1\n"
+ "movdqa 32(%0), %%xmm2\n"
+ "movdqa 48(%0), %%xmm3\n"
+ "movdqa 64(%0), %%xmm4\n"
+ "movdqa 80(%0), %%xmm5\n"
+ "movdqa 96(%0), %%xmm6\n"
+ "movdqa 112(%0), %%xmm7\n"
+ "addq $128, %0\n"
+ "movdqa %%xmm0, (%1)\n"
+ "movdqa %%xmm1, 16(%1)\n"
+ "movdqa %%xmm2, 32(%1)\n"
+ "movdqa %%xmm3, 48(%1)\n"
+ "movdqa %%xmm4, 64(%1)\n"
+ "movdqa %%xmm5, 80(%1)\n"
+ "movdqa %%xmm6, 96(%1)\n"
+ "movdqa %%xmm7, 112(%1)\n"
+ "addq $128, %1\n"
+ "cmpq %2, %0\n"
+ "jne 0b"
+ : "=r"(src), "=r"(dest)
+ : "r"((char *)src + 4096), "0"(src), "1"(dest)
+ : "xmm0", "xmm1", "xmm2", "xmm3",
+ "xmm4", "xmm5", "xmm6", "xmm7");
+}
+
+#if 0 /* XXX enable if detected on the cpu */
+void pagecopy_ymm8(void *dest, const void *src)
+{
+ asm volatile("0:\n"
+ "vmovdqa (%0), %%ymm0\n"
+ "vmovdqa 32(%0), %%ymm1\n"
+ "vmovdqa 64(%0), %%ymm2\n"
+ "vmovdqa 96(%0), %%ymm3\n"
+ "vmovdqa 128(%0), %%ymm4\n"
+ "vmovdqa 160(%0), %%ymm5\n"
+ "vmovdqa 192(%0), %%ymm6\n"
+ "vmovdqa 224(%0), %%ymm7\n"
+ "addq $256, %0\n"
+ "vmovdqa %%ymm0, (%1)\n"
+ "vmovdqa %%ymm1, 32(%1)\n"
+ "vmovdqa %%ymm2, 64(%1)\n"
+ "vmovdqa %%ymm3, 96(%1)\n"
+ "vmovdqa %%ymm4, 128(%1)\n"
+ "vmovdqa %%ymm5, 160(%1)\n"
+ "vmovdqa %%ymm6, 192(%1)\n"
+ "vmovdqa %%ymm7, 224(%1)\n"
+ "addq $256, %1\n"
+ "cmpq %2, %0\n"
+ "jne 0b"
+ : "=r"(src), "=r"(dest)
+ : "r"((char *)src + 4096), "0"(src), "1"(dest)
+ : "xmm0", "xmm1", "xmm2", "xmm3",
+ "xmm4", "xmm5", "xmm6", "xmm7");
+}
+#endif
diff --git a/c5/pagecopy.h b/c5/pagecopy.h
new file mode 100644
--- /dev/null
+++ b/c5/pagecopy.h
@@ -0,0 +1,2 @@
+
+void pagecopy(void *dest, const void *src);
diff --git a/c5/test/support.py b/c5/test/support.py
new file mode 100644
--- /dev/null
+++ b/c5/test/support.py
@@ -0,0 +1,107 @@
+import os
+import cffi
+
+# ----------
+
+parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+header_files = [os.path.join(parent_dir, _n) for _n in
+ "core.h pagecopy.h".split()]
+source_files = [os.path.join(parent_dir, _n) for _n in
+ "core.c pagecopy.c".split()]
+
+_pycache_ = os.path.join(parent_dir, 'test', '__pycache__')
+if os.path.exists(_pycache_):
+ _fs = [_f for _f in os.listdir(_pycache_) if _f.startswith('_cffi_')]
+ if _fs:
+ _fsmtime = min(os.stat(os.path.join(_pycache_, _f)).st_mtime
+ for _f in _fs)
+ if any(os.stat(src).st_mtime >= _fsmtime
+ for src in header_files + source_files):
+ import shutil
+ shutil.rmtree(_pycache_)
+
+# ----------
+
+ffi = cffi.FFI()
+ffi.cdef("""
+void stm_setup(void);
+void stm_setup_process(void);
+
+void stm_start_transaction(void);
+_Bool stm_stop_transaction(void);
+struct object_s *stm_allocate(size_t size);
+
+void stm_read(struct object_s *object);
+void stm_write(struct object_s *object);
+_Bool _stm_was_read(struct object_s *object);
+_Bool _stm_was_written(struct object_s *object);
+
+struct local_data_s *_stm_save_local_state(void);
+void _stm_restore_local_state(struct local_data_s *p);
+void _stm_teardown(void);
+void _stm_teardown_process(void);
+""")
+
+lib = ffi.verify('''
+#include "core.h"
+''', sources=source_files,
+ define_macros=[('STM_TESTS', '1')],
+ undef_macros=['NDEBUG'],
+ include_dirs=[parent_dir],
+ extra_compile_args=['-g', '-O0'])
+
+def intptr(p):
+ return int(ffi.cast("intptr_t", p))
+
+def stm_allocate(size):
+ return ffi.cast("char *", lib.stm_allocate(size))
+
+def stm_read(ptr):
+ lib.stm_read(ffi.cast("struct object_s *", ptr))
+
+def stm_write(ptr):
+ lib.stm_write(ffi.cast("struct object_s *", ptr))
+
+def _stm_was_read(ptr):
+ return lib._stm_was_read(ffi.cast("struct object_s *", ptr))
+
+def _stm_was_written(ptr):
+ return lib._stm_was_written(ffi.cast("struct object_s *", ptr))
+
+def stm_start_transaction():
+ lib.stm_start_transaction()
+
+def stm_stop_transaction(expected_conflict):
+ res = lib.stm_stop_transaction()
+ if expected_conflict:
+ assert res == 0
+ else:
+ assert res == 1
+
+
+class BaseTest(object):
+
+ def setup_method(self, meth):
+ lib.stm_setup()
+ lib.stm_setup_process()
+ self.saved_states = {}
+ self.current_proc = "main"
+
+ def teardown_method(self, meth):
+ lib._stm_teardown_process()
+ for saved_state in self.saved_states.values():
+ lib._stm_restore_local_state(saved_state)
+ lib._stm_teardown_process()
+ del self.saved_states
+ lib._stm_teardown()
+
+ def switch(self, process_name):
+ self.saved_states[self.current_proc] = lib._stm_save_local_state()
+ try:
+ target_saved_state = self.saved_states.pop(process_name)
+ except KeyError:
+ lib.stm_setup_process()
+ else:
+ lib._stm_restore_local_state(target_saved_state)
+ self.current_proc = process_name
diff --git a/c5/test/test_basic.py b/c5/test/test_basic.py
new file mode 100644
--- /dev/null
+++ b/c5/test/test_basic.py
@@ -0,0 +1,153 @@
+from support import *
+
+
+class TestBasic(BaseTest):
+
+ def test_thread_local_allocations(self):
+ p1 = stm_allocate(16)
+ p2 = stm_allocate(16)
+ assert intptr(p2) - intptr(p1) == 16
+ p3 = stm_allocate(16)
+ assert intptr(p3) - intptr(p2) == 16
+ #
+ self.switch("sub1")
+ p1s = stm_allocate(16)
+ assert abs(intptr(p1s) - intptr(p3)) >= 4000
+ #
+ self.switch("main")
+ p4 = stm_allocate(16)
+ assert intptr(p4) - intptr(p3) == 16
+
+ def test_read_write_1(self):
+ stm_start_transaction()
+ p1 = stm_allocate(16)
+ p1[8] = 'a'
+ stm_stop_transaction(False)
+ #
+ self.switch("sub1")
+ stm_start_transaction()
+ stm_write(p1)
+ assert p1[8] == 'a'
+ p1[8] = 'b'
+ #
+ self.switch("main")
+ stm_start_transaction()
+ stm_read(p1)
+ assert p1[8] == 'a'
+ #
+ self.switch("sub1")
+ stm_stop_transaction(False)
+ #
+ self.switch("main")
+ assert p1[8] == 'a'
+
+ def test_start_transaction_updates(self):
+ stm_start_transaction()
+ p1 = stm_allocate(16)
+ p1[8] = 'a'
+ stm_stop_transaction(False)
+ #
+ self.switch("sub1")
+ stm_start_transaction()
+ stm_write(p1)
+ assert p1[8] == 'a'
+ p1[8] = 'b'
+ stm_stop_transaction(False)
+ #
+ self.switch("main")
+ assert p1[8] == 'a'
+ stm_start_transaction()
+ assert p1[8] == 'b'
+
+ def test_resolve_no_conflict_empty(self):
+ stm_start_transaction()
+ #
+ self.switch("sub1")
+ stm_start_transaction()
+ stm_stop_transaction(False)
+ #
+ self.switch("main")
+ stm_stop_transaction(False)
+
+ def test_resolve_no_conflict_write_only_in_already_committed(self):
+ stm_start_transaction()
+ p1 = stm_allocate(16)
+ p1[8] = 'a'
+ stm_stop_transaction(False)
+ stm_start_transaction()
+ #
+ self.switch("sub1")
+ stm_start_transaction()
+ stm_write(p1)
+ p1[8] = 'b'
+ stm_stop_transaction(False)
+ #
+ self.switch("main")
+ assert p1[8] == 'a'
+ stm_stop_transaction(False)
+ assert p1[8] == 'b'
+
+ def test_resolve_write_read_conflict(self):
+ stm_start_transaction()
+ p1 = stm_allocate(16)
+ p1[8] = 'a'
+ stm_stop_transaction(False)
+ stm_start_transaction()
+ #
+ self.switch("sub1")
+ stm_start_transaction()
+ stm_write(p1)
+ p1[8] = 'b'
+ stm_stop_transaction(False)
+ #
+ self.switch("main")
+ stm_read(p1)
+ assert p1[8] == 'a'
+ stm_stop_transaction(expected_conflict=True)
+ assert p1[8] in ('a', 'b')
+ stm_start_transaction()
+ assert p1[8] == 'b'
+
+ def test_resolve_write_write_conflict(self):
+ stm_start_transaction()
+ p1 = stm_allocate(16)
+ p1[8] = 'a'
+ stm_stop_transaction(False)
+ stm_start_transaction()
+ #
+ self.switch("sub1")
+ stm_start_transaction()
+ stm_write(p1)
+ p1[8] = 'b'
+ stm_stop_transaction(False)
+ #
+ self.switch("main")
+ assert p1[8] == 'a'
+ stm_write(p1)
+ p1[8] = 'c'
+ stm_stop_transaction(expected_conflict=True)
+ assert p1[8] in ('a', 'b')
+ stm_start_transaction()
+ assert p1[8] == 'b'
+
+ def test_resolve_write_write_no_conflict(self):
+ stm_start_transaction()
+ p1 = stm_allocate(16)
+ p2 = stm_allocate(16)
+ p1[8] = 'a'
+ p2[8] = 'A'
+ stm_stop_transaction(False)
+ stm_start_transaction()
+ #
+ self.switch("sub1")
+ stm_start_transaction()
+ stm_write(p1)
+ p1[8] = 'b'
+ stm_stop_transaction(False)
+ #
+ self.switch("main")
+ stm_write(p2)
+ p2[8] = 'C'
+ stm_stop_transaction(False)
+ assert p1[8] == 'b'
+ assert p2[8] == 'C'
diff --git a/c5/test/test_bug.py b/c5/test/test_bug.py
new file mode 100644
--- /dev/null
+++ b/c5/test/test_bug.py
@@ -0,0 +1,429 @@
+from support import *
+
+
+class TestBug(BaseTest):
+
+ def test_bug1(self):
+ stm_start_transaction()
+ p8 = stm_allocate(16)
+ p8[8] = '\x08'
+ stm_stop_transaction(False)
+ #
+ self.switch("sub1")
+ self.switch("main")
+ stm_start_transaction()
+ stm_write(p8)
+ p8[8] = '\x97'
+ #
+ self.switch("sub1")
+ stm_start_transaction()
+ stm_read(p8)
+ assert p8[8] == '\x08'
+
+ def test_bug2(self):
+ stm_start_transaction()
+ p0 = stm_allocate(16)
+ p1 = stm_allocate(16)
+ p2 = stm_allocate(16)
+ p3 = stm_allocate(16)
+ p4 = stm_allocate(16)
+ p5 = stm_allocate(16)
+ p6 = stm_allocate(16)
+ p7 = stm_allocate(16)
+ p8 = stm_allocate(16)
+ p9 = stm_allocate(16)
+ p0[8] = '\x00'
+ p1[8] = '\x01'
+ p2[8] = '\x02'
+ p3[8] = '\x03'
+ p4[8] = '\x04'
+ p5[8] = '\x05'
+ p6[8] = '\x06'
+ p7[8] = '\x07'
+ p8[8] = '\x08'
+ p9[8] = '\t'
+ stm_stop_transaction(False)
+ self.switch(0)
+ self.switch(1)
+ self.switch(2)
+ #
+ self.switch(1)
+ stm_start_transaction()
+ stm_read(p7)
+ assert p7[8] == '\x07'
+ #
+ self.switch(1)
+ stm_read(p0)
+ assert p0[8] == '\x00'
+ #
+ self.switch(1)
+ stm_read(p4)
+ assert p4[8] == '\x04'
+ #
+ self.switch(0)
+ stm_start_transaction()
+ stm_read(p3)
+ assert p3[8] == '\x03'
+ #
+ self.switch(2)
+ stm_start_transaction()
+ stm_read(p8)
+ assert p8[8] == '\x08'
+ stm_write(p8)
+ p8[8] = '\x08'
+ #
+ self.switch(0)
+ stm_read(p0)
+ assert p0[8] == '\x00'
+ #
+ self.switch(0)
+ stm_read(p0)
+ assert p0[8] == '\x00'
+ #
+ self.switch(1)
+ stm_read(p2)
+ assert p2[8] == '\x02'
+ #
+ self.switch(2)
+ stm_read(p2)
+ assert p2[8] == '\x02'
+ #
+ self.switch(2)
+ stm_read(p2)
+ assert p2[8] == '\x02'
+ stm_write(p2)
+ p2[8] = 'm'
+ #
+ self.switch(0)
+ stm_read(p4)
+ assert p4[8] == '\x04'
+ stm_write(p4)
+ p4[8] = '\xc5'
+ #
+ self.switch(2)
+ stm_read(p1)
+ assert p1[8] == '\x01'
+ #
+ self.switch(2)
+ stm_stop_transaction(False) #1
+ # ['\x00', '\x01', 'm', '\x03', '\x04', '\x05', '\x06', '\x07',
'\x08', '\t']
+ # log: [8, 2]
+ #
+ self.switch(0)
+ stm_stop_transaction(False) #2
+ # ['\x00', '\x01', 'm', '\x03', '\xc5', '\x05', '\x06', '\x07',
'\x08', '\t']
+ # log: [4]
+ #
+ self.switch(0)
+ stm_start_transaction()
+ stm_read(p6)
+ assert p6[8] == '\x06'
+ #
+ self.switch(0)
+ stm_read(p4)
+ assert p4[8] == '\xc5'
+ #
+ self.switch(0)
+ stm_read(p4)
+ assert p4[8] == '\xc5'
+ #
+ self.switch(1)
+ stm_read(p0)
+ assert p0[8] == '\x00'
+ #
+ self.switch(1)
+ stm_stop_transaction(True) #3
+ # conflict: 0xdf0a8028
+ #
+ self.switch(2)
+ stm_start_transaction()
+ stm_read(p6)
+ assert p6[8] == '\x06'
+ #
+ self.switch(1)
+ stm_start_transaction()
+ stm_read(p1)
+ assert p1[8] == '\x01'
+ #
+ self.switch(0)
+ stm_read(p4)
+ assert p4[8] == '\xc5'
+ stm_write(p4)
+ p4[8] = '\x0c'
+ #
+ self.switch(2)
+ stm_read(p2)
+ assert p2[8] == 'm'
+ stm_write(p2)
+ p2[8] = '\x81'
+ #
+ self.switch(2)
+ stm_read(p7)
+ assert p7[8] == '\x07'
+ #
+ self.switch(0)
+ stm_read(p5)
+ assert p5[8] == '\x05'
+ stm_write(p5)
+ p5[8] = 'Z'
+ #
+ self.switch(1)
+ stm_stop_transaction(False) #4
+ # ['\x00', '\x01', 'm', '\x03', '\xc5', '\x05', '\x06', '\x07',
'\x08', '\t']
+ # log: []
+ #
+ self.switch(2)
+ stm_read(p8)
+ assert p8[8] == '\x08'
+ #
+ self.switch(0)
+ stm_read(p0)
+ assert p0[8] == '\x00'
+ #
+ self.switch(1)
+ stm_start_transaction()
+ stm_read(p0)
+ assert p0[8] == '\x00'
+ #
+ self.switch(2)
+ stm_read(p9)
+ assert p9[8] == '\t'
+ stm_write(p9)
+ p9[8] = '\x81'
+ #
+ self.switch(0)
+ stm_read(p0)
+ assert p0[8] == '\x00'
+ #
+ self.switch(1)
+ stm_read(p2)
+ assert p2[8] == 'm'
+ #
+ self.switch(2)
+ stm_read(p9)
+ assert p9[8] == '\x81'
+ stm_write(p9)
+ p9[8] = 'g'
+ #
+ self.switch(1)
+ stm_read(p3)
+ assert p3[8] == '\x03'
+ #
+ self.switch(2)
+ stm_read(p7)
+ assert p7[8] == '\x07'
+ #
+ self.switch(1)
+ stm_read(p1)
+ assert p1[8] == '\x01'
+ #
+ self.switch(0)
+ stm_read(p2)
+ assert p2[8] == 'm'
+ stm_write(p2)
+ p2[8] = 'T'
+ #
+ self.switch(2)
+ stm_read(p4)
+ assert p4[8] == '\xc5'
+ #
+ self.switch(2)
+ stm_read(p9)
+ assert p9[8] == 'g'
+ #
+ self.switch(2)
+ stm_read(p1)
+ assert p1[8] == '\x01'
+ stm_write(p1)
+ p1[8] = 'L'
+ #
+ self.switch(0)
+ stm_read(p0)
+ assert p0[8] == '\x00'
+ #
+ self.switch(2)
+ stm_read(p0)
+ assert p0[8] == '\x00'
+ stm_write(p0)
+ p0[8] = '\xf3'
+ #
+ self.switch(1)
+ stm_stop_transaction(False) #5
+ # ['\x00', '\x01', 'm', '\x03', '\xc5', '\x05', '\x06', '\x07',
'\x08', '\t']
+ # log: []
+ #
+ self.switch(0)
+ stm_read(p1)
+ assert p1[8] == '\x01'
+ stm_write(p1)
+ p1[8] = '*'
+ #
+ self.switch(1)
+ stm_start_transaction()
+ stm_read(p3)
+ assert p3[8] == '\x03'
+ stm_write(p3)
+ p3[8] = '\xd2'
+ #
+ self.switch(0)
+ stm_stop_transaction(False) #6
+ # ['\x00', '*', 'T', '\x03', '\x0c', 'Z', '\x06', '\x07', '\x08', '\t']
+ # log: [1, 2, 4, 5]
+ #
+ self.switch(1)
+ stm_read(p7)
+ assert p7[8] == '\x07'
+ stm_write(p7)
+ p7[8] = '.'
+ #
+ self.switch(0)
+ stm_start_transaction()
+ stm_read(p7)
+ assert p7[8] == '\x07'
+ #
+ self.switch(1)
+ stm_read(p2)
+ assert p2[8] == 'm'
+ stm_write(p2)
+ p2[8] = '\xe9'
+ #
+ self.switch(1)
+ stm_read(p0)
+ assert p0[8] == '\x00'
+ #
+ self.switch(0)
+ stm_read(p1)
+ assert p1[8] == '*'
+ #
+ self.switch(0)
+ stm_read(p8)
+ assert p8[8] == '\x08'
+ stm_write(p8)
+ p8[8] = 'X'
+ #
+ self.switch(2)
+ stm_stop_transaction(True) #7
+ # conflict: 0xdf0a8018
+ #
+ self.switch(1)
+ stm_read(p9)
+ assert p9[8] == '\t'
+ #
+ self.switch(0)
+ stm_read(p8)
+ assert p8[8] == 'X'
+ #
+ self.switch(1)
+ stm_read(p4)
+ assert p4[8] == '\xc5'
+ stm_write(p4)
+ p4[8] = '\xb2'
+ #
+ self.switch(0)
+ stm_read(p9)
+ assert p9[8] == '\t'
+ #
+ self.switch(2)
+ stm_start_transaction()
+ stm_read(p5)
+ assert p5[8] == 'Z'
+ stm_write(p5)
+ p5[8] = '\xfa'
+ #
+ self.switch(2)
+ stm_read(p3)
+ assert p3[8] == '\x03'
+ #
+ self.switch(1)
+ stm_read(p9)
+ assert p9[8] == '\t'
+ #
+ self.switch(1)
+ stm_read(p8)
+ assert p8[8] == '\x08'
+ stm_write(p8)
+ p8[8] = 'g'
+ #
+ self.switch(1)
+ stm_read(p8)
+ assert p8[8] == 'g'
+ #
+ self.switch(2)
+ stm_read(p5)
+ assert p5[8] == '\xfa'
+ stm_write(p5)
+ p5[8] = '\x86'
+ #
+ self.switch(2)
+ stm_read(p6)
+ assert p6[8] == '\x06'
+ #
+ self.switch(1)
+ stm_read(p4)
+ assert p4[8] == '\xb2'
+ stm_write(p4)
+ p4[8] = '\xce'
+ #
+ self.switch(2)
+ stm_read(p2)
+ assert p2[8] == 'T'
+ stm_write(p2)
+ p2[8] = 'Q'
+ #
+ self.switch(1)
+ stm_stop_transaction(True) #8
+ # conflict: 0xdf0a8028
+ #
+ self.switch(2)
+ stm_stop_transaction(False) #9
+ # ['\x00', '*', 'Q', '\x03', '\x0c', '\x86', '\x06', '\x07', '\x08',
'\t']
+ # log: [2, 5]
+ #
+ self.switch(0)
+ stm_read(p0)
+ assert p0[8] == '\x00'
+ #
+ self.switch(1)
+ stm_start_transaction()
+ stm_read(p3)
+ assert p3[8] == '\x03'
+ #
+ self.switch(1)
+ stm_read(p5)
+ assert p5[8] == '\x86'
+ #
+ self.switch(2)
+ stm_start_transaction()
+ stm_read(p4)
+ assert p4[8] == '\x0c'
+ stm_write(p4)
+ p4[8] = '{'
+ #
+ self.switch(1)
+ stm_read(p2)
+ assert p2[8] == 'Q'
+ #
+ self.switch(2)
+ stm_read(p3)
+ assert p3[8] == '\x03'
+ stm_write(p3)
+ p3[8] = 'V'
+ #
+ self.switch(1)
+ stm_stop_transaction(False) #10
+ # ['\x00', '*', 'Q', '\x03', '\x0c', '\x86', '\x06', '\x07', '\x08',
'\t']
+ # log: []
+ #
+ self.switch(1)
+ stm_start_transaction()
+ stm_read(p7)
+ assert p7[8] == '\x07'
+ #
+ self.switch(2)
+ stm_read(p0)
+ assert p0[8] == '\x00'
+ stm_write(p0)
+ p0[8] = 'P'
+ #
+ self.switch(0)
+ stm_stop_transaction(False) #11
diff --git a/c5/test/test_random.py b/c5/test/test_random.py
new file mode 100644
--- /dev/null
+++ b/c5/test/test_random.py
@@ -0,0 +1,85 @@
+from support import *
+import sys, random
+
+
+class TestRandom(BaseTest):
+
+ def test_fixed_16_bytes_objects(self):
+ rnd = random.Random(1010)
+
+ N_OBJECTS = 10
+ N_THREADS = 3
+ print >> sys.stderr, 'stm_start_transaction()'
+ stm_start_transaction()
+ plist = [stm_allocate(16) for i in range(N_OBJECTS)]
+ read_sets = [{} for i in range(N_THREADS)]
+ write_sets = [{} for i in range(N_THREADS)]
+ active_transactions = {}
+
+ for i in range(N_OBJECTS):
+ print >> sys.stderr, 'p%d = stm_allocate(16)' % i
+ for i in range(N_OBJECTS):
+ print >> sys.stderr, 'p%d[8] = %r' % (i, chr(i))
+ plist[i][8] = chr(i)
+ head_state = [[chr(i) for i in range(N_OBJECTS)]]
+ commit_log = []
+ print >> sys.stderr, 'stm_stop_transaction(False)'
+ stm_stop_transaction(False)
+
+ for i in range(N_THREADS):
+ print >> sys.stderr, 'self.switch(%d)' % i
+ self.switch(i)
+ stop_count = 1
+
+ for i in range(10000):
+ n_thread = rnd.randrange(0, N_THREADS)
+ print >> sys.stderr, '#\nself.switch(%d)' % n_thread
+ self.switch(n_thread)
+ if n_thread not in active_transactions:
+ print >> sys.stderr, 'stm_start_transaction()'
+ stm_start_transaction()
+ active_transactions[n_thread] = len(commit_log)
+
+ action = rnd.randrange(0, 7)
+ if action < 6:
+ is_write = action >= 4
+ i = rnd.randrange(0, N_OBJECTS)
+ print >> sys.stderr, "stm_read(p%d)" % i
+ stm_read(plist[i])
+ got = plist[i][8]
+ print >> sys.stderr, "assert p%d[8] ==" % i,
+ my_head_state = head_state[active_transactions[n_thread]]
+ prev = read_sets[n_thread].setdefault(i, my_head_state[i])
+ print >> sys.stderr, "%r" % (prev,)
+ assert got == prev
+ #
+ if is_write:
+ print >> sys.stderr, 'stm_write(p%d)' % i
+ stm_write(plist[i])
+ newval = chr(rnd.randrange(0, 256))
+ print >> sys.stderr, 'p%d[8] = %r' % (i, newval)
+ plist[i][8] = newval
+ read_sets[n_thread][i] = write_sets[n_thread][i] = newval
+ else:
+ src_index = active_transactions.pop(n_thread)
+ conflict = False
+ for i in range(src_index, len(commit_log)):
+ for j in commit_log[i]:
+ if j in read_sets[n_thread]:
+ conflict = True
+ print >> sys.stderr, "stm_stop_transaction(%r) #%d" % (
+ conflict, stop_count)
+ stop_count += 1
+ stm_stop_transaction(conflict)
+ #
+ if not conflict:
+ hs = head_state[-1][:]
+ for i, newval in write_sets[n_thread].items():
+ hs[i] = newval
+ assert plist[i][8] == newval
+ head_state.append(hs)
+ commit_log.append(write_sets[n_thread].keys())
+ print >> sys.stderr, '#', head_state[-1]
+ print >> sys.stderr, '# log:', commit_log[-1]
+ write_sets[n_thread].clear()
+ read_sets[n_thread].clear()
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit