blob: abaee7626b450faf47de75ac8b75de88ae08dc67 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.idea.monitor;
import com.android.ddmlib.Client;
import com.android.tools.chartlib.TimelineData;
import com.google.common.collect.Lists;
import com.intellij.openapi.application.ApplicationManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public abstract class DeviceSampler implements Runnable {
/**
* Sample type when the device cannot be seen.
*/
public static final int TYPE_UNREACHABLE = 0;
/**
* Sample created from a valid response.
*/
public static final int TYPE_DATA = 1;
/**
* The device is reachable but no response was received in time.
*/
public static final int TYPE_TIMEOUT = 2;
/**
* This is the valid start index for inherited classes.
*/
public static final int INHERITED_TYPE_START = 3;
@NotNull protected TimelineData myData;
@NotNull protected final List<TimelineEventListener> myListeners = Lists.newLinkedList();
protected int mySampleFrequencyMs;
/**
* The future representing the task being executed, which will return null upon successful completion.
* If null, no current task is being executed.
*/
@Nullable protected volatile Future<?> myExecutingTask;
@Nullable protected volatile Client myClient;
@NotNull private final Semaphore myDataSemaphore;
protected volatile boolean myRunning;
public DeviceSampler(@NotNull TimelineData data, int sampleFrequencyMs) {
myData = data;
mySampleFrequencyMs = sampleFrequencyMs;
myDataSemaphore = new Semaphore(0, true);
}
@SuppressWarnings("ConstantConditions")
public void start() {
if (myExecutingTask == null && myClient != null) {
myRunning = true;
myExecutingTask = ApplicationManager.getApplication().executeOnPooledThread(this);
myClient.setHeapInfoUpdateEnabled(true);
for (TimelineEventListener listener : myListeners) {
listener.onStart();
}
}
}
@SuppressWarnings("ConstantConditions")
public void stop() {
if (myExecutingTask != null) {
myRunning = false;
myDataSemaphore.release();
myData.clear();
try {
// Wait for the task to finish.
myExecutingTask.get();
}
catch (InterruptedException e) {
// Ignore
}
catch (ExecutionException e) {
// Rethrow the original cause of the exception on this thread.
throw new RuntimeException(e.getCause());
}
if (myClient != null) {
myClient.setHeapInfoUpdateEnabled(false);
}
myExecutingTask = null;
for (TimelineEventListener listener : myListeners) {
listener.onStop();
}
}
}
public void setClient(@Nullable Client client) {
if (client != myClient) {
stop();
myClient = client;
myData.clear();
start();
}
}
@Nullable
/**
* This method returns a local copy of <code>myClient</code>, as it is volatile.
*/
public Client getClient() {
return myClient;
}
public void addListener(TimelineEventListener listener) {
myListeners.add(listener);
}
public boolean isRunning() {
return myExecutingTask != null && myRunning;
}
protected void forceSample() {
myDataSemaphore.release();
}
@Override
public void run() {
long timeToWait = mySampleFrequencyMs;
while (myRunning) {
try {
long start = System.currentTimeMillis();
boolean acquired = myDataSemaphore.tryAcquire(timeToWait, TimeUnit.MILLISECONDS);
if (myRunning) {
sample(acquired);
}
timeToWait -= System.currentTimeMillis() - start;
if (timeToWait <= 0) {
timeToWait = mySampleFrequencyMs;
}
}
catch (InterruptedException e) {
myRunning = false;
}
}
}
@NotNull
public abstract String getName();
@NotNull
public abstract String getDescription();
protected abstract void sample(boolean forced) throws InterruptedException;
}