ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/jsr166e/SequenceLock.java
(Generate patch)

Comparing jsr166/src/jsr166e/SequenceLock.java (file contents):
Revision 1.4 by jsr166, Fri Jul 15 18:44:43 2011 UTC vs.
Revision 1.16 by jsr166, Mon Dec 5 04:50:19 2011 UTC

# Line 6 | Line 6
6  
7   package jsr166e;
8   import java.util.concurrent.TimeUnit;
9 + import java.util.concurrent.TimeoutException;
10   import java.util.concurrent.locks.Lock;
11   import java.util.concurrent.locks.ReentrantLock;
12   import java.util.concurrent.locks.Condition;
# Line 22 | Line 23 | import java.io.IOException;
23   * the lock is held. When it is even (i.e., ({@code lock.getSequence()
24   * & 1L) == 0L}), the lock is released. Method {@link
25   * #awaitAvailability} can be used to await availability of the lock,
26 < * returning its current sequence number. Sequence numbers are of type
27 < * {@code long} to ensure that they will not wrap around until
28 < * hundreds of years of use under current processor rates.  A
29 < * SequenceLock can be created with a specified number of
30 < * spins. Attempts to lock or await release retry at least the given
31 < * number of times before blocking. If not specified, a default,
32 < * possibly platform-specific, value is used.
26 > * returning its current sequence number. Sequence numbers (as well as
27 > * reentrant hold counts) are of type {@code long} to ensure that they
28 > * will not wrap around until hundreds of years of use under current
29 > * processor rates.  A SequenceLock can be created with a specified
30 > * number of spins. Attempts to acquire the lock in method {@link
31 > * #lock} will retry at least the given number of times before
32 > * blocking. If not specified, a default, possibly platform-specific,
33 > * value is used.
34   *
35   * <p>Except for the lack of support for specified fairness policies,
36 < * or {@link Condition} objects, a SequenceLock can be used in the same
37 < * way as {@link ReentrantLock}, and has a nearly identical
38 < * API. SequenceLocks may be preferable in contexts in which multiple
39 < * threads invoke read-only methods much more frequently than fully
40 < * locked methods.
36 > * or {@link Condition} objects, a SequenceLock can be used in the
37 > * same way as {@link ReentrantLock}. It provides similar status and
38 > * monitoring methods, such as {@link #isHeldByCurrentThread}.
39 > * SequenceLocks may be preferable in contexts in which multiple
40 > * threads invoke short read-only methods much more frequently than
41 > * fully locked methods.
42   *
43   * <p> Methods {@code awaitAvailability} and {@code getSequence} can
44   * be used together to define (partially) optimistic read-only methods
45   * that are usually more efficient than ReadWriteLocks when they
46 < * apply.  These read-only methods typically read multiple field
47 < * values into local variables when the lock is not held, retrying if
48 < * the sequence number changed while doing so.  Alternatively, because
49 < * {@code awaitAvailability} accommodates reentrancy, a method can
50 < * retry a bounded number of times before switching to locking mode.
51 < * While conceptually straightforward, expressing these ideas can be
52 < * verbose. For example:
46 > * apply.  These methods should in general be structured as loops that
47 > * await lock availability, then read {@code volatile} fields into
48 > * local variables (and may further read other values derived from
49 > * these, for example the {@code length} of a {@code volatile} array),
50 > * and retry if the sequence number changed while doing so.
51 > * Alternatively, because {@code awaitAvailability} accommodates
52 > * reentrancy, a method can retry a bounded number of times before
53 > * switching to locking mode.  While conceptually straightforward,
54 > * expressing these ideas can be verbose. For example:
55   *
56 < * <pre> {@code
56 > *  <pre> {@code
57   * class Point {
58 < *     private float x, y;
59 < *     private final SequenceLock sl = new SequenceLock();
58 > *   private volatile double x, y;
59 > *   private final SequenceLock sl = new SequenceLock();
60   *
61 < *     void move(float deltaX, float deltaY) { // an excluively locked method
62 < *        sl.lock();
63 < *        try {
64 < *            x += deltaX;
65 < *            y += deltaY;
66 < *        } finally {
67 < *          sl.unlock();
68 < *      }
69 < *  }
61 > *   // an exclusively locked method
62 > *   void move(double deltaX, double deltaY) {
63 > *     sl.lock();
64 > *     try {
65 > *       x += deltaX;
66 > *       y += deltaY;
67 > *     } finally {
68 > *       sl.unlock();
69 > *     }
70 > *   }
71   *
72 < *  float distanceFromOriginV1() { // A read-only method
73 < *      float currentX, currentY;
74 < *      long seq;
75 < *      do {
76 < *          seq = sl.awaitAvailability();
77 < *          currentX = x;
78 < *          currentY = y;
79 < *      } while (sl.getSequence() != seq); // retry if sequence changed
80 < *      return (float)Math.sqrt(currentX * currentX + currentY * currentY);
81 < *  }
72 > *   // A read-only method
73 > *   double distanceFromOriginV1() {
74 > *     double currentX, currentY;
75 > *     long seq;
76 > *     do {
77 > *       seq = sl.awaitAvailability();
78 > *       currentX = x;
79 > *       currentY = y;
80 > *     } while (sl.getSequence() != seq); // retry if sequence changed
81 > *     return Math.sqrt(currentX * currentX + currentY * currentY);
82 > *   }
83   *
84 < *  float distanceFromOriginV2() { // Uses bounded retries before locking
85 < *      float currentX, currentY;
86 < *      long seq;
87 < *      int retries = RETRIES_BEFORE_LOCKING; // for example 8
88 < *      try {
89 < *        do {
90 < *           if (--retries < 0)
91 < *              sl.lock();
92 < *           seq = sl.awaitAvailability();
93 < *           currentX = x;
94 < *           currentY = y;
95 < *        } while (sl.getSequence() != seq);
96 < *      } finally {
97 < *        if (retries < 0)
98 < *           sl.unlock();
99 < *      }
100 < *      return (float)Math.sqrt(currentX * currentX + currentY * currentY);
101 < *  }
102 < *}}</pre>
84 > *   // Uses bounded retries before locking
85 > *   double distanceFromOriginV2() {
86 > *     double currentX, currentY;
87 > *     long seq;
88 > *     int retries = RETRIES_BEFORE_LOCKING; // for example 8
89 > *     try {
90 > *       do {
91 > *         if (--retries < 0)
92 > *           sl.lock();
93 > *         seq = sl.awaitAvailability();
94 > *         currentX = x;
95 > *         currentY = y;
96 > *       } while (sl.getSequence() != seq);
97 > *     } finally {
98 > *       if (retries < 0)
99 > *         sl.unlock();
100 > *     }
101 > *     return Math.sqrt(currentX * currentX + currentY * currentY);
102 > *   }
103 > * }}</pre>
104   *
105   * @since 1.8
106   * @author Doug Lea
# Line 101 | Line 109 | public class SequenceLock implements Loc
109      private static final long serialVersionUID = 7373984872572414699L;
110  
111      static final class Sync extends AbstractQueuedLongSynchronizer {
112 +        static final long serialVersionUID = 2540673546047039555L;
113 +
114          /**
115           * The number of times to spin in lock() and awaitAvailability().
116           */
# Line 109 | Line 119 | public class SequenceLock implements Loc
119          /**
120           * The number of reentrant holds on this lock. Uses a long for
121           * compatibility with other AbstractQueuedLongSynchronizer
122 <         * operations.
122 >         * operations. Accessed only by lock holder.
123           */
124          long holds;
125  
# Line 151 | Line 161 | public class SequenceLock implements Loc
161          }
162  
163          public final long tryAcquireShared(long unused) {
164 <            return ((getState() & 1L) == 0L ||
165 <                    getExclusiveOwnerThread() == Thread.currentThread()) ?
166 <                1L : -1L; // must return long
164 >            return (((getState() & 1L) == 0L) ? 1L :
165 >                    (getExclusiveOwnerThread() == Thread.currentThread()) ? 0L:
166 >                    -1L);
167          }
168  
169          public final boolean tryReleaseShared(long unused) {
170 <            return true;
170 >            return (getState() & 1L) == 0L;
171          }
172  
173          public final Condition newCondition() {
# Line 172 | Line 182 | public class SequenceLock implements Loc
182  
183          final void lock() {
184              int k = spins;
185 <            while (!tryAcquire(1)) {
185 >            while (!tryAcquire(1L)) {
186                  if (k == 0) {
187 <                    acquire(1);
187 >                    acquire(1L);
188                      break;
189                  }
190                  --k;
# Line 183 | Line 193 | public class SequenceLock implements Loc
193  
194          final long awaitAvailability() {
195              long s;
186            int k = spins;
196              while (((s = getState()) & 1L) != 0L &&
197                     getExclusiveOwnerThread() != Thread.currentThread()) {
198 <                if (k > 0)
199 <                    --k;
191 <                else {
192 <                    acquireShared(1);
193 <                    releaseShared(1);
194 <                }
198 >                acquireShared(1L);
199 >                releaseShared(1L);
200              }
201              return s;
202          }
203  
204 +        final long tryAwaitAvailability(long nanos)
205 +            throws InterruptedException, TimeoutException {
206 +            Thread current = Thread.currentThread();
207 +            for (;;) {
208 +                long s = getState();
209 +                if ((s & 1L) == 0L || getExclusiveOwnerThread() == current) {
210 +                    releaseShared(1L);
211 +                    return s;
212 +                }
213 +                if (!tryAcquireSharedNanos(1L, nanos))
214 +                    throw new TimeoutException();
215 +                // since tryAcquireSharedNanos doesn't return seq
216 +                // retry with minimal wait time.
217 +                nanos = 1L;
218 +            }
219 +        }
220 +
221          final boolean isLocked() {
222              return (getState() & 1L) != 0L;
223          }
# Line 221 | Line 243 | public class SequenceLock implements Loc
243      /**
244       * The default spin value for constructor. Future versions of this
245       * class might choose platform-specific values.  Currently, except
246 <     * on uniprocessors, it is set to a small value that ovecomes near
246 >     * on uniprocessors, it is set to a small value that overcomes near
247       * misses between releases and acquires.
248       */
249      static final int DEFAULT_SPINS =
# Line 229 | Line 251 | public class SequenceLock implements Loc
251  
252      /**
253       * Creates an instance of {@code SequenceLock} with the default
254 <     * number of retry attempts to lock or await release before
233 <     * blocking.
254 >     * number of retry attempts to acquire the lock before blocking.
255       */
256      public SequenceLock() { sync = new Sync(DEFAULT_SPINS); }
257  
258      /**
259 <     * Creates an instance of {@code SequenceLock} that
260 <     * will retry attempts to lock or await release
261 <     * at least the given number times before blocking.
259 >     * Creates an instance of {@code SequenceLock} that will retry
260 >     * attempts to acquire the lock at least the given number times
261 >     * before blocking.
262       */
263      public SequenceLock(int spins) { sync = new Sync(spins); }
264  
265      /**
266       * Returns the current sequence number of this lock.  The sequence
267 <     * number is advanced upon each lock or unlock action. When this
268 <     * value is odd, the lock is held; when even, it is released.
267 >     * number is advanced upon each acquire or release action. When
268 >     * this value is odd, the lock is held; when even, it is released.
269       *
270       * @return the current sequence number
271       */
# Line 263 | Line 284 | public class SequenceLock implements Loc
284      public long awaitAvailability() { return sync.awaitAvailability(); }
285  
286      /**
287 <     * Acquires the lock.
287 >     * Returns the current sequence number if the lock is, or
288 >     * becomes, available within the specified waiting time.
289 >     *
290 >     * <p>If the lock is not available, the current thread becomes
291 >     * disabled for thread scheduling purposes and lies dormant until
292 >     * one of three things happens:
293 >     *
294 >     * <ul>
295       *
296 <     * <p>Acquires the lock if it is not held by another thread and returns
297 <     * immediately, setting the lock hold count to one.
296 >     * <li>The lock becomes available, in which case the current
297 >     * sequence number is returned.
298       *
299 <     * <p>If the current thread already holds the lock then the hold
300 <     * count is incremented by one and the method returns immediately.
299 >     * <li>Some other thread {@linkplain Thread#interrupt interrupts}
300 >     * the current thread, in which case this method throws
301 >     * {@link InterruptedException}.
302 >     *
303 >     * <li>The specified waiting time elapses, in which case
304 >     * this method throws {@link TimeoutException}.
305 >     *
306 >     * </ul>
307       *
308 <     * <p>If the lock is held by another thread then the
309 <     * current thread becomes disabled for thread scheduling
310 <     * purposes and lies dormant until the lock has been acquired,
311 <     * at which time the lock hold count is set to one.
308 >     * @param timeout the time to wait for availability
309 >     * @param unit the time unit of the timeout argument
310 >     * @return the current sequence number if the lock is available
311 >     *         upon return from this method
312 >     * @throws InterruptedException if the current thread is interrupted
313 >     * @throws TimeoutException if the lock was not available within
314 >     * the specified waiting time
315 >     * @throws NullPointerException if the time unit is null
316 >     */
317 >    public long tryAwaitAvailability(long timeout, TimeUnit unit)
318 >        throws InterruptedException, TimeoutException {
319 >        return sync.tryAwaitAvailability(unit.toNanos(timeout));
320 >    }
321 >
322 >    /**
323 >     * Acquires the lock.
324 >     *
325 >     * <p>If the current thread already holds this lock then the hold count
326 >     * is incremented by one and the method returns immediately without
327 >     * incrementing the sequence number.
328 >     *
329 >     * <p>If this lock not held by another thread, this method
330 >     * increments the sequence number (which thus becomes an odd
331 >     * number), sets the lock hold count to one, and returns
332 >     * immediately.
333 >     *
334 >     * <p>If the lock is held by another thread then the current
335 >     * thread may retry acquiring this lock, depending on the {@code
336 >     * spin} count established in constructor.  If the lock is still
337 >     * not acquired, the current thread becomes disabled for thread
338 >     * scheduling purposes and lies dormant until enabled by
339 >     * some other thread releasing the lock.
340       */
341      public void lock() { sync.lock(); }
342  
# Line 282 | Line 344 | public class SequenceLock implements Loc
344       * Acquires the lock unless the current thread is
345       * {@linkplain Thread#interrupt interrupted}.
346       *
285     * <p>Acquires the lock if it is not held by another thread and returns
286     * immediately, setting the lock hold count to one.
287     *
347       * <p>If the current thread already holds this lock then the hold count
348 <     * is incremented by one and the method returns immediately.
348 >     * is incremented by one and the method returns immediately without
349 >     * incrementing the sequence number.
350       *
351 <     * <p>If the lock is held by another thread then the
352 <     * current thread becomes disabled for thread scheduling
353 <     * purposes and lies dormant until one of two things happens:
351 >     * <p>If this lock not held by another thread, this method
352 >     * increments the sequence number (which thus becomes an odd
353 >     * number), sets the lock hold count to one, and returns
354 >     * immediately.
355 >     *
356 >     * <p>If the lock is held by another thread then the current
357 >     * thread may retry acquiring this lock, depending on the {@code
358 >     * spin} count established in constructor.  If the lock is still
359 >     * not acquired, the current thread becomes disabled for thread
360 >     * scheduling purposes and lies dormant until one of two things
361 >     * happens:
362       *
363       * <ul>
364       *
# Line 302 | Line 370 | public class SequenceLock implements Loc
370       * </ul>
371       *
372       * <p>If the lock is acquired by the current thread then the lock hold
373 <     * count is set to one.
373 >     * count is set to one and the sequence number is incremented.
374       *
375       * <p>If the current thread:
376       *
# Line 325 | Line 393 | public class SequenceLock implements Loc
393       * @throws InterruptedException if the current thread is interrupted
394       */
395      public void lockInterruptibly() throws InterruptedException {
396 <        sync.acquireInterruptibly(1);
396 >        sync.acquireInterruptibly(1L);
397      }
398  
399      /**
400       * Acquires the lock only if it is not held by another thread at the time
401       * of invocation.
402       *
403 <     * <p>Acquires the lock if it is not held by another thread and
404 <     * returns immediately with the value {@code true}, setting the
405 <     * lock hold count to one.
403 >     * <p>If the current thread already holds this lock then the hold
404 >     * count is incremented by one and the method returns {@code true}
405 >     * without incrementing the sequence number.
406 >     *
407 >     * <p>If this lock not held by another thread, this method
408 >     * increments the sequence number (which thus becomes an odd
409 >     * number), sets the lock hold count to one, and returns {@code
410 >     * true}.
411       *
412 <     * <p> If the current thread already holds this lock then the hold
413 <     * count is incremented by one and the method returns {@code true}.
341 <     *
342 <     * <p>If the lock is held by another thread then this method will return
343 <     * immediately with the value {@code false}.
412 >     * <p>If the lock is held by another thread then this method
413 >     * returns {@code false}.
414       *
415       * @return {@code true} if the lock was free and was acquired by the
416       *         current thread, or the lock was already held by the current
417       *         thread; and {@code false} otherwise
418       */
419 <    public boolean tryLock() { return sync.tryAcquire(1); }
419 >    public boolean tryLock() { return sync.tryAcquire(1L); }
420  
421      /**
422       * Acquires the lock if it is not held by another thread within the given
423       * waiting time and the current thread has not been
424       * {@linkplain Thread#interrupt interrupted}.
425       *
426 <     * <p>Acquires the lock if it is not held by another thread and returns
427 <     * immediately with the value {@code true}, setting the lock hold count
428 <     * to one. If this lock has been set to use a fair ordering policy then
429 <     * an available lock <em>will not</em> be acquired if any other threads
430 <     * are waiting for the lock. This is in contrast to the {@link #tryLock()}
431 <     * method. If you want a timed {@code tryLock} that does permit barging on
432 <     * a fair lock then combine the timed and un-timed forms together:
433 <     *
434 <     *  <pre> {@code
435 <     * if (lock.tryLock() ||
436 <     *     lock.tryLock(timeout, unit)) {
437 <     *   ...
438 <     * }}</pre>
439 <     *
440 <     * <p>If the current thread
371 <     * already holds this lock then the hold count is incremented by one and
372 <     * the method returns {@code true}.
373 <     *
374 <     * <p>If the lock is held by another thread then the
375 <     * current thread becomes disabled for thread scheduling
376 <     * purposes and lies dormant until one of three things happens:
426 >     * <p>If the current thread already holds this lock then the hold count
427 >     * is incremented by one and the method returns immediately without
428 >     * incrementing the sequence number.
429 >     *
430 >     * <p>If this lock not held by another thread, this method
431 >     * increments the sequence number (which thus becomes an odd
432 >     * number), sets the lock hold count to one, and returns
433 >     * immediately.
434 >     *
435 >     * <p>If the lock is held by another thread then the current
436 >     * thread may retry acquiring this lock, depending on the {@code
437 >     * spin} count established in constructor.  If the lock is still
438 >     * not acquired, the current thread becomes disabled for thread
439 >     * scheduling purposes and lies dormant until one of three things
440 >     * happens:
441       *
442       * <ul>
443       *
# Line 423 | Line 487 | public class SequenceLock implements Loc
487       */
488      public boolean tryLock(long timeout, TimeUnit unit)
489          throws InterruptedException {
490 <        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
490 >        return sync.tryAcquireNanos(1L, unit.toNanos(timeout));
491      }
492  
493      /**
494       * Attempts to release this lock.
495       *
496 <     * <p>If the current thread is the holder of this lock then the hold
497 <     * count is decremented.  If the hold count is now zero then the lock
498 <     * is released.  If the current thread is not the holder of this
499 <     * lock then {@link IllegalMonitorStateException} is thrown.
496 >     * <p>If the current thread is the holder of this lock then the
497 >     * hold count is decremented.  If the hold count is now zero then
498 >     * the sequence number is incremented (thus becoming an even
499 >     * number) and the lock is released.  If the current thread is not
500 >     * the holder of this lock then {@link
501 >     * IllegalMonitorStateException} is thrown.
502       *
503       * @throws IllegalMonitorStateException if the current thread does not
504       *         hold this lock
# Line 461 | Line 527 | public class SequenceLock implements Loc
527       * @return the number of holds on this lock by the current thread,
528       *         or zero if this lock is not held by the current thread
529       */
530 <    public long getHoldCount() { return sync.getHoldCount();  }
530 >    public long getHoldCount() { return sync.getHoldCount(); }
531  
532      /**
533       * Queries if this lock is held by the current thread.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines