annotate libitm/method-ml.cc @ 158:494b0b89df80 default tip

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 18:13:55 +0900
parents 1830386684a0
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
1 /* Copyright (C) 2012-2020 Free Software Foundation, Inc.
111
kono
parents:
diff changeset
2 Contributed by Torvald Riegel <triegel@redhat.com>.
kono
parents:
diff changeset
3
kono
parents:
diff changeset
4 This file is part of the GNU Transactional Memory Library (libitm).
kono
parents:
diff changeset
5
kono
parents:
diff changeset
6 Libitm is free software; you can redistribute it and/or modify it
kono
parents:
diff changeset
7 under the terms of the GNU General Public License as published by
kono
parents:
diff changeset
8 the Free Software Foundation; either version 3 of the License, or
kono
parents:
diff changeset
9 (at your option) any later version.
kono
parents:
diff changeset
10
kono
parents:
diff changeset
11 Libitm is distributed in the hope that it will be useful, but WITHOUT ANY
kono
parents:
diff changeset
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
kono
parents:
diff changeset
13 FOR A PARTICULAR PURPOSE. See the GNU General Public License for
kono
parents:
diff changeset
14 more details.
kono
parents:
diff changeset
15
kono
parents:
diff changeset
16 Under Section 7 of GPL version 3, you are granted additional
kono
parents:
diff changeset
17 permissions described in the GCC Runtime Library Exception, version
kono
parents:
diff changeset
18 3.1, as published by the Free Software Foundation.
kono
parents:
diff changeset
19
kono
parents:
diff changeset
20 You should have received a copy of the GNU General Public License and
kono
parents:
diff changeset
21 a copy of the GCC Runtime Library Exception along with this program;
kono
parents:
diff changeset
22 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
kono
parents:
diff changeset
23 <http://www.gnu.org/licenses/>. */
kono
parents:
diff changeset
24
kono
parents:
diff changeset
25 #include "libitm_i.h"
kono
parents:
diff changeset
26
kono
parents:
diff changeset
27 using namespace GTM;
kono
parents:
diff changeset
28
kono
parents:
diff changeset
29 namespace {
kono
parents:
diff changeset
30
kono
parents:
diff changeset
31 // This group consists of all TM methods that synchronize via multiple locks
kono
parents:
diff changeset
32 // (or ownership records).
kono
parents:
diff changeset
33 struct ml_mg : public method_group
kono
parents:
diff changeset
34 {
kono
parents:
diff changeset
35 static const gtm_word LOCK_BIT = (~(gtm_word)0 >> 1) + 1;
kono
parents:
diff changeset
36 static const gtm_word INCARNATION_BITS = 3;
kono
parents:
diff changeset
37 static const gtm_word INCARNATION_MASK = 7;
kono
parents:
diff changeset
38 // Maximum time is all bits except the lock bit, the overflow reserve bit,
kono
parents:
diff changeset
39 // and the incarnation bits).
kono
parents:
diff changeset
40 static const gtm_word TIME_MAX = (~(gtm_word)0 >> (2 + INCARNATION_BITS));
kono
parents:
diff changeset
41 // The overflow reserve bit is the MSB of the timestamp part of an orec,
kono
parents:
diff changeset
42 // so we can have TIME_MAX+1 pending timestamp increases before we overflow.
kono
parents:
diff changeset
43 static const gtm_word OVERFLOW_RESERVE = TIME_MAX + 1;
kono
parents:
diff changeset
44
kono
parents:
diff changeset
45 static bool is_locked(gtm_word o) { return o & LOCK_BIT; }
kono
parents:
diff changeset
46 static gtm_word set_locked(gtm_thread *tx)
kono
parents:
diff changeset
47 {
kono
parents:
diff changeset
48 return ((uintptr_t)tx >> 1) | LOCK_BIT;
kono
parents:
diff changeset
49 }
kono
parents:
diff changeset
50 // Returns a time that includes the lock bit, which is required by both
kono
parents:
diff changeset
51 // validate() and is_more_recent_or_locked().
kono
parents:
diff changeset
52 static gtm_word get_time(gtm_word o) { return o >> INCARNATION_BITS; }
kono
parents:
diff changeset
53 static gtm_word set_time(gtm_word time) { return time << INCARNATION_BITS; }
kono
parents:
diff changeset
54 static bool is_more_recent_or_locked(gtm_word o, gtm_word than_time)
kono
parents:
diff changeset
55 {
kono
parents:
diff changeset
56 // LOCK_BIT is the MSB; thus, if O is locked, it is larger than TIME_MAX.
kono
parents:
diff changeset
57 return get_time(o) > than_time;
kono
parents:
diff changeset
58 }
kono
parents:
diff changeset
59 static bool has_incarnation_left(gtm_word o)
kono
parents:
diff changeset
60 {
kono
parents:
diff changeset
61 return (o & INCARNATION_MASK) < INCARNATION_MASK;
kono
parents:
diff changeset
62 }
kono
parents:
diff changeset
63 static gtm_word inc_incarnation(gtm_word o) { return o + 1; }
kono
parents:
diff changeset
64
kono
parents:
diff changeset
65 // The shared time base.
kono
parents:
diff changeset
66 atomic<gtm_word> time __attribute__((aligned(HW_CACHELINE_SIZE)));
kono
parents:
diff changeset
67
kono
parents:
diff changeset
68 // The array of ownership records.
kono
parents:
diff changeset
69 atomic<gtm_word>* orecs __attribute__((aligned(HW_CACHELINE_SIZE)));
kono
parents:
diff changeset
70 char tailpadding[HW_CACHELINE_SIZE - sizeof(atomic<gtm_word>*)];
kono
parents:
diff changeset
71
kono
parents:
diff changeset
72 // Location-to-orec mapping. Stripes of 32B mapped to 2^16 orecs using
kono
parents:
diff changeset
73 // multiplicative hashing. See Section 5.2.2 of Torvald Riegel's PhD thesis
kono
parents:
diff changeset
74 // for the background on this choice of hash function and parameters:
kono
parents:
diff changeset
75 // http://nbn-resolving.de/urn:nbn:de:bsz:14-qucosa-115596
kono
parents:
diff changeset
76 // We pick the Mult32 hash because it works well with fewer orecs (i.e.,
kono
parents:
diff changeset
77 // less space overhead and just 32b multiplication).
kono
parents:
diff changeset
78 // We may want to check and potentially change these settings once we get
kono
parents:
diff changeset
79 // better or just more benchmarks.
kono
parents:
diff changeset
80 static const gtm_word L2O_ORECS_BITS = 16;
kono
parents:
diff changeset
81 static const gtm_word L2O_ORECS = 1 << L2O_ORECS_BITS;
kono
parents:
diff changeset
82 // An iterator over the orecs covering the region [addr,addr+len).
kono
parents:
diff changeset
83 struct orec_iterator
kono
parents:
diff changeset
84 {
kono
parents:
diff changeset
85 static const gtm_word L2O_SHIFT = 5;
kono
parents:
diff changeset
86 static const uint32_t L2O_MULT32 = 81007;
kono
parents:
diff changeset
87 uint32_t mult;
kono
parents:
diff changeset
88 size_t orec;
kono
parents:
diff changeset
89 size_t orec_end;
kono
parents:
diff changeset
90 orec_iterator (const void* addr, size_t len)
kono
parents:
diff changeset
91 {
kono
parents:
diff changeset
92 uint32_t a = (uintptr_t) addr >> L2O_SHIFT;
kono
parents:
diff changeset
93 uint32_t ae = ((uintptr_t) addr + len + (1 << L2O_SHIFT) - 1)
kono
parents:
diff changeset
94 >> L2O_SHIFT;
kono
parents:
diff changeset
95 mult = a * L2O_MULT32;
kono
parents:
diff changeset
96 orec = mult >> (32 - L2O_ORECS_BITS);
kono
parents:
diff changeset
97 // We can't really avoid this second multiplication unless we use a
kono
parents:
diff changeset
98 // branch instead or know more about the alignment of addr. (We often
kono
parents:
diff changeset
99 // know len at compile time because of instantiations of functions
kono
parents:
diff changeset
100 // such as _ITM_RU* for accesses of specific lengths.
kono
parents:
diff changeset
101 orec_end = (ae * L2O_MULT32) >> (32 - L2O_ORECS_BITS);
kono
parents:
diff changeset
102 }
kono
parents:
diff changeset
103 size_t get() { return orec; }
kono
parents:
diff changeset
104 void advance()
kono
parents:
diff changeset
105 {
kono
parents:
diff changeset
106 // We cannot simply increment orec because L2O_MULT32 is larger than
kono
parents:
diff changeset
107 // 1 << (32 - L2O_ORECS_BITS), and thus an increase of the stripe (i.e.,
kono
parents:
diff changeset
108 // addr >> L2O_SHIFT) could increase the resulting orec index by more
kono
parents:
diff changeset
109 // than one; with the current parameters, we would roughly acquire a
kono
parents:
diff changeset
110 // fourth more orecs than necessary for regions covering more than orec.
kono
parents:
diff changeset
111 // Keeping mult around as extra state shouldn't matter much.
kono
parents:
diff changeset
112 mult += L2O_MULT32;
kono
parents:
diff changeset
113 orec = mult >> (32 - L2O_ORECS_BITS);
kono
parents:
diff changeset
114 }
kono
parents:
diff changeset
115 bool reached_end() { return orec == orec_end; }
kono
parents:
diff changeset
116 };
kono
parents:
diff changeset
117
kono
parents:
diff changeset
118 virtual void init()
kono
parents:
diff changeset
119 {
kono
parents:
diff changeset
120 // We assume that an atomic<gtm_word> is backed by just a gtm_word, so
kono
parents:
diff changeset
121 // starting with zeroed memory is fine.
kono
parents:
diff changeset
122 orecs = (atomic<gtm_word>*) xcalloc(
kono
parents:
diff changeset
123 sizeof(atomic<gtm_word>) * L2O_ORECS, true);
kono
parents:
diff changeset
124 // This store is only executed while holding the serial lock, so relaxed
kono
parents:
diff changeset
125 // memory order is sufficient here.
kono
parents:
diff changeset
126 time.store(0, memory_order_relaxed);
kono
parents:
diff changeset
127 }
kono
parents:
diff changeset
128
kono
parents:
diff changeset
129 virtual void fini()
kono
parents:
diff changeset
130 {
kono
parents:
diff changeset
131 free(orecs);
kono
parents:
diff changeset
132 }
kono
parents:
diff changeset
133
kono
parents:
diff changeset
134 // We only re-initialize when our time base overflows. Thus, only reset
kono
parents:
diff changeset
135 // the time base and the orecs but do not re-allocate the orec array.
kono
parents:
diff changeset
136 virtual void reinit()
kono
parents:
diff changeset
137 {
kono
parents:
diff changeset
138 // This store is only executed while holding the serial lock, so relaxed
kono
parents:
diff changeset
139 // memory order is sufficient here. Same holds for the memset.
kono
parents:
diff changeset
140 time.store(0, memory_order_relaxed);
kono
parents:
diff changeset
141 // The memset below isn't strictly kosher because it bypasses
kono
parents:
diff changeset
142 // the non-trivial assignment operator defined by std::atomic. Using
kono
parents:
diff changeset
143 // a local void* is enough to prevent GCC from warning for this.
kono
parents:
diff changeset
144 void *p = orecs;
kono
parents:
diff changeset
145 memset(p, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
kono
parents:
diff changeset
146 }
kono
parents:
diff changeset
147 };
kono
parents:
diff changeset
148
kono
parents:
diff changeset
149 static ml_mg o_ml_mg;
kono
parents:
diff changeset
150
kono
parents:
diff changeset
151
kono
parents:
diff changeset
152 // The multiple lock, write-through TM method.
kono
parents:
diff changeset
153 // Maps each memory location to one of the orecs in the orec array, and then
kono
parents:
diff changeset
154 // acquires the associated orec eagerly before writing through.
kono
parents:
diff changeset
155 // Writes require undo-logging because we are dealing with several locks/orecs
kono
parents:
diff changeset
156 // and need to resolve deadlocks if necessary by aborting one of the
kono
parents:
diff changeset
157 // transactions.
kono
parents:
diff changeset
158 // Reads do time-based validation with snapshot time extensions. Incarnation
kono
parents:
diff changeset
159 // numbers are used to decrease contention on the time base (with those,
kono
parents:
diff changeset
160 // aborted transactions do not need to acquire a new version number for the
kono
parents:
diff changeset
161 // data that has been previously written in the transaction and needs to be
kono
parents:
diff changeset
162 // rolled back).
kono
parents:
diff changeset
163 // gtm_thread::shared_state is used to store a transaction's current
kono
parents:
diff changeset
164 // snapshot time (or commit time). The serial lock uses ~0 for inactive
kono
parents:
diff changeset
165 // transactions and 0 for active ones. Thus, we always have a meaningful
kono
parents:
diff changeset
166 // timestamp in shared_state that can be used to implement quiescence-based
kono
parents:
diff changeset
167 // privatization safety.
kono
parents:
diff changeset
168 class ml_wt_dispatch : public abi_dispatch
kono
parents:
diff changeset
169 {
kono
parents:
diff changeset
170 protected:
kono
parents:
diff changeset
171 static void pre_write(gtm_thread *tx, const void *addr, size_t len)
kono
parents:
diff changeset
172 {
kono
parents:
diff changeset
173 gtm_word snapshot = tx->shared_state.load(memory_order_relaxed);
kono
parents:
diff changeset
174 gtm_word locked_by_tx = ml_mg::set_locked(tx);
kono
parents:
diff changeset
175
kono
parents:
diff changeset
176 // Lock all orecs that cover the region.
kono
parents:
diff changeset
177 ml_mg::orec_iterator oi(addr, len);
kono
parents:
diff changeset
178 do
kono
parents:
diff changeset
179 {
kono
parents:
diff changeset
180 // Load the orec. Relaxed memory order is sufficient here because
kono
parents:
diff changeset
181 // either we have acquired the orec or we will try to acquire it with
kono
parents:
diff changeset
182 // a CAS with stronger memory order.
kono
parents:
diff changeset
183 gtm_word o = o_ml_mg.orecs[oi.get()].load(memory_order_relaxed);
kono
parents:
diff changeset
184
kono
parents:
diff changeset
185 // Check whether we have acquired the orec already.
kono
parents:
diff changeset
186 if (likely (locked_by_tx != o))
kono
parents:
diff changeset
187 {
kono
parents:
diff changeset
188 // If not, acquire. Make sure that our snapshot time is larger or
kono
parents:
diff changeset
189 // equal than the orec's version to avoid masking invalidations of
kono
parents:
diff changeset
190 // our snapshot with our own writes.
kono
parents:
diff changeset
191 if (unlikely (ml_mg::is_locked(o)))
kono
parents:
diff changeset
192 tx->restart(RESTART_LOCKED_WRITE);
kono
parents:
diff changeset
193
kono
parents:
diff changeset
194 if (unlikely (ml_mg::get_time(o) > snapshot))
kono
parents:
diff changeset
195 {
kono
parents:
diff changeset
196 // We only need to extend the snapshot if we have indeed read
kono
parents:
diff changeset
197 // from this orec before. Given that we are an update
kono
parents:
diff changeset
198 // transaction, we will have to extend anyway during commit.
kono
parents:
diff changeset
199 // ??? Scan the read log instead, aborting if we have read
kono
parents:
diff changeset
200 // from data covered by this orec before?
kono
parents:
diff changeset
201 snapshot = extend(tx);
kono
parents:
diff changeset
202 }
kono
parents:
diff changeset
203
kono
parents:
diff changeset
204 // We need acquire memory order here to synchronize with other
kono
parents:
diff changeset
205 // (ownership) releases of the orec. We do not need acq_rel order
kono
parents:
diff changeset
206 // because whenever another thread reads from this CAS'
kono
parents:
diff changeset
207 // modification, then it will abort anyway and does not rely on
kono
parents:
diff changeset
208 // any further happens-before relation to be established.
kono
parents:
diff changeset
209 if (unlikely (!o_ml_mg.orecs[oi.get()].compare_exchange_strong(
kono
parents:
diff changeset
210 o, locked_by_tx, memory_order_acquire)))
kono
parents:
diff changeset
211 tx->restart(RESTART_LOCKED_WRITE);
kono
parents:
diff changeset
212
kono
parents:
diff changeset
213 // We use an explicit fence here to avoid having to use release
kono
parents:
diff changeset
214 // memory order for all subsequent data stores. This fence will
kono
parents:
diff changeset
215 // synchronize with loads of the data with acquire memory order.
kono
parents:
diff changeset
216 // See post_load() for why this is necessary.
kono
parents:
diff changeset
217 // Adding require memory order to the prior CAS is not sufficient,
kono
parents:
diff changeset
218 // at least according to the Batty et al. formalization of the
kono
parents:
diff changeset
219 // memory model.
kono
parents:
diff changeset
220 atomic_thread_fence(memory_order_release);
kono
parents:
diff changeset
221
kono
parents:
diff changeset
222 // We log the previous value here to be able to use incarnation
kono
parents:
diff changeset
223 // numbers when we have to roll back.
kono
parents:
diff changeset
224 // ??? Reserve capacity early to avoid capacity checks here?
kono
parents:
diff changeset
225 gtm_rwlog_entry *e = tx->writelog.push();
kono
parents:
diff changeset
226 e->orec = o_ml_mg.orecs + oi.get();
kono
parents:
diff changeset
227 e->value = o;
kono
parents:
diff changeset
228 }
kono
parents:
diff changeset
229 oi.advance();
kono
parents:
diff changeset
230 }
kono
parents:
diff changeset
231 while (!oi.reached_end());
kono
parents:
diff changeset
232
kono
parents:
diff changeset
233 // Do undo logging. We do not know which region prior writes logged
kono
parents:
diff changeset
234 // (even if orecs have been acquired), so just log everything.
kono
parents:
diff changeset
235 tx->undolog.log(addr, len);
kono
parents:
diff changeset
236 }
kono
parents:
diff changeset
237
kono
parents:
diff changeset
238 static void pre_write(const void *addr, size_t len)
kono
parents:
diff changeset
239 {
kono
parents:
diff changeset
240 gtm_thread *tx = gtm_thr();
kono
parents:
diff changeset
241 pre_write(tx, addr, len);
kono
parents:
diff changeset
242 }
kono
parents:
diff changeset
243
kono
parents:
diff changeset
244 // Returns true iff all the orecs in our read log still have the same time
kono
parents:
diff changeset
245 // or have been locked by the transaction itself.
kono
parents:
diff changeset
246 static bool validate(gtm_thread *tx)
kono
parents:
diff changeset
247 {
kono
parents:
diff changeset
248 gtm_word locked_by_tx = ml_mg::set_locked(tx);
kono
parents:
diff changeset
249 // ??? This might get called from pre_load() via extend(). In that case,
kono
parents:
diff changeset
250 // we don't really need to check the new entries that pre_load() is
kono
parents:
diff changeset
251 // adding. Stop earlier?
kono
parents:
diff changeset
252 for (gtm_rwlog_entry *i = tx->readlog.begin(), *ie = tx->readlog.end();
kono
parents:
diff changeset
253 i != ie; i++)
kono
parents:
diff changeset
254 {
kono
parents:
diff changeset
255 // Relaxed memory order is sufficient here because we do not need to
kono
parents:
diff changeset
256 // establish any new synchronizes-with relationships. We only need
kono
parents:
diff changeset
257 // to read a value that is as least as current as enforced by the
kono
parents:
diff changeset
258 // callers: extend() loads global time with acquire, and trycommit()
kono
parents:
diff changeset
259 // increments global time with acquire. Therefore, we will see the
kono
parents:
diff changeset
260 // most recent orec updates before the global time that we load.
kono
parents:
diff changeset
261 gtm_word o = i->orec->load(memory_order_relaxed);
kono
parents:
diff changeset
262 // We compare only the time stamp and the lock bit here. We know that
kono
parents:
diff changeset
263 // we have read only committed data before, so we can ignore
kono
parents:
diff changeset
264 // intermediate yet rolled-back updates presented by the incarnation
kono
parents:
diff changeset
265 // number bits.
kono
parents:
diff changeset
266 if (ml_mg::get_time(o) != ml_mg::get_time(i->value)
kono
parents:
diff changeset
267 && o != locked_by_tx)
kono
parents:
diff changeset
268 return false;
kono
parents:
diff changeset
269 }
kono
parents:
diff changeset
270 return true;
kono
parents:
diff changeset
271 }
kono
parents:
diff changeset
272
kono
parents:
diff changeset
273 // Tries to extend the snapshot to a more recent time. Returns the new
kono
parents:
diff changeset
274 // snapshot time and updates TX->SHARED_STATE. If the snapshot cannot be
kono
parents:
diff changeset
275 // extended to the current global time, TX is restarted.
kono
parents:
diff changeset
276 static gtm_word extend(gtm_thread *tx)
kono
parents:
diff changeset
277 {
kono
parents:
diff changeset
278 // We read global time here, even if this isn't strictly necessary
kono
parents:
diff changeset
279 // because we could just return the maximum of the timestamps that
kono
parents:
diff changeset
280 // validate sees. However, the potential cache miss on global time is
kono
parents:
diff changeset
281 // probably a reasonable price to pay for avoiding unnecessary extensions
kono
parents:
diff changeset
282 // in the future.
kono
parents:
diff changeset
283 // We need acquire memory oder because we have to synchronize with the
kono
parents:
diff changeset
284 // increment of global time by update transactions, whose lock
kono
parents:
diff changeset
285 // acquisitions we have to observe (also see trycommit()).
kono
parents:
diff changeset
286 gtm_word snapshot = o_ml_mg.time.load(memory_order_acquire);
kono
parents:
diff changeset
287 if (!validate(tx))
kono
parents:
diff changeset
288 tx->restart(RESTART_VALIDATE_READ);
kono
parents:
diff changeset
289
kono
parents:
diff changeset
290 // Update our public snapshot time. Probably useful to decrease waiting
kono
parents:
diff changeset
291 // due to quiescence-based privatization safety.
kono
parents:
diff changeset
292 // Use release memory order to establish synchronizes-with with the
kono
parents:
diff changeset
293 // privatizers; prior data loads should happen before the privatizers
kono
parents:
diff changeset
294 // potentially modify anything.
kono
parents:
diff changeset
295 tx->shared_state.store(snapshot, memory_order_release);
kono
parents:
diff changeset
296 return snapshot;
kono
parents:
diff changeset
297 }
kono
parents:
diff changeset
298
kono
parents:
diff changeset
299 // First pass over orecs. Load and check all orecs that cover the region.
kono
parents:
diff changeset
300 // Write to read log, extend snapshot time if necessary.
kono
parents:
diff changeset
301 static gtm_rwlog_entry* pre_load(gtm_thread *tx, const void* addr,
kono
parents:
diff changeset
302 size_t len)
kono
parents:
diff changeset
303 {
kono
parents:
diff changeset
304 // Don't obtain an iterator yet because the log might get resized.
kono
parents:
diff changeset
305 size_t log_start = tx->readlog.size();
kono
parents:
diff changeset
306 gtm_word snapshot = tx->shared_state.load(memory_order_relaxed);
kono
parents:
diff changeset
307 gtm_word locked_by_tx = ml_mg::set_locked(tx);
kono
parents:
diff changeset
308
kono
parents:
diff changeset
309 ml_mg::orec_iterator oi(addr, len);
kono
parents:
diff changeset
310 do
kono
parents:
diff changeset
311 {
kono
parents:
diff changeset
312 // We need acquire memory order here so that this load will
kono
parents:
diff changeset
313 // synchronize with the store that releases the orec in trycommit().
kono
parents:
diff changeset
314 // In turn, this makes sure that subsequent data loads will read from
kono
parents:
diff changeset
315 // a visible sequence of side effects that starts with the most recent
kono
parents:
diff changeset
316 // store to the data right before the release of the orec.
kono
parents:
diff changeset
317 gtm_word o = o_ml_mg.orecs[oi.get()].load(memory_order_acquire);
kono
parents:
diff changeset
318
kono
parents:
diff changeset
319 if (likely (!ml_mg::is_more_recent_or_locked(o, snapshot)))
kono
parents:
diff changeset
320 {
kono
parents:
diff changeset
321 success:
kono
parents:
diff changeset
322 gtm_rwlog_entry *e = tx->readlog.push();
kono
parents:
diff changeset
323 e->orec = o_ml_mg.orecs + oi.get();
kono
parents:
diff changeset
324 e->value = o;
kono
parents:
diff changeset
325 }
kono
parents:
diff changeset
326 else if (!ml_mg::is_locked(o))
kono
parents:
diff changeset
327 {
kono
parents:
diff changeset
328 // We cannot read this part of the region because it has been
kono
parents:
diff changeset
329 // updated more recently than our snapshot time. If we can extend
kono
parents:
diff changeset
330 // our snapshot, then we can read.
kono
parents:
diff changeset
331 snapshot = extend(tx);
kono
parents:
diff changeset
332 goto success;
kono
parents:
diff changeset
333 }
kono
parents:
diff changeset
334 else
kono
parents:
diff changeset
335 {
kono
parents:
diff changeset
336 // If the orec is locked by us, just skip it because we can just
kono
parents:
diff changeset
337 // read from it. Otherwise, restart the transaction.
kono
parents:
diff changeset
338 if (o != locked_by_tx)
kono
parents:
diff changeset
339 tx->restart(RESTART_LOCKED_READ);
kono
parents:
diff changeset
340 }
kono
parents:
diff changeset
341 oi.advance();
kono
parents:
diff changeset
342 }
kono
parents:
diff changeset
343 while (!oi.reached_end());
kono
parents:
diff changeset
344 return &tx->readlog[log_start];
kono
parents:
diff changeset
345 }
kono
parents:
diff changeset
346
kono
parents:
diff changeset
347 // Second pass over orecs, verifying that the we had a consistent read.
kono
parents:
diff changeset
348 // Restart the transaction if any of the orecs is locked by another
kono
parents:
diff changeset
349 // transaction.
kono
parents:
diff changeset
350 static void post_load(gtm_thread *tx, gtm_rwlog_entry* log)
kono
parents:
diff changeset
351 {
kono
parents:
diff changeset
352 for (gtm_rwlog_entry *end = tx->readlog.end(); log != end; log++)
kono
parents:
diff changeset
353 {
kono
parents:
diff changeset
354 // Check that the snapshot is consistent. We expect the previous data
kono
parents:
diff changeset
355 // load to have acquire memory order, or be atomic and followed by an
kono
parents:
diff changeset
356 // acquire fence.
kono
parents:
diff changeset
357 // As a result, the data load will synchronize with the release fence
kono
parents:
diff changeset
358 // issued by the transactions whose data updates the data load has read
kono
parents:
diff changeset
359 // from. This forces the orec load to read from a visible sequence of
kono
parents:
diff changeset
360 // side effects that starts with the other updating transaction's
kono
parents:
diff changeset
361 // store that acquired the orec and set it to locked.
kono
parents:
diff changeset
362 // We therefore either read a value with the locked bit set (and
kono
parents:
diff changeset
363 // restart) or read an orec value that was written after the data had
kono
parents:
diff changeset
364 // been written. Either will allow us to detect inconsistent reads
kono
parents:
diff changeset
365 // because it will have a higher/different value.
kono
parents:
diff changeset
366 // Also note that differently to validate(), we compare the raw value
kono
parents:
diff changeset
367 // of the orec here, including incarnation numbers. We must prevent
kono
parents:
diff changeset
368 // returning uncommitted data from loads (whereas when validating, we
kono
parents:
diff changeset
369 // already performed a consistent load).
kono
parents:
diff changeset
370 gtm_word o = log->orec->load(memory_order_relaxed);
kono
parents:
diff changeset
371 if (log->value != o)
kono
parents:
diff changeset
372 tx->restart(RESTART_VALIDATE_READ);
kono
parents:
diff changeset
373 }
kono
parents:
diff changeset
374 }
kono
parents:
diff changeset
375
kono
parents:
diff changeset
376 template <typename V> static V load(const V* addr, ls_modifier mod)
kono
parents:
diff changeset
377 {
kono
parents:
diff changeset
378 // Read-for-write should be unlikely, but we need to handle it or will
kono
parents:
diff changeset
379 // break later WaW optimizations.
kono
parents:
diff changeset
380 if (unlikely(mod == RfW))
kono
parents:
diff changeset
381 {
kono
parents:
diff changeset
382 pre_write(addr, sizeof(V));
kono
parents:
diff changeset
383 return *addr;
kono
parents:
diff changeset
384 }
kono
parents:
diff changeset
385 if (unlikely(mod == RaW))
kono
parents:
diff changeset
386 return *addr;
kono
parents:
diff changeset
387 // ??? Optimize for RaR?
kono
parents:
diff changeset
388
kono
parents:
diff changeset
389 gtm_thread *tx = gtm_thr();
kono
parents:
diff changeset
390 gtm_rwlog_entry* log = pre_load(tx, addr, sizeof(V));
kono
parents:
diff changeset
391
kono
parents:
diff changeset
392 // Load the data.
kono
parents:
diff changeset
393 // This needs to have acquire memory order (see post_load()).
kono
parents:
diff changeset
394 // Alternatively, we can put an acquire fence after the data load but this
kono
parents:
diff changeset
395 // is probably less efficient.
kono
parents:
diff changeset
396 // FIXME We would need an atomic load with acquire memory order here but
kono
parents:
diff changeset
397 // we can't just forge an atomic load for nonatomic data because this
kono
parents:
diff changeset
398 // might not work on all implementations of atomics. However, we need
kono
parents:
diff changeset
399 // the acquire memory order and we can only establish this if we link
kono
parents:
diff changeset
400 // it to the matching release using a reads-from relation between atomic
kono
parents:
diff changeset
401 // loads. Also, the compiler is allowed to optimize nonatomic accesses
kono
parents:
diff changeset
402 // differently than atomic accesses (e.g., if the load would be moved to
kono
parents:
diff changeset
403 // after the fence, we potentially don't synchronize properly anymore).
kono
parents:
diff changeset
404 // Instead of the following, just use an ordinary load followed by an
kono
parents:
diff changeset
405 // acquire fence, and hope that this is good enough for now:
kono
parents:
diff changeset
406 // V v = atomic_load_explicit((atomic<V>*)addr, memory_order_acquire);
kono
parents:
diff changeset
407 V v = *addr;
kono
parents:
diff changeset
408 atomic_thread_fence(memory_order_acquire);
kono
parents:
diff changeset
409
kono
parents:
diff changeset
410 // ??? Retry the whole load if it wasn't consistent?
kono
parents:
diff changeset
411 post_load(tx, log);
kono
parents:
diff changeset
412
kono
parents:
diff changeset
413 return v;
kono
parents:
diff changeset
414 }
kono
parents:
diff changeset
415
kono
parents:
diff changeset
416 template <typename V> static void store(V* addr, const V value,
kono
parents:
diff changeset
417 ls_modifier mod)
kono
parents:
diff changeset
418 {
kono
parents:
diff changeset
419 if (likely(mod != WaW))
kono
parents:
diff changeset
420 pre_write(addr, sizeof(V));
kono
parents:
diff changeset
421 // FIXME We would need an atomic store here but we can't just forge an
kono
parents:
diff changeset
422 // atomic load for nonatomic data because this might not work on all
kono
parents:
diff changeset
423 // implementations of atomics. However, we need this store to link the
kono
parents:
diff changeset
424 // release fence in pre_write() to the acquire operation in load, which
kono
parents:
diff changeset
425 // is only guaranteed if we have a reads-from relation between atomic
kono
parents:
diff changeset
426 // accesses. Also, the compiler is allowed to optimize nonatomic accesses
kono
parents:
diff changeset
427 // differently than atomic accesses (e.g., if the store would be moved
kono
parents:
diff changeset
428 // to before the release fence in pre_write(), things could go wrong).
kono
parents:
diff changeset
429 // atomic_store_explicit((atomic<V>*)addr, value, memory_order_relaxed);
kono
parents:
diff changeset
430 *addr = value;
kono
parents:
diff changeset
431 }
kono
parents:
diff changeset
432
kono
parents:
diff changeset
433 public:
kono
parents:
diff changeset
434 static void memtransfer_static(void *dst, const void* src, size_t size,
kono
parents:
diff changeset
435 bool may_overlap, ls_modifier dst_mod, ls_modifier src_mod)
kono
parents:
diff changeset
436 {
kono
parents:
diff changeset
437 gtm_rwlog_entry* log = 0;
kono
parents:
diff changeset
438 gtm_thread *tx = 0;
kono
parents:
diff changeset
439
kono
parents:
diff changeset
440 if (src_mod == RfW)
kono
parents:
diff changeset
441 {
kono
parents:
diff changeset
442 tx = gtm_thr();
kono
parents:
diff changeset
443 pre_write(tx, src, size);
kono
parents:
diff changeset
444 }
kono
parents:
diff changeset
445 else if (src_mod != RaW && src_mod != NONTXNAL)
kono
parents:
diff changeset
446 {
kono
parents:
diff changeset
447 tx = gtm_thr();
kono
parents:
diff changeset
448 log = pre_load(tx, src, size);
kono
parents:
diff changeset
449 }
kono
parents:
diff changeset
450 // ??? Optimize for RaR?
kono
parents:
diff changeset
451
kono
parents:
diff changeset
452 if (dst_mod != NONTXNAL && dst_mod != WaW)
kono
parents:
diff changeset
453 {
kono
parents:
diff changeset
454 if (src_mod != RfW && (src_mod == RaW || src_mod == NONTXNAL))
kono
parents:
diff changeset
455 tx = gtm_thr();
kono
parents:
diff changeset
456 pre_write(tx, dst, size);
kono
parents:
diff changeset
457 }
kono
parents:
diff changeset
458
kono
parents:
diff changeset
459 // FIXME We should use atomics here (see store()). Let's just hope that
kono
parents:
diff changeset
460 // memcpy/memmove are good enough.
kono
parents:
diff changeset
461 if (!may_overlap)
kono
parents:
diff changeset
462 ::memcpy(dst, src, size);
kono
parents:
diff changeset
463 else
kono
parents:
diff changeset
464 ::memmove(dst, src, size);
kono
parents:
diff changeset
465
kono
parents:
diff changeset
466 // ??? Retry the whole memtransfer if it wasn't consistent?
kono
parents:
diff changeset
467 if (src_mod != RfW && src_mod != RaW && src_mod != NONTXNAL)
kono
parents:
diff changeset
468 {
kono
parents:
diff changeset
469 // See load() for why we need the acquire fence here.
kono
parents:
diff changeset
470 atomic_thread_fence(memory_order_acquire);
kono
parents:
diff changeset
471 post_load(tx, log);
kono
parents:
diff changeset
472 }
kono
parents:
diff changeset
473 }
kono
parents:
diff changeset
474
kono
parents:
diff changeset
475 static void memset_static(void *dst, int c, size_t size, ls_modifier mod)
kono
parents:
diff changeset
476 {
kono
parents:
diff changeset
477 if (mod != WaW)
kono
parents:
diff changeset
478 pre_write(dst, size);
kono
parents:
diff changeset
479 // FIXME We should use atomics here (see store()). Let's just hope that
kono
parents:
diff changeset
480 // memset is good enough.
kono
parents:
diff changeset
481 ::memset(dst, c, size);
kono
parents:
diff changeset
482 }
kono
parents:
diff changeset
483
kono
parents:
diff changeset
484 virtual gtm_restart_reason begin_or_restart()
kono
parents:
diff changeset
485 {
kono
parents:
diff changeset
486 // We don't need to do anything for nested transactions.
kono
parents:
diff changeset
487 gtm_thread *tx = gtm_thr();
kono
parents:
diff changeset
488 if (tx->parent_txns.size() > 0)
kono
parents:
diff changeset
489 return NO_RESTART;
kono
parents:
diff changeset
490
kono
parents:
diff changeset
491 // Read the current time, which becomes our snapshot time.
kono
parents:
diff changeset
492 // Use acquire memory oder so that we see the lock acquisitions by update
kono
parents:
diff changeset
493 // transcations that incremented the global time (see trycommit()).
kono
parents:
diff changeset
494 gtm_word snapshot = o_ml_mg.time.load(memory_order_acquire);
kono
parents:
diff changeset
495 // Re-initialize method group on time overflow.
kono
parents:
diff changeset
496 if (snapshot >= o_ml_mg.TIME_MAX)
kono
parents:
diff changeset
497 return RESTART_INIT_METHOD_GROUP;
kono
parents:
diff changeset
498
kono
parents:
diff changeset
499 // We don't need to enforce any ordering for the following store. There
kono
parents:
diff changeset
500 // are no earlier data loads in this transaction, so the store cannot
kono
parents:
diff changeset
501 // become visible before those (which could lead to the violation of
kono
parents:
diff changeset
502 // privatization safety). The store can become visible after later loads
kono
parents:
diff changeset
503 // but this does not matter because the previous value will have been
kono
parents:
diff changeset
504 // smaller or equal (the serial lock will set shared_state to zero when
kono
parents:
diff changeset
505 // marking the transaction as active, and restarts enforce immediate
kono
parents:
diff changeset
506 // visibility of a smaller or equal value with a barrier (see
kono
parents:
diff changeset
507 // rollback()).
kono
parents:
diff changeset
508 tx->shared_state.store(snapshot, memory_order_relaxed);
kono
parents:
diff changeset
509 return NO_RESTART;
kono
parents:
diff changeset
510 }
kono
parents:
diff changeset
511
kono
parents:
diff changeset
512 virtual bool trycommit(gtm_word& priv_time)
kono
parents:
diff changeset
513 {
kono
parents:
diff changeset
514 gtm_thread* tx = gtm_thr();
kono
parents:
diff changeset
515
kono
parents:
diff changeset
516 // If we haven't updated anything, we can commit.
kono
parents:
diff changeset
517 if (!tx->writelog.size())
kono
parents:
diff changeset
518 {
kono
parents:
diff changeset
519 tx->readlog.clear();
kono
parents:
diff changeset
520 // We still need to ensure privatization safety, unfortunately. While
kono
parents:
diff changeset
521 // we cannot have privatized anything by ourselves (because we are not
kono
parents:
diff changeset
522 // an update transaction), we can have observed the commits of
kono
parents:
diff changeset
523 // another update transaction that privatized something. Because any
kono
parents:
diff changeset
524 // commit happens before ensuring privatization, our snapshot and
kono
parents:
diff changeset
525 // commit can thus have happened before ensuring privatization safety
kono
parents:
diff changeset
526 // for this commit/snapshot time. Therefore, before we can return to
kono
parents:
diff changeset
527 // nontransactional code that might use the privatized data, we must
kono
parents:
diff changeset
528 // ensure privatization safety for our snapshot time.
kono
parents:
diff changeset
529 // This still seems to be better than not allowing use of the
kono
parents:
diff changeset
530 // snapshot time before privatization safety has been ensured because
kono
parents:
diff changeset
531 // we at least can run transactions such as this one, and in the
kono
parents:
diff changeset
532 // meantime the transaction producing this commit time might have
kono
parents:
diff changeset
533 // finished ensuring privatization safety for it.
kono
parents:
diff changeset
534 priv_time = tx->shared_state.load(memory_order_relaxed);
kono
parents:
diff changeset
535 return true;
kono
parents:
diff changeset
536 }
kono
parents:
diff changeset
537
kono
parents:
diff changeset
538 // Get a commit time.
kono
parents:
diff changeset
539 // Overflow of o_ml_mg.time is prevented in begin_or_restart().
kono
parents:
diff changeset
540 // We need acq_rel here because (1) the acquire part is required for our
kono
parents:
diff changeset
541 // own subsequent call to validate(), and the release part is necessary to
kono
parents:
diff changeset
542 // make other threads' validate() work as explained there and in extend().
kono
parents:
diff changeset
543 gtm_word ct = o_ml_mg.time.fetch_add(1, memory_order_acq_rel) + 1;
kono
parents:
diff changeset
544
kono
parents:
diff changeset
545 // Extend our snapshot time to at least our commit time.
kono
parents:
diff changeset
546 // Note that we do not need to validate if our snapshot time is right
kono
parents:
diff changeset
547 // before the commit time because we are never sharing the same commit
kono
parents:
diff changeset
548 // time with other transactions.
kono
parents:
diff changeset
549 // No need to reset shared_state, which will be modified by the serial
kono
parents:
diff changeset
550 // lock right after our commit anyway.
kono
parents:
diff changeset
551 gtm_word snapshot = tx->shared_state.load(memory_order_relaxed);
kono
parents:
diff changeset
552 if (snapshot < ct - 1 && !validate(tx))
kono
parents:
diff changeset
553 return false;
kono
parents:
diff changeset
554
kono
parents:
diff changeset
555 // Release orecs.
kono
parents:
diff changeset
556 // See pre_load() / post_load() for why we need release memory order.
kono
parents:
diff changeset
557 // ??? Can we use a release fence and relaxed stores?
kono
parents:
diff changeset
558 gtm_word v = ml_mg::set_time(ct);
kono
parents:
diff changeset
559 for (gtm_rwlog_entry *i = tx->writelog.begin(), *ie = tx->writelog.end();
kono
parents:
diff changeset
560 i != ie; i++)
kono
parents:
diff changeset
561 i->orec->store(v, memory_order_release);
kono
parents:
diff changeset
562
kono
parents:
diff changeset
563 // We're done, clear the logs.
kono
parents:
diff changeset
564 tx->writelog.clear();
kono
parents:
diff changeset
565 tx->readlog.clear();
kono
parents:
diff changeset
566
kono
parents:
diff changeset
567 // Need to ensure privatization safety. Every other transaction must
kono
parents:
diff changeset
568 // have a snapshot time that is at least as high as our commit time
kono
parents:
diff changeset
569 // (i.e., our commit must be visible to them).
kono
parents:
diff changeset
570 priv_time = ct;
kono
parents:
diff changeset
571 return true;
kono
parents:
diff changeset
572 }
kono
parents:
diff changeset
573
kono
parents:
diff changeset
574 virtual void rollback(gtm_transaction_cp *cp)
kono
parents:
diff changeset
575 {
kono
parents:
diff changeset
576 // We don't do anything for rollbacks of nested transactions.
kono
parents:
diff changeset
577 // ??? We could release locks here if we snapshot writelog size. readlog
kono
parents:
diff changeset
578 // is similar. This is just a performance optimization though. Nested
kono
parents:
diff changeset
579 // aborts should be rather infrequent, so the additional save/restore
kono
parents:
diff changeset
580 // overhead for the checkpoints could be higher.
kono
parents:
diff changeset
581 if (cp != 0)
kono
parents:
diff changeset
582 return;
kono
parents:
diff changeset
583
kono
parents:
diff changeset
584 gtm_thread *tx = gtm_thr();
kono
parents:
diff changeset
585 gtm_word overflow_value = 0;
kono
parents:
diff changeset
586
kono
parents:
diff changeset
587 // Release orecs.
kono
parents:
diff changeset
588 for (gtm_rwlog_entry *i = tx->writelog.begin(), *ie = tx->writelog.end();
kono
parents:
diff changeset
589 i != ie; i++)
kono
parents:
diff changeset
590 {
kono
parents:
diff changeset
591 // If possible, just increase the incarnation number.
kono
parents:
diff changeset
592 // See pre_load() / post_load() for why we need release memory order.
kono
parents:
diff changeset
593 // ??? Can we use a release fence and relaxed stores? (Same below.)
kono
parents:
diff changeset
594 if (ml_mg::has_incarnation_left(i->value))
kono
parents:
diff changeset
595 i->orec->store(ml_mg::inc_incarnation(i->value),
kono
parents:
diff changeset
596 memory_order_release);
kono
parents:
diff changeset
597 else
kono
parents:
diff changeset
598 {
kono
parents:
diff changeset
599 // We have an incarnation overflow. Acquire a new timestamp, and
kono
parents:
diff changeset
600 // use it from now on as value for each orec whose incarnation
kono
parents:
diff changeset
601 // number cannot be increased.
kono
parents:
diff changeset
602 // Overflow of o_ml_mg.time is prevented in begin_or_restart().
kono
parents:
diff changeset
603 // See pre_load() / post_load() for why we need release memory
kono
parents:
diff changeset
604 // order.
kono
parents:
diff changeset
605 if (!overflow_value)
kono
parents:
diff changeset
606 // Release memory order is sufficient but required here.
kono
parents:
diff changeset
607 // In contrast to the increment in trycommit(), we need release
kono
parents:
diff changeset
608 // for the same reason but do not need the acquire because we
kono
parents:
diff changeset
609 // do not validate subsequently.
kono
parents:
diff changeset
610 overflow_value = ml_mg::set_time(
kono
parents:
diff changeset
611 o_ml_mg.time.fetch_add(1, memory_order_release) + 1);
kono
parents:
diff changeset
612 i->orec->store(overflow_value, memory_order_release);
kono
parents:
diff changeset
613 }
kono
parents:
diff changeset
614 }
kono
parents:
diff changeset
615
kono
parents:
diff changeset
616 // We need this release fence to ensure that privatizers see the
kono
parents:
diff changeset
617 // rolled-back original state (not any uncommitted values) when they read
kono
parents:
diff changeset
618 // the new snapshot time that we write in begin_or_restart().
kono
parents:
diff changeset
619 atomic_thread_fence(memory_order_release);
kono
parents:
diff changeset
620
kono
parents:
diff changeset
621 // We're done, clear the logs.
kono
parents:
diff changeset
622 tx->writelog.clear();
kono
parents:
diff changeset
623 tx->readlog.clear();
kono
parents:
diff changeset
624 }
kono
parents:
diff changeset
625
kono
parents:
diff changeset
626 virtual bool snapshot_most_recent()
kono
parents:
diff changeset
627 {
kono
parents:
diff changeset
628 // This is the same code as in extend() except that we do not restart
kono
parents:
diff changeset
629 // on failure but simply return the result, and that we don't validate
kono
parents:
diff changeset
630 // if our snapshot is already most recent.
kono
parents:
diff changeset
631 gtm_thread* tx = gtm_thr();
kono
parents:
diff changeset
632 gtm_word snapshot = o_ml_mg.time.load(memory_order_acquire);
kono
parents:
diff changeset
633 if (snapshot == tx->shared_state.load(memory_order_relaxed))
kono
parents:
diff changeset
634 return true;
kono
parents:
diff changeset
635 if (!validate(tx))
kono
parents:
diff changeset
636 return false;
kono
parents:
diff changeset
637
kono
parents:
diff changeset
638 // Update our public snapshot time. Necessary so that we do not prevent
kono
parents:
diff changeset
639 // other transactions from ensuring privatization safety.
kono
parents:
diff changeset
640 tx->shared_state.store(snapshot, memory_order_release);
kono
parents:
diff changeset
641 return true;
kono
parents:
diff changeset
642 }
kono
parents:
diff changeset
643
kono
parents:
diff changeset
644 virtual bool supports(unsigned number_of_threads)
kono
parents:
diff changeset
645 {
kono
parents:
diff changeset
646 // Each txn can commit and fail and rollback once before checking for
kono
parents:
diff changeset
647 // overflow, so this bounds the number of threads that we can support.
kono
parents:
diff changeset
648 // In practice, this won't be a problem but we check it anyway so that
kono
parents:
diff changeset
649 // we never break in the occasional weird situation.
kono
parents:
diff changeset
650 return (number_of_threads * 2 <= ml_mg::OVERFLOW_RESERVE);
kono
parents:
diff changeset
651 }
kono
parents:
diff changeset
652
kono
parents:
diff changeset
653 CREATE_DISPATCH_METHODS(virtual, )
kono
parents:
diff changeset
654 CREATE_DISPATCH_METHODS_MEM()
kono
parents:
diff changeset
655
kono
parents:
diff changeset
656 ml_wt_dispatch() : abi_dispatch(false, true, false, false, 0, &o_ml_mg)
kono
parents:
diff changeset
657 { }
kono
parents:
diff changeset
658 };
kono
parents:
diff changeset
659
kono
parents:
diff changeset
660 } // anon namespace
kono
parents:
diff changeset
661
kono
parents:
diff changeset
662 static const ml_wt_dispatch o_ml_wt_dispatch;
kono
parents:
diff changeset
663
kono
parents:
diff changeset
664 abi_dispatch *
kono
parents:
diff changeset
665 GTM::dispatch_ml_wt ()
kono
parents:
diff changeset
666 {
kono
parents:
diff changeset
667 return const_cast<ml_wt_dispatch *>(&o_ml_wt_dispatch);
kono
parents:
diff changeset
668 }