/** * */ package ecologylab.bigsemantics.collecting; import ecologylab.appframework.Memory; import ecologylab.appframework.OutOfMemoryErrorHandler; import ecologylab.appframework.types.prefs.Pref; import ecologylab.appframework.types.prefs.PrefBoolean; import ecologylab.bigsemantics.metadata.builtins.DocumentClosure; import ecologylab.bigsemantics.model.text.TermVector; import ecologylab.bigsemantics.model.text.TermVectorWeightStrategy; import ecologylab.collections.PrioritizedPool; import ecologylab.collections.RunnablePool; import ecologylab.collections.WeightSet; import ecologylab.concurrent.DownloadMonitor; import ecologylab.concurrent.Monitor; import ecologylab.generic.ConsoleUtils; import ecologylab.generic.Generic; /** * A PrioritizedPool of references to Media, like Images. A thread wakes up periodically and parses * the top ranked one. * * @author andruid */ public class MediaReferencesPool extends PrioritizedPool<DocumentClosure> implements Runnable, RunnablePool { /** * Maximum number of prefetched images to hold on to. */ static final int POOL_SIZE = 300; static final int EMPTY_SLEEP = 900; static final int MID_SLEEP = 1500; static final int FULL_SLEEP = 3000; static final int EXTRA_SLEEP = 15000; Thread thread; int priority; /** * How long the agent plans to sleep for in the current / next iteration. Also used as a * comparison to see if sleep time has been decreased. */ int sleep; private SemanticsSessionScope infoCollector; boolean paused; private static final DownloadMonitor<DocumentClosure> REGULAR_IMAGE_DOWNLOAD_MONITOR = SemanticsDownloadMonitors .regularImageDownloadMonitor(); public static final int LOW_PRIORITY = REGULAR_IMAGE_DOWNLOAD_MONITOR .lowPriority(); public static final int MID_PRIORITY = LOW_PRIORITY + 1; public static final int HIGH_PRIORITY = MID_PRIORITY + 1; ; /** * This lock is for sleeping. prune() takes the lock on this, so we need a separate one. */ final Object sleepLock = new Object(); /** * This lock is for start, stop, pause, and unpause. prune() takes the lock on <code>this</code>, * so we need a separate one here. */ final Object startAndStopLock = new Object(); /** * Controls whether or not we periodically automatically download the ImgElements that have been * discovered. In other words, controls whether or not we collect images that we know about. Set * as a preference at runtime, and via a menu entry. */ PrefBoolean downloadImagesAutomatically = Pref .usePrefBoolean( "download_images_automatically", true); public MediaReferencesPool(int numSets, int maxSetSize, TermVector piv, SemanticsSessionScope infoCollector) { super(); this.infoCollector = infoCollector; weightSets = new WeightSet[numSets]; // TODO -- should there be a special weighting strategy here?! for (int i = 0; i < numSets; i++) weightSets[i] = new WeightSet<DocumentClosure>(maxSetSize, infoCollector.getCrawler(), new TermVectorWeightStrategy(piv)); } /** * Pause the image collecting agent. */ public void pause() { synchronized (startAndStopLock) { paused = true; } } /** * Unpause the image collecting agent. */ public void unpause() { synchronized (startAndStopLock) { paused = false; startAndStopLock.notify(); } } /** * This is the main loop of the image collecting agent. */ public void run() { try { while (thread != null) { // ThreadDebugger.waitIfPaused(this.thread); synchronized (startAndStopLock) { if (paused || !downloadImagesAutomatically.value()) { Monitor.wait(startAndStopLock); // debug("After wait!"); } } int sleep = adjustPriorityAndSleepTime(); REGULAR_IMAGE_DOWNLOAD_MONITOR.waitIfTooManyPending(); if (!Memory.reclaimIfLow()) { // changed to use imgElement not ImgVisual 2/20/08 by andruid // TODO -- download images here!!! // ImageElement imgElement = infoCollector.selectImgReference(); // if (imgElement != null) // imgElement.download(infoCollector.maxAgentImageDimension()); } synchronized (sleepLock) { try { sleepLock.wait(sleep); } catch (InterruptedException e) { e.printStackTrace(); } } } } catch (OutOfMemoryError e) { stop(); ConsoleUtils.obtrusiveConsoleOutput("BLANKET OutOfMemory in VisualPool"); OutOfMemoryErrorHandler.handleException(e); } } /** * Check to see if we're low on images to present to the user. Adjust the priority of our agent * thread based on this. * * @param downloadMonitor * * @return true if our priority has been increased */ private int adjustPriorityAndSleepTime() { int size = size(); int sleep = (size < 6) && (REGULAR_IMAGE_DOWNLOAD_MONITOR.waitingToDownload() < 4) ? EMPTY_SLEEP : (size < 15) ? MID_SLEEP : (size < 30) ? FULL_SLEEP : EXTRA_SLEEP; if (thread != null) { if (size > 10) thread.setPriority(LOW_PRIORITY); else if (size < 5) thread.setPriority(MID_PRIORITY); } // else we're in the midst of shutdown! return sleep; } void adjustPriorityAndWakeupAsNeeded() { this.adjustPriorityAndWakeupAsNeeded(); } @Override public void clear(boolean doRecycle) { pause(); super.clear(doRecycle); unpause(); } /** * Start the image collecting agent. */ public void start() { if (thread == null) // inexpensive test! { synchronized (startAndStopLock) { if (thread == null) // test again inside the synchronized block, if we must { thread = new Thread(this, "VisualPool"); // ThreadDebugger.registerMyself(thread); Generic.setPriority(thread, HIGH_PRIORITY); thread.start(); } } } } /** * Stop the image collecting agent. */ public void stop() { synchronized (startAndStopLock) { if (thread != null) thread = null; } } /** * Select the most important image currently in our pool of candidates. This can call prune(), so * it shares the lock on <code>this</code> with prune(). */ // TODO -- get rid of this method?! public synchronized DocumentClosure selectCandidate() { return pruneAndMaxSelect(); } /** * Toggle the on-ness of the image collecting agent that prefetches candidate images. */ public synchronized void toggleCollectingAgent() { boolean shouldDownloadImagesAutomatically = !downloadImagesAutomatically.value(); downloadImagesAutomatically.setValue(shouldDownloadImagesAutomatically); if (shouldDownloadImagesAutomatically) // turn the crawler back on { if (thread == null) { start(); } else unpause(); infoCollector.displayStatus("Turning on agent that downloads images."); } else // turn the crawler off { pause(); infoCollector.displayStatus("Turning off agent that downloads images."); } } public boolean isRunnable() { return true; } }