[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