View Javadoc
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 }