|
|
85359c |
2016-01-16 Torvald Riegel <triegel@redhat.com>
|
|
|
85359c |
|
|
|
85359c |
* method-gl.cc (gl_wt_dispatch::trycommit): Ensure proxy privatization
|
|
|
85359c |
safety.
|
|
|
85359c |
* method-ml.cc (ml_wt_dispatch::trycommit): Likewise.
|
|
|
85359c |
* libitm/testsuite/libitm.c/priv-1.c: New.
|
|
|
85359c |
|
|
|
85359c |
--- libitm/method-gl.cc
|
|
|
85359c |
+++ libitm/method-gl.cc
|
|
|
85359c |
@@ -291,12 +291,18 @@ public:
|
|
|
85359c |
// See begin_or_restart() for why we need release memory order here.
|
|
|
85359c |
v = gl_mg::clear_locked(v) + 1;
|
|
|
85359c |
o_gl_mg.orec.store(v, memory_order_release);
|
|
|
85359c |
-
|
|
|
85359c |
- // Need to ensure privatization safety. Every other transaction must
|
|
|
85359c |
- // have a snapshot time that is at least as high as our commit time
|
|
|
85359c |
- // (i.e., our commit must be visible to them).
|
|
|
85359c |
- priv_time = v;
|
|
|
85359c |
}
|
|
|
85359c |
+
|
|
|
85359c |
+ // Need to ensure privatization safety. Every other transaction must have
|
|
|
85359c |
+ // a snapshot time that is at least as high as our commit time (i.e., our
|
|
|
85359c |
+ // commit must be visible to them). Because of proxy privatization, we
|
|
|
85359c |
+ // must ensure that even if we are a read-only transaction. See
|
|
|
85359c |
+ // ml_wt_dispatch::trycommit() for details: We can't get quite the same
|
|
|
85359c |
+ // set of problems because we just use one orec and thus, for example,
|
|
|
85359c |
+ // there cannot be concurrent writers -- but we can still get pending
|
|
|
85359c |
+ // loads to privatized data when not ensuring privatization safety, which
|
|
|
85359c |
+ // is problematic if the program unmaps the privatized memory.
|
|
|
85359c |
+ priv_time = v;
|
|
|
85359c |
return true;
|
|
|
85359c |
}
|
|
|
85359c |
|
|
|
85359c |
--- libitm/method-ml.cc
|
|
|
85359c |
+++ libitm/method-ml.cc
|
|
|
85359c |
@@ -513,6 +513,21 @@ public:
|
|
|
85359c |
if (!tx->writelog.size())
|
|
|
85359c |
{
|
|
|
85359c |
tx->readlog.clear();
|
|
|
85359c |
+ // We still need to ensure privatization safety, unfortunately. While
|
|
|
85359c |
+ // we cannot have privatized anything by ourselves (because we are not
|
|
|
85359c |
+ // an update transaction), we can have observed the commits of
|
|
|
85359c |
+ // another update transaction that privatized something. Because any
|
|
|
85359c |
+ // commit happens before ensuring privatization, our snapshot and
|
|
|
85359c |
+ // commit can thus have happened before ensuring privatization safety
|
|
|
85359c |
+ // for this commit/snapshot time. Therefore, before we can return to
|
|
|
85359c |
+ // nontransactional code that might use the privatized data, we must
|
|
|
85359c |
+ // ensure privatization safety for our snapshot time.
|
|
|
85359c |
+ // This still seems to be better than not allowing use of the
|
|
|
85359c |
+ // snapshot time before privatization safety has been ensured because
|
|
|
85359c |
+ // we at least can run transactions such as this one, and in the
|
|
|
85359c |
+ // meantime the transaction producing this commit time might have
|
|
|
85359c |
+ // finished ensuring privatization safety for it.
|
|
|
85359c |
+ priv_time = tx->shared_state.load(memory_order_relaxed);
|
|
|
85359c |
return true;
|
|
|
85359c |
}
|
|
|
85359c |
|
|
|
85359c |
--- /dev/null
|
|
|
85359c |
+++ libitm/testsuite/libitm.c/priv-1.c
|
|
|
85359c |
@@ -0,0 +1,117 @@
|
|
|
85359c |
+/* Quick stress test for proxy privatization. */
|
|
|
85359c |
+
|
|
|
85359c |
+/* We need to use a TM method that has to enforce privatization safety
|
|
|
85359c |
+ explicitly. */
|
|
|
85359c |
+/* { dg-set-target-env-var ITM_DEFAULT_METHOD "ml_wt" } */
|
|
|
85359c |
+/* { dg-options "-std=gnu11" } */
|
|
|
85359c |
+
|
|
|
85359c |
+#include <stdlib.h>
|
|
|
85359c |
+#include <stdio.h>
|
|
|
85359c |
+#include <pthread.h>
|
|
|
85359c |
+
|
|
|
85359c |
+/* Make them likely to be mapped to different orecs. */
|
|
|
85359c |
+#define ALIGN __attribute__((aligned (256)))
|
|
|
85359c |
+/* Don't make these static to work around PR 68591. */
|
|
|
85359c |
+int x ALIGN;
|
|
|
85359c |
+int *ptr ALIGN;
|
|
|
85359c |
+int *priv_ptr ALIGN;
|
|
|
85359c |
+int priv_value ALIGN;
|
|
|
85359c |
+int barrier ALIGN = 0;
|
|
|
85359c |
+const int iters = 100;
|
|
|
85359c |
+
|
|
|
85359c |
+static void arrive_and_wait (int expected_value)
|
|
|
85359c |
+{
|
|
|
85359c |
+ int now = __atomic_add_fetch (&barrier, 1, __ATOMIC_ACQ_REL);
|
|
|
85359c |
+ while (now < expected_value)
|
|
|
85359c |
+ __atomic_load (&barrier, &now, __ATOMIC_ACQUIRE);
|
|
|
85359c |
+}
|
|
|
85359c |
+
|
|
|
85359c |
+static void __attribute__((transaction_pure,noinline)) delay (int i)
|
|
|
85359c |
+{
|
|
|
85359c |
+ for (volatile int v = 0; v < i; v++);
|
|
|
85359c |
+}
|
|
|
85359c |
+
|
|
|
85359c |
+/* This tries to catch a case in which proxy privatization safety is not
|
|
|
85359c |
+ ensured by privatization_user. Specifically, it's access to the value
|
|
|
85359c |
+ of it's transactional snapshot of ptr must read from an uncommitted write
|
|
|
85359c |
+ by writer; thus, writer must still be active but must have read ptr before
|
|
|
85359c |
+ proxy can privatize *ptr by assigning to ptr.
|
|
|
85359c |
+ We try to make this interleaving more likely by delaying the commit of
|
|
|
85359c |
+ writer and the start of proxy. */
|
|
|
85359c |
+static void *writer (void *dummy __attribute__((unused)))
|
|
|
85359c |
+{
|
|
|
85359c |
+ for (int i = 0; i < iters; i++)
|
|
|
85359c |
+ {
|
|
|
85359c |
+ /* Initialize state in each round. */
|
|
|
85359c |
+ x = 0;
|
|
|
85359c |
+ ptr = &x;
|
|
|
85359c |
+ priv_ptr = NULL;
|
|
|
85359c |
+ int wrote = 1;
|
|
|
85359c |
+ arrive_and_wait (i * 6 + 3);
|
|
|
85359c |
+ /* Interference by another writer. Has a conflict with the proxy
|
|
|
85359c |
+ privatizer. */
|
|
|
85359c |
+ __transaction_atomic
|
|
|
85359c |
+ {
|
|
|
85359c |
+ if (ptr != NULL)
|
|
|
85359c |
+ *ptr = 1;
|
|
|
85359c |
+ else
|
|
|
85359c |
+ wrote = 0;
|
|
|
85359c |
+ delay (2000000);
|
|
|
85359c |
+ }
|
|
|
85359c |
+ arrive_and_wait (i * 6 + 6);
|
|
|
85359c |
+ /* If the previous transaction committed first, wrote == 1 and x == 1;
|
|
|
85359c |
+ otherwise, if the proxy came first, wrote == 0 and priv_value == 0.
|
|
|
85359c |
+ */
|
|
|
85359c |
+ if (wrote != priv_value)
|
|
|
85359c |
+ abort ();
|
|
|
85359c |
+ }
|
|
|
85359c |
+ return NULL;
|
|
|
85359c |
+}
|
|
|
85359c |
+
|
|
|
85359c |
+static void *proxy (void *dummy __attribute__((unused)))
|
|
|
85359c |
+{
|
|
|
85359c |
+ for (int i = 0; i < iters; i++)
|
|
|
85359c |
+ {
|
|
|
85359c |
+ arrive_and_wait (i * 6 + 3);
|
|
|
85359c |
+ delay(1000000);
|
|
|
85359c |
+ __transaction_atomic
|
|
|
85359c |
+ {
|
|
|
85359c |
+ /* Hand-off to privatization-user and its read-only transaction and
|
|
|
85359c |
+ subsequent use of privatization. */
|
|
|
85359c |
+ priv_ptr = ptr;
|
|
|
85359c |
+ ptr = NULL;
|
|
|
85359c |
+ }
|
|
|
85359c |
+ arrive_and_wait (i * 6 + 6);
|
|
|
85359c |
+ }
|
|
|
85359c |
+ return NULL;
|
|
|
85359c |
+}
|
|
|
85359c |
+
|
|
|
85359c |
+static void *privatization_user (void *dummy __attribute__((unused)))
|
|
|
85359c |
+{
|
|
|
85359c |
+ for (int i = 0; i < iters; i++)
|
|
|
85359c |
+ {
|
|
|
85359c |
+ arrive_and_wait (i * 6 + 3);
|
|
|
85359c |
+ /* Spin until we have gotten a pointer from the proxy. Then access
|
|
|
85359c |
+ the value pointed to nontransactionally. */
|
|
|
85359c |
+ int *p = NULL;
|
|
|
85359c |
+ while (p == NULL)
|
|
|
85359c |
+ __transaction_atomic { p = priv_ptr; }
|
|
|
85359c |
+ priv_value = *p;
|
|
|
85359c |
+ arrive_and_wait (i * 6 + 6);
|
|
|
85359c |
+ }
|
|
|
85359c |
+ return NULL;
|
|
|
85359c |
+}
|
|
|
85359c |
+
|
|
|
85359c |
+int main()
|
|
|
85359c |
+{
|
|
|
85359c |
+ pthread_t p[3];
|
|
|
85359c |
+
|
|
|
85359c |
+ pthread_create (p+0, NULL, writer, NULL);
|
|
|
85359c |
+ pthread_create (p+1, NULL, proxy, NULL);
|
|
|
85359c |
+ pthread_create (p+2, NULL, privatization_user, NULL);
|
|
|
85359c |
+
|
|
|
85359c |
+ for (int i = 0; i < 3; ++i)
|
|
|
85359c |
+ pthread_join (p[i], NULL);
|
|
|
85359c |
+
|
|
|
85359c |
+ return 0;
|
|
|
85359c |
+}
|