package gdsc.colocalisation.cda.engine;
/*-----------------------------------------------------------------------------
* GDSC Plugins for ImageJ
*
* Copyright (C) 2011 Alex Herbert
* Genome Damage and Stability Centre
* University of Sussex, UK
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*---------------------------------------------------------------------------*/
import ij.ImageStack;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* Performs the Confined Displacement Algorithm (CDA).
* <p>
* Multi-threaded for speed. Uses a BlockingQueue to hold the work which is then processed sequentially by worker
* threads.
*/
public class CDAEngine
{
private BlockingQueue<CDAJob> jobs = null;
private List<CDAWorker> workers = new LinkedList<CDAWorker>();
private List<Thread> threads = new LinkedList<Thread>();
/**
* Constructor
*
* @param threads
* The number of threads to use (set to 1 if less than 1)
*/
public CDAEngine(ImageStack imageStack1, ImageStack roiStack1, ImageStack confinedStack, ImageStack imageStack2,
ImageStack roiStack2, double denom1, double denom2, List<CalculationResult> results, int totalSteps,
int threads)
{
if (threads < 1)
threads = 1;
createQueue(threads);
results = Collections.synchronizedList(results);
// Create the workers
for (int i = 0; i < threads; i++)
{
CDAWorker worker = new CDAWorker(imageStack1, roiStack1, imageStack2, roiStack2, confinedStack, denom1,
denom2, results, jobs, totalSteps);
Thread t = new Thread(worker);
workers.add(worker);
this.threads.add(t);
t.start();
}
}
/**
* This method checks if all the worker threads are ready to accept jobs, waiting a short period if necessary.
* Note that jobs can still be queued if this method returns false.
*
* @return True if ready to accept jobs, false if the workers are still initialising.
*/
public boolean isInitialised()
{
boolean ok = checkWorkers();
if (ok)
return true;
ok = true;
for (CDAWorker worker : workers)
{
if (!checkWorkerWithDelay(worker))
ok = false;
}
if (ok)
return true;
// Re-check as they may have now initialised
return checkWorkers();
}
private boolean checkWorkerWithDelay(CDAWorker worker)
{
for (int i = 0; !worker.isInitialised() && i < 5; i++)
{
try
{
Thread.sleep(20);
}
catch (InterruptedException e)
{
}
}
return worker.isInitialised();
}
private boolean checkWorkers()
{
for (CDAWorker worker : workers)
if (!worker.isInitialised())
return false;
return true;
}
private void createQueue(int threads)
{
this.jobs = new ArrayBlockingQueue<CDAJob>(threads * 2);
}
/**
* Adds the work to the current queue.
*/
public void run(int n, int x, int y)
{
if (threads.isEmpty())
return;
put(n, x, y);
}
private void put(int n, int x, int y)
{
try
{
jobs.put(new CDAJob(n, x, y));
}
catch (InterruptedException e)
{
// TODO - Handle thread errors
throw new RuntimeException("Unexpected interruption", e);
}
}
/**
* Signal that no more fitting work will be added to the queue
*
* @param now
* Stop the work immediately, otherwise finish all work in the queue
*/
public void end(boolean now)
{
if (threads.isEmpty())
return;
if (now)
{
// Request worker shutdown
for (CDAWorker worker : workers)
worker.finish();
// Workers may be waiting for a job.
// Add null jobs if the queue is not at capacity so they can be collected by alive workers.
// If there are already jobs then the worker will stop due to the finish() signal.
for (int i = 0; i < threads.size(); i++)
{
jobs.offer(new CDAJob(-1, 0, 0)); // non-blocking add to queue
}
}
else
{
// Finish all the worker threads by passing in a null job
for (int i = 0; i < threads.size(); i++)
{
put(-1, 0, 0); // blocking add to queue
}
}
// Collect all the threads
for (int i = 0; i < threads.size(); i++)
{
try
{
threads.get(i).join();
}
catch (InterruptedException e)
{
// TODO - Handle thread errors
e.printStackTrace();
}
}
threads.clear();
}
}