1 /*
2 * Copyright (c) 2016. Amazon.com, Inc. or its affiliates. All Rights Reserved.
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 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15 package com.amazonaws.util;
16
17 /**
18 * Manages capacity of a finite resource. Capacity can be acquired and
19 * released.
20 */
21 public class CapacityManager {
22
23 private volatile int availableCapacity;
24 private final int maxCapacity;
25
26 private final Object lock = new Object();
27
28 /**
29 * Creates a CapacityManager.
30 *
31 * @param maxCapacity maximum capacity of this resource.
32 * available capacity will initially be set to this value.
33 * if a negative value is provided the capacity manager will operate in a no-op
34 * passthrough mode in which all acquire calls will return true.
35 */
36 public CapacityManager(final int maxCapacity) {
37 this.maxCapacity = maxCapacity;
38 this.availableCapacity = maxCapacity;
39 }
40
41 /**
42 * Attempts to acquire a single capacity unit.
43 * If acquired, capacity will be consumed from the available pool.
44
45 * @return true if capacity can be acquired, false if not
46 */
47 public boolean acquire() {
48 return acquire(1);
49 }
50
51 /**
52 * Attempts to acquire a given amount of capacity.
53 * If acquired, capacity will be consumed from the available pool.
54 *
55 * @param capacity capacity to acquire
56 * @return true if capacity can be acquired, false if not
57 * @throws IllegalArgumentException if given capacity is negative
58 */
59 public boolean acquire(int capacity) {
60 if (capacity < 0) {
61 throw new IllegalArgumentException("capacity to acquire cannot be negative");
62 }
63
64 if (availableCapacity < 0) {
65 return true;
66 }
67
68 synchronized (lock) {
69 if (availableCapacity - capacity >= 0) {
70 availableCapacity -= capacity;
71 return true;
72 } else {
73 return false;
74 }
75 }
76 }
77
78 /**
79 * Releases a single unit of capacity back to the pool, making it available
80 * to consumers.
81 */
82 public void release() {
83 release(1);
84 }
85
86 /**
87 * Releases a given amount of capacity back to the pool, making it available
88 * to consumers.
89 *
90 * @param capacity capacity to release
91 * @throws IllegalArgumentException if given capacity is negative
92 */
93 public void release(int capacity) {
94 if (capacity < 0) {
95 throw new IllegalArgumentException("capacity to release cannot be negative");
96 }
97
98 // in the common 'good' case where we have our full capacity available we can
99 // short circuit going any further and avoid unnecessary locking.
100 if (availableCapacity >= 0 && availableCapacity != maxCapacity) {
101 synchronized (lock) {
102 availableCapacity = Math.min((availableCapacity + capacity), maxCapacity);
103 }
104 }
105 }
106
107 /**
108 * Returns the currently consumed capacity.
109 *
110 * @return consumed capacity
111 */
112 public int consumedCapacity() {
113 return (availableCapacity < 0) ? 0 : (maxCapacity - availableCapacity);
114 }
115
116 /**
117 * Returns the currently available capacity.
118 *
119 * @return available capacity
120 */
121 public int availableCapacity() {
122 return availableCapacity;
123 }
124 }
125