Commits

Remi Meier committed 8a3b774

fix impersonating the other thread when forcing minor collections in other threads.
also use the direct thread locals in some places (for better or worse).

Comments (0)

Files changed (7)

 # gcc address sanitizer: -fPIE -pie -fsanitize=address -lasan -fno-omit-frame-pointer
 
 debug-%: %.c ${H_FILES} ${C_FILES}
-	gcc -pthread -DDUMP_EXTRA=1 ${DEBUG} $< -o debug-$* -Wall ${C_FILES} -lrt
+	gcc -Wall -pthread -DDUMP_EXTRA=1 ${DEBUG} $< -o debug-$* -Wall ${C_FILES} -lrt
 
 release-%: %.c ${H_FILES} ${C_FILES} stmgc.c
 	gcc -pthread -DNDEBUG -O2 -g $< -o release-$* -Wall stmgc.c -lrt
     }
 
   struct tx_descriptor *d = thread_descriptor;
-  assert(*d->active_ref >= 1);
+  assert(stm_active >= 1);
 
   /* We need the collection_lock for the sequel; this is required notably
      because we're about to edit flags on a protected object.
 void SpinLoop(int num)
 {
   struct tx_descriptor *d = thread_descriptor;
-  assert(*d->active_ref >= 1);
+  assert(stm_active >= 1);
   assert(num < SPINLOOP_REASONS);
   d->num_spinloops[num]++;
   smp_spinloop();
       assert(!stm_has_got_any_lock(d));
     }
 
-  assert(*d->active_ref != 0);
+  assert(stm_active != 0);
   assert(!is_inevitable(d));
   assert(num < ABORT_REASONS);
   d->num_aborts[num]++;
   SpinLoop(SPLP_ABORT);
 
   /* make the transaction no longer active */
-  *d->active_ref = 0;
+  stm_active = 0;
   d->atomic = 0;
 
   /* release the lock */
 void AbortNowIfDelayed(void)
 {
   struct tx_descriptor *d = thread_descriptor;
-  if (*d->active_ref < 0)
+  if (stm_active < 0)
     {
-      int reason = -*d->active_ref;
-      *d->active_ref = 1;
+      int reason = -stm_active;
+      stm_active = 1;
       AbortTransaction(reason);
     }
 }
 {
   struct tx_descriptor *d = thread_descriptor;
   init_transaction(d, 0);
-  *d->active_ref = 1;
+  stm_active = 1;
   d->setjmp_buf = buf;
   d->longjmp_callback = longjmp_callback;
   d->old_thread_local_obj = stm_thread_local_obj;
 
   spinlock_release(d->public_descriptor->collection_lock);
   d->num_commits++;
-  *d->active_ref = 0;
+  stm_active = 0;
   if (!stay_inevitable)
     stm_stop_sharedlock();
 
 {   /* must save roots around this call */
   revision_t cur_time;
   struct tx_descriptor *d = thread_descriptor;
-  if (d == NULL || *d->active_ref != 1)
+  if (d == NULL || stm_active != 1)
     return;  /* I am already inevitable, or not in a transaction at all
                 (XXX statically we should know when we're outside
                 a transaction) */
       assert(d->my_lock & 1);
       assert(d->my_lock >= LOCKED);
       stm_private_rev_num = -d->my_lock;
+      /* Attention: in the following, we add references to real thread-locals
+         to the thread_descriptor. Make sure that force_minor_collections()
+         fakes all of them when doing minor collections in other threads! */
       d->active_ref = &stm_active;
       d->nursery_current_ref = &stm_nursery_current;
       d->nursery_nextlimit_ref = &stm_nursery_nextlimit;
       d->private_revision_ref = &stm_private_rev_num;
       d->read_barrier_cache_ref = &stm_read_barrier_cache;
+
       stm_thread_local_obj = NULL;
       d->thread_local_obj_ref = &stm_thread_local_obj;
       d->max_aborts = -1;
 void stm_call_on_abort(void *key, void callback(void *))
 {
     struct tx_descriptor *d = thread_descriptor;
-    if (d == NULL || *d->active_ref != 1)
+    if (d == NULL || stm_active != 1)
         return;   /* ignore callbacks if we're outside a transaction or
                      in an inevitable transaction (which cannot abort) */
     if (callback == NULL) {
     struct tx_descriptor *saved = thread_descriptor;
     revision_t saved_private_rev = stm_private_rev_num;
     char *saved_read_barrier_cache = stm_read_barrier_cache;
+    int saved_active = stm_active;
+    char *saved_nursery_current = stm_nursery_current;
+    char *saved_nursery_nextlimit = stm_nursery_nextlimit;
 
     assert(saved_private_rev == *saved->private_revision_ref);
     assert(saved_read_barrier_cache == *saved->read_barrier_cache_ref);
+    assert(saved_active == *saved->active_ref);
+    assert(saved_nursery_current == *saved->nursery_current_ref);
+    assert(saved_nursery_nextlimit == *saved->nursery_nextlimit_ref);
 
     for (d = stm_tx_head; d; d = d->tx_next) {
         /* Force a minor collection to run in the thread 'd'.
             /* Hack: temporarily pretend that we "are" the other thread...
              */
             assert(d->shadowstack_end_ref && *d->shadowstack_end_ref);
-            thread_descriptor = d;
-            stm_private_rev_num = *d->private_revision_ref;
+            /* set thread locals to expected values */
+            thread_descriptor      = d;
+            stm_private_rev_num    = *d->private_revision_ref;
             stm_read_barrier_cache = *d->read_barrier_cache_ref;
+            stm_active             = *d->active_ref;
+            stm_nursery_current    = *d->nursery_current_ref;
+            stm_nursery_nextlimit  = *d->nursery_nextlimit_ref;
+            /* save, then point _refs to the new thread-locals */
+            revision_t *d_private_revision_ref = d->private_revision_ref;
+            char **d_read_barrier_cache_ref    = d->read_barrier_cache_ref;
+            int   *d_active_ref                = d->active_ref;
+            char **d_nursery_current_ref       = d->nursery_current_ref;
+            char **d_nursery_nextlimit_ref     = d->nursery_nextlimit_ref;
+            d->private_revision_ref   = &stm_private_rev_num;
+            d->read_barrier_cache_ref = &stm_read_barrier_cache;
+            d->active_ref             = &stm_active;
+            d->nursery_current_ref    = &stm_nursery_current;
+            d->nursery_nextlimit_ref  = &stm_nursery_nextlimit;
 
+            /* we impersonated the other thread. */
             stmgc_minor_collect_no_abort();
 
-            assert(stm_private_rev_num == *d->private_revision_ref);
-            *d->read_barrier_cache_ref = stm_read_barrier_cache;
-
-            thread_descriptor = saved;
-            stm_private_rev_num = saved_private_rev;
-            stm_read_barrier_cache = saved_read_barrier_cache;
+            /* priv_rev didn't change! others may have */
+            assert(*d_private_revision_ref == stm_private_rev_num);
+            *d_read_barrier_cache_ref = stm_read_barrier_cache;
+            *d_active_ref             = stm_active;
+            *d_nursery_current_ref    = stm_nursery_current;
+            *d_nursery_nextlimit_ref  = stm_nursery_nextlimit;
+            /* restore _ref pointers in other thread */
+            d->private_revision_ref = d_private_revision_ref;
+            d->read_barrier_cache_ref = d_read_barrier_cache_ref;
+            d->active_ref = d_active_ref;
+            d->nursery_current_ref = d_nursery_current_ref;
+            d->nursery_nextlimit_ref = d_nursery_nextlimit_ref;
         }
     }
+    /* restore current thread */
+    thread_descriptor = saved;
+    stm_private_rev_num = saved_private_rev;
+    stm_read_barrier_cache = saved_read_barrier_cache;
+    stm_active = saved_active;
+    stm_nursery_current = saved_nursery_current;
+    stm_nursery_nextlimit = saved_nursery_nextlimit;
     stmgc_minor_collect_no_abort();
 }
 
     assert(d->nursery_base == NULL);
     d->nursery_base = stm_malloc(GC_NURSERY);       /* start of nursery */
     d->nursery_end = d->nursery_base + GC_NURSERY;  /* end of nursery */
-    *d->nursery_current_ref = d->nursery_base;           /* current position */
-    *d->nursery_nextlimit_ref = d->nursery_base;         /* next section limit */
+    stm_nursery_current = d->nursery_base;           /* current position */
+    stm_nursery_nextlimit = d->nursery_base;         /* next section limit */
     d->nursery_cleared = NC_REGULAR;
 
     dprintf(("minor: nursery is at [%p to %p]\n", d->nursery_base,
 void stmgc_minor_collect_soon(void)
 {
     struct tx_descriptor *d = thread_descriptor;
-    *d->nursery_current_ref = d->nursery_end;
+    stm_nursery_current = d->nursery_end;
 }
 
 inline static gcptr allocate_nursery(size_t size, revision_t tid)
     /* if 'tid == -1', we must not collect */
     struct tx_descriptor *d = thread_descriptor;
     gcptr P;
-    char *cur = *d->nursery_current_ref;
+    char *cur = stm_nursery_current;
     char *end = cur + size;
     assert((size & 3) == 0);
-    *d->nursery_current_ref = end;
-    if (end > *d->nursery_nextlimit_ref) {
+    stm_nursery_current = end;
+    if (end > stm_nursery_nextlimit) {
         P = allocate_next_section(size, tid);
     }
     else {
        First fix 'nursery_current', left to a bogus value by the caller.
     */
     struct tx_descriptor *d = thread_descriptor;
-    *d->nursery_current_ref -= allocate_size;
+    stm_nursery_current -= allocate_size;
 
     /* Are we asking for a "reasonable" number of bytes, i.e. a value
        at most equal to one section?
     }
 
     /* Are we at the end of the nursery? */
-    if (*d->nursery_nextlimit_ref == d->nursery_end ||
-        *d->nursery_current_ref == d->nursery_end) {   // stmgc_minor_collect_soon()
+    if (stm_nursery_nextlimit == d->nursery_end ||
+        stm_nursery_current == d->nursery_end) {   // stmgc_minor_collect_soon()
         /* Yes */
         if (tid == -1)
             return NULL;    /* cannot collect */
 
     /* Clear the next section */
     if (d->nursery_cleared != NC_ALREADY_CLEARED)
-        memset(*d->nursery_nextlimit_ref, 0, GC_NURSERY_SECTION);
-    *d->nursery_nextlimit_ref += GC_NURSERY_SECTION;
+        memset(stm_nursery_nextlimit, 0, GC_NURSERY_SECTION);
+    stm_nursery_nextlimit += GC_NURSERY_SECTION;
 
     /* Return the object from there */
-    gcptr P = (gcptr)(*d->nursery_current_ref);
-    *d->nursery_current_ref += allocate_size;
+    gcptr P = (gcptr)(stm_nursery_current);
+    stm_nursery_current += allocate_size;
     assert(*d->nursery_current_ref <= *d->nursery_nextlimit_ref);
 
     P->h_tid = tid;
 /* change the default transaction length, and ask if now would be a good
    time to break the transaction (by returning from the 'callback' above
    with a positive value). */
-void stm_set_transaction_length(long length_max);
+void stm_set_transaction_length(long length_max); /* save roots! */
 _Bool stm_should_break_transaction(void);
 
 /* change the atomic counter by 'delta' and return the new value.  Used
    stm_inspect_abort_info().  (XXX details not documented yet) */
 void stm_abort_info_push(gcptr obj, long fieldoffsets[]);
 void stm_abort_info_pop(long count);
-char *stm_inspect_abort_info(void);    /* turns inevitable */
+char *stm_inspect_abort_info(void);    /* turns inevitable, push roots! */
 
 /* mostly for debugging support */
 void stm_abort_and_retry(void);
 static revision_t sync_required = 0;
 
 void stm_set_transaction_length(long length_max)
-{
+{                               /* save roots around this call! */
     BecomeInevitable("set_transaction_length");
     if (length_max <= 0) {
         length_max = 1;
                                    d->reads_size_limit_nonatomic));
     /* if is_inevitable(), reads_size_limit_nonatomic should be 0
        (and thus reads_size_limit too, if !d->atomic.) */
-    if (*d->active_ref == 2)
+    if (stm_active == 2)
         assert(d->reads_size_limit_nonatomic == 0);
 #endif
 
            has configured 'reads_size_limit_nonatomic' to a smaller value.
            When such a shortened transaction succeeds, the next one will
            see its length limit doubled, up to the maximum. */
-        if (counter == 0 && *d->active_ref != 2) {
+        if (counter == 0 && stm_active != 2) {
             unsigned long limit = d->reads_size_limit_nonatomic;
             if (limit != 0 && limit < (stm_regular_length_limit >> 1))
                 limit = (limit << 1) | 1;
             /* atomic transaction: a common case is that callback() returned
                even though we are atomic because we need a major GC.  For
                that case, release and reaquire the rw lock here. */
-            assert(*d->active_ref >= 1);
+            assert(stm_active >= 1);
             stm_possible_safe_point();
         }
 
 {   /* must save roots around this call */
     struct tx_descriptor *d = thread_descriptor;
     if (d->atomic) {
-        assert(*d->active_ref >= 1);
+        assert(stm_active >= 1);
         stm_possible_safe_point();
     }
     else {
 int stm_in_transaction(void)
 {
     struct tx_descriptor *d = thread_descriptor;
-    return d && *d->active_ref;
+    return d && stm_active;
 }
 
 /************************************************************/
 void stm_partial_commit_and_resume_other_threads(void)
 {                               /* push gc roots! */
     struct tx_descriptor *d = thread_descriptor;
-    assert(*d->active_ref == 2);
+    assert(stm_active == 2);
     int atomic = d->atomic;
 
     /* Give up atomicity during commit. This still works because
 
     /* Warning, may block waiting for rwlock_in_transaction while another
        thread runs a major GC */
-    assert(*thread_descriptor->active_ref);
+    assert(stm_active);
     assert(in_single_thread != thread_descriptor);
 
     stm_stop_sharedlock();
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.