1 package locklib;
2
3 import java.util.HashMap;
4 import java.util.HashSet;
5 import java.util.LinkedList;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Set;
9
10 import ensure.Ensure;
11
12 /**
13 * A target is an object that can be locked. Targets can be arranged
14 * hierarchically. Locking a target may impose locks on its parent targets,
15 * depending on the {@link LockPolicy} present in the {@link LockManager}.
16 * @param <LockType> the type of locks used by this target
17 */
18 /*
19 * Implementation notes:
20 *
21 * This class is prone to deadlocks. In order to avoid
22 * deadlocks while navigating the hierarchy, we must always acquire child
23 * synchronization locks before parent synchronization locks. We also must
24 * acquire target synchronization locks before acquiring synchronization
25 * locks on grants or futures.
26 *
27 * Locking is done top-down in the hierarchy.
28 *
29 * Cancellation and unlock are done bottom-up in the hierarchy. Cancellation
30 * starts at the bottom and keeps canceling futures until it either reaches
31 * the top of the hierarchy or until it finds a locked node. If it finds a
32 * locked node, it switches to unlocking. Unlocking unlocks all nodes from
33 * the bottom of a chain (or, at least, the bottom of the chain that has been
34 * acquired) upwards.
35 *
36 * The most complex part of the algorithm is locking that may happen during
37 * unlock. When unlock of a target is done, it may be possible to grant
38 * pending locks. However, granting locks may need to proceed down the
39 * hierarchy. Because synchronization locks on children must be acquired
40 * before synchronization locks on parents, it is not possible to proceed
41 * downwards with the synchronization.
42 *
43 * To solve this, we will collect the targets that had locks released during
44 * unlock (because after releasing the locks the hierarchy may change) and
45 * will try to grant locks pending on those targets after all synchronization
46 * locks have been released.
47 *
48 * The way locklib works, unlock and cancellation are "immediate" actions and
49 * lock may take time.
50 */
51 public class Target<LockType> {
52 /**
53 * Children of this target. Maps children to all the children's
54 * children.
55 */
56 private Map<Target<LockType>, Set<Target<LockType>>> m_children;
57
58 /**
59 * Parent of this target, <code>null</code> if none.
60 */
61 private Target<LockType> m_parent;
62
63 /**
64 * The lock manager.
65 */
66 private LockManager<LockType> m_manager;
67
68 /**
69 * Lock requests currently held.
70 */
71 private Set<LockGrant<LockType>> m_locks;
72
73 /**
74 * The waiting queue, if any.
75 */
76 private WaitQueue<LockType> m_queue;
77
78 /**
79 * Creates a new target.
80 * @param lm the target's lock manager
81 */
82 public Target(LockManager<LockType> lm) {
83 Ensure.not_null(lm, "lm == null");
84 m_manager = lm;
85 m_parent = null;
86 m_children = new HashMap<>();
87 m_locks = new HashSet<>();
88 m_queue = null;
89 }
90
91 /**
92 * Creates a new target, child of an existing parent.
93 * @param parent the target's parent
94 */
95 public Target(Target<LockType> parent) {
96 this(Ensure.not_null(parent, "parent == null").m_manager);
97 parent(parent);
98 }
99
100 /**
101 * Sets the parent target of this target. Invoking this method does not
102 * add or remove any locks from either this target, its current parent (if
103 * any) or its new parent (if any).
104 * @param p the parent target or <code>null</code> if this target should
105 * not have a parent
106 * @throws CyclicTargetHierarchyException setting the parent as
107 * <code>p</code> would yield a cycle
108 */
109 public synchronized void parent(Target<LockType> p) {
110 if (p == m_parent) {
111 return;
112 }
113
114 if (p == this) {
115 throw new CyclicTargetHierarchyException("Cannot add a target as "
116 + "a parent of itself.");
117 }
118
119 if (p != null && p.m_manager != m_manager) {
120 throw new DifferentLockManagersException("Target {" + p + "} has "
121 + "lock manager {" + p.m_manager + "} and this {" + this
122 + "} has lock manager { " + m_manager + "}.");
123 }
124
125 if (m_locks.size() > 0) {
126 throw new HierarchyChangeBreaksChainException("Cannot set parent "
127 + "of target {" + this + "} to {" + p + "} because it "
128 + "would break existing lock chains.");
129 }
130
131 if (m_parent != null) {
132 m_parent.remove_child(this);
133 m_parent = null;
134 }
135
136 if (p != null) {
137 /*
138 * Lock the new parent and check that we are not a parent of p.
139 */
140 synchronized (p) {
141 if (m_children.containsKey(p)) {
142 throw new CyclicTargetHierarchyException("Cannot add {"
143 + p + "} as a parent of {" + this + "} because "
144 + "it is a child.");
145 }
146
147 for (Set<Target<LockType>> ch : m_children.values()) {
148 if (ch.contains(p)) {
149 throw new CyclicTargetHierarchyException("Cannot add {"
150 + p + "} as a parent of {" + this + "} "
151 + "because it is a (non-immediate) "
152 + "child.");
153 }
154 }
155
156 p.m_children.put(this, new HashSet<Target<LockType>>());
157 m_parent = p;
158 update_parent_children();
159 }
160 }
161 }
162
163 /**
164 * Removes a child from the parent. This method is invoked with the
165 * child already lock by the current thread.
166 * @param c the child
167 */
168 private synchronized void remove_child(Target<LockType> c) {
169 Ensure.not_null(c, "c == null");
170 Ensure.is_true(m_children.containsKey(c), "!m_children.containsKey(c)");
171 m_children.remove(c);
172
173 update_parent_children();
174 }
175
176 /**
177 * Informs the parent, if any, that the target's children have changed.
178 * This method is invoked with this object already locked.
179 */
180 private void update_parent_children() {
181 if (m_parent != null) {
182 Set<Target<LockType>> ch = new HashSet<>(m_children.keySet());
183
184 for (Set<Target<LockType>> cch : m_children.values()) {
185 ch.addAll(cch);
186 }
187
188 m_parent.update_children(this, ch);
189 }
190 }
191
192 /**
193 * Updates the list of children of one of this object's child. The child
194 * is already locked.
195 * @param c the child
196 * @param cch the child's children
197 */
198 private synchronized void update_children(Target<LockType> c,
199 Set<Target<LockType>> cch) {
200 Ensure.not_null(c, "c == null");
201 Ensure.is_true(m_children.containsKey(c), "!m_children.containsKey(c)");
202 Ensure.not_null(cch, "cch == null");
203
204 m_children.put(c, cch);
205 update_parent_children();
206 }
207
208 /**
209 * Obtains the parent of this target.
210 * @return the parent or <code>null</code> if none
211 */
212 public synchronized Target<LockType> parent() {
213 return m_parent;
214 }
215
216 /**
217 * Obtains the set of all children (non-recursive) of this target.
218 * @return all children
219 */
220 public synchronized Set<Target<LockType>> children() {
221 return new HashSet<>(m_children.keySet());
222 }
223
224 /**
225 * <p>Requests an asynchronous lock of this target. This method will return
226 * immediately. The {@link LockGrantFuture#get()} method (or its timed
227 * variant) may be used to wait for the lock to be acquired.</p>
228 *
229 * <p>This method will create a chain starting from the top of the
230 * hierarchy of this target up to this target.</p>
231 * @param request the lock request which may not be already acquired
232 * @return the lock grant information
233 */
234 public synchronized LockGrantFuture<LockType> lock(
235 LockRequest<LockType> request) {
236 Ensure.not_null(request, "request == null");
237
238 LockGrantFuture<LockType> future = null;
239
240 /*
241 * If there is a parent, we must get its future before this one.
242 * Locking must be done upwards in the hierarchy.
243 */
244 LockGrantFuture<LockType> plock = null;
245 if (m_parent != null) {
246 LockType plocktype = m_manager.policy().parent_lock(
247 request.type());
248 plock = m_parent.lock(new LockRequest<>(plocktype,
249 request.holder()));
250 }
251
252 /*
253 * Create the future with the lock grant.
254 */
255 future = new LockGrantFuture<>(request, this, plock);
256
257 /*
258 * Try to acquire the lock. If we fail to acquire the lock, put it in
259 * the queue.
260 */
261 TryLockResult<LockType> r = try_lock(future);
262 Ensure.is_null(r.successor(), "r.successor() != null");
263 if (!r.granted()) {
264 m_manager.pre_checker().pre_check_wait(future.request(), this);
265 if (m_queue == null) {
266 m_queue = m_manager.wait_queue_factory().make();
267 }
268
269 m_queue.enqueue(future);
270 }
271
272 return future;
273 }
274
275 /**
276 * Tries to lock a future of a lock in this target. If the future's
277 * predecessor has not been acquired yet, it does nothing. Otherwise tries
278 * to acquire the lock. If the future is acquired, it is removed from the
279 * wait queue, if it is there, and the future for the successor in the
280 * chain is returned.
281 * @param future the future
282 * @return the first element in the pair is whether the lock was
283 * granted or not; the second element in the pair is
284 * <code>null</code> if the lock was not acquired or if it was
285 * but there is no successor in the chain; the second element in the pair
286 * is the successor of the successfully granted lock if the lock was
287 * granted
288 */
289 private synchronized TryLockResult<LockType> try_lock(
290 LockGrantFuture<LockType> future) {
291 Ensure.not_null(future, "future == null");
292 Ensure.same(this, future.target(), "future.target() != this");
293
294 /*
295 * Set to not null if we should process the successor.
296 */
297 LockGrantFuture<LockType> successor;
298 boolean succeeded;
299
300 synchronized (future) {
301 Ensure.is_false(future.isDone(), "future.isDone()");
302
303 successor = future.successor_future();
304
305 /*
306 * If the future has a predecessor which is not done yet,
307 * we can't do anything.
308 */
309 LockGrantFuture<LockType> pf = future.predecessor_future();
310 LockGrant<LockType> plg = null;
311 if (pf != null) {
312 synchronized (pf) {
313 if (!pf.isDone()) {
314 return new TryLockResult<>(false, null);
315 }
316
317 plg = pf.get_now();
318 }
319 }
320
321 /*
322 * The future either doesn't have a predecessor or, if it has,
323 * been granted. Note that the predecessor cannot be cancelled
324 * because that would require canceling this future.
325 */
326 if (pf != null) {
327 Ensure.is_false(pf.isCancelled(), "pf.isCancelled()");
328 }
329
330 if (can_grant(future.request())) {
331 LockGrant<LockType> lg;
332 lg = grant(future.request(), plg);
333 future.acquired(lg);
334 if (m_queue != null) {
335 m_queue.dequeue(future);
336 }
337
338 succeeded = true;
339 } else {
340 succeeded = false;
341 }
342 }
343
344 return new TryLockResult<>(succeeded, successor);
345 }
346
347 /**
348 * Unlocks a target, which must have been previously locked. This method
349 * releases all locks a holder has in this target.
350 * @param g the lock grant
351 * @throws NotLockedException the lock is not locked or was unlocked
352 * concurrently during execution of this operation
353 */
354 public void unlock(LockGrant<LockType> g) {
355 Ensure.not_null(g, "g == null");
356 Ensure.same(this, g.target(), "g.target() != this");
357 Ensure.is_null(g.successor(), "g.successor() != null");
358
359 List<Target<LockType>> unlocked_targets = new LinkedList<>();
360 unlock_chain(g, unlocked_targets);
361
362 /*
363 * See if there are any pending locks in unlocked targets that can be
364 * granted. Note that we are currently not holding any synchronization
365 * locks.
366 */
367 for (Target<LockType> t : unlocked_targets) {
368 t.try_lock_any();
369 }
370 }
371
372 /**
373 * Tries to lock any pending locks. This method may propagate to children
374 * targets if a lock is granted and it has successor futures.
375 */
376 private void try_lock_any() {
377 /*
378 * Place in next all futures that we want to try to lock. These are
379 * initially,
380 */
381 LinkedList<LockGrantFuture<LockType>> next = new LinkedList<>();
382
383 /*
384 * Process pending lock requests by queue order. Collect all successors
385 * in next.
386 */
387 synchronized (this) {
388 boolean done;
389 do {
390 done = false;
391 if (m_queue != null) {
392 /*
393 * Try all pending locks.
394 */
395 for (LockGrantFuture<LockType> pending : m_queue) {
396 TryLockResult<LockType> r = try_lock(pending);
397 if (r.granted()) {
398 done = true;
399 if (r.successor() != null) {
400 next.add(r.successor());
401 }
402
403 break;
404 }
405 }
406 }
407 } while (done);
408 }
409
410 /*
411 * Keep processing next and adding successors while we manage to
412 * succeed in granting locks.
413 */
414 while (next.size() > 0) {
415 LockGrantFuture<LockType> f = next.removeFirst();
416 Target<LockType> t = f.target();
417 synchronized (t) {
418 synchronized (f) {
419 if (!f.isDone()) {
420 TryLockResult<LockType> r = t.try_lock(f);
421 if (r.successor() != null) {
422 next.add(r.successor());
423 }
424 }
425 }
426 }
427 }
428 }
429
430 /**
431 * Unlocks a grant in the current target. Also releases any locks on
432 * predecessors in the lock chain.
433 * @param g the lock grant
434 * @param unlocked receives unlocked targets
435 * @throws NotLockedException the lock is not locked (or was unlocked
436 * concurrently during execution of this operation)
437 */
438 private synchronized void unlock_chain(LockGrant<LockType> g,
439 List<Target<LockType>> unlocked) {
440 Ensure.not_null(g, "g == null");
441
442 LockGrant<LockType> pred = g.predecessor();
443 synchronized (g) {
444 /*
445 * Check that the lock is still valid.
446 */
447 if (!g.valid()) {
448 throw new NotLockedException("Lock grant is no longer "
449 + "valid. It has already been released.");
450 }
451
452 /*
453 * If there is a successor in the chain, it must have already
454 * been released as locklib users cannot unlock chains from
455 * the middle.
456 */
457 LockGrant<LockType> sl = g.successor();
458 if (sl != null) {
459 Ensure.is_false(sl.valid(), "sl.valid()");
460 }
461
462 /*
463 * Release the lock.
464 */
465 Ensure.is_true(m_locks.contains(g), "!m_locks.contains(g)");
466 m_locks.remove(g);
467 m_manager.api().unlocked(g);
468 g.invalidate();
469 unlocked.add(this);
470 }
471
472
473 /*
474 * Unlock upwards in the hierarchy. Note that it is not possible
475 * that the predecessor has been released concurrently because we
476 * hold the synchronization lock for this target.
477 */
478 if (pred != null) {
479 Ensure.is_true(pred.valid(), "!pred.valid()");
480 pred.target().unlock_chain(pred, unlocked);
481 }
482 }
483
484 /**
485 * Checks if a given lock request can be granted immediately, ignoring
486 * the need to acquire a parent lock.
487 * @param request the request
488 * @return can the request be granted?
489 */
490 synchronized boolean can_grant(LockRequest<LockType> request) {
491 /*
492 * Promotion locks are treated differently as they ignore
493 * starvation rules.
494 */
495 boolean is_promotion = false;
496
497 Ensure.not_null(request, "request == null");
498 for (LockGrant<LockType> l : m_locks) {
499 if (l.request().holder() == request.holder()) {
500 is_promotion = true;
501 } else if (!m_manager.policy().is_compatible(l.request().type(),
502 request.type())) {
503 return false;
504 }
505 }
506
507 if (!is_promotion && !m_manager.policy().starvation_allowed()) {
508 /*
509 * Starvation control: we will deny a grant even if there are no
510 * incompatible locks if we're not the first lock in the queue. If
511 * there is no queue, it is granted, of course :)
512 */
513 if (m_queue != null) {
514 for (LockGrantFuture<LockType> f : m_queue) {
515 if (f.request() == request) {
516 /*
517 * The first queued request is the same as this one so
518 * we will grant the lock.
519 */
520 break;
521 }
522
523 if (f.predecessor_future() == null ||
524 f.predecessor_future().isDone()) {
525 /*
526 * The first queued request does not have a predecessor
527 * or has one but it has already been granted. We won't
528 * allow the request 'request' to move ahead of this
529 * one to prevent starvation.
530 */
531 return false;
532 }
533
534 /*
535 * The queued request has a predecessor that has not been
536 * granted. It will be ignored as it won't be used to
537 * prevent starvation.
538 */
539 }
540 }
541
542 }
543
544 return true;
545 }
546
547 /**
548 * Creates a grant of a request. This method can only be invoked when the
549 * grant can actually be granted.
550 * @param request the request
551 * @param plock an optional parent lock
552 * @return the grant
553 */
554 private synchronized LockGrant<LockType> grant(
555 LockRequest<LockType> request, LockGrant<LockType> plock) {
556 Ensure.not_null(request, "request == null");
557 LockGrant<LockType> g = new LockGrant<>(request, this, plock);
558 m_locks.add(g);
559 m_manager.api().locked(g);
560 return g;
561 }
562
563 /**
564 * Cancels a future. If the future has already been locked, it does
565 * nothing. If the future has not yet been locked, it cancels the future
566 * and cancels or unlocks all its predecessors in the chain. The future
567 * must be the last element in the chain.
568 * @param future the future
569 * @return has the future been cancelled? If the future was already
570 * cancelled, returns <code>true</code>
571 */
572 boolean cancel(LockGrantFuture<LockType> future) {
573 Ensure.not_null(future, "future == null");
574
575 LockGrant<LockType> to_release;
576 synchronized (this) {
577 synchronized (future) {
578 Ensure.is_null(future.successor_future(),
579 "future.successor_future() == null");
580
581 if (future.isDone()) {
582 return future.isCancelled();
583 }
584
585 to_release = cancel_chain(future);
586 }
587 }
588
589 if (to_release != null) {
590 unlock(to_release);
591 }
592
593 return true;
594 }
595
596 /**
597 * Cancels a future. This method will operate recursively to all
598 * predecessors of a future until all predecessors are cancelled or a
599 * locked future is found. If a locked future is found, it is returned.
600 * @param future the future
601 * @return the locked future found or <code>null</code> if all locks
602 * were cancelled
603 */
604 synchronized LockGrant<LockType> cancel_chain(
605 LockGrantFuture<LockType> future) {
606 Ensure.not_null(future, "future == null");
607
608 synchronized (future) {
609 Ensure.same(future.target(), this, "future.target() == this");
610 if (future.isDone()) {
611 LockGrant<LockType> grant = future.get_now();
612 Ensure.is_true(grant.valid(), "!grant.valid()");
613 return grant;
614 }
615
616 future.cancelled();
617
618 /*
619 * A future cancelled must exist in the wait queue, so remove it.
620 */
621 Ensure.not_null(m_queue, "m_queue == null");
622 m_queue.dequeue(future);
623
624 LockGrantFuture<LockType> p = future.predecessor_future();
625 if (p != null) {
626 return p.target().cancel_chain(p);
627 } else {
628 return null;
629 }
630 }
631 }
632
633 /**
634 * Result of {@link Target#try_lock(LockGrantFuture)}.
635 * @param <LockType> the type of locks used by this target
636 */
637 private static class TryLockResult<LockType> {
638 /**
639 * Was the lock was granted?
640 */
641 private boolean m_granted;
642
643 /**
644 * Successor of the successfully granted lock if the lock was granted.
645 * <code>null</code> if the lock was not acquired or if it was but there
646 * is no successor in the chain.
647 */
648 private LockGrantFuture<LockType> m_successor;
649
650 /**
651 * Creates a new try lock result
652 * @param granted was the lock granted?
653 * @param successor what is the lock successor?
654 */
655 TryLockResult(boolean granted, LockGrantFuture<LockType> successor) {
656 m_granted = granted;
657 m_successor = successor;
658 }
659
660 /**
661 * Checks if the lock was granted.
662 * @return was the lock granted?
663 */
664 boolean granted() {
665 return m_granted;
666 }
667
668 /**
669 * Obtains the lock successor.
670 * @return the lock successor, <code>null</code> if none
671 */
672 LockGrantFuture<LockType> successor() {
673 return m_successor;
674 }
675 }
676 }