Package: StandardTicker

StandardTicker

nameinstructionbranchcomplexitylinemethod
StandardTicker(Ticker.HeartBeatPolicy, Callable, ScheduledExecutorService)
M: 18 C: 41
69%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 11
100%
M: 0 C: 1
100%
call()
M: 7 C: 12
63%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 0 C: 3
100%
M: 0 C: 1
100%
cancel()
M: 6 C: 18
75%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
createExponentialTicker(long, long, long, Callable, ScheduledExecutorService)
M: 6 C: 10
63%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
createTicker(Ticker.HeartBeatPolicy, Callable, ScheduledExecutorService)
M: 6 C: 11
65%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
getCurrentTimeMillis()
M: 11 C: 9
45%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 1 C: 3
75%
M: 0 C: 1
100%
getIntervalToNextTickMillis()
M: 6 C: 25
81%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
scheduleNextTick()
M: 6 C: 21
78%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 0 C: 4
100%
M: 0 C: 1
100%
start()
M: 12 C: 8
40%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 3 C: 3
50%
M: 0 C: 1
100%
tick()
M: 8 C: 38
83%
M: 3 C: 5
63%
M: 3 C: 2
40%
M: 0 C: 8
100%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2008, 2010 VMware Inc.
3: * All rights reserved. This program and the accompanying materials
4: * are made available under the terms of the Eclipse Public License v1.0
5: * which accompanies this distribution, and is available at
6: * http://www.eclipse.org/legal/epl-v10.html
7: *
8: * Contributors:
9: * VMware Inc. - initial contribution
10: *******************************************************************************/
11:
12: package org.eclipse.virgo.nano.core.internal.blueprint;
13:
14: import java.util.concurrent.Callable;
15: import java.util.concurrent.ScheduledExecutorService;
16: import java.util.concurrent.ScheduledFuture;
17: import java.util.concurrent.TimeUnit;
18: import java.util.concurrent.atomic.AtomicLong;
19:
20:
21:
22:
23: /**
24: * {@link StandardTicker} provides a heart-beat for tracking unanticipated delays. The heart-beat is configurable using a
25: * policy such as {@link ExponentialHeartBeatPolicy} which lengthens the heart-beat interval exponentially over time
26: * until it reaches a fixed upper bound. On each heart-beat until the ticker is cancelled, an action is called.
27: * <p />
28: *
29: * <strong>Concurrent Semantics</strong><br />
30: *
31: * This class is thread safe.
32: *
33: * @param <V> the result type of the action taken on each heart-beat
34: */
35: public final class StandardTicker<V> implements Ticker, Callable<V> {
36:
37: private static final long OVERDUE = -1;
38:
39: private final long creationTimeMillis;
40:
41: private long lastTickMillis;
42:
43: private final HeartBeatPolicy heartBeatPolicy;
44:
45: private long heartBeatIntervalMillis;
46:
47: private boolean tickedAtLeastOnce = false;
48:
49: private boolean cancelled = false;
50:
51: private ScheduledFuture<V> scheduledFuture;
52:
53: private final Callable<V> action;
54:
55: private final ScheduledExecutorService scheduledExecutorService;
56:
57: private Object monitor = new Object();
58:
59: /**
60: * Create a {@link Ticker} with the given heart-beat policy, action to be called on each tick, and executor service
61: * for scheduling ticks and set it ticking.
62: *
63: * @param <V> the action's result type
64: * @param heartBeatPolicy a policy which determines the possibly variable intervals between heartbeats
65: * @param action the thread safe action to be called on each tick
66: * @param scheduledExecutorService the executor service for scheduling ticks
67: * @return the constructed and ticking ticker
68: */
69: public static <V> Ticker createTicker(HeartBeatPolicy heartBeatPolicy, Callable<V> action, ScheduledExecutorService scheduledExecutorService) {
70: StandardTicker<V> ticker = new StandardTicker<V>(heartBeatPolicy, action, scheduledExecutorService);
71: ticker.start();
72: return ticker;
73: }
74:
75: /**
76: * Create a {@link Ticker} with an exponential heart beat policy and a given action to be called on each tick and an
77: * executor service for scheduling ticks and set it ticking. The exponential heart beat policy has the given initial
78: * interval which then increases by the given percentage on each tick until the given maximum interval is reached.
79: *
80: * @param <V> the action's result type
81: * @param initialHeartbeatIntervalMillis the initial interval
82: * @param heartBeatIncreasePercentage the percentage increase on each tick
83: * @param maxHeartBeatIntervalMillis the maximum interval
84: * @param action the thread safe action to be called on each tick
85: * @param scheduledExecutorService the executor service for scheduling ticks
86: * @return the constructed and ticking ticker
87: */
88: public static <V> Ticker createExponentialTicker(long initialHeartbeatIntervalMillis, long heartBeatIncreasePercentage,
89: long maxHeartBeatIntervalMillis, Callable<V> action, ScheduledExecutorService scheduledExecutorService) {
90: return createTicker(new ExponentialHeartBeatPolicy(initialHeartbeatIntervalMillis, heartBeatIncreasePercentage, maxHeartBeatIntervalMillis),
91: action, scheduledExecutorService);
92: }
93:
94: /**
95: * Construct a Ticker with the given heart beat policy, action to be called on each tick, and executor service for
96: * scheduling ticks.
97: *
98: * @param heartBeatPolicy a policy which determines the possibly variable intervals between heartbeats
99: * @param action the thread safe action to be called on each tick
100: * @param scheduledExecutorService the executor service for scheduling ticks
101: */
102: private StandardTicker(HeartBeatPolicy heartBeatPolicy, Callable<V> action, ScheduledExecutorService scheduledExecutorService) {
103: this.heartBeatPolicy = heartBeatPolicy;
104: this.heartBeatIntervalMillis = this.heartBeatPolicy.getNextHeartBeatIntervalMillis();
105: this.creationTimeMillis = System.currentTimeMillis();
106: this.lastTickMillis = this.creationTimeMillis;
107: this.action = action;
108: this.scheduledExecutorService = scheduledExecutorService;
109: }
110:
111: /**
112: * Start this ticker ticking.
113: */
114: private void start() {
115:• if (getIntervalToNextTickMillis() == OVERDUE) {
116: try {
117: this.call();
118: } catch (Exception e) {
119: }
120: } else {
121: scheduleNextTick();
122: }
123: }
124:
125: /**
126: * Schedule the next tick of this ticker.
127: */
128: private void scheduleNextTick() {
129: synchronized (this.monitor) {
130:• if (!this.cancelled) {
131: this.scheduledFuture = this.scheduledExecutorService.schedule(this, getIntervalToNextTickMillis(), TimeUnit.MILLISECONDS);
132: }
133: }
134: }
135:
136: /**
137: * {@inheritDoc}
138: */
139: public V call() throws Exception {
140: boolean ticked = tick();
141: scheduleNextTick();
142:• return ticked ? this.action.call() : null;
143: }
144:
145: /**
146: * {@inheritDoc}
147: */
148: public boolean cancel() {
149: synchronized (this.monitor) {
150: this.cancelled = true;
151: this.scheduledFuture.cancel(true);
152: return this.tickedAtLeastOnce;
153: }
154: }
155:
156: /**
157: * Determine whether a tick is due and, if so, update the ticker state to count down to the next tick and return
158: * <code>true</code>. If no tick is due, do not update the ticker state and return <code>false</code>.
159: *
160: * @return <code>true</code> if and only if the ticker tickedAtLeastOnce
161: */
162: private boolean tick() {
163: synchronized (this.monitor) {
164: boolean ticked = false;
165:• if (!cancelled && getIntervalToNextTickMillis() == OVERDUE) {
166: ticked = true;
167: this.lastTickMillis = getCurrentTimeMillis();
168: this.heartBeatIntervalMillis = this.heartBeatPolicy.getNextHeartBeatIntervalMillis();
169: }
170:• this.tickedAtLeastOnce = this.tickedAtLeastOnce || ticked;
171: return ticked;
172: }
173: }
174:
175: /**
176: * Get the current time.
177: *
178: * Pre-condition: the monitor must be held.
179: *
180: * Post-condition: result >= this.lastTickMillis.
181: *
182: * @return the current time in milliseconds
183: */
184: private long getCurrentTimeMillis() {
185: long currentTimeMillis = System.currentTimeMillis();
186:• if (currentTimeMillis < this.lastTickMillis) {
187:         throw new IllegalArgumentException("Time must not go backwards");
188: }
189: return currentTimeMillis;
190: }
191:
192: /**
193: * Get the time interval until the next tick is due, or OVERDUE if the next tick is overdue.
194: *
195: * @return the time interval in milliseconds, or OVERDUE if the next tick is overdue
196: */
197: private long getIntervalToNextTickMillis() {
198: synchronized (this.monitor) {
199: long intervalSinceLastTickMillis = getCurrentTimeMillis() - this.lastTickMillis;
200:• return intervalSinceLastTickMillis < this.heartBeatIntervalMillis ? this.heartBeatIntervalMillis - intervalSinceLastTickMillis : OVERDUE;
201: }
202: }
203:
204: /**
205: * {@link ExponentialHeartBeatPolicy} is a {@link HeartBeatPolicy} which returns intervals starting with a given
206: * initial interval and increasing by a given percentage up to a given maximum interval.
207: * <p />
208: *
209: * <strong>Concurrent Semantics</strong><br />
210: *
211: * This class is thread safe.
212: */
213: private final static class ExponentialHeartBeatPolicy implements HeartBeatPolicy {
214:
215: private final long maxHeartBeatIntervalMillis;
216:
217: private final long heartBeatIncreasePercentage;
218:
219: private AtomicLong heartBeatIntervalMillis;
220:
221: /**
222: * Construct a {@link Ticker.HeartBeatPolicy HeartBeatPolicy} which the given initial interval which then increases by the given
223: * percentage on each tick until the given maximum interval is reached.
224: *
225: * @param initialHeartbeatIntervalMillis the initial interval
226: * @param heartBeatIncreasePercentage the percentage increase on each tick
227: * @param maxHeartBeatIntervalMillis the maximum interval
228: */
229: public ExponentialHeartBeatPolicy(long initialHeartbeatIntervalMillis, long heartBeatIncreasePercentage, long maxHeartBeatIntervalMillis) {
230: this.heartBeatIntervalMillis = new AtomicLong(initialHeartbeatIntervalMillis);
231: this.heartBeatIncreasePercentage = heartBeatIncreasePercentage;
232: this.maxHeartBeatIntervalMillis = maxHeartBeatIntervalMillis;
233: }
234:
235: /**
236: * {@inheritDoc}
237: */
238: public long getNextHeartBeatIntervalMillis() {
239: boolean success = false;
240: long nextHeartBeatIntervalMillis = 0;
241: while (!success) {
242: nextHeartBeatIntervalMillis = this.heartBeatIntervalMillis.get();
243: if (nextHeartBeatIntervalMillis < maxHeartBeatIntervalMillis) {
244: long newHeartBeatIntervalMillis = Math.min((nextHeartBeatIntervalMillis * (100 + heartBeatIncreasePercentage)) / 100,
245: maxHeartBeatIntervalMillis);
246: success = this.heartBeatIntervalMillis.compareAndSet(nextHeartBeatIntervalMillis, newHeartBeatIntervalMillis);
247: } else {
248: success = true;
249: }
250: }
251: return nextHeartBeatIntervalMillis;
252: }
253:
254: }
255:
256: }