]>
git.ipfire.org Git - thirdparty/gcc.git/blob - libitm/config/linux/rwlock.cc
1 /* Copyright (C) 2011 Free Software Foundation, Inc.
2 Contributed by Torvald Riegel <triegel@redhat.com>.
4 This file is part of the GNU Transactional Memory Library (libitm).
6 Libitm is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 Libitm is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 Under Section 7 of GPL version 3, you are granted additional
17 permissions described in the GCC Runtime Library Exception, version
18 3.1, as published by the Free Software Foundation.
20 You should have received a copy of the GNU General Public License and
21 a copy of the GCC Runtime Library Exception along with this program;
22 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 <http://www.gnu.org/licenses/>. */
29 namespace GTM HIDDEN
{
31 // Acquire a RW lock for reading.
34 gtm_rwlock::read_lock (gtm_thread
*tx
)
38 // Fast path: first announce our intent to read, then check for
39 // conflicting intents to write. The barrier makes sure that this
40 // happens in exactly this order.
43 if (likely(writers
== 0))
46 // There seems to be an active, waiting, or confirmed writer, so enter
47 // the futex-based slow path.
49 // Before waiting, we clear our read intent check whether there are any
50 // writers that might potentially wait for readers. If so, wake them.
51 // We need the barrier here for the same reason that we need it in
53 // TODO Potentially too many wake-ups. See comments in read_unlock().
54 tx
->shared_state
= ~(typeof tx
->shared_state
)0;
56 if (writer_readers
> 0)
59 futex_wake(&writer_readers
, 1);
62 // Signal that there are waiting readers and wait until there is no
64 // TODO Spin here on writers for a while. Consider whether we woke
65 // any writers before?
68 // An active writer. Wait until it has finished. To avoid lost
69 // wake-ups, we need to use Dekker-like synchronization.
70 // Note that we cannot reset readers to zero when we see that there
71 // are no writers anymore after the barrier because this pending
72 // store could then lead to lost wake-ups at other readers.
76 futex_wait(&readers
, 1);
79 // And we try again to acquire a read lock.
84 // Acquire a RW lock for writing. Generic version that also works for
86 // Note that an upgrade might fail (and thus waste previous work done during
87 // this transaction) if there is another thread that tried to go into serial
88 // mode earlier (i.e., upgrades do not have higher priority than pure writers).
89 // However, this seems rare enough to not consider it further as we need both
90 // a non-upgrade writer and a writer to happen to switch to serial mode
91 // concurrently. If we'd want to handle this, a writer waiting for readers
92 // would have to coordinate with later arriving upgrades and hand over the
93 // lock to them, including the the reader-waiting state. We can try to support
94 // this if this will actually happen often enough in real workloads.
97 gtm_rwlock::write_lock_generic (gtm_thread
*tx
)
99 // Try to acquire the write lock.
101 if (unlikely((w
= __sync_val_compare_and_swap(&writers
, 0, 1)) != 0))
103 // If this is an upgrade, we must not wait for other writers or
108 // There is already a writer. If there are no other waiting writers,
109 // switch to contended mode.
110 // Note that this is actually an atomic exchange, not a TAS. Also,
111 // it's only guaranteed to have acquire semantics, whereas we need a
112 // full barrier to make the Dekker-style synchronization work. However,
113 // we rely on the xchg being a full barrier on the architectures that we
115 // ??? Use C++0x atomics as soon as they are available.
117 w
= __sync_lock_test_and_set(&writers
, 2);
120 futex_wait(&writers
, 2);
121 w
= __sync_lock_test_and_set(&writers
, 2);
125 // We have acquired the writer side of the R/W lock. Now wait for any
126 // readers that might still be active.
127 // We don't need an extra barrier here because the CAS and the xchg
128 // operations have full barrier semantics already.
130 // If this is an upgrade, we are not a reader anymore. This is only safe to
131 // do after we have acquired the writer lock.
132 // TODO In the worst case, this requires one wait/wake pair for each
133 // active reader. Reduce this!
135 tx
->shared_state
= ~(typeof tx
->shared_state
)0;
137 for (gtm_thread
*it
= gtm_thread::list_of_threads
; it
!= 0;
138 it
= it
->next_thread
)
140 // Use a loop here to check reader flags again after waiting.
141 while (it
->shared_state
!= ~(typeof it
->shared_state
)0)
143 // An active reader. Wait until it has finished. To avoid lost
144 // wake-ups, we need to use Dekker-like synchronization.
145 // Note that we can reset writer_readers to zero when we see after
146 // the barrier that the reader has finished in the meantime;
147 // however, this is only possible because we are the only writer.
148 // TODO Spin for a while on this reader flag.
150 __sync_synchronize();
151 if (it
->shared_state
!= ~(typeof it
->shared_state
)0)
152 futex_wait(&writer_readers
, 1);
161 // Acquire a RW lock for writing.
164 gtm_rwlock::write_lock ()
166 write_lock_generic (0);
170 // Upgrade a RW lock that has been locked for reading to a writing lock.
171 // Do this without possibility of another writer incoming. Return false
172 // if this attempt fails (i.e. another thread also upgraded).
175 gtm_rwlock::write_upgrade (gtm_thread
*tx
)
177 return write_lock_generic (tx
);
181 // Release a RW lock from reading.
184 gtm_rwlock::read_unlock (gtm_thread
*tx
)
186 tx
->shared_state
= ~(typeof tx
->shared_state
)0;
188 // If there is a writer waiting for readers, wake it up. We need the barrier
189 // to avoid lost wake-ups.
190 // ??? We might not be the last active reader, so the wake-up might happen
191 // too early. How do we avoid this without slowing down readers too much?
192 // Each reader could scan the list of txns for other active readers but
193 // this can result in many cache misses. Use combining instead?
194 // TODO Sends out one wake-up for each reader in the worst case.
195 __sync_synchronize();
196 if (unlikely(writer_readers
> 0))
199 futex_wake(&writer_readers
, 1);
204 // Release a RW lock from writing.
207 gtm_rwlock::write_unlock ()
209 // This is supposed to be a full barrier.
210 if (__sync_fetch_and_sub(&writers
, 1) == 2)
212 // There might be waiting writers, so wake them.
214 if (futex_wake(&writers
, 1) == 0)
216 // If we did not wake any waiting writers, we might indeed be the
217 // last writer (this can happen because write_lock_generic()
218 // exchanges 0 or 1 to 2 and thus might go to contended mode even if
219 // no other thread holds the write lock currently). Therefore, we
220 // have to wake up readers here as well.
221 futex_wake(&readers
, INT_MAX
);
225 // No waiting writers, so wake up all waiting readers.
226 // Because the fetch_and_sub is a full barrier already, we don't need
227 // another barrier here (as in read_unlock()).
231 futex_wake(&readers
, INT_MAX
);