@@ -42,9 +42,7 @@ public AtomicRateLimiter(String name, RateLimiterConfig rateLimiterConfig) {
42
42
permissionsPerCycle = rateLimiterConfig .getLimitForPeriod ();
43
43
44
44
waitingThreads = new AtomicInteger (0 );
45
- long activeCycle = nanoTime () / cyclePeriodInNanos ;
46
- int activePermissions = permissionsPerCycle ;
47
- state = new AtomicReference <>(new State (activeCycle , activePermissions , 0 ));
45
+ state = new AtomicReference <>(new State (0 , 0 , 0 ));
48
46
}
49
47
50
48
/**
@@ -69,7 +67,7 @@ public boolean getPermission(final Duration timeoutDuration) {
69
67
* @return next {@link State}
70
68
*/
71
69
private State calculateNextState (final long timeoutInNanos , final State activeState ) {
72
- long currentNanos = nanoTime ();
70
+ long currentNanos = currentNanoTime ();
73
71
long currentCycle = currentNanos / cyclePeriodInNanos ;
74
72
75
73
long nextCycle = activeState .activeCycle ;
@@ -85,6 +83,7 @@ private State calculateNextState(final long timeoutInNanos, final State activeSt
85
83
return nextState ;
86
84
}
87
85
86
+
88
87
/**
89
88
* Calculates time to wait for next permission as
90
89
* [time to the next cycle] + [duration of full cycles until reserved permissions expire]
@@ -147,19 +146,27 @@ private boolean waitForPermissionIfNecessary(final long timeoutInNanos, final lo
147
146
148
147
/**
149
148
* Parks {@link Thread} for nanosToWait.
149
+ * <p>If the current thread is {@linkplain Thread#interrupted}
150
+ * while waiting for a permit then it won't throw {@linkplain InterruptedException},
151
+ * but its interrupt status will be set.
150
152
*
151
153
* @param nanosToWait nanoseconds caller need to wait
152
154
* @return true if caller was not {@link Thread#interrupted} while waiting
153
155
*/
154
156
private boolean waitForPermission (final long nanosToWait ) {
155
157
waitingThreads .incrementAndGet ();
156
- long deadline = nanoTime () + nanosToWait ;
157
- while (nanoTime () < deadline || currentThread ().isInterrupted ()) {
158
- long sleepBlockDuration = deadline - nanoTime ();
158
+ long deadline = currentNanoTime () + nanosToWait ;
159
+ boolean wasInterrupted = false ;
160
+ while (currentNanoTime () < deadline && !wasInterrupted ) {
161
+ long sleepBlockDuration = deadline - currentNanoTime ();
159
162
parkNanos (sleepBlockDuration );
163
+ wasInterrupted = Thread .interrupted ();
160
164
}
161
165
waitingThreads .decrementAndGet ();
162
- return !currentThread ().isInterrupted ();
166
+ if (wasInterrupted ) {
167
+ currentThread ().interrupt ();
168
+ }
169
+ return !wasInterrupted ;
163
170
}
164
171
165
172
/**
@@ -182,7 +189,7 @@ public RateLimiterConfig getRateLimiterConfig() {
182
189
* {@inheritDoc}
183
190
*/
184
191
@ Override
185
- public Metrics getMetrics () {
192
+ public AtomicRateLimiterMetrics getMetrics () {
186
193
return new AtomicRateLimiterMetrics ();
187
194
}
188
195
@@ -201,6 +208,7 @@ public Metrics getMetrics() {
201
208
* </ul>
202
209
*/
203
210
private static class State {
211
+
204
212
private final long activeCycle ;
205
213
private final int activePermissions ;
206
214
private final long nanosToWait ;
@@ -210,12 +218,14 @@ public State(final long activeCycle, final int activePermissions, final long nan
210
218
this .activePermissions = activePermissions ;
211
219
this .nanosToWait = nanosToWait ;
212
220
}
221
+
213
222
}
214
223
215
224
/**
216
225
* Enhanced {@link Metrics} with some implementation specific details
217
226
*/
218
227
public final class AtomicRateLimiterMetrics implements Metrics {
228
+
219
229
private AtomicRateLimiterMetrics () {
220
230
}
221
231
@@ -228,5 +238,33 @@ private AtomicRateLimiterMetrics() {
228
238
public int getNumberOfWaitingThreads () {
229
239
return waitingThreads .get ();
230
240
}
241
+
242
+ /**
243
+ * @return estimated time duration in nanos to wait for the next permission
244
+ */
245
+ public long getNanosToWait () {
246
+ State currentState = state .get ();
247
+ State estimatedState = calculateNextState (-1 , currentState );
248
+ return estimatedState .nanosToWait ;
249
+ }
250
+
251
+ /**
252
+ * Estimates count of permissions available permissions.
253
+ * Can be negative if some permissions where reserved.
254
+ *
255
+ * @return estimated count of permissions
256
+ */
257
+ public long getAvailablePermissions () {
258
+ State currentState = state .get ();
259
+ State estimatedState = calculateNextState (-1 , currentState );
260
+ return estimatedState .activePermissions ;
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Created only for test purposes. Simply calls {@link System#nanoTime()}
266
+ */
267
+ private long currentNanoTime () {
268
+ return nanoTime ();
231
269
}
232
270
}
0 commit comments