| /******************************************************************************* |
| * Copyright (c) 2013, 2019 Ericsson |
| * |
| * All rights reserved. This program and the accompanying materials are |
| * made available under the terms of the Eclipse Public License 2.0 which |
| * accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Alexandre Montplaisir - Initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.tmf.core.signal; |
| |
| import java.util.Timer; |
| import java.util.TimerTask; |
| |
| import org.eclipse.jdt.annotation.NonNullByDefault; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.tracecompass.tmf.core.component.ITmfComponent; |
| |
| /** |
| * "Buffer" between a TmfComponent and the signal manager. You can use this if |
| * you want to throttle the amount of signals your component will send. |
| * <p> |
| * It works by specifying a delay, then calling {@link #queue}. The signals will |
| * only be really sent if no other call to {@link #queue} happens within $delay |
| * milliseconds afterwards. This guarantees that only the *last* signal is |
| * actually broadcasted. |
| * <p> |
| * Note that this class does not discriminate for signal types, sources, or |
| * whatever. If you want to throttle different signals in different ways, you |
| * can use multiple signal throttlers in your component and call them |
| * accordingly. |
| * <p> |
| * This throttler will broadcast the signal from a TimerThread. |
| * |
| * @author Alexandre Montplaisir |
| */ |
| @NonNullByDefault |
| public class TmfSignalThrottler { |
| |
| private final @Nullable ITmfComponent fComponent; |
| private final long fDelay; |
| private final Timer fTimer; |
| |
| private TimerTask fCurrentTask; |
| |
| /** |
| * Constructor |
| * |
| * @param component |
| * The optional source component of the signals. If non-null, its |
| * {@link ITmfComponent#broadcast} method will be used to finally |
| * send the signal. If null, the generic |
| * {@link TmfSignalManager#dispatchSignal} is used. |
| * @param delay |
| * Time to wait before actually sending signals (in ms) |
| */ |
| public TmfSignalThrottler(@Nullable ITmfComponent component, long delay) { |
| this.fComponent = component; |
| this.fDelay = delay; |
| this.fTimer = new Timer(); |
| |
| /* |
| * Initialize currentTask to something, so we don't have to do a null |
| * check every time. |
| */ |
| fCurrentTask = new TimerTask() { |
| @Override |
| public void run() { |
| // Do nothing |
| } |
| }; |
| } |
| |
| /** |
| * Queue a signal for sending. It will only be forward to the centralized |
| * signal handler if 'delay' elapses without another signal being sent |
| * through this method. |
| * |
| * You call this instead of {@link ITmfComponent#broadcast} or |
| * {@link TmfSignalManager#dispatchSignal}. |
| * |
| * @param signal |
| * The signal to queue for broadcasting |
| */ |
| public synchronized void queue(TmfSignal signal) { |
| fCurrentTask.cancel(); |
| fCurrentTask = new BroadcastRequest(signal); |
| fTimer.schedule(fCurrentTask, fDelay); |
| } |
| |
| /** |
| * Dispose method. Will prevent any pending signal from being sent, and this |
| * throttler from be used again. |
| */ |
| public synchronized void dispose() { |
| fTimer.cancel(); |
| fTimer.purge(); |
| } |
| |
| private class BroadcastRequest extends TimerTask { |
| |
| private final TmfSignal signal; |
| |
| BroadcastRequest(TmfSignal signal) { |
| this.signal = signal; |
| } |
| |
| @Override |
| public void run() { |
| dispatchSignal(signal); |
| } |
| } |
| |
| /** |
| * Dispatch the signal |
| * |
| * @param signal |
| * the signal |
| * @since 5.0 |
| */ |
| protected void dispatchSignal(TmfSignal signal) { |
| if (fComponent != null) { |
| fComponent.broadcast(signal); |
| } else { |
| TmfSignalManager.dispatchSignal(signal); |
| } |
| } |
| } |