Rewrite a large portion of the computer manager service to fix some thread leaks and improve performance

This commit is contained in:
Cameron Gutman 2014-11-16 17:20:04 -08:00
parent afbe64f3ff
commit 99e3b5f33b

View File

@ -1,8 +1,7 @@
package com.limelight.computers;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicInteger;
import com.limelight.LimeLog;
@ -30,7 +29,7 @@ public class ComputerManagerService extends Service {
private AtomicInteger dbRefCount = new AtomicInteger(0);
private IdentityManager idManager;
private final HashMap<ComputerDetails, Thread> pollingThreads = new HashMap<ComputerDetails, Thread>();
private final LinkedList<PollingTuple> pollingTuples = new LinkedList<PollingTuple>();
private ComputerManagerListener listener = null;
private AtomicInteger activePolls = new AtomicInteger(0);
@ -102,20 +101,8 @@ public class ComputerManagerService extends Service {
@Override
public void run() {
while (!isInterrupted()) {
ComputerDetails originalDetails = new ComputerDetails();
originalDetails.update(details);
// Check if this poll has modified the details
if (runPoll(details) && !originalDetails.equals(details)) {
// Replace our thread entry with the new one
synchronized (pollingThreads) {
if (pollingThreads.remove(originalDetails) != null) {
// This could have gone away in the meantime, so don't
// add it back if it has
pollingThreads.put(details, this);
}
}
}
runPoll(details);
// Wait until the next polling interval
try {
@ -138,23 +125,15 @@ public class ComputerManagerService extends Service {
// Start mDNS autodiscovery too
discoveryBinder.startDiscovery(MDNS_QUERY_PERIOD_MS);
// Start polling known machines
if (!getLocalDatabaseReference()) {
return;
}
List<ComputerDetails> computerList = dbManager.getAllComputers();
releaseLocalDatabaseReference();
synchronized (pollingThreads) {
for (ComputerDetails computer : computerList) {
synchronized (pollingTuples) {
for (PollingTuple tuple : pollingTuples) {
// This polling thread might already be there
if (!pollingThreads.containsKey(computer)) {
if (tuple.thread == null) {
// Report this computer initially
listener.notifyComputerUpdated(computer);
listener.notifyComputerUpdated(tuple.computer);
Thread t = createPollingThread(computer);
pollingThreads.put(computer, t);
t.start();
tuple.thread = createPollingThread(tuple.computer);
tuple.thread.start();
}
}
}
@ -208,11 +187,14 @@ public class ComputerManagerService extends Service {
discoveryBinder.stopDiscovery();
// Stop polling
synchronized (pollingThreads) {
for (Thread t : pollingThreads.values()) {
t.interrupt();
synchronized (pollingTuples) {
for (PollingTuple tuple : pollingTuples) {
if (tuple.thread != null) {
// Interrupt and remove the thread
tuple.thread.interrupt();
tuple.thread = null;
}
}
pollingThreads.clear();
}
// Remove the listener
@ -249,13 +231,26 @@ public class ComputerManagerService extends Service {
fakeDetails.remoteIp = addr;
// Spawn a thread for this computer
synchronized (pollingThreads) {
synchronized (pollingTuples) {
// This polling thread might already be there
if (!pollingThreads.containsKey(fakeDetails)) {
Thread t = createPollingThread(fakeDetails);
pollingThreads.put(fakeDetails, t);
t.start();
for (PollingTuple tuple : pollingTuples) {
if (tuple.computer.localIp.equals(addr) ||
tuple.computer.remoteIp.equals(addr)) {
// This is the same computer
if (tuple.thread == null) {
tuple.thread = createPollingThread(fakeDetails);
tuple.thread.start();
}
// Found an entry so we're done
return;
}
}
// If we got here, we didn't find an entry
PollingTuple tuple = new PollingTuple(fakeDetails, createPollingThread(fakeDetails));
pollingTuples.add(tuple);
tuple.thread.start();
}
}
@ -280,6 +275,20 @@ public class ComputerManagerService extends Service {
// Remove it from the database
dbManager.deleteComputer(name);
synchronized (pollingTuples) {
// Remove the computer from the computer list
for (PollingTuple tuple : pollingTuples) {
if (tuple.computer.name.equals(name)) {
if (tuple.thread != null) {
// Interrupt the thread on this entry
tuple.thread.interrupt();
}
pollingTuples.remove(tuple);
break;
}
}
}
releaseLocalDatabaseReference();
}
@ -376,6 +385,18 @@ public class ComputerManagerService extends Service {
// Initialize the DB
dbManager = new ComputerDatabaseManager(this);
dbRefCount.set(1);
// Grab known machines into our computer list
if (!getLocalDatabaseReference()) {
return;
}
for (ComputerDetails computer : dbManager.getAllComputers()) {
// Add this computer without a thread
pollingTuples.add(new PollingTuple(computer, null));
}
releaseLocalDatabaseReference();
}
@Override
@ -396,3 +417,13 @@ public class ComputerManagerService extends Service {
return binder;
}
}
class PollingTuple {
public Thread thread;
public ComputerDetails computer;
public PollingTuple(ComputerDetails computer, Thread thread) {
this.computer = computer;
this.thread = thread;
}
}