-/* Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+/* Copyright (C) 2011-2024 Free Software Foundation, Inc.
Contributed by Torvald Riegel <triegel@redhat.com>.
This file is part of the GNU Transactional Memory Library (libitm).
static gtm_word clear_locked(gtm_word l) { return l & ~LOCK_BIT; }
// The global ownership record.
- atomic<gtm_word> orec;
+ // No tail-padding necessary (the virtual functions aren't used frequently).
+ atomic<gtm_word> orec __attribute__((aligned(HW_CACHELINE_SIZE)));
virtual void init()
{
virtual void fini() { }
};
-// TODO cacheline padding
static gl_mg o_gl_mg;
// See begin_or_restart() for why we need release memory order here.
v = gl_mg::clear_locked(v) + 1;
o_gl_mg.orec.store(v, memory_order_release);
-
- // Need to ensure privatization safety. Every other transaction must
- // have a snapshot time that is at least as high as our commit time
- // (i.e., our commit must be visible to them).
- priv_time = v;
}
+
+ // Need to ensure privatization safety. Every other transaction must have
+ // a snapshot time that is at least as high as our commit time (i.e., our
+ // commit must be visible to them). Because of proxy privatization, we
+ // must ensure that even if we are a read-only transaction. See
+ // ml_wt_dispatch::trycommit() for details: We can't get quite the same
+ // set of problems because we just use one orec and thus, for example,
+ // there cannot be concurrent writers -- but we can still get pending
+ // loads to privatized data when not ensuring privatization safety, which
+ // is problematic if the program unmaps the privatized memory.
+ priv_time = v;
return true;
}
// value that is correct wrt. privatization safety.
if (gl_mg::is_locked(v))
{
- // Release the global orec, increasing its version number / timestamp.
- // See begin_or_restart() for why we need release memory order here.
+ // With our rollback, global time increases.
v = gl_mg::clear_locked(v) + 1;
- o_gl_mg.orec.store(v, memory_order_release);
- // Also reset the timestamp published via shared_state.
+ // First reset the timestamp published via shared_state. Release
+ // memory order will make this happen after undoing prior data writes.
+ // This must also happen before we actually release the global orec
+ // next, so that future update transactions in other threads observe
+ // a meaningful snapshot time for our transaction; otherwise, they
+ // could read a shared_store value with the LOCK_BIT set, which can
+ // break privatization safety because it's larger than the actual
+ // snapshot time. Note that we only need to consider other update
+ // transactions because only those will potentially privatize data.
tx->shared_state.store(v, memory_order_release);
- // We need a store-load barrier after this store to prevent it
- // from becoming visible after later data loads because the
- // previous value of shared_state has been higher than the actual
- // snapshot time (the lock bit had been set), which could break
- // privatization safety. We do not need a barrier before this
- // store (see pre_write() for an explanation).
- // ??? What is the precise reasoning in the C++11 model?
- atomic_thread_fence(memory_order_seq_cst);
+ // Release the global orec, increasing its version number / timestamp.
+ // See begin_or_restart() for why we need release memory order here,
+ // and we also need it to make future update transactions read the
+ // prior update to shared_state too (update transactions acquire the
+ // global orec with acquire memory order).
+ o_gl_mg.orec.store(v, memory_order_release);
}
}
+ virtual bool snapshot_most_recent()
+ {
+ // This is the same check as in validate() except that we do not restart
+ // on failure but simply return the result.
+ return o_gl_mg.orec.load(memory_order_relaxed)
+ == gtm_thr()->shared_state.load(memory_order_relaxed);
+ }
+
+
CREATE_DISPATCH_METHODS(virtual, )
CREATE_DISPATCH_METHODS_MEM()
- gl_wt_dispatch() : abi_dispatch(false, true, false, false, &o_gl_mg)
+ gl_wt_dispatch() : abi_dispatch(false, true, false, false, 0, &o_gl_mg)
{ }
};