/*******************************************************************************
* ALMA - Atacama Large Millimeter Array
* Copyright (c) ESO - European Southern Observatory, 2011
* (in the framework of the ALMA collaboration).
* All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*******************************************************************************/
package alma.acs.eventbrowser.parts;
import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.logging.Logger;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.widgets.Display;
import alma.acs.nsstatistics.ArchiveEventData;
import alma.acs.nsstatistics.EventData;
import alma.acs.eventbrowser.status.StatusLineWriter;
import alma.acs.util.StopWatch;
public class PopulateEventList {
private static final Runtime runtime = Runtime.getRuntime();
/**
* Remove first rows when free memory < this (here 20% of max)
*/
private static final long MEMORY_MARGIN_IN_BYTES = runtime.maxMemory() / 5;
/**
* Run the memory checker this often
*/
private static final int CHECK_MEMORY_FREQUENCY = 10;
private static final int QUEUE_DRAIN_LIMIT = 1000;
/**
* When memory is low, delete this many rows from start of table
*/
private static final int PERCENTAGE_TO_DELETE = 20;
private Logger logger;
private TableViewer viewer;
private final Display display;
private BlockingQueue<?> queue;
private final String threadName;
private StatusLineWriter statusLineWriter;
public PopulateEventList(Logger logger, TableViewer viewer,
StatusLineWriter statusLineWriter, BlockingQueue<?> queue, String threadName) {
super();
this.logger = logger;
this.viewer = viewer;
this.statusLineWriter = statusLineWriter;
this.queue = queue;
display = viewer.getControl().getDisplay();
this.threadName = threadName;
}
private long cycles = 0;
private final long max_memory = runtime.maxMemory();
Thread getThreadForEventList() {
Runnable eventListRunnable = new Runnable() {
public Runnable r = new Runnable() {
private long totalNumberDrained;
public void run() {
//final Display display = viewer.getControl().getDisplay();
if (!display.isDisposed()) {
ArrayList c = new ArrayList(QUEUE_DRAIN_LIMIT);
int numberDrained = queue.drainTo(c, QUEUE_DRAIN_LIMIT);
if (numberDrained == 0)
return;
totalNumberDrained += numberDrained;
synchronized (this) { // TODO -- figure out why this doesn't work; need same lock on display everywhere?
if (!display.isDisposed())
viewer.add(c.toArray());
}
if (cycles++ % CHECK_MEMORY_FREQUENCY == 0) {
StopWatch sw = new StopWatch(logger);
freeMemoryIfNecessary();
sw.logLapTime("Check free memory");
logger.fine("Total rows processed so far: "+totalNumberDrained);
String rateStr;
if (threadName.equals("NC Events"))
rateStr = String.format("Average event rate from all subscribed channels: %.2f events/s",EventData.getAverageRate());
else
rateStr = String.format("Average archiving rate: %.2f monitor points/s",ArchiveEventData.getAverageRate());
statusLineWriter.setMessage(rateStr);
}
}
}
};
public void run() {
//final Display display = viewer.getControl().getDisplay();
while (!Thread.currentThread().isInterrupted()) {
if (display.isDisposed())
return;
try {
display.syncExec(r);
//display.asyncExec(r);
Thread.sleep(1000);
// System.out.println("Event list iteration " + ++i);
// System.out.println("Queue has "
// + Application.equeue.remainingCapacity()
// + " slots left.");
} catch (InterruptedException e) {
// TODO: Change thread model, as this happens at shutdown
System.out.println("Event monitoring was interrupted!");
break;
// Application.setMonitoring(false);
// startMonitoringAction.setEnabled(true);
} catch (SWTException e) {
// eat it
break;
}
}
// Application.setMonitoring(false);
// startMonitoringAction.setEnabled(true);
}
};
final Thread th = new Thread(eventListRunnable, threadName);
return th;
}
private void freeMemoryIfNecessary() {
long ultimateFreeMemory = max_memory - (runtime.totalMemory()-runtime.freeMemory());
if (ultimateFreeMemory < MEMORY_MARGIN_IN_BYTES) {
int itemCount = viewer.getTable().getItemCount();
int number_to_delete = (itemCount*PERCENTAGE_TO_DELETE)/100;
logger.fine("Now have "+itemCount+" rows in event table.");
logger.fine("Remaining allocatable memory is: "+ultimateFreeMemory+" bytes. Margin required is "+MEMORY_MARGIN_IN_BYTES);
logger.fine("Will attempt to delete the first "+number_to_delete+" rows.");
Object[] els = new Object[number_to_delete];
for( int i = 0; i < number_to_delete; i++ ) {
els[i] = viewer.getElementAt(i);
}
logger.fine("...removing "+number_to_delete+" of the oldest rows to avoid running out of memory, thread=" + Thread.currentThread().getName());
viewer.remove(els);
logger.fine("Remove done!");
logger.fine("...item count reduced by: "+(itemCount-viewer.getTable().getItemCount()));
System.gc(); // Force GC so that we'll know how much memory is left the next time around
}
}
}