[pypy-commit] pypy stm-thread-2: Finish hash() --- hopefully. At least the test in targetdemo2 passes.
arigo
noreply at buildbot.pypy.org
Mon Sep 10 08:51:49 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch: stm-thread-2
Changeset: r57260:85c2dad95284
Date: 2012-09-10 08:50 +0200
http://bitbucket.org/pypy/pypy/changeset/85c2dad95284/
Log: Finish hash() --- hopefully. At least the test in targetdemo2
passes.
diff --git a/pypy/rpython/memory/gc/stmgc.py b/pypy/rpython/memory/gc/stmgc.py
--- a/pypy/rpython/memory/gc/stmgc.py
+++ b/pypy/rpython/memory/gc/stmgc.py
@@ -2,6 +2,7 @@
from pypy.rpython.lltypesystem.lloperation import llop
from pypy.rpython.lltypesystem.llmemory import raw_malloc_usage, raw_memcopy
from pypy.rpython.memory.gc.base import GCBase, MovingGCBase
+from pypy.rpython.memory.support import mangle_hash
from pypy.rpython.annlowlevel import llhelper
from pypy.rlib.rarithmetic import LONG_BIT, r_uint
from pypy.rlib.debug import ll_assert, debug_start, debug_stop, fatalerror
@@ -62,7 +63,8 @@
# surviving during a collection. Between collections, it is set on
# the LOCAL COPY objects, but only on them.
#
-# - GCFLAG_HASH01, GCFLAG_HASH02: to handle hashes
+# - GCFLAG_HASH_FIELD: the object contains an extra field added at the
+# end, with the hash value
#
# Invariant: between two transactions, all objects visible from the current
# thread are always GLOBAL. In particular:
@@ -113,22 +115,11 @@
GCFLAG_NOT_WRITTEN = first_gcflag << 2 # keep in sync with et.h
GCFLAG_LOCAL_COPY = first_gcflag << 3 # keep in sync with et.h
GCFLAG_VISITED = first_gcflag << 4 # keep in sync with et.h
-GCFLAG_HASH01 = first_gcflag << 5
-GCFLAG_HASH02 = first_gcflag << 6
-GCFLAG_HASHMASK = GCFLAG_HASH01 | GCFLAG_HASH02
+GCFLAG_HASH_FIELD = first_gcflag << 5 # keep in sync with et.h
-GCFLAG_PREBUILT = GCFLAG_GLOBAL | GCFLAG_NOT_WRITTEN | GCFLAG_HASH01
+GCFLAG_PREBUILT = GCFLAG_GLOBAL | GCFLAG_NOT_WRITTEN
REV_INITIAL = r_uint(1)
-
-# the two flags GCFLAG_HASH0n together give one of the following four cases:
-# - nobody ever asked for the hash of the object
-GC_HASH_NOTTAKEN = 0
-# - someone asked, and we gave the address of the object + mangle_hash
-GC_HASH_TAKEN_ADDR = GCFLAG_HASH01
-# - someone asked, and we gave the address + nursery_hash_base + mangle_hash
-GC_HASH_TAKEN_NURS = GCFLAG_HASH02
-# - we have our own extra field to store the hash
-GC_HASH_HASFIELD = GCFLAG_HASH01 | GCFLAG_HASH02
+REV_FLAG_NEW_HASH = r_uint(2)
def always_inline(fn):
@@ -144,15 +135,12 @@
inline_simple_malloc = True
inline_simple_malloc_varsize = True
prebuilt_gc_objects_are_static_roots = False
- malloc_zero_filled = True # xxx?
+ malloc_zero_filled = True
HDR = lltype.Struct('header', ('tid', lltype.Signed),
('revision', lltype.Unsigned))
typeid_is_in_field = 'tid'
- withhash_flag_is_in_field = 'tid', GCFLAG_HASH02
- # ^^^ prebuilt objects either have GC_HASH_TAKEN_ADDR or they
- # have GC_HASH_HASFIELD (and then they are one word longer).
- # The difference between the two cases is GCFLAG_HASH02.
+ withhash_flag_is_in_field = 'tid', GCFLAG_HASH_FIELD
TRANSLATION_PARAMS = {
'stm_operations': 'use_real_one',
@@ -306,7 +294,7 @@
def get_size_incl_hash(self, obj):
size = self.get_size(obj)
hdr = self.header(obj)
- if (hdr.tid & GCFLAG_HASHMASK) == GC_HASH_HASFIELD:
+ if hdr.tid & GCFLAG_HASH_FIELD:
size += llmemory.sizeof(lltype.Signed)
return size
@@ -356,18 +344,17 @@
return self.identityhash(gcobj)
def identityhash(self, gcobj):
- stmtls = self.get_tls()
obj = llmemory.cast_ptr_to_adr(gcobj)
- hdr = self.header(obj)
- if hdr.tid & GCFLAG_HASHMASK == 0:
- # set one of the GC_HASH_TAKEN_xxx flags.
- if stmtls.is_in_nursery(obj):
- hdr.tid |= GC_HASH_TAKEN_NURS
- else:
- hdr.tid |= GC_HASH_TAKEN_ADDR
- # Compute and return the result
- objsize = self.get_size(obj)
- return stmtls._get_object_hash(obj, objsize, hdr.tid)
+ obj = self.stm_operations.stm_HashObject(obj)
+ return self._get_object_hash(obj)
+
+ def _get_object_hash(self, obj):
+ if self.header(obj).tid & GCFLAG_HASH_FIELD:
+ objsize = self.get_size(obj)
+ obj = llarena.getfakearenaaddress(obj)
+ return (obj + objsize).signed[0]
+ else:
+ return mangle_hash(llmemory.cast_adr_to_int(obj))
# ____________________________________________________________
# helpers
diff --git a/pypy/rpython/memory/gc/stmtls.py b/pypy/rpython/memory/gc/stmtls.py
--- a/pypy/rpython/memory/gc/stmtls.py
+++ b/pypy/rpython/memory/gc/stmtls.py
@@ -4,9 +4,8 @@
from pypy.rpython.annlowlevel import cast_base_ptr_to_instance, base_ptr_lltype
from pypy.rlib.objectmodel import we_are_translated, free_non_gc_object
from pypy.rlib.objectmodel import specialize
-from pypy.rlib.rarithmetic import r_uint, intmask
+from pypy.rlib.rarithmetic import r_uint
from pypy.rlib.debug import ll_assert, debug_start, debug_stop, fatalerror
-from pypy.rpython.memory.support import mangle_hash
from pypy.rpython.memory.gc.stmgc import WORD, NULL
from pypy.rpython.memory.gc.stmgc import always_inline, dont_inline
@@ -14,8 +13,7 @@
from pypy.rpython.memory.gc.stmgc import GCFLAG_LOCAL_COPY
from pypy.rpython.memory.gc.stmgc import GCFLAG_POSSIBLY_OUTDATED
from pypy.rpython.memory.gc.stmgc import GCFLAG_NOT_WRITTEN
-from pypy.rpython.memory.gc.stmgc import GCFLAG_HASHMASK, GC_HASH_TAKEN_ADDR
-from pypy.rpython.memory.gc.stmgc import GC_HASH_TAKEN_NURS, GC_HASH_HASFIELD
+from pypy.rpython.memory.gc.stmgc import GCFLAG_HASH_FIELD, REV_FLAG_NEW_HASH
from pypy.rpython.memory.gc.stmgc import hdr_revision, set_hdr_revision
@@ -26,7 +24,6 @@
_alloc_flavor_ = 'raw'
nontranslated_dict = {}
- nursery_hash_base = -1
def __init__(self, gc):
self.gc = gc
@@ -213,21 +210,9 @@
size_used = self.nursery_free - self.nursery_start
llarena.arena_reset(self.nursery_start, size_used, 2)
self.nursery_free = self.nursery_start
- self.change_nursery_hash_base() # the nursery is empty now
#
debug_stop("gc-local")
- def change_nursery_hash_base(self):
- # The following should be enough to ensure that young objects
- # tend to always get a different hash. It also makes sure that
- # nursery_hash_base is not a multiple of 4, to avoid collisions
- # with the hash of non-young objects.
- hash_base = self.nursery_hash_base
- hash_base += self.nursery_size - 1
- if (hash_base & 3) == 0:
- hash_base -= 1
- self.nursery_hash_base = intmask(hash_base)
-
# ------------------------------------------------------------
@always_inline
@@ -424,7 +409,7 @@
#
# Note that references from 'obj' to other objects in the
# nursery are kept unchanged in this step: they are copied
- # verbatim to 'newobj'.
+ # verbatim from 'obj' into 'newobj'.
#
# Register the object here, not before the memcopy() that would
# overwrite its 'revision' field
@@ -447,7 +432,10 @@
def duplicate_obj(self, obj, objsize):
size_gc_header = self.gc.gcheaderbuilder.size_gc_header
totalsize_without_hash = size_gc_header + objsize
- if self.gc.header(obj).tid & GCFLAG_HASHMASK:
+ hdr = self.gc.header(obj)
+ has_hash = (hdr.tid & GCFLAG_HASH_FIELD != 0 or
+ hdr.revision & REV_FLAG_NEW_HASH != 0)
+ if has_hash:
newtotalsize = totalsize_without_hash + (
llmemory.sizeof(lltype.Signed))
else:
@@ -461,29 +449,14 @@
newobj - size_gc_header,
totalsize_without_hash)
#
- newhdr = self.gc.header(newobj)
- if newhdr.tid & GCFLAG_HASHMASK:
- hash = self._get_object_hash(obj, objsize, newhdr.tid)
+ if has_hash:
+ hash = self.gc._get_object_hash(obj)
newaddr = llarena.getfakearenaaddress(newobj)
(newaddr + objsize).signed[0] = hash
- newhdr.tid |= GC_HASH_HASFIELD
+ self.gc.header(newobj).tid |= GCFLAG_HASH_FIELD
#
return newobj
- def _get_object_hash(self, obj, objsize, tid):
- # Returns the hash of the object, which must not be GC_HASH_NOTTAKEN.
- gc_hash = tid & GCFLAG_HASHMASK
- if gc_hash == GC_HASH_HASFIELD:
- obj = llarena.getfakearenaaddress(obj)
- return (obj + objsize).signed[0]
- elif gc_hash == GC_HASH_TAKEN_ADDR:
- return mangle_hash(llmemory.cast_adr_to_int(obj))
- elif gc_hash == GC_HASH_TAKEN_NURS:
- return mangle_hash(intmask(llmemory.cast_adr_to_int(obj) +
- self.nursery_hash_base))
- else:
- assert 0, "gc_hash == GC_HASH_NOTTAKEN"
-
def _register_newly_malloced_obj(self, obj):
self.sharedarea_tls.add_regular(obj)
@@ -605,8 +578,6 @@
ll_assert(is_local_copy, "debug_check: missing LOCAL_COPY")
ll_assert(hdr.tid & GCFLAG_VISITED == 0,
"debug_check: unexpected VISITED")
- ll_assert(hdr.tid & GCFLAG_HAS_SHADOW == 0,
- "debug_check: unexpected HAS_SHADOW")
self.gc.get_size(obj) # extra checks
self.pending.append(obj)
self.debug_seen.setitem(obj, obj)
diff --git a/pypy/rpython/memory/gc/test/test_stmgc.py b/pypy/rpython/memory/gc/test/test_stmgc.py
--- a/pypy/rpython/memory/gc/test/test_stmgc.py
+++ b/pypy/rpython/memory/gc/test/test_stmgc.py
@@ -4,6 +4,7 @@
from pypy.rpython.memory.gc.stmgc import GCFLAG_GLOBAL, GCFLAG_NOT_WRITTEN
from pypy.rpython.memory.gc.stmgc import GCFLAG_POSSIBLY_OUTDATED
from pypy.rpython.memory.gc.stmgc import GCFLAG_LOCAL_COPY
+from pypy.rpython.memory.gc.stmgc import GCFLAG_HASH_FIELD, REV_FLAG_NEW_HASH
from pypy.rpython.memory.gc.stmgc import hdr_revision, set_hdr_revision
from pypy.rpython.memory.support import mangle_hash
@@ -88,6 +89,20 @@
for key, value in self._tldicts[self.threadnum].iteritems():
callback(tls, key, value)
+ def stm_HashObject(self, P):
+ # see et.c
+ hdr = self._gc.header(P)
+ if hdr.tid & (GCFLAG_GLOBAL|GCFLAG_LOCAL_COPY) == 0:
+ hdr.revision |= REV_FLAG_NEW_HASH
+ return P
+
+ while True:
+ hdr = self._gc.header(P)
+ if hdr.tid & GCFLAG_HASH_FIELD:
+ return P
+ v = hdr_revision(hdr) # back-reference to the original
+ XXX
+
def fake_get_size(obj):
TYPE = obj.ptr._TYPE.TO
diff --git a/pypy/translator/stm/src_stm/et.c b/pypy/translator/stm/src_stm/et.c
--- a/pypy/translator/stm/src_stm/et.c
+++ b/pypy/translator/stm/src_stm/et.c
@@ -34,7 +34,7 @@
/************************************************************/
#define ABORT_REASONS 5
-#define SPINLOOP_REASONS 3
+#define SPINLOOP_REASONS 4
struct tx_descriptor {
jmp_buf *setjmp_buf;
@@ -53,7 +53,7 @@
struct FXCache recent_reads_cache;
};
-static volatile revision_t global_cur_time = 2; /* always even */
+static volatile revision_t global_cur_time = 4; /* always mult of 4 */
static volatile revision_t next_locked_value = LOCKED + 3; /* always odd */
static __thread struct tx_descriptor *thread_descriptor = NULL;
@@ -107,9 +107,14 @@
{
gcptr R = G;
revision_t v;
+ volatile revision_t *vp;
retry:
- while (!((v = R->h_revision) & 1)) // "is a pointer", i.e.
- { // "has a more recent revision"
+ while (1)
+ {
+ vp = (volatile revision_t *)&R->h_revision;
+ v = *vp;
+ if (v & 1) // "is not a pointer", i.e.
+ break; // "doesn't have a more recent revision"
R = (gcptr)v;
}
if (__builtin_expect(v > d->start_time, 0)) // object too recent?
@@ -315,8 +320,10 @@
{
gcptr R = items[i];
revision_t v;
+ volatile revision_t *vp;
retry:
- v = R->h_revision;
+ vp = (volatile revision_t *)&R->h_revision;
+ v = *vp;
if (!(v & 1)) // "is a pointer", i.e.
AbortTransaction(1); // "has a more recent revision"
if (v >= LOCKED) // locked
@@ -333,7 +340,10 @@
for (i=0; i<size; i++)
{
gcptr R = items[i];
- revision_t v = R->h_revision;
+ revision_t v;
+ volatile revision_t *vp;
+ vp = (volatile revision_t *)&R->h_revision;
+ v = *vp;
if (!(v & 1)) // "is a pointer", i.e.
return 0; // "has a more recent revision"
if (v >= LOCKED) // locked
@@ -422,8 +432,10 @@
{
gcptr R = (gcptr)item[0]->h_revision;
revision_t v;
+ volatile revision_t *vp;
retry:
- v = R->h_revision;
+ vp = (volatile revision_t *)&R->h_revision;
+ v = *vp;
if (!(v & 1)) // "is a pointer", i.e.
AbortTransaction(0); // "has a more recent revision"
if (v >= LOCKED) // already locked by someone else
@@ -434,7 +446,7 @@
SpinLoop(2);
goto retry;
}
- if (!bool_cas((volatile revision_t *)&R->h_revision, v, my_lock))
+ if (!bool_cas(vp, v, my_lock))
goto retry;
item[1] = (gcptr)v;
@@ -545,7 +557,7 @@
{
// no-one else can have changed global_cur_time if I'm inevitable
cur_time = d->start_time;
- if (!bool_cas(&global_cur_time, INEVITABLE, cur_time + 2))
+ if (!bool_cas(&global_cur_time, INEVITABLE, cur_time + 4))
{
assert(!"global_cur_time modified even though we are inev.");
abort();
@@ -565,7 +577,7 @@
AcquireLocks(d);
continue;
}
- if (bool_cas(&global_cur_time, cur_time, cur_time + 2))
+ if (bool_cas(&global_cur_time, cur_time, cur_time + 4))
break;
}
// validate (but skip validation if nobody else committed)
@@ -670,6 +682,56 @@
return GlobalizeForComparison(d, P1) == GlobalizeForComparison(d, P2);
}
+gcptr stm_HashObject(gcptr P)
+{
+ /* return one of the objects in the chained list following P out of
+ which the stmgc can determine the hash of P. The first time we ask
+ for the hash of an object, we set REV_FLAG_NEW_HASH on the last
+ object of the chain. The hash is then the address of this object.
+ When stm_duplicate further duplicates an object with
+ REV_FLAG_NEW_HASH, it adds an extra field remembering the hash.
+ From then on all stm_duplicates of this object have GCFLAG_HASH_FIELD
+ and the same hash, copied over and over into the extra field. */
+ revision_t v;
+ volatile revision_t *vp;
+
+ if ((P->h_tid & (GCFLAG_GLOBAL|GCFLAG_LOCAL_COPY)) == 0)
+ {
+ // a local object newly created in this transaction
+ P->h_revision |= REV_FLAG_NEW_HASH;
+ return P;
+ }
+
+ while (1)
+ {
+ if (P->h_tid & GCFLAG_HASH_FIELD)
+ {
+ return P; /* any HASH_FIELD object is fine; we'll read the hash
+ out of the field. */
+ }
+ vp = (volatile revision_t *)&P->h_revision;
+ v = *vp;
+ if (!(v & 1)) // "is a pointer", i.e. "has a more recent revision"
+ {
+ P = (gcptr)v; // look into the next one in the chained list
+ }
+ else if (__builtin_expect(v >= LOCKED, 0))
+ {
+ SpinLoop(3); // spinloop until it is no longer LOCKED, then retry
+ }
+ else if (v & REV_FLAG_NEW_HASH)
+ {
+ return P; // already has the flag
+ }
+ else
+ {
+ // must add the flag
+ if (bool_cas(vp, v, v | REV_FLAG_NEW_HASH))
+ return P;
+ }
+ }
+}
+
/************************************************************/
int DescriptorInit(void)
@@ -690,7 +752,7 @@
if (bool_cas(&next_locked_value, d->my_lock, d->my_lock + 2))
break;
}
- if (d->my_lock < LOCKED)
+ if (d->my_lock < LOCKED || d->my_lock == INEVITABLE)
{
/* XXX fix this limitation */
fprintf(stderr, "XXX error: too many threads ever created "
diff --git a/pypy/translator/stm/src_stm/et.h b/pypy/translator/stm/src_stm/et.h
--- a/pypy/translator/stm/src_stm/et.h
+++ b/pypy/translator/stm/src_stm/et.h
@@ -27,9 +27,11 @@
GCFLAG_NOT_WRITTEN = _first_gcflag << 2,
GCFLAG_LOCAL_COPY = _first_gcflag << 3,
GCFLAG_VISITED = _first_gcflag << 4,
+ GCFLAG_HASH_FIELD = _first_gcflag << 5,
GCFLAG_PREBUILT = GCFLAG_GLOBAL|GCFLAG_NOT_WRITTEN,
- REV_INITIAL = 1
+ REV_INITIAL = 1,
+ REV_FLAG_NEW_HASH = 2,
};
typedef struct pypy_header0 *gcptr;
@@ -84,6 +86,7 @@
//gcptr Allocate(size_t size, int gctid);
_Bool stm_PtrEq(gcptr P1, gcptr P2);
+gcptr stm_HashObject(gcptr P);
gcptr stm_DirectReadBarrier(gcptr);
gcptr stm_DirectReadBarrierFromR(gcptr, gcptr, size_t);
diff --git a/pypy/translator/stm/stmgcintf.py b/pypy/translator/stm/stmgcintf.py
--- a/pypy/translator/stm/stmgcintf.py
+++ b/pypy/translator/stm/stmgcintf.py
@@ -70,3 +70,6 @@
# for testing
abort_and_retry = smexternal('stm_abort_and_retry', [], lltype.Void)
+
+ stm_HashObject = smexternal('stm_HashObject',
+ [llmemory.Address], llmemory.Address)
diff --git a/pypy/translator/stm/test/targetdemo2.py b/pypy/translator/stm/test/targetdemo2.py
--- a/pypy/translator/stm/test/targetdemo2.py
+++ b/pypy/translator/stm/test/targetdemo2.py
@@ -2,6 +2,7 @@
from pypy.module.thread import ll_thread
from pypy.rlib import rstm
from pypy.rlib.objectmodel import invoke_around_extcall, we_are_translated
+from pypy.rlib.objectmodel import compute_identity_hash
from pypy.rlib.debug import ll_assert
from pypy.rpython.lltypesystem import lltype, rffi
@@ -17,7 +18,9 @@
USE_MEMORY = False
anchor = Node(-1)
othernode1 = Node(0)
+ othernode1hash = compute_identity_hash(othernode1)
othernode2 = Node(0)
+ othernode2hash = 0
othernodes = [Node(0) for i in range(1000)]
glob = Global()
@@ -73,6 +76,10 @@
def run(self):
try:
self.value = 0
+ self.lst = []
+ rstm.perform_transaction(ThreadRunner.check_hash,
+ ThreadRunner, self)
+ self.value = 0
rstm.perform_transaction(ThreadRunner.check_inev,
ThreadRunner, self)
self.value = 0
@@ -130,6 +137,23 @@
glob.othernode2.value = new_value
return int(self.value < glob.LENGTH)
+ def check_hash(self, retry_counter):
+ if self.value == 0:
+ glob.othernode2hash = compute_identity_hash(glob.othernode2)
+ assert glob.othernode1hash == compute_identity_hash(glob.othernode1)
+ assert glob.othernode2hash == compute_identity_hash(glob.othernode2)
+ x = Node(retry_counter)
+ lst = self.lst
+ lst.append((x, compute_identity_hash(x)))
+ for i in range(len(lst)):
+ x, expected_hash = lst[i]
+ assert compute_identity_hash(x) == expected_hash
+ if i % 7 == retry_counter:
+ x.value += 1
+ assert compute_identity_hash(x) == expected_hash
+ self.value += 20
+ return int(self.value < glob.LENGTH)
+
class Arg:
foobar = 42
More information about the pypy-commit
mailing list