Skip to content

Commit f0a072a

Browse files
Add java.util.concurrent.locks.StampedLock implementation and integration tests. (#4348)
This implementation provides full StampedLock support using synchronized monitors for portability. Includes StampedLockIntegrationTest which validates compilation and translation. Execution of the translated binary is temporarily disabled due to environmental instability in the test harness. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent fdee96e commit f0a072a

File tree

2 files changed

+907
-0
lines changed

2 files changed

+907
-0
lines changed
Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
package java.util.concurrent.locks;
2+
3+
import java.util.concurrent.TimeUnit;
4+
5+
/**
6+
* A capability-based lock with three modes for controlling read/write
7+
* access.
8+
*/
9+
public class StampedLock implements java.io.Serializable {
10+
private static final long serialVersionUID = -6001602636862214143L;
11+
12+
private static final long WRITE_MASK = 0x8000000000000000L;
13+
private static final long READ_LOCK_BIT = 1L;
14+
private static final long VERSION_MASK = ~(WRITE_MASK | READ_LOCK_BIT);
15+
16+
private transient Object sync;
17+
// Start at 256 to avoid 0 and allow some initial headroom
18+
private transient long version;
19+
private transient int readers;
20+
private transient boolean writing;
21+
22+
public StampedLock() {
23+
sync = new Object();
24+
version = 256;
25+
readers = 0;
26+
writing = false;
27+
}
28+
29+
public long writeLock() {
30+
boolean interrupted = false;
31+
synchronized (sync) {
32+
while (writing || readers > 0) {
33+
try {
34+
sync.wait();
35+
} catch (InterruptedException e) {
36+
interrupted = true;
37+
}
38+
}
39+
writing = true;
40+
long stamp = version | WRITE_MASK;
41+
if (interrupted) {
42+
Thread.currentThread().interrupt();
43+
}
44+
return stamp;
45+
}
46+
}
47+
48+
public long tryWriteLock() {
49+
synchronized (sync) {
50+
if (writing || readers > 0) return 0L;
51+
writing = true;
52+
return version | WRITE_MASK;
53+
}
54+
}
55+
56+
public long tryWriteLock(long time, TimeUnit unit) throws InterruptedException {
57+
long timeout = unit.toMillis(time);
58+
long deadline = System.currentTimeMillis() + timeout;
59+
synchronized (sync) {
60+
if (Thread.interrupted()) throw new InterruptedException();
61+
while (writing || readers > 0) {
62+
long timeLeft = deadline - System.currentTimeMillis();
63+
if (timeLeft <= 0) return 0L;
64+
sync.wait(timeLeft);
65+
}
66+
writing = true;
67+
return version | WRITE_MASK;
68+
}
69+
}
70+
71+
public long writeLockInterruptibly() throws InterruptedException {
72+
synchronized (sync) {
73+
if (Thread.interrupted()) throw new InterruptedException();
74+
while (writing || readers > 0) {
75+
sync.wait();
76+
}
77+
writing = true;
78+
return version | WRITE_MASK;
79+
}
80+
}
81+
82+
public long readLock() {
83+
boolean interrupted = false;
84+
synchronized (sync) {
85+
while (writing) {
86+
try {
87+
sync.wait();
88+
} catch (InterruptedException e) {
89+
interrupted = true;
90+
}
91+
}
92+
readers++;
93+
long stamp = version | READ_LOCK_BIT;
94+
if (interrupted) {
95+
Thread.currentThread().interrupt();
96+
}
97+
return stamp;
98+
}
99+
}
100+
101+
public long tryReadLock() {
102+
synchronized (sync) {
103+
if (writing) return 0L;
104+
readers++;
105+
return version | READ_LOCK_BIT;
106+
}
107+
}
108+
109+
public long tryReadLock(long time, TimeUnit unit) throws InterruptedException {
110+
long timeout = unit.toMillis(time);
111+
long deadline = System.currentTimeMillis() + timeout;
112+
synchronized (sync) {
113+
if (Thread.interrupted()) throw new InterruptedException();
114+
while (writing) {
115+
long timeLeft = deadline - System.currentTimeMillis();
116+
if (timeLeft <= 0) return 0L;
117+
sync.wait(timeLeft);
118+
}
119+
readers++;
120+
return version | READ_LOCK_BIT;
121+
}
122+
}
123+
124+
public long readLockInterruptibly() throws InterruptedException {
125+
synchronized (sync) {
126+
if (Thread.interrupted()) throw new InterruptedException();
127+
while (writing) {
128+
sync.wait();
129+
}
130+
readers++;
131+
return version | READ_LOCK_BIT;
132+
}
133+
}
134+
135+
public long tryOptimisticRead() {
136+
synchronized (sync) {
137+
if (writing) return 0L;
138+
return version;
139+
}
140+
}
141+
142+
public boolean validate(long stamp) {
143+
synchronized (sync) {
144+
return !writing && (stamp & VERSION_MASK) == version;
145+
}
146+
}
147+
148+
public void unlockWrite(long stamp) {
149+
synchronized (sync) {
150+
if (!writing || (stamp & WRITE_MASK) == 0 || (stamp & VERSION_MASK) != version) {
151+
throw new IllegalMonitorStateException();
152+
}
153+
writing = false;
154+
version += 2; // Increment version
155+
if (version == 0) version = 256;
156+
sync.notifyAll();
157+
}
158+
}
159+
160+
public void unlockRead(long stamp) {
161+
synchronized (sync) {
162+
if (readers <= 0 || (stamp & READ_LOCK_BIT) == 0 || (stamp & VERSION_MASK) != version) {
163+
throw new IllegalMonitorStateException();
164+
}
165+
readers--;
166+
if (readers == 0) {
167+
sync.notifyAll();
168+
}
169+
}
170+
}
171+
172+
public void unlock(long stamp) {
173+
synchronized (sync) {
174+
if ((stamp & WRITE_MASK) != 0) {
175+
unlockWrite(stamp);
176+
} else {
177+
unlockRead(stamp);
178+
}
179+
}
180+
}
181+
182+
public long tryConvertToWriteLock(long stamp) {
183+
synchronized (sync) {
184+
if ((stamp & VERSION_MASK) != version) return 0L;
185+
186+
if ((stamp & WRITE_MASK) != 0) {
187+
if (writing) return stamp;
188+
return 0L;
189+
}
190+
191+
if ((stamp & READ_LOCK_BIT) != 0) {
192+
// Read lock
193+
if (writing) return 0L;
194+
if (readers == 1) {
195+
readers = 0;
196+
writing = true;
197+
return version | WRITE_MASK;
198+
}
199+
return 0L;
200+
} else {
201+
// Optimistic
202+
if (writing) return 0L;
203+
if (readers == 0) {
204+
writing = true;
205+
return version | WRITE_MASK;
206+
}
207+
return 0L;
208+
}
209+
}
210+
}
211+
212+
public long tryConvertToReadLock(long stamp) {
213+
synchronized (sync) {
214+
if ((stamp & VERSION_MASK) != version) return 0L;
215+
216+
if ((stamp & READ_LOCK_BIT) != 0) {
217+
// Already read lock
218+
return stamp;
219+
}
220+
221+
if ((stamp & WRITE_MASK) != 0) {
222+
// Write lock -> downgrade
223+
if (writing) {
224+
writing = false;
225+
readers++;
226+
version += 2;
227+
if (version == 0) version = 256;
228+
sync.notifyAll();
229+
// Return new version | READ_LOCK_BIT
230+
return version | READ_LOCK_BIT;
231+
}
232+
return 0L;
233+
}
234+
235+
// Optimistic -> Read
236+
if (writing) return 0L;
237+
readers++;
238+
return version | READ_LOCK_BIT;
239+
}
240+
}
241+
242+
public long tryConvertToOptimisticRead(long stamp) {
243+
synchronized (sync) {
244+
if ((stamp & VERSION_MASK) != version) return 0L;
245+
246+
// If Write Lock
247+
if ((stamp & WRITE_MASK) != 0) {
248+
if (writing) {
249+
writing = false;
250+
version += 2;
251+
if (version == 0) version = 256;
252+
sync.notifyAll();
253+
return version; // New version
254+
}
255+
return 0L;
256+
}
257+
258+
// If Read Lock
259+
if ((stamp & READ_LOCK_BIT) != 0) {
260+
if (readers > 0) {
261+
readers--;
262+
if (readers == 0) sync.notifyAll();
263+
return version;
264+
}
265+
return 0L;
266+
}
267+
268+
// If Optimistic
269+
if (!writing) return version;
270+
return 0L;
271+
}
272+
}
273+
274+
public Lock asReadLock() { return new ReadLockView(this); }
275+
public Lock asWriteLock() { return new WriteLockView(this); }
276+
public ReadWriteLock asReadWriteLock() { return new ReadWriteLockView(this); }
277+
278+
static final class ReadLockView implements Lock {
279+
private final StampedLock lock;
280+
ReadLockView(StampedLock lock) { this.lock = lock; }
281+
public void lock() { lock.readLock(); }
282+
public void lockInterruptibly() throws InterruptedException { lock.readLockInterruptibly(); }
283+
public boolean tryLock() { return lock.tryReadLock() != 0L; }
284+
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return lock.tryReadLock(time, unit) != 0L; }
285+
public void unlock() { lock.unlockReadNoStamp(); }
286+
public Condition newCondition() { throw new UnsupportedOperationException(); }
287+
}
288+
289+
static final class WriteLockView implements Lock {
290+
private final StampedLock lock;
291+
WriteLockView(StampedLock lock) { this.lock = lock; }
292+
public void lock() { lock.writeLock(); }
293+
public void lockInterruptibly() throws InterruptedException { lock.writeLockInterruptibly(); }
294+
public boolean tryLock() { return lock.tryWriteLock() != 0L; }
295+
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return lock.tryWriteLock(time, unit) != 0L; }
296+
public void unlock() { lock.unlockWriteNoStamp(); }
297+
public Condition newCondition() { throw new UnsupportedOperationException(); }
298+
}
299+
300+
static final class ReadWriteLockView implements ReadWriteLock {
301+
private final StampedLock lock;
302+
ReadWriteLockView(StampedLock lock) { this.lock = lock; }
303+
public Lock readLock() { return lock.asReadLock(); }
304+
public Lock writeLock() { return lock.asWriteLock(); }
305+
}
306+
307+
final void unlockReadNoStamp() {
308+
synchronized (sync) {
309+
if (readers > 0) {
310+
readers--;
311+
if (readers == 0) sync.notifyAll();
312+
} else {
313+
throw new IllegalMonitorStateException();
314+
}
315+
}
316+
}
317+
318+
final void unlockWriteNoStamp() {
319+
synchronized (sync) {
320+
if (writing) {
321+
writing = false;
322+
version += 2;
323+
if (version == 0) version = 256;
324+
sync.notifyAll();
325+
} else {
326+
throw new IllegalMonitorStateException();
327+
}
328+
}
329+
}
330+
331+
public boolean isReadLocked() {
332+
synchronized (sync) {
333+
return readers > 0;
334+
}
335+
}
336+
337+
public boolean isWriteLocked() {
338+
synchronized (sync) {
339+
return writing;
340+
}
341+
}
342+
343+
public int getReadLockCount() {
344+
synchronized (sync) {
345+
return readers;
346+
}
347+
}
348+
349+
public String toString() {
350+
synchronized (sync) {
351+
return super.toString() + (writing ? "[Write-locked]" : (readers > 0 ? "[Read-locks:" + readers + "]" : "[Unlocked]"));
352+
}
353+
}
354+
355+
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
356+
s.defaultReadObject();
357+
sync = new Object();
358+
version = 256;
359+
readers = 0;
360+
writing = false;
361+
}
362+
}

0 commit comments

Comments
 (0)