@@ -38,52 +38,58 @@ uint64_t GTM::gtm_spin_count_var = 1000;
static _ITM_transactionId_t global_tid;
-/* Allocate a transaction structure. Reuse an old one if possible. */
+// Provides a on-thread-exit callback used to release per-thread data.
+static pthread_key_t tx_release_key;
+static pthread_once_t tx_release_once = PTHREAD_ONCE_INIT;
+
+
+/* Allocate a transaction structure. */
void *
GTM::gtm_transaction::operator new (size_t s)
{
- gtm_thread *thr = setup_gtm_thr ();
void *tx;
assert(s == sizeof(gtm_transaction));
- if (thr->free_tx_count == 0)
- tx = xmalloc (sizeof (gtm_transaction));
- else
- {
- thr->free_tx_count--;
- tx = thr->free_tx[thr->free_tx_idx];
- thr->free_tx_idx = (thr->free_tx_idx + 1) % gtm_thread::MAX_FREE_TX;
- }
+ tx = xmalloc (sizeof (gtm_transaction), true);
memset (tx, 0, sizeof (gtm_transaction));
return tx;
}
-/* Queue a transaction structure for freeing. We never free the given
- transaction immediately -- this is a requirement of abortTransaction
- as the jmpbuf is used immediately after calling this function. Thus
- the requirement that this queue be per-thread. */
+/* Free the given transaction. Raises an error if the transaction is still
+ in use. */
void
GTM::gtm_transaction::operator delete(void *tx)
{
- gtm_thread *thr = gtm_thr ();
- unsigned idx
- = (thr->free_tx_idx + thr->free_tx_count) % gtm_thread::MAX_FREE_TX;
+ free(tx);
+}
- if (thr->free_tx_count == gtm_thread::MAX_FREE_TX)
+static void
+thread_exit_handler(void *dummy __attribute__((unused)))
+{
+ gtm_transaction *tx = gtm_tx();
+ if (tx)
{
- thr->free_tx_idx = (thr->free_tx_idx + 1) % gtm_thread::MAX_FREE_TX;
- free (thr->free_tx[idx]);
+ if (tx->nesting > 0)
+ GTM_fatal("Thread exit while a transaction is still active.");
+ delete tx;
+ set_gtm_tx(NULL);
}
- else
- thr->free_tx_count++;
+ if (pthread_setspecific(tx_release_key, NULL))
+ GTM_fatal("Setting tx release TLS key failed.");
+}
- thr->free_tx[idx] = tx;
+static void
+thread_exit_init()
+{
+ if (pthread_key_create(&tx_release_key, thread_exit_handler))
+ GTM_fatal("Creating tx release TLS key failed.");
}
+
#ifndef HAVE_64BIT_SYNC_BUILTINS
static pthread_mutex_t global_tid_lock = PTHREAD_MUTEX_INITIALIZER;
#endif
@@ -113,10 +119,17 @@ GTM::gtm_transaction::begin_transaction (uint32_t prop, const gtm_jmpbuf *jb)
GTM_fatal("pr_undoLogCode not supported");
tx = gtm_tx();
- if (tx == NULL)
+ if (unlikely(tx == NULL))
{
tx = new gtm_transaction;
set_gtm_tx(tx);
+
+ if (pthread_once(&tx_release_once, thread_exit_init))
+ GTM_fatal("Initializing tx release TLS key failed.");
+ // Any non-null value is sufficient to trigger releasing of this
+ // transaction when the current thread terminates.
+ if (pthread_setspecific(tx_release_key, tx))
+ GTM_fatal("Setting tx release TLS key failed.");
}
if (tx->nesting > 0)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+/* Copyright (C) 2008, 2009, 2011 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Transactional Memory Library (libitm).
@@ -41,15 +41,6 @@ struct gtm_thread
abi_dispatch *disp;
#endif
- // The maximum number of free gtm_transaction structs to be kept.
- // This number must be greater than 1 in order for transaction abort
- // to be handled properly.
- static const unsigned MAX_FREE_TX = 8;
-
- // A queue of free gtm_transaction structs.
- void *free_tx[MAX_FREE_TX];
- unsigned free_tx_idx, free_tx_count;
-
// The value returned by _ITM_getThreadnum to identify this thread.
// ??? At present, this is densely allocated beginning with 1 and
// we don't bother filling in this value until it is requested.
new file mode 100644
@@ -0,0 +1,47 @@
+/* This test triggers execution of the code that releases per-thread
+ transaction data when a thread exists, potentially repeatedly. However,
+ we currently cannot check whether the data has indeed been released. */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+static int round = 0;
+static pthread_key_t key;
+
+static void
+thread_exit_handler(void *dummy __attribute__((unused)))
+{
+ if (round == 0)
+ abort();
+ if (round == 1)
+ {
+ // ??? It would be good if we could check here that the transaction has
+ // indeed been released.
+ __transaction { round++; }
+ if (pthread_setspecific(key, &round))
+ abort();
+ }
+ // ??? It would be good if we could check here that the transaction has
+ // indeed been released (again).
+}
+
+static void *thread (void *dummy __attribute__((unused)))
+{
+ if (pthread_key_create(&key, thread_exit_handler))
+ abort();
+ if (pthread_setspecific(key, &round))
+ abort();
+ __transaction { round++; }
+ return NULL;
+}
+
+int main()
+{
+ pthread_t pt;
+ pthread_create(&pt, NULL, thread, NULL);
+ pthread_join(pt, NULL);
+ if (round != 2)
+ abort();
+ return 0;
+}