| /******************************************************************************* |
| * Copyright (c) 2004, 2015 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); |
| } |
| |
| @Override |
| 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 |
| 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 (final IStringPoolParticipant current : toRun) { |
| if (monitor.isCanceled()) |
| break; |
| SafeRunner.run(new ISafeRunnable() { |
| @Override |
| public void handleException(Throwable exception) { |
| //exceptions are already logged, so nothing to do |
| } |
| |
| @Override |
| public void run() { |
| current.shareStrings(pool); |
| } |
| }); |
| } |
| return pool.getSavedStringCount(); |
| } |
| } |