1 /**
2 * Copyright 2008 Atlassian Pty Ltd
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.atlassian.util.concurrent;
18
19 import java.util.concurrent.Callable;
20 import java.util.concurrent.locks.Lock;
21
22 /**
23 * Static factory for producing {@link Manager} instances.
24 */
25 public class LockManagers {
26
27 private static final class Defaults {
28 private static final int CAPACITY = 128;
29 }
30
31 /**
32 * Convenience method that simply calls {@link LockManagers#weakLockManager(Function, int)} with
33 * a default capacity of 128.
34 *
35 * @see LockManagers#weakLockManager(Function, int)
36 */
37 public static <T, D> LockManager<T> weakLockManager(final Function<T, D> stripeFunction) {
38 return weakLockManager(stripeFunction, Defaults.CAPACITY);
39 }
40
41 /**
42 * The particular {@link Lock} is resolved using a {@link Function} that resolves to a
43 * descriptor used to look up a Lock instance. This allows for a finite set of locks to be used
44 * even if the set of T is essentially unbounded.
45 * <p>
46 * For instance:
47 *
48 * <pre>
49 * LockManager<Identifiable, Integer> manager = LockManagers.weakLockManager(new Function<Identifiable, Integer>() {
50 * Integer get(Identifiable thing) {
51 * return thing.getId() % 16;
52 * }
53 * };
54 * </pre>
55 *
56 * uses only 16 possible locks as the function returns the modulo 16 of the thing's id.
57 *
58 * @param <T> the type of the thing used to look up locks
59 * @param <D> the type used to map lock instances
60 * @param stripeFunction to convert Ts to Ds.
61 * @return a new {@link LockManager} instance that stores created {@link Lock} instances with
62 * weak references.
63 */
64 public static <T, D> LockManager<T> weakLockManager(final Function<T, D> stripeFunction, final int capacity) {
65 return new Manager<T, D>(new WeakLockMap<D>(capacity), stripeFunction);
66 }
67
68 private LockManagers() {
69 throw new AssertionError("cannot instantiate!");
70 }
71
72 /**
73 * Default implementation of {@link LockManager}
74 *
75 * @param <T> the input type
76 * @param <D> the type used for the internal lock resolution.
77 */
78 static class Manager<T, D> implements LockManager<T> {
79 private final Function<D, Lock> lockResolver;
80 private final Function<T, D> stripeFunction;
81
82 Manager(final Function<D, Lock> lockResolver, final Function<T, D> stripeFunction) {
83 this.lockResolver = lockResolver;
84 this.stripeFunction = stripeFunction;
85 }
86
87 public <R> R withLock(final T descriptor, final Callable<R> callable) throws Exception {
88 final Lock lock = lockResolver.get(stripeFunction.get(descriptor));
89 lock.lock();
90 try {
91 return callable.call();
92 }
93 finally {
94 lock.unlock();
95 }
96 }
97
98 public void withLock(final T descriptor, final Runnable runnable) {
99 final Lock lock = lockResolver.get(stripeFunction.get(descriptor));
100 lock.lock();
101 try {
102 runnable.run();
103 }
104 finally {
105 lock.unlock();
106 }
107 }
108 }
109 }