aboutsummaryrefslogtreecommitdiff
path: root/libitm/method-wbetl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libitm/method-wbetl.cc')
-rw-r--r--libitm/method-wbetl.cc628
1 files changed, 0 insertions, 628 deletions
diff --git a/libitm/method-wbetl.cc b/libitm/method-wbetl.cc
deleted file mode 100644
index 093d1c7..0000000
--- a/libitm/method-wbetl.cc
+++ /dev/null
@@ -1,628 +0,0 @@
-/* Copyright (C) 2009, 2011 Free Software Foundation, Inc.
- Contributed by Richard Henderson <rth@redhat.com>.
-
- This file is part of the GNU Transactional Memory Library (libitm).
-
- Libitm is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- Libitm is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- Under Section 7 of GPL version 3, you are granted additional
- permissions described in the GCC Runtime Library Exception, version
- 3.1, as published by the Free Software Foundation.
-
- You should have received a copy of the GNU General Public License and
- a copy of the GCC Runtime Library Exception along with this program;
- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
- <http://www.gnu.org/licenses/>. */
-
-#include "libitm_i.h"
-
-namespace {
-
-using namespace GTM;
-
-class wbetl_dispatch : public abi_dispatch
-{
- private:
- static const size_t RW_SET_SIZE = 4096;
-
- struct r_entry
- {
- gtm_version version;
- gtm_stmlock *lock;
- };
-
- r_entry *m_rset_entries;
- size_t m_rset_nb_entries;
- size_t m_rset_size;
-
- struct w_entry
- {
- /* There's a hashtable where the locks are held, so multiple
- cachelines can hash to a given bucket. This link points to the
- possible next cacheline that also hashes to this bucket. */
- struct w_entry *next;
-
- /* Every entry in this bucket (accessed by NEXT) has the same LOCK
- address below. */
- gtm_stmlock *lock;
-
- gtm_cacheline *addr;
- gtm_cacheline *value;
- gtm_version version;
- };
-
- w_entry *m_wset_entries;
- size_t m_wset_nb_entries;
- size_t m_wset_size;
- bool m_wset_reallocate;
-
- gtm_version m_start;
- gtm_version m_end;
-
- gtm_cacheline_page *m_cache_page;
- unsigned m_n_cache_page;
-
- private:
- bool local_w_entry_p (w_entry *w);
- bool has_read (gtm_stmlock *lock);
- bool validate();
- bool extend();
-
- gtm_cacheline *do_write_lock(gtm_cacheline *);
- gtm_cacheline *do_after_write_lock(gtm_cacheline *);
- const gtm_cacheline *do_read_lock(const gtm_cacheline *, bool);
-
- public:
- wbetl_dispatch();
-
- virtual const gtm_cacheline *read_lock(const gtm_cacheline *, ls_modifier);
- virtual mask_pair write_lock(gtm_cacheline *, ls_modifier);
-
- virtual bool trycommit();
- virtual void rollback();
- virtual void reinit();
- virtual void fini();
- virtual bool trydropreference (void *, size_t);
-};
-
-/* Check if W is one of our write locks. */
-
-inline bool
-wbetl_dispatch::local_w_entry_p (w_entry *w)
-{
- return (m_wset_entries <= w && w < m_wset_entries + m_wset_nb_entries);
-}
-
-/* Check if stripe has been read previously. */
-
-inline bool
-wbetl_dispatch::has_read (gtm_stmlock *lock)
-{
- // ??? Consider using an AA tree to lookup the r_set entries.
- size_t n = m_rset_nb_entries;
- for (size_t i = 0; i < n; ++i)
- if (m_rset_entries[i].lock == lock)
- return true;
-
- return false;
-}
-
-/* Validate read set, i.e. check if all read addresses are still valid now. */
-
-bool
-wbetl_dispatch::validate ()
-{
- __sync_synchronize ();
-
- size_t n = m_rset_nb_entries;
- for (size_t i = 0; i < n; ++i)
- {
- r_entry *r = &m_rset_entries[i];
- gtm_stmlock l = *r->lock;
-
- if (gtm_stmlock_owned_p (l))
- {
- w_entry *w = (w_entry *) gtm_stmlock_get_addr (l);
-
- // If someone has locked us, it better be by someone in the
- // current thread.
- if (!local_w_entry_p (w))
- return false;
- }
- else if (gtm_stmlock_get_version (l) != r->version)
- return false;
- }
-
- return true;
-}
-
-/* Extend the snapshot range. */
-
-bool
-wbetl_dispatch::extend ()
-{
- gtm_version now = gtm_get_clock ();
-
- if (validate ())
- {
- m_end = now;
- return true;
- }
- return false;
-}
-
-/* Acquire a write lock on ADDR. */
-
-gtm_cacheline *
-wbetl_dispatch::do_write_lock(gtm_cacheline *addr)
-{
- gtm_stmlock *lock;
- gtm_stmlock l, l2;
- gtm_version version;
- w_entry *w, *prev = NULL;
-
- lock = gtm_get_stmlock (addr);
- l = *lock;
-
- restart_no_load:
- if (gtm_stmlock_owned_p (l))
- {
- w = (w_entry *) gtm_stmlock_get_addr (l);
-
- /* Did we previously write the same address? */
- if (local_w_entry_p (w))
- {
- prev = w;
- while (1)
- {
- if (addr == prev->addr)
- return prev->value;
- if (prev->next == NULL)
- break;
- prev = prev->next;
- }
-
- /* Get version from previous entry write set. */
- version = prev->version;
-
- /* If there's not enough entries, we must reallocate the array,
- which invalidates all pointers to write set entries, which
- means we have to restart the transaction. */
- if (m_wset_nb_entries == m_wset_size)
- {
- m_wset_size *= 2;
- m_wset_reallocate = true;
- gtm_tx()->restart (RESTART_REALLOCATE);
- }
-
- w = &m_wset_entries[m_wset_nb_entries];
- goto do_write;
- }
-
- gtm_tx()->restart (RESTART_LOCKED_WRITE);
- }
- else
- {
- version = gtm_stmlock_get_version (l);
-
- /* We might have read an older version previously. */
- if (version > m_end)
- {
- if (has_read (lock))
- gtm_tx()->restart (RESTART_VALIDATE_WRITE);
- }
-
- /* Extend write set, aborting to reallocate write set entries. */
- if (m_wset_nb_entries == m_wset_size)
- {
- m_wset_size *= 2;
- m_wset_reallocate = true;
- gtm_tx()->restart (RESTART_REALLOCATE);
- }
-
- /* Acquire the lock. */
- w = &m_wset_entries[m_wset_nb_entries];
- l2 = gtm_stmlock_set_owned (w);
- l = __sync_val_compare_and_swap (lock, l, l2);
- if (l != l2)
- goto restart_no_load;
- }
-
- do_write:
- m_wset_nb_entries++;
- if (prev != NULL)
- prev->next = w;
- w->next = 0;
- w->lock = lock;
- w->addr = addr;
- w->version = version;
-
- gtm_cacheline_page *page = m_cache_page;
- unsigned index = m_n_cache_page;
-
- if (page == NULL || index == gtm_cacheline_page::LINES)
- {
- gtm_cacheline_page *npage = new gtm_cacheline_page;
- npage->prev = page;
- m_cache_page = page = npage;
- m_n_cache_page = 1;
- index = 0;
- }
- else
- m_n_cache_page = index + 1;
-
- gtm_cacheline *line = &page->lines[index];
- w->value = line;
- page->masks[index] = 0;
- *line = *addr;
-
- return line;
-}
-
-gtm_cacheline *
-wbetl_dispatch::do_after_write_lock (gtm_cacheline *addr)
-{
- gtm_stmlock *lock;
- gtm_stmlock l;
- w_entry *w;
-
- lock = gtm_get_stmlock (addr);
- l = *lock;
- assert (gtm_stmlock_owned_p (l));
-
- w = (w_entry *) gtm_stmlock_get_addr (l);
- assert (local_w_entry_p (w));
-
- while (1)
- {
- if (addr == w->addr)
- return w->value;
- w = w->next;
- }
-}
-
-/* Acquire a read lock on ADDR. */
-
-const gtm_cacheline *
-wbetl_dispatch::do_read_lock (const gtm_cacheline *addr, bool after_read)
-{
- gtm_stmlock *lock;
- gtm_stmlock l, l2;
- gtm_version version;
- w_entry *w;
-
- lock = gtm_get_stmlock (addr);
- l = *lock;
-
- restart_no_load:
- if (gtm_stmlock_owned_p (l))
- {
- w = (w_entry *) gtm_stmlock_get_addr (l);
-
- /* Did we previously write the same address? */
- if (local_w_entry_p (w))
- {
- while (1)
- {
- if (addr == w->addr)
- return w->value;
- if (w->next == NULL)
- return addr;
- w = w->next;
- }
- }
-
- gtm_tx()->restart (RESTART_LOCKED_READ);
- }
-
- version = gtm_stmlock_get_version (l);
-
- /* If version is no longer valid, re-validate the read set. */
- if (version > m_end)
- {
- if (!extend ())
- gtm_tx()->restart (RESTART_VALIDATE_READ);
-
- if (!after_read)
- {
- // Verify that the version has not yet been overwritten. The read
- // value has not yet been added to read set and may not have been
- // checked during the extend.
- //
- // ??? This only makes sense if we're actually reading the value
- // and returning it now -- which I believe the original TinySTM
- // did. This doesn't make a whole lot of sense when we're
- // manipulating cachelines as we are now. Do we need some other
- // form of lock verification here, or is the validate call in
- // trycommit sufficient?
-
- __sync_synchronize ();
- l2 = *lock;
- if (l != l2)
- {
- l = l2;
- goto restart_no_load;
- }
- }
- }
-
- if (!after_read)
- {
- r_entry *r;
-
- /* Add the address and version to the read set. */
- if (m_rset_nb_entries == m_rset_size)
- {
- m_rset_size *= 2;
-
- m_rset_entries = (r_entry *)
- xrealloc (m_rset_entries, m_rset_size * sizeof(r_entry));
- }
- r = &m_rset_entries[m_rset_nb_entries++];
- r->version = version;
- r->lock = lock;
- }
-
- return addr;
-}
-
-const gtm_cacheline *
-wbetl_dispatch::read_lock (const gtm_cacheline *addr, ls_modifier ltype)
-{
- switch (ltype)
- {
- case NONTXNAL:
- return addr;
- case R:
- return do_read_lock (addr, false);
- case RaR:
- return do_read_lock (addr, true);
- case RaW:
- return do_after_write_lock (const_cast<gtm_cacheline *>(addr));
- case RfW:
- return do_write_lock (const_cast<gtm_cacheline *>(addr));
- default:
- abort ();
- }
-}
-
-abi_dispatch::mask_pair
-wbetl_dispatch::write_lock (gtm_cacheline *addr, ls_modifier ltype)
-{
- gtm_cacheline *line;
-
- switch (ltype)
- {
- case NONTXNAL:
- return mask_pair (addr, &mask_sink);
- case W:
- case WaR:
- line = do_write_lock (addr);
- break;
- case WaW:
- line = do_after_write_lock (addr);
- break;
- default:
- abort ();
- }
-
- return mask_pair (line, gtm_cacheline_page::mask_for_page_line (line));
-}
-
-/* Commit the transaction. */
-
-bool
-wbetl_dispatch::trycommit ()
-{
- const size_t n = m_wset_nb_entries;
- if (n != 0)
- {
- /* Get commit timestamp. */
- gtm_version t = gtm_inc_clock ();
-
- /* Validate only if a concurrent transaction has started since. */
- if (m_start != t - 1 && !validate ())
- return false;
-
- /* Install new versions. */
- for (size_t i = 0; i < n; ++i)
- {
- w_entry *w = &m_wset_entries[i];
- gtm_cacheline_mask mask
- = *gtm_cacheline_page::mask_for_page_line (w->value);
-
- /* Filter out any updates that overlap the libitm stack. */
- mask = gtm_mask_stack (w->addr, mask);
-
- gtm_cacheline::copy_mask (w->addr, w->value, mask);
- }
-
- /* Only emit barrier after all cachelines are copied. */
- gtm_cacheline::copy_mask_wb ();
-
- /* Drop locks. */
- for (size_t i = 0; i < n; ++i)
- {
- w_entry *w = &m_wset_entries[i];
-
- /* Every link along the chain has the same lock, but only
- bother dropping the lock once per bucket (at the end). */
- if (w->next == NULL)
- *w->lock = gtm_stmlock_set_version (t);
- }
- }
-
- __sync_synchronize ();
- return true;
-}
-
-void
-wbetl_dispatch::rollback ()
-{
- /* Drop locks. */
- const size_t n = m_wset_nb_entries;
- for (size_t i = 0; i < n; ++i)
- {
- w_entry *w = &m_wset_entries[i];
-
- /* Every link along the chain has the same lock, but only
- bother dropping the lock once per bucket (at the end). */
- if (w->next == NULL)
- *w->lock = gtm_stmlock_set_version (w->version);
- }
-
- __sync_synchronize ();
-}
-
-void
-wbetl_dispatch::reinit ()
-{
- gtm_cacheline_page *page;
-
- m_rset_nb_entries = 0;
- m_wset_nb_entries = 0;
-
- if (m_wset_reallocate)
- {
- m_wset_reallocate = 0;
- m_wset_entries = (w_entry *)
- xrealloc (m_wset_entries, m_wset_size * sizeof(w_entry));
- }
-
- page = m_cache_page;
- if (page)
- {
- /* Release all but one of the pages of cachelines. */
- gtm_cacheline_page *prev = page->prev;
- if (prev)
- {
- page->prev = 0;
- delete prev;
- }
-
- /* Start the next cacheline allocation from the beginning. */
- m_n_cache_page = 0;
- }
-
- m_start = m_end = gtm_get_clock ();
-}
-
-void
-wbetl_dispatch::fini ()
-{
- delete m_cache_page;
- free (m_rset_entries);
- free (m_wset_entries);
- delete this;
-}
-
-/* Attempt to drop any internal references to PTR. Return TRUE if successful.
-
- This is an adaptation of the transactional memcpy function.
-
- What we do here is flush out the current transactional content of
- PTR to real memory, and remove the write mask bits associated with
- it so future commits will ignore this piece of memory. */
-
-bool
-wbetl_dispatch::trydropreference (void *ptr, size_t size)
-{
- if (size == 0)
- return true;
-
- if (!validate ())
- return false;
-
- uintptr_t isrc = (uintptr_t)ptr;
- // The position in the source cacheline where *PTR starts.
- uintptr_t sofs = isrc & (CACHELINE_SIZE - 1);
- gtm_cacheline *src
- = reinterpret_cast<gtm_cacheline *>(isrc & -CACHELINE_SIZE);
- unsigned char *dst = (unsigned char *)ptr;
- abi_dispatch::mask_pair pair;
-
- // If we're trying to drop a reference, we should already have a
- // write lock on it. If we don't have one, there's no work to do.
- if (!gtm_stmlock_owned_p (*gtm_get_stmlock (src)))
- return true;
-
- // We copy the data in three stages:
-
- // (a) Copy stray bytes at the beginning that are smaller than a
- // cacheline.
- if (sofs != 0)
- {
- size_t sleft = CACHELINE_SIZE - sofs;
- size_t min = (size <= sleft ? size : sleft);
-
- // WaW will give us the current locked entry.
- pair = this->write_lock (src, WaW);
-
- // *jedi mind wave*...these aren't the droids you're looking for.
- *pair.mask &= ~((((gtm_cacheline_mask)1 << min) - 1) << sofs);
-
- memcpy (dst, &pair.line->b[sofs], min);
- dst += min;
- src++;
- size -= min;
- }
-
- // (b) Copy subsequent cacheline sized chunks.
- while (size >= CACHELINE_SIZE)
- {
- pair = this->write_lock(src, WaW);
- *pair.mask = 0;
- memcpy (dst, pair.line, CACHELINE_SIZE);
- dst += CACHELINE_SIZE;
- src++;
- size -= CACHELINE_SIZE;
- }
-
- // (c) Copy anything left over.
- if (size != 0)
- {
- pair = this->write_lock(src, WaW);
- *pair.mask &= ~(((gtm_cacheline_mask)1 << size) - 1);
- memcpy (dst, pair.line, size);
- }
-
- // No need to drop locks, since we're going to abort the transaction
- // anyhow.
-
- return true;
-}
-
-
-wbetl_dispatch::wbetl_dispatch ()
- : abi_dispatch (false, false)
-{
- m_rset_entries = (r_entry *) xmalloc (RW_SET_SIZE * sizeof(r_entry));
- m_rset_nb_entries = 0;
- m_rset_size = RW_SET_SIZE;
-
- m_wset_entries = (w_entry *) xmalloc (RW_SET_SIZE * sizeof(w_entry));
- m_wset_nb_entries = 0;
- m_wset_size = RW_SET_SIZE;
- m_wset_reallocate = false;
-
- m_start = m_end = gtm_get_clock ();
-
- m_cache_page = 0;
- m_n_cache_page = 0;
-}
-
-} // anon namespace
-
-abi_dispatch *
-GTM::dispatch_wbetl ()
-{
- return new wbetl_dispatch ();
-}