Basic.java revision 12745:f068a4ffddd2
1/*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation.
7 *
8 * This code is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11 * version 2 for more details (a copy is included in the LICENSE file that
12 * accompanied this code).
13 *
14 * You should have received a copy of the GNU General Public License version
15 * 2 along with this work; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
19 * or visit www.oracle.com if you need additional information or have any
20 * questions.
21 */
22
23/*
24 * This file is available under and governed by the GNU General Public
25 * License version 2 only, as published by the Free Software Foundation.
26 * However, the following notice accompanied the original version of this
27 * file:
28 *
29 * Written by Doug Lea with assistance from members of JCP JSR-166
30 * Expert Group and released to the public domain, as explained at
31 * http://creativecommons.org/publicdomain/zero/1.0/
32 */
33
34/*
35 * @test
36 * @bug 8005697
37 * @summary Basic tests for StampedLock
38 * @author Chris Hegarty
39 */
40
41import java.util.Iterator;
42import java.util.concurrent.Phaser;
43import java.util.concurrent.TimeUnit;
44import static java.util.concurrent.TimeUnit.*;
45import java.util.concurrent.atomic.AtomicInteger;
46import java.util.concurrent.locks.Lock;
47import java.util.concurrent.locks.ReadWriteLock;
48import java.util.concurrent.locks.StampedLock;
49
50public class Basic {
51
52    static void checkResult(Locker l, Class<? extends Throwable> c) {
53        Throwable t = l.thrown();
54        if (! ((t == null && c == null) || (c != null && c.isInstance(t)))) {
55            fail("Mismatch in thread " +
56                 l.getName() + ": " +
57                 t + ", " +
58                 (c == null ? "<null>" : c.getName()));
59        }
60
61        if (c == null)
62            check(l.stamp() != 0L);  // must have acquired the lock
63        else
64            check(l.stamp() == 0L);  // must NOT have acquired the lock
65    }
66
67    //----------------------------------------------------------------
68    // Mechanism to get all test threads into "running" mode.
69    //----------------------------------------------------------------
70    static void toTheStartingGate(Phaser gate) {
71        try {
72            gate.arriveAndAwaitAdvance();
73        } catch (Throwable t) {
74            unexpected(t);
75        }
76    }
77
78    abstract static class Locker extends Thread {
79        static AtomicInteger count = new AtomicInteger(1);
80        private volatile Throwable thrown;
81        private volatile long stamp;;
82        protected void thrown(Throwable thrown) { this.thrown = thrown; }
83        public Throwable thrown() { return thrown; }
84        protected void stamp(long stamp) { this.stamp = stamp; }
85        public long stamp() { return stamp; }
86
87        Locker() {
88            this("Locker");
89        }
90
91        Locker(String name) {
92            this.setName(name + ":" + count.getAndIncrement());
93            this.setDaemon(true);
94        }
95    }
96
97    abstract static class Reader extends Locker {
98        Reader() { super("Reader"); }
99        Reader(String name) { super(name); }
100    }
101
102    static Reader reader(final StampedLock sl, final Phaser gate) {
103        return new Reader() { public void run() {
104            if (gate != null ) toTheStartingGate(gate);
105            stamp(sl.readLock());
106            try {
107                check(sl.validate(stamp()));
108                check(sl.isReadLocked());
109                check(!sl.isWriteLocked());
110            } finally { sl.unlockRead(stamp()); } }};
111    }
112
113    static Reader readerView(final StampedLock sl, final Phaser gate) {
114        return new Reader("ReaderView") { public void run() {
115            if (gate != null ) toTheStartingGate(gate);
116            final Lock rl = sl.asReadLock();
117            rl.lock();
118            try {
119                stamp(1L);   // got the lock
120                check(sl.isReadLocked());
121                check(!sl.isWriteLocked());
122            } finally { rl.unlock(); } }};
123    }
124
125    static Reader reader(StampedLock sl, Phaser gate, boolean view) {
126        return view ? readerView(sl, gate) : reader(sl, gate);
127    }
128
129    static Reader interruptibleReader(final StampedLock sl,
130                                      final long timeout,
131                                      final TimeUnit unit,
132                                      final Phaser gate) {
133        return new Reader("InterruptibleReader") { public void run() {
134            if (gate != null ) toTheStartingGate(gate);
135            try {
136                if (timeout < 0)
137                    stamp(sl.readLockInterruptibly());
138                else
139                    stamp(sl.tryReadLock(timeout, unit));
140                check(sl.validate(stamp()));
141                check(sl.isReadLocked());
142                check(!sl.isWriteLocked());
143            } catch (Throwable x) { thrown(x);
144            } finally { if (stamp() != 0L) sl.unlockRead(stamp()); } }};
145    }
146
147    static Reader interruptibleReaderView(final StampedLock sl,
148                                          final long timeout,
149                                          final TimeUnit unit,
150                                          final Phaser gate) {
151        return new Reader("InterruptibleReaderView") { public void run() {
152            if (gate != null ) toTheStartingGate(gate);
153            final Lock rl = sl.asReadLock();
154
155            try {
156                if (timeout < 0)
157                    rl.lockInterruptibly();
158                else
159                    rl.tryLock(timeout, unit);
160                stamp(1L);  // got the lock
161                check(sl.isReadLocked());
162                check(!sl.isWriteLocked());
163            } catch (Throwable x) { thrown(x);
164            } finally { if (stamp() != 0L) rl.unlock(); } }};
165    }
166
167    static Reader interruptibleReader(final StampedLock sl,
168                                      final long timeout,
169                                      final TimeUnit unit,
170                                      final Phaser gate,
171                                      final boolean view) {
172        return view ? interruptibleReaderView(sl, timeout, unit, gate)
173                    : interruptibleReader(sl, timeout, unit, gate);
174    }
175
176    abstract static class Writer extends Locker {
177        Writer() { super("Writer"); }
178        Writer(String name) { super(name); }
179    }
180
181    static Writer writer(final StampedLock sl, final Phaser gate) {
182        return new Writer() { public void run() {
183            if (gate != null ) toTheStartingGate(gate);
184            try {
185                stamp(sl.writeLock());
186                check(sl.validate(stamp()));
187                check(!sl.isReadLocked());
188                check(sl.isWriteLocked());
189            } finally { sl.unlockWrite(stamp()); } }};
190    }
191
192    static Writer writerView(final StampedLock sl, final Phaser gate) {
193        return new Writer("WriterView") { public void run() {
194            if (gate != null ) toTheStartingGate(gate);
195            Lock wl = sl.asWriteLock();
196            wl.lock();
197            try {
198                stamp(1L);  // got the lock
199                check(!sl.isReadLocked());
200                check(sl.isWriteLocked());
201            } finally { wl.unlock(); } }};
202    }
203
204    static Writer writer(StampedLock sl, Phaser gate, boolean view) {
205        return view ? writerView(sl, gate) : writer(sl, gate);
206    }
207
208    static Writer interruptibleWriter(final StampedLock sl,
209                                      final long timeout,
210                                      final TimeUnit unit,
211                                      final Phaser gate) {
212        return new Writer("InterruptibleWriter") { public void run() {
213            if (gate != null ) toTheStartingGate(gate);
214            try {
215                if (timeout < 0)
216                    stamp(sl.writeLockInterruptibly());
217                else
218                    stamp(sl.tryWriteLock(timeout, unit));
219                check(sl.validate(stamp()));
220                check(!sl.isReadLocked());
221                check(sl.isWriteLocked());
222            } catch (Throwable x) { thrown(x);
223            } finally { if (stamp() != 0L) sl.unlockWrite(stamp()); } }};
224    }
225
226    static Writer interruptibleWriterView(final StampedLock sl,
227                                          final long timeout,
228                                          final TimeUnit unit,
229                                          final Phaser gate) {
230        return new Writer("InterruptibleWriterView") { public void run() {
231            if (gate != null ) toTheStartingGate(gate);
232            Lock wl = sl.asWriteLock();
233            try {
234                if (timeout < 0)
235                    wl.lockInterruptibly();
236                else
237                    wl.tryLock(timeout, unit);
238                stamp(1L);  // got the lock
239                check(!sl.isReadLocked());
240                check(sl.isWriteLocked());
241            } catch (Throwable x) { thrown(x);
242            } finally { if (stamp() != 0L) wl.unlock(); } }};
243    }
244
245    static Writer interruptibleWriter(final StampedLock sl,
246                                      final long timeout,
247                                      final TimeUnit unit,
248                                      final Phaser gate,
249                                      final boolean view) {
250        return view ? interruptibleWriterView(sl, timeout, unit, gate)
251                    : interruptibleWriter(sl, timeout, unit, gate);
252    }
253
254    // Returns an infinite lazy list of all possible reader combinations.
255    static Iterator<Reader> readerIterator(final StampedLock sl,
256                                           final Phaser gate) {
257        return new Iterator<Reader>() {
258            int i = 0;
259            boolean view = false;
260            public boolean hasNext() { return true; }
261            public Reader next() {
262                switch ((i++)&7) {
263                    case 1: case 4: case 7:
264                        return reader(sl, gate, view ^= true);
265                    case 2: case 5:
266                        return interruptibleReader(sl, -1, SECONDS, gate, view ^= true);
267                    default:
268                        return interruptibleReader(sl, 30, SECONDS, gate, view ^= true); }}
269            public void remove() {throw new UnsupportedOperationException();}};
270    }
271
272    // Returns an infinite lazy list of all possible writer combinations.
273    static Iterator<Writer> writerIterator(final StampedLock sl,
274                                           final Phaser gate) {
275        return new Iterator<Writer>() {
276            int i = 0;
277            boolean view = false;
278            public boolean hasNext() { return true; }
279            public Writer next() {
280                switch ((i++)&7) {
281                    case 1: case 4: case 7:
282                        return writer(sl, gate, view ^= true);
283                    case 2: case 5:
284                        return interruptibleWriter(sl, -1, SECONDS, gate, view ^= true);
285                    default:
286                        return interruptibleWriter(sl, 30, SECONDS, gate, view ^= true); }}
287            public void remove() {throw new UnsupportedOperationException();}};
288    }
289
290    private static void realMain(String[] args) throws Throwable {
291
292        Thread.currentThread().setName("mainThread");
293
294        //----------------------------------------------------------------
295        // Some basic sanity
296        //----------------------------------------------------------------
297        try {
298            final StampedLock sl = new StampedLock();
299            check(!sl.isReadLocked());
300            check(!sl.isWriteLocked());
301            long stamp = sl.tryOptimisticRead();
302            check(stamp != 0L);
303            check(sl.validate(stamp));
304            check(!sl.validate(0));
305
306            stamp = sl.writeLock();
307            try {
308                check(sl.validate(stamp));
309                check(!sl.isReadLocked());
310                check(sl.isWriteLocked());
311                check(sl.tryReadLock() == 0L);
312                check(sl.tryReadLock(100, MILLISECONDS) == 0L);
313                check(sl.tryOptimisticRead() == 0L);
314                check(sl.tryWriteLock() == 0L);
315                check(sl.tryWriteLock(100, MILLISECONDS) == 0L);
316                check(!sl.tryUnlockRead());
317                check(sl.tryConvertToWriteLock(stamp) == stamp);
318                try {
319                    sl.unlockRead(stamp);
320                    fail("Expected unlockRead to throw when not holding read lock");
321                } catch (IllegalMonitorStateException x) {
322                    pass();
323                }
324                check(sl.validate(stamp));
325            } finally {
326                sl.unlockWrite(stamp);
327            }
328            check(!sl.isWriteLocked());
329
330            stamp = sl.readLock();
331            try {
332                check(sl.validate(stamp));
333                check(sl.isReadLocked());
334                check(!sl.isWriteLocked());
335                check(sl.tryOptimisticRead() != 0L);
336                check(sl.tryWriteLock() == 0L);
337                check(sl.tryWriteLock(100, MILLISECONDS) == 0L);
338                check(!sl.tryUnlockWrite());
339                check(sl.tryConvertToReadLock(stamp) == stamp);
340                try {
341                    sl.unlockWrite(stamp);
342                    fail("Expected unlockWrite to throw when not holding read lock");
343                } catch (IllegalMonitorStateException x) {
344                    pass();
345                }
346                check(sl.validate(stamp));
347            } finally {
348                sl.unlockRead(stamp);
349            }
350            check(!sl.isReadLocked());
351
352            stamp = sl.tryReadLock(100, MILLISECONDS);
353            try {
354                check(stamp != 0L);
355            } finally {
356                sl.unlockRead(stamp);
357            }
358        } catch (Throwable t) { unexpected(t); }
359
360        //----------------------------------------------------------------
361        // Multiple writers single reader
362        //----------------------------------------------------------------
363        try {
364            StampedLock sl = new StampedLock();
365            Phaser gate = new Phaser(102);
366            Iterator<Writer> writers = writerIterator(sl, gate);
367            Iterator<Reader> readers = readerIterator(sl, gate);
368            for (int i = 0; i < 10; i++) {
369                check(!sl.isReadLocked());
370                check(!sl.isWriteLocked());
371                check(!sl.tryUnlockRead());
372                check(!sl.tryUnlockWrite());
373                check(sl.tryOptimisticRead() != 0L);
374                Locker[] wThreads = new Locker[100];;
375                for (int j=0; j<100; j++)
376                    wThreads[j] = writers.next();
377                for (int j=0; j<100; j++)
378                    wThreads[j].start();
379                Reader reader = readers.next(); reader.start();
380                toTheStartingGate(gate);
381                reader.join();
382                for (int j=0; j<100; j++)
383                    wThreads[j].join();
384                for (int j=0; j<100; j++)
385                    checkResult(wThreads[j], null);
386                checkResult(reader, null);
387            }
388        } catch (Throwable t) { unexpected(t); }
389
390        //----------------------------------------------------------------
391        // Multiple readers single writer
392        //----------------------------------------------------------------
393        try {
394            StampedLock sl = new StampedLock();
395            Phaser gate = new Phaser(102);
396            Iterator<Writer> writers = writerIterator(sl, gate);
397            Iterator<Reader> readers = readerIterator(sl, gate);
398            for (int i = 0; i < 10; i++) {
399                check(!sl.isReadLocked());
400                check(!sl.isWriteLocked());
401                check(!sl.tryUnlockRead());
402                check(!sl.tryUnlockWrite());
403                check(sl.tryOptimisticRead() != 0L);
404                Locker[] rThreads = new Locker[100];;
405                for (int j=0; j<100; j++)
406                    rThreads[j] = readers.next();
407                for (int j=0; j<100; j++)
408                    rThreads[j].start();
409                Writer writer = writers.next(); writer.start();
410                toTheStartingGate(gate);
411                writer.join();
412                for (int j=0; j<100; j++)
413                    rThreads[j].join();
414                for (int j=0; j<100; j++)
415                    checkResult(rThreads[j], null);
416                checkResult(writer, null);
417            }
418        } catch (Throwable t) { unexpected(t); }
419
420        //----------------------------------------------------------------
421        // thread interrupted
422        //----------------------------------------------------------------
423        try {
424            boolean view = false;
425            StampedLock sl = new StampedLock();
426            for (long timeout : new long[] { -1L, 30L, -1L, 30L }) {
427                long stamp = sl.writeLock();
428                try {
429                    Reader r = interruptibleReader(sl, timeout, SECONDS, null, view);
430                    r.start();
431                    // allow r to block
432                    Thread.sleep(2000);
433                    r.interrupt();
434                    r.join();
435                    checkResult(r, InterruptedException.class);
436                } finally {
437                    sl.unlockWrite(stamp);
438                }
439                stamp = sl.readLock();
440                try {
441                    Writer w = interruptibleWriter(sl, timeout, SECONDS, null, view);
442                    w.start();
443                    // allow w to block
444                    Thread.sleep(2000);
445                    w.interrupt();
446                    w.join();
447                    checkResult(w, InterruptedException.class);
448                } finally {
449                    sl.unlockRead(stamp);
450                }
451                check(!sl.isReadLocked());
452                check(!sl.isWriteLocked());
453                check(!sl.tryUnlockRead());
454                check(!sl.tryUnlockWrite());
455                check(sl.tryOptimisticRead() != 0L);
456                if (timeout == 30L)
457                    view = true;
458            }
459        } catch (Throwable t) { unexpected(t); }
460
461        //----------------------------------------------------------------
462        // timeout
463        //----------------------------------------------------------------
464        try {
465            StampedLock sl = new StampedLock();
466            for (long timeout : new long[] { 0L, 5L }) {
467                long stamp = sl.writeLock();
468                try {
469                    check(sl.tryReadLock(timeout, SECONDS) == 0L);
470                } finally {
471                    sl.unlockWrite(stamp);
472                }
473                stamp = sl.readLock();
474                try {
475                    check(sl.tryWriteLock(timeout, SECONDS) == 0L);
476                } finally {
477                    sl.unlockRead(stamp);
478                }
479                check(!sl.isReadLocked());
480                check(!sl.isWriteLocked());
481                check(!sl.tryUnlockRead());
482                check(!sl.tryUnlockWrite());
483                check(sl.tryOptimisticRead() != 0L);
484            }
485        } catch (Throwable t) { unexpected(t); }
486
487        //----------------------------------------------------------------
488        // optimistic read
489        //----------------------------------------------------------------
490        try {
491            StampedLock sl = new StampedLock();
492            Iterator<Writer> writers = writerIterator(sl, null);
493            Iterator<Reader> readers = readerIterator(sl, null);
494            for (int i = 0; i < 10; i++) {
495                check(!sl.isReadLocked());
496                check(!sl.isWriteLocked());
497                check(!sl.tryUnlockRead());
498                check(!sl.tryUnlockWrite());
499                long stamp = sl.tryOptimisticRead();
500                check(stamp != 0L);
501                check(sl.tryConvertToOptimisticRead(stamp) == stamp);
502                Reader r = readers.next(); r.start();
503                r.join();
504                checkResult(r, null);
505                check(sl.validate(stamp));
506                check(sl.tryConvertToOptimisticRead(stamp) == stamp);
507                Writer w = writers.next(); w.start();
508                w.join();
509                checkResult(w, null);
510                check(sl.validate(stamp) == false);
511            }
512        } catch (Throwable t) { unexpected(t); }
513
514        //----------------------------------------------------------------
515        // convert
516        //----------------------------------------------------------------
517        try {
518            StampedLock sl = new StampedLock();
519            for (int i = 0; i < 2; i++) {
520                check(!sl.isReadLocked());
521                check(!sl.isWriteLocked());
522                check(!sl.tryUnlockRead());
523                check(!sl.tryUnlockWrite());
524                long stamp = sl.tryOptimisticRead();
525                check(stamp != 0L);
526                check((stamp = sl.tryConvertToReadLock(stamp)) != 0L);
527                check(sl.validate(stamp));
528                check(sl.isReadLocked());
529                check(sl.tryWriteLock() == 0L);
530                check(sl.tryWriteLock(1L, SECONDS) == 0L);
531                check((stamp = sl.tryConvertToWriteLock(stamp)) != 0L);
532                check(sl.validate(stamp));
533                check(!sl.isReadLocked());
534                check(sl.isWriteLocked());
535                check(sl.tryReadLock(1L, SECONDS) == 0L);
536                if (i != 0) {
537                    sl.unlockWrite(stamp);
538                    continue;
539                }
540                // convert down
541                check((stamp = sl.tryConvertToReadLock(stamp)) != 0L);
542                check(sl.validate(stamp));
543                check(sl.isReadLocked());
544                check(!sl.isWriteLocked());
545                check(sl.tryWriteLock() == 0L);
546                check(sl.tryWriteLock(1L, SECONDS) == 0L);
547                check((stamp = sl.tryConvertToOptimisticRead(stamp)) != 0L);
548                check(sl.validate(stamp));
549                check(!sl.isReadLocked());
550                check(!sl.isWriteLocked());
551                check(sl.validate(stamp));
552            }
553        } catch (Throwable t) { unexpected(t); }
554
555        //----------------------------------------------------------------
556        // views
557        //----------------------------------------------------------------
558        try {
559            StampedLock sl = new StampedLock();
560
561            Lock rl = sl.asReadLock();
562            Lock wl = sl.asWriteLock();
563            for (int i = 0; i < 2; i++) {
564                rl.lock();
565                try {
566                    check(sl.isReadLocked());
567                    check(!sl.isWriteLocked());
568                    check(sl.tryWriteLock() == 0L);
569                    check(sl.tryWriteLock(1L, SECONDS) == 0L);
570                } finally {
571                    rl.unlock();
572                }
573                check(!sl.isReadLocked());
574                check(!sl.isWriteLocked());
575
576                wl.lock();
577                try {
578                    check(!sl.isReadLocked());
579                    check(sl.isWriteLocked());
580                    check(sl.tryWriteLock() == 0L);
581                    check(sl.tryWriteLock(1L, SECONDS) == 0L);
582                } finally {
583                    wl.unlock();
584                }
585                check(!sl.isReadLocked());
586                check(!sl.isWriteLocked());
587
588                ReadWriteLock rwl = sl.asReadWriteLock();
589                rl = rwl.readLock();
590                wl = rwl.writeLock();
591            }
592        } catch (Throwable t) { unexpected(t); }
593    }
594
595    //--------------------- Infrastructure ---------------------------
596    static volatile int passed = 0, failed = 0;
597    static void pass() {passed++;}
598    static void fail() {failed++; Thread.dumpStack();}
599    static void fail(String msg) {System.out.println(msg); fail();}
600    static void unexpected(Throwable t) {failed++; t.printStackTrace();}
601    static void check(boolean cond) {if (cond) pass(); else fail();}
602    static void equal(Object x, Object y) {
603        if (x == null ? y == null : x.equals(y)) pass();
604        else fail(x + " not equal to " + y);}
605    public static void main(String[] args) throws Throwable {
606        try {realMain(args);} catch (Throwable t) {unexpected(t);}
607        System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
608        if (failed > 0) throw new AssertionError("Some tests failed");}
609}
610