Copyright (c) 2012, 2016 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2012, 2016 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.eclipse.osgi.container; import java.util.Collections; import java.util.EnumSet; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.osgi.container.ModuleContainerAdaptor.ModuleEvent; import org.eclipse.osgi.framework.util.ThreadInfoReport; import org.eclipse.osgi.internal.container.EquinoxReentrantLock; import org.eclipse.osgi.internal.debug.Debug; import org.eclipse.osgi.internal.messages.Msg; import org.eclipse.osgi.report.resolution.ResolutionReport; import org.eclipse.osgi.util.NLS; import org.osgi.framework.AdminPermission; import org.osgi.framework.BundleException; import org.osgi.framework.BundleReference; import org.osgi.framework.startlevel.BundleStartLevel; import org.osgi.framework.wiring.BundleRevision; import org.osgi.service.resolver.ResolutionException;
A module represents a set of revisions installed in a module container.
Since:3.10
/** * A module represents a set of revisions installed in a * module {@link ModuleContainer container}. * @since 3.10 */
public abstract class Module implements BundleReference, BundleStartLevel, Comparable<Module> {
The possible start options for a module
/** * The possible start options for a module */
public static enum StartOptions {
The module start operation is transient and the persistent autostart or activation policy setting of the module is not modified.
/** * The module start operation is transient and the persistent * autostart or activation policy setting of the module is not modified. */
TRANSIENT,
The module start operation must activate the module according to the module's declared activation policy.
/** * The module start operation must activate the module according to the module's declared * activation policy. */
USE_ACTIVATION_POLICY,
The module start operation is transient and the persistent activation policy setting will be used.
/** * The module start operation is transient and the persistent activation policy * setting will be used. */
TRANSIENT_RESUME,
The module start operation is transient and will only happen if auto start setting is persistent.
/** * The module start operation is transient and will only happen if {@link Settings#AUTO_START auto start} * setting is persistent. */
TRANSIENT_IF_AUTO_START,
The module start operation that indicates the module is being started because of a lazy start trigger class load.
/** * The module start operation that indicates the module is being started because of a * lazy start trigger class load. */
LAZY_TRIGGER;
Tests if this option is contained in the specified options
/** * Tests if this option is contained in the specified options */
public boolean isContained(StartOptions... options) { for (StartOptions option : options) { if (equals(option)) { return true; } } return false; } }
The possible start options for a module
/** * The possible start options for a module */
public static enum StopOptions {
The module stop operation is transient and the persistent autostart setting of the module is not modified.
/** * The module stop operation is transient and the persistent * autostart setting of the module is not modified. */
TRANSIENT;
Tests if this option is contained in the specified options
/** * Tests if this option is contained in the specified options */
public boolean isContained(StopOptions... options) { for (StopOptions option : options) { if (equals(option)) { return true; } } return false; } }
An enumeration of the possible states a module may be in.
/** * An enumeration of the possible {@link Module#getState() states} a module may be in. */
public static enum State {
The module is installed but not yet resolved.
/** * The module is installed but not yet resolved. */
INSTALLED,
The module is resolved and able to be started.
/** * The module is resolved and able to be started. */
RESOLVED,
The module is waiting for a trigger class load to proceed with starting.
/** * The module is waiting for a {@link StartOptions#LAZY_TRIGGER trigger} * class load to proceed with starting. */
LAZY_STARTING,
The module is in the process of starting.
/** * The module is in the process of starting. */
STARTING,
The module is now running.
/** * The module is now running. */
ACTIVE,
The module is in the process of stopping
/** * The module is in the process of stopping */
STOPPING,
The module is uninstalled and may not be used.
/** * The module is uninstalled and may not be used. */
UNINSTALLED }
An enumeration of persistent settings for a module
/** * An enumeration of persistent settings for a module */
public static enum Settings {
The module has been set to auto start.
/** * The module has been set to auto start. */
AUTO_START,
The module has been set to use its activation policy.
/** * The module has been set to use its activation policy. */
USE_ACTIVATION_POLICY,
The module has been set for parallel activation from start-level
Since:3.15
/** * The module has been set for parallel activation from start-level * @since 3.15 */
PARALLEL_ACTIVATION }
A set of states that indicate a module is active.
/** * A set of {@link State states} that indicate a module is active. */
public static final EnumSet<State> ACTIVE_SET = EnumSet.of(State.STARTING, State.LAZY_STARTING, State.ACTIVE, State.STOPPING);
A set of states that indicate a module is resolved.
/** * A set of {@link State states} that indicate a module is resolved. */
public static final EnumSet<State> RESOLVED_SET = EnumSet.of(State.RESOLVED, State.STARTING, State.LAZY_STARTING, State.ACTIVE, State.STOPPING); private final Long id; private final String location; private final ModuleRevisions revisions; final EquinoxReentrantLock stateChangeLock = new EquinoxReentrantLock(); private final EnumSet<ModuleEvent> stateTransitionEvents = EnumSet.noneOf(ModuleEvent.class); private final EnumSet<Settings> settings; final AtomicInteger inStart = new AtomicInteger(0); private volatile State state = State.INSTALLED; private volatile int startlevel; private volatile long lastModified;
Constructs a new module with the specified id, location and container.
Params:
  • id – the new module id
  • location – the new module location
  • container – the container for the new module
  • settings – the persisted settings. May be null if there are no settings.
  • startlevel – the persisted start level or initial start level.
/** * Constructs a new module with the specified id, location and * container. * @param id the new module id * @param location the new module location * @param container the container for the new module * @param settings the persisted settings. May be {@code null} if there are no settings. * @param startlevel the persisted start level or initial start level. */
public Module(Long id, String location, ModuleContainer container, EnumSet<Settings> settings, int startlevel) { this.id = id; this.location = location; this.revisions = new ModuleRevisions(this, container); this.settings = settings == null ? EnumSet.noneOf(Settings.class) : EnumSet.copyOf(settings); this.startlevel = startlevel; }
Returns the module id.
Returns:the module id.
/** * Returns the module id. * @return the module id. */
public final Long getId() { return id; }
Returns the module location
Returns:the module location
/** Returns the module location * @return the module location */
public final String getLocation() { return location; }
Returns the ModuleRevisions associated with this module.
Returns:the ModuleRevisions associated with this module
/** * Returns the {@link ModuleRevisions} associated with this module. * @return the {@link ModuleRevisions} associated with this module */
public final ModuleRevisions getRevisions() { return revisions; }
Returns the module container this module is contained in.
Returns:the module container.
/** * Returns the module container this module is contained in. * @return the module container. */
public final ModuleContainer getContainer() { return revisions.getContainer(); }
Returns the current revision associated with this module. If the module is uninstalled then the last current revision is returned.
Returns:the current revision associated with this module.
/** * Returns the current {@link ModuleRevision revision} associated with this module. * If the module is uninstalled then the last current revision is returned. * @return the current {@link ModuleRevision revision} associated with this module. */
public final ModuleRevision getCurrentRevision() { return revisions.getCurrentRevision(); }
Returns the current state of this module.
Returns:the current state of this module.
/** * Returns the current {@link State state} of this module. * @return the current state of this module. */
public final State getState() { return state; } final void setState(State state) { this.state = state; } @Override public final int getStartLevel() { checkValid(); return this.startlevel; } @Override public final void setStartLevel(int startLevel) { revisions.getContainer().setStartLevel(this, startLevel); } @Override public final boolean isPersistentlyStarted() { checkValid(); return settings.contains(Settings.AUTO_START); } @Override public final boolean isActivationPolicyUsed() { checkValid(); return settings.contains(Settings.USE_ACTIVATION_POLICY); } final void storeStartLevel(int newStartLevel) { this.startlevel = newStartLevel; }
Returns the time when this module was last modified. A module is considered to be modified when it is installed, updated or uninstalled.

The time value is a the number of milliseconds since January 1, 1970, 00:00:00 UTC.

Returns:the time when this bundle was last modified.
/** * Returns the time when this module was last modified. A module is considered * to be modified when it is installed, updated or uninstalled. * <p> * The time value is a the number of milliseconds since January 1, 1970, 00:00:00 UTC. * @return the time when this bundle was last modified. */
public final long getLastModified() { return this.lastModified; } final void setlastModified(long lastModified) { this.lastModified = lastModified; } private static final EnumSet<ModuleEvent> VALID_RESOLVED_TRANSITION = EnumSet.of(ModuleEvent.STARTED); private static final EnumSet<ModuleEvent> VALID_STOPPED_TRANSITION = EnumSet.of(ModuleEvent.UPDATED, ModuleEvent.UNRESOLVED, ModuleEvent.UNINSTALLED);
Acquires the module lock for state changes by the current thread for the specified transition event. Certain transition events locks may be nested within other transition event locks. For example, a resolved transition event lock may be nested within a started transition event lock. A stopped transition lock may be nested within an updated, unresolved or uninstalled transition lock.
Params:
  • transitionEvent – the transition event to acquire the lock for.
Throws:
/** * Acquires the module lock for state changes by the current thread for the specified * transition event. Certain transition events locks may be nested within other * transition event locks. For example, a resolved transition event lock may be * nested within a started transition event lock. A stopped transition lock * may be nested within an updated, unresolved or uninstalled transition lock. * @param transitionEvent the transition event to acquire the lock for. * @throws BundleException */
protected final void lockStateChange(ModuleEvent transitionEvent) throws BundleException { boolean previousInterruption = Thread.interrupted(); boolean invalid = false; try { boolean acquired = stateChangeLock.tryLock(revisions.getContainer().getModuleLockTimeout(), TimeUnit.SECONDS); Set<ModuleEvent> currentTransition = Collections.emptySet(); if (acquired) { boolean isValidTransition = true; switch (transitionEvent) { case STARTED : case UPDATED : case UNINSTALLED : case UNRESOLVED : // These states must be initiating transition states // no other transition state is allowed when these are kicked off isValidTransition = stateTransitionEvents.isEmpty(); break; case RESOLVED : isValidTransition = VALID_RESOLVED_TRANSITION.containsAll(stateTransitionEvents); break; case STOPPED : isValidTransition = VALID_STOPPED_TRANSITION.containsAll(stateTransitionEvents); break; default : isValidTransition = false; break; } if (!isValidTransition) { currentTransition = EnumSet.copyOf(stateTransitionEvents); invalid = true; stateChangeLock.unlock(); } else { stateTransitionEvents.add(transitionEvent); return; } } else { currentTransition = EnumSet.copyOf(stateTransitionEvents); } Throwable cause; if (invalid) { cause = new IllegalStateException(NLS.bind(Msg.Module_LockStateError, transitionEvent, currentTransition)); } else { cause = new TimeoutException(NLS.bind(Msg.Module_LockTimeout, revisions.getContainer().getModuleLockTimeout())).initCause(new ThreadInfoReport(stateChangeLock.toString())); } String exceptonInfo = toString() + ' ' + transitionEvent + ' ' + currentTransition; throw new BundleException(Msg.Module_LockError + exceptonInfo, BundleException.STATECHANGE_ERROR, cause); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new BundleException(Msg.Module_LockError + toString() + " " + transitionEvent, BundleException.STATECHANGE_ERROR, e); //$NON-NLS-1$ } finally { if (previousInterruption) { Thread.currentThread().interrupt(); } } }
Releases the lock for state changes for the specified transition event.
Params:
  • transitionEvent –
/** * Releases the lock for state changes for the specified transition event. * @param transitionEvent */
protected final void unlockStateChange(ModuleEvent transitionEvent) { if (stateChangeLock.getHoldCount() == 0 || !stateTransitionEvents.contains(transitionEvent)) throw new IllegalMonitorStateException("Current thread does not hold the state change lock for: " + transitionEvent); //$NON-NLS-1$ stateTransitionEvents.remove(transitionEvent); stateChangeLock.unlock(); }
Returns true if the current thread holds the state change lock for the specified transition event.
Params:
  • transitionEvent –
Returns:true if the current thread holds the state change lock for the specified transition event.
/** * Returns true if the current thread holds the state change lock for the specified transition event. * @param transitionEvent * @return true if the current thread holds the state change lock for the specified transition event. */
public final boolean holdsTransitionEventLock(ModuleEvent transitionEvent) { return stateChangeLock.getHoldCount() > 0 && stateTransitionEvents.contains(transitionEvent); }
Returns the thread that currently owns the state change lock for this module, or null if not owned.
Returns:the owner, or null if not owned.
/** * Returns the thread that currently owns the state change lock for this module, or * <code>null</code> if not owned. * @return the owner, or <code>null</code> if not owned. */
public final Thread getStateChangeOwner() { return stateChangeLock.getOwner(); }
Starts this module
Params:
  • options – the options for starting
Throws:
/** * Starts this module * @param options the options for starting * @throws BundleException if an errors occurs while starting */
public void start(StartOptions... options) throws BundleException { ModuleContainer container = getContainer(); long startTime = 0; if (container.DEBUG_BUNDLE_START_TIME) { startTime = System.nanoTime(); } container.checkAdminPermission(getBundle(), AdminPermission.EXECUTE); if (options == null) { options = new StartOptions[0]; } ModuleEvent event; if (StartOptions.LAZY_TRIGGER.isContained(options)) { setTrigger(); if (stateChangeLock.getHoldCount() > 0 && stateTransitionEvents.contains(ModuleEvent.STARTED)) { // nothing to do here; the current thread is activating the bundle. return; } } BundleException startError = null; boolean lockedStarted = false; // Indicate we are in the middle of a start. // This must be incremented before we acquire the STARTED lock the first time. inStart.incrementAndGet(); try { lockStateChange(ModuleEvent.STARTED); lockedStarted = true; checkValid(); if (StartOptions.TRANSIENT_IF_AUTO_START.isContained(options) && !settings.contains(Settings.AUTO_START)) { // Do nothing; this is a request to start only if the module is set for auto start return; } checkFragment(); persistStartOptions(options); if (getStartLevel() > container.getStartLevel()) { if (StartOptions.TRANSIENT.isContained(options)) { // it is an error to attempt to transient start a bundle without its start level met throw new BundleException(Msg.Module_Transient_StartError, BundleException.START_TRANSIENT_ERROR); } // Do nothing; start level is not met return; } if (State.ACTIVE.equals(getState())) return; if (getState().equals(State.INSTALLED)) { ResolutionReport report; // must unlock to avoid out of order locks when multiple unresolved // bundles are started at the same time from different threads unlockStateChange(ModuleEvent.STARTED); lockedStarted = false; try { report = container.resolve(Collections.singletonList(this), true); } finally { lockStateChange(ModuleEvent.STARTED); lockedStarted = true; } // need to check valid again in case someone uninstalled the bundle checkValid(); ResolutionException e = report.getResolutionException(); if (e != null) { if (e.getCause() instanceof BundleException) { throw (BundleException) e.getCause(); } } if (State.ACTIVE.equals(getState())) return; if (getState().equals(State.INSTALLED)) { String reportMessage = report.getResolutionReportMessage(getCurrentRevision()); throw new BundleException(Msg.Module_ResolveError + reportMessage, BundleException.RESOLVE_ERROR); } } try { event = doStart(options); } catch (BundleException e) { // must return state to resolved setState(State.RESOLVED); startError = e; // must always publish the STOPPED event on error event = ModuleEvent.STOPPED; } } finally { if (lockedStarted) { unlockStateChange(ModuleEvent.STARTED); } inStart.decrementAndGet(); } if (event != null) { if (!EnumSet.of(ModuleEvent.STARTED, ModuleEvent.LAZY_ACTIVATION, ModuleEvent.STOPPED).contains(event)) throw new IllegalStateException("Wrong event type: " + event); //$NON-NLS-1$ publishEvent(event); // only print bundleTime information if we actually fired an event for this bundle if (container.DEBUG_BUNDLE_START_TIME) { Debug.println(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms for total start time event " + event + " - " + this); //$NON-NLS-1$ //$NON-NLS-2$ } } if (startError != null) { throw startError; } } final void publishEvent(ModuleEvent type) { revisions.getContainer().getAdaptor().publishModuleEvent(type, this, this); }
Stops this module.
Params:
  • options – options for stopping
Throws:
/** * Stops this module. * @param options options for stopping * @throws BundleException if an error occurs while stopping */
public void stop(StopOptions... options) throws BundleException { revisions.getContainer().checkAdminPermission(getBundle(), AdminPermission.EXECUTE); if (options == null) options = new StopOptions[0]; ModuleEvent event; BundleException stopError = null; lockStateChange(ModuleEvent.STOPPED); try { checkValid(); checkFragment(); persistStopOptions(options); if (!Module.ACTIVE_SET.contains(getState())) return; try { event = doStop(); } catch (BundleException e) { stopError = e; // must always publish the STOPPED event event = ModuleEvent.STOPPED; } } finally { unlockStateChange(ModuleEvent.STOPPED); } if (event != null) { if (!ModuleEvent.STOPPED.equals(event)) throw new IllegalStateException("Wrong event type: " + event); //$NON-NLS-1$ publishEvent(event); } if (stopError != null) throw stopError; } private void checkFragment() throws BundleException { ModuleRevision current = getCurrentRevision(); if ((current.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) { throw new BundleException(Msg.Module_Fragment_InvalidOperation, BundleException.INVALID_OPERATION); } } @Override public final int compareTo(Module o) { int slcomp = this.startlevel - o.startlevel; if (slcomp != 0) { return slcomp; } long idcomp = getId() - o.getId(); return (idcomp < 0L) ? -1 : ((idcomp > 0L) ? 1 : 0); } final void checkValid() { if (getState().equals(State.UNINSTALLED)) throw new IllegalStateException(Msg.Module_UninstalledError); } private ModuleEvent doStart(StartOptions... options) throws BundleException { boolean isLazyTrigger = StartOptions.LAZY_TRIGGER.isContained(options); if (isLazyTrigger) { if (!State.LAZY_STARTING.equals(getState())) { // need to make sure we transition through the lazy starting state setState(State.LAZY_STARTING); // need to publish the lazy event unlockStateChange(ModuleEvent.STARTED); try { publishEvent(ModuleEvent.LAZY_ACTIVATION); } finally { lockStateChange(ModuleEvent.STARTED); } if (State.ACTIVE.equals(getState())) { // A sync listener must have caused the bundle to activate return null; } // continue on to normal starting } if (getContainer().DEBUG_MONITOR_LAZY) { Debug.printStackTrace(new Exception("Module is being lazy activated: " + this)); //$NON-NLS-1$ } } else { if (isLazyActivate(options) && !isTriggerSet()) { if (State.LAZY_STARTING.equals(getState())) { // a sync listener must have tried to start this module again with the lazy option return null; // no event to publish; nothing to do } // set the lazy starting state and return lazy activation event for firing setState(State.LAZY_STARTING); return ModuleEvent.LAZY_ACTIVATION; } } // time to actual start the module if (!State.STARTING.equals(getState())) { // TODO this starting state check should not be needed // but we do it because of the way the system module init works setState(State.STARTING); publishEvent(ModuleEvent.STARTING); } try { startWorker(); setState(State.ACTIVE); return ModuleEvent.STARTED; } catch (Throwable t) { // must fire stopping event setState(State.STOPPING); publishEvent(ModuleEvent.STOPPING); if (t instanceof BundleException) throw (BundleException) t; throw new BundleException(Msg.Module_StartError, BundleException.ACTIVATOR_ERROR, t); } } private void setTrigger() { ModuleLoader loader = getCurrentLoader(); if (loader != null) { loader.getAndSetTrigger(); } } private boolean isTriggerSet() { ModuleLoader loader = getCurrentLoader(); return loader == null ? false : loader.isTriggerSet(); } private ModuleLoader getCurrentLoader() { ModuleRevision current = getCurrentRevision(); if (current == null) { return null; } ModuleWiring wiring = current.getWiring(); if (wiring == null) { return null; } try { return wiring.getModuleLoader(); } catch (UnsupportedOperationException e) { // just ignore and return null; return null; } }
Performs any work associated with starting a module. For example, loading and calling start on an activator.
Throws:
  • BundleException – if there was an exception starting the module
/** * Performs any work associated with starting a module. For example, * loading and calling start on an activator. * @throws BundleException if there was an exception starting the module */
protected void startWorker() throws BundleException { // Do nothing } private ModuleEvent doStop() throws BundleException { setState(State.STOPPING); publishEvent(ModuleEvent.STOPPING); try { stopWorker(); return ModuleEvent.STOPPED; } catch (Throwable t) { if (t instanceof BundleException) throw (BundleException) t; throw new BundleException(Msg.Module_StopError, BundleException.ACTIVATOR_ERROR, t); } finally { // must always set the state to stopped setState(State.RESOLVED); } }
Performs any work associated with stopping a module. For example, calling stop on an activator.
Throws:
  • BundleException – if there was an exception stopping the module
/** * Performs any work associated with stopping a module. For example, * calling stop on an activator. * @throws BundleException if there was an exception stopping the module */
protected void stopWorker() throws BundleException { // Do nothing } @Override public String toString() { return getCurrentRevision() + " [id=" + id + "]"; //$NON-NLS-1$ //$NON-NLS-2$ } private void persistStartOptions(StartOptions... options) { if (StartOptions.TRANSIENT.isContained(options) || StartOptions.TRANSIENT_RESUME.isContained(options) || StartOptions.LAZY_TRIGGER.isContained(options)) { return; } if (StartOptions.USE_ACTIVATION_POLICY.isContained(options)) { settings.add(Settings.USE_ACTIVATION_POLICY); } else { settings.remove(Settings.USE_ACTIVATION_POLICY); } settings.add(Settings.AUTO_START); revisions.getContainer().moduleDatabase.persistSettings(settings, this); } private void persistStopOptions(StopOptions... options) { if (StopOptions.TRANSIENT.isContained(options)) return; settings.remove(Settings.USE_ACTIVATION_POLICY); settings.remove(Settings.AUTO_START); revisions.getContainer().moduleDatabase.persistSettings(settings, this); }
Set if this module should be activated in parallel with other modules that have the same start level.
Params:
  • parallelActivation – true if the module should be started in parallel; false otherwise
Since:3.15
/** * Set if this module should be activated in parallel with other modules that have * the same {@link #getStartLevel() start level}. * @param parallelActivation true if the module should be started in parallel; false otherwise * @since 3.15 */
public void setParallelActivation(boolean parallelActivation) { if (parallelActivation) { settings.add(Settings.PARALLEL_ACTIVATION); } else { settings.remove(Settings.PARALLEL_ACTIVATION); } revisions.getContainer().moduleDatabase.persistSettings(settings, this); }
Returns if this module should be activated in parallel with other modules that have the same start level.
Returns:true if the module should be started in parallel; false otherwise
Since:3.15
/** * Returns if this module should be activated in parallel with other modules that have * the same {@link #getStartLevel() start level}. * @return true if the module should be started in parallel; false otherwise * @since 3.15 */
public boolean isParallelActivated() { return settings.contains(Settings.PARALLEL_ACTIVATION); }
The container is done with the revision and it has been completely removed. This method allows the resources behind the revision to be cleaned up.
Params:
  • revision – the revision to clean up
/** * The container is done with the revision and it has been completely removed. * This method allows the resources behind the revision to be cleaned up. * @param revision the revision to clean up */
abstract protected void cleanup(ModuleRevision revision); final boolean isLazyActivate(StartOptions... options) { if (StartOptions.TRANSIENT.isContained(options)) { if (!StartOptions.USE_ACTIVATION_POLICY.isContained(options)) { return false; } } else if (!settings.contains(Settings.USE_ACTIVATION_POLICY)) { return false; } return hasLazyActivatePolicy(); } final boolean hasLazyActivatePolicy() { ModuleRevision current = getCurrentRevision(); return current == null ? false : current.hasLazyActivatePolicy(); }
Used internally by the container to determine if any thread is in the middle of a start operation on this module.
Returns:
/** * Used internally by the container to determine if any thread is in the middle * of a start operation on this module. * @return */
final boolean inStart() { return inStart.get() > 0; } }