/******************************************************************************* * Copyright (c) 2004, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM - Initial API and implementation * James Blackburn (Broadcom Corp.) - ongoing development *******************************************************************************/ package org.eclipse.core.internal.utils; import java.util.*; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.*; import org.osgi.framework.Bundle; /** * Performs string sharing passes on all string pool participants registered * with the platform. */ public class StringPoolJob extends Job { private static final long INITIAL_DELAY = 10000;//ten seconds private static final long RESCHEDULE_DELAY = 300000;//five minutes private long lastDuration; /** * Stores all registered string pool participants, along with the scheduling * rule required when running it. */ private Map<IStringPoolParticipant, ISchedulingRule> participants = Collections.synchronizedMap(new HashMap<IStringPoolParticipant, ISchedulingRule>(10)); private final Bundle systemBundle = Platform.getBundle("org.eclipse.osgi"); //$NON-NLS-1$ public StringPoolJob() { super(Messages.utils_stringJobName); setSystem(true); setPriority(DECORATE); } /** * Adds a string pool participant. The job periodically builds * a string pool and asks all registered participants to share their strings in * the pool. Once all participants have added their strings to the pool, the * pool is discarded to avoid additional memory overhead. * * Adding a participant that is equal to a participant already registered will * replace the scheduling rule associated with the participant, but will otherwise * be ignored. * * @param participant The participant to add * @param rule The scheduling rule that must be owned at the time the * participant is called. This allows a participant to protect their data structures * against access at unsafe times. * * @see #removeStringPoolParticipant(IStringPoolParticipant) * @since 3.1 */ public void addStringPoolParticipant(IStringPoolParticipant participant, ISchedulingRule rule) { participants.put(participant, rule); if (getState() == Job.SLEEPING) wakeUp(INITIAL_DELAY); else schedule(INITIAL_DELAY); } /** * Removes the indicated log listener from the set of registered string * pool participants. If no such participant is registered, no action is taken. * * @param participant the participant to deregister * @see #addStringPoolParticipant(IStringPoolParticipant, ISchedulingRule) * @since 3.1 */ public void removeStringPoolParticipant(IStringPoolParticipant participant) { participants.remove(participant); } /* (non-Javadoc) * Method declared on Job */ protected IStatus run(IProgressMonitor monitor) { //if the system is shutting down, don't build if (systemBundle.getState() == Bundle.STOPPING) return Status.OK_STATUS; //copy current participants to handle concurrent additions and removals to map @SuppressWarnings("unchecked") Map.Entry<IStringPoolParticipant, ISchedulingRule>[] entries = participants.entrySet().toArray(new Map.Entry[participants.size()]); ISchedulingRule[] rules = new ISchedulingRule[entries.length]; IStringPoolParticipant[] toRun = new IStringPoolParticipant[entries.length]; for (int i = 0; i < toRun.length; i++) { toRun[i] = entries[i].getKey(); rules[i] = entries[i].getValue(); } final ISchedulingRule rule = MultiRule.combine(rules); long start = -1; int savings = 0; final IJobManager jobManager = Job.getJobManager(); try { jobManager.beginRule(rule, monitor); start = System.currentTimeMillis(); savings = shareStrings(toRun, monitor); } finally { jobManager.endRule(rule); } if (start > 0) { lastDuration = System.currentTimeMillis() - start; if (Policy.DEBUG_STRINGS) Policy.debug("String sharing saved " + savings + " bytes in: " + lastDuration); //$NON-NLS-1$ //$NON-NLS-2$ } //throttle frequency if it takes too long long scheduleDelay = Math.max(RESCHEDULE_DELAY, lastDuration*100); if (Policy.DEBUG_STRINGS) Policy.debug("Rescheduling string sharing job in: " + scheduleDelay); //$NON-NLS-1$ schedule(scheduleDelay); return Status.OK_STATUS; } private int shareStrings(IStringPoolParticipant[] toRun, IProgressMonitor monitor) { final StringPool pool = new StringPool(); for (int i = 0; i < toRun.length; i++) { if (monitor.isCanceled()) break; final IStringPoolParticipant current = toRun[i]; SafeRunner.run(new ISafeRunnable() { public void handleException(Throwable exception) { //exceptions are already logged, so nothing to do } public void run() { current.shareStrings(pool); } }); } return pool.getSavedStringCount(); } }