blob: c1281e2cd14067f815c34aceab1f3426412c6bc1 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2006, 2021 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.nico.core.runtime;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.osgi.util.NLS;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.lang.NonNull;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.status.ErrorStatus;
import org.eclipse.statet.jcommons.status.InfoStatus;
import org.eclipse.statet.jcommons.status.OkStatus;
import org.eclipse.statet.jcommons.status.Status;
import org.eclipse.statet.jcommons.ts.core.RunnableStatus;
import org.eclipse.statet.jcommons.ts.core.SystemRunnable;
import org.eclipse.statet.jcommons.ts.core.ToolQueue;
import org.eclipse.statet.jcommons.ts.core.ToolRunnable;
import org.eclipse.statet.internal.nico.core.Messages;
import org.eclipse.statet.nico.core.NicoCore;
/**
* Queue with ToolRunnable waiting to be processed by the tool/controller.
*
* Usage: You get your queue via accessor of the ToolProcess.
*
* DebugEvents for a lifecycle of an entry:<pre>
* CHANGE (CONTENT)
* [ENTRIES_ADDED]
* |
* +--------------------+---------------------+
* | | |
* | | CHANGE (CONTENT)
* | | [ENTRY_START_PROCESSING]
* CHANGE (CONTENT) | |
* [ENTRIES_DELETE] | CHANGE (CONTENT)
* | [ENTRY_FINISH_PROCESSING]
* TERMINATE
* [ENTRIES_ABANDONED]
* </pre>
* The events of this type are sended by the queue (source element).
*
*/
@NonNullByDefault
public final class Queue implements ToolQueue {
public static final byte INIT_STATE= 1;
public static final byte PROCESSING_STATE= 2;
public static final byte IDLING_STATE= 3;
public static final byte PAUSED_STATE= 5;
public static final byte TERMINATED_STATE= 6;
/**
* Constant for detail of a DebugEvent, sending the complete queue.
* This does not signalising, that the queue has changed.
* <p>
* The queue entries (<code>List&lt;ToolRunnable&gt;</code>) are attached as
* data to this event. The source of the event is the ToolProcess.
* <p>
* Usage: Events of this type are sended by the ToolProcess/its queue.
* The constant is applicable for DebugEvents of kind
* <code>MODEL_SPECIFIC</code>.</p>
*/
public static final int QUEUE_INFO= 1;
public static final int STATE_REQUEST= 5;
/**
* TaskDelta for events of the queue.
*
* Type is a event type of {@link ToolRunnable} events
*/
public static final class TaskDelta {
public final int type;
public final int position;
/**
* One or multiple runnable effected by this event.
* STARTING and FINISHING events have always only a single item.
*/
public final ImList<ToolRunnable> data;
private TaskDelta(final int type, final int position, final ImList<ToolRunnable> data) {
this.type= type;
this.position= position;
this.data= data;
}
}
public static final boolean isStateChange(final DebugEvent event) {
return (event.getKind() == DebugEvent.CHANGE && event.getDetail() == DebugEvent.STATE);
}
public static final boolean isStateRequest(final DebugEvent event) {
return (event.getKind() == DebugEvent.MODEL_SPECIFIC && event.getDetail() == STATE_REQUEST);
}
public static final class StateDelta {
public final byte oldState;
public final byte newState;
private StateDelta(final byte oldState, final byte newState) {
this.oldState= oldState;
this.newState= newState;
}
}
private static final int toRequestState(final int state) {
return (state == IDLING_STATE) ? PROCESSING_STATE : state;
}
public static final int IF_ABSENT= 1 << 0;
private static class RunnableInfoStatus extends InfoStatus implements RunnableStatus {
private final ToolRunnable runnable;
public RunnableInfoStatus(final int code, final String message, final ToolRunnable runnable) {
super(NicoCore.BUNDLE_ID, code, message);
this.runnable= runnable;
}
@Override
public ToolRunnable getRunnable() {
return this.runnable;
}
}
private static class RunnableOkStatus extends OkStatus implements RunnableStatus {
private final ToolRunnable runnable;
public RunnableOkStatus(final String message, final ToolRunnable runnable) {
super(NicoCore.BUNDLE_ID, "OK");
this.runnable= runnable;
}
@Override
public ToolRunnable getRunnable() {
return this.runnable;
}
}
private static final Status ADDED_STATUS= new OkStatus(NicoCore.BUNDLE_ID, "Added.");
public static final Status ALREADY_PRESENT_STATUS= new InfoStatus(NicoCore.BUNDLE_ID, "Already in queue.");
private static ToolRunnable checkRunnable(final @Nullable ToolRunnable runnable) {
if (runnable == null) {
throw new NullPointerException("runnable"); //$NON-NLS-1$
}
return runnable;
}
private static ImList<ToolRunnable> checkRunnableList(final @Nullable ToolRunnable runnable) {
if (runnable == null) {
throw new NullPointerException("runnable"); //$NON-NLS-1$
}
return ImCollections.newList(runnable);
}
private static ImList<ToolRunnable> checkRunnableList(final @Nullable List<ToolRunnable> runnables) {
if (runnables == null) {
throw new NullPointerException("runnables"); //$NON-NLS-1$
}
final ImList<ToolRunnable> finalRunnables= ImCollections.toList(runnables);
final int i= finalRunnables.indexOf(null);
if (i >= 0) {
throw new NullPointerException("runnable[" + i + ']'); //$NON-NLS-1$
}
return finalRunnables;
}
static final int RUN_NONE= -2;
static final int RUN_SUSPEND= -1;
static final int RUN_CONTROL= 1;
static final int RUN_HOT= 2;
static final int RUN_OTHER= 3;
static final int RUN_DEFAULT= 4;
private static class RankedItem {
final ToolRunnable runnable;
final int rank;
public RankedItem(final ToolRunnable runnable, final int rank) {
this.runnable= runnable;
this.rank= rank;
}
@Override
public int hashCode() {
return this.runnable.hashCode();
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
final RankedItem other= (RankedItem) obj;
return (this.runnable.equals(other.runnable) );
}
}
public static interface Section {
}
private static abstract class SubQueue implements Section {
protected final LinkedList<ToolRunnable> list= new LinkedList<>();
private @Nullable SubQueue next;
private boolean state;
public abstract int getAllSize();
public abstract int getAll(final ToolRunnable[] array, final int i);
public int getStartIdx() {
int idx= 0;
SubQueue next= this.next;
while (next != null) {
idx+= next.getAllSize();
next= next.next;
}
return idx;
}
public int getAppendIdx() {
return getStartIdx() + this.list.size();
}
public void append(final ImList<ToolRunnable> runnables) {
if (runnables.size() == 1) {
this.list.add(runnables.get(0));
}
else {
this.list.addAll(runnables);
}
}
public void append(final ToolRunnable runnable) {
this.list.add(runnable);
}
public void dispose() {
this.state= true;
this.list.clear();
this.next= null;
}
public boolean isDispose() {
return this.state;
}
}
private static final class TopLevelQueue extends SubQueue {
public TopLevelQueue() {
}
@Override
public int getAllSize() {
return this.list.size();
}
@Override
public int getAll(final ToolRunnable[] array, int i) {
for (final ToolRunnable runnable : this.list) {
array[i++]= runnable;
}
return i;
}
}
private static final class InsertQueue extends SubQueue {
protected final ToolRunnable insertRunnable;
public InsertQueue(final ToolRunnable insertRunnable) {
this.insertRunnable= insertRunnable;
}
@Override
public int getAllSize() {
return this.list.size() + 1;
}
@Override
public int getAll(final ToolRunnable[] array, int i) {
for (final ToolRunnable runnable : this.list) {
array[i++]= runnable;
}
array[i++]= this.insertRunnable;
return i;
}
}
private final List<SubQueue> sectionStack= new ArrayList<>();
private final SubQueue topLevelSection; // sectionStack.first
private SubQueue currentSection; // sectionStack.last
private @Nullable ImList<ToolRunnable> singleIOCache= null;
private final Deque<ImList<ToolRunnable>> finishedExpected= new ArrayDeque<>();
private final List<DebugEvent> eventList= new ArrayList<>(5);
private final ToolProcess process;
private boolean resetOnIdle= true;
private final List<RankedItem> onIdleList= new ArrayList<>();
private final LinkedList<ToolRunnable> currentIdleList= new LinkedList<>();
private final LinkedList<ToolRunnable> hotList= new LinkedList<>();
private byte state;
private byte stateRequest= PROCESSING_STATE;
Queue(final ToolProcess process) {
this.process= process;
this.topLevelSection= new TopLevelQueue();
this.sectionStack.add(this.topLevelSection);
this.currentSection= this.topLevelSection;
}
private final Status acceptSubmit(final ToolStatus toolStatus) {
if (toolStatus == ToolStatus.TERMINATED) {
return new ErrorStatus(NicoCore.BUNDLE_ID, -1,
NLS.bind(Messages.ToolController_ToolTerminated_message, this.process.getLabel(0)),
null );
}
return ADDED_STATUS;
}
private final Status acceptSubmit(final ToolStatus toolStatus, final SubQueue section) {
if (toolStatus == ToolStatus.TERMINATED) {
return new ErrorStatus(NicoCore.BUNDLE_ID, -1,
NLS.bind(Messages.ToolController_ToolTerminated_message, this.process.getLabel(0)),
null );
}
if (section.isDispose()) {
return new ErrorStatus(NicoCore.BUNDLE_ID, -1,
NLS.bind(Messages.ToolController_QueueSectionTerminated_message, this.process.getLabel(0)),
null );
}
return ADDED_STATUS;
}
public synchronized void sendElements() {
checkIOCache();
final ImList<ToolRunnable> runnables= internal_createCompleteList();
final DebugEvent event= new DebugEvent(this, DebugEvent.MODEL_SPECIFIC, QUEUE_INFO);
event.setData(runnables);
this.eventList.add(event);
internal_fireEvents();
}
public Section getTopLevelSection() {
return this.topLevelSection;
}
public synchronized Section getCurrentSection() {
return this.currentSection;
}
public synchronized int getCurrentSize() {
if (this.singleIOCache != null) {
return 1;
}
return this.currentSection.list.size();
}
/**
* Submits the runnable for the tool.
* <p>
* The runnable will be added to the queue and will be run, if it's its turn.
*
* @param runnable the runnable to add
* @return the status of the queue operation.
*/
@Override
public Status add(final ToolRunnable runnable) {
final ImList<ToolRunnable> checkedRunnables= checkRunnableList(runnable);
return doAdd(checkedRunnables, null);
}
/**
* Submits the runnables for the tool.
* <p>
* The runnables will be added en block to the queue and will be runned, if it's its turn.
*
* @param runnables the runnables to add.
* @return the status of the queue operation.
*/
public Status add(final List<ToolRunnable> runnables) {
final ImList<ToolRunnable> checkedRunnables= checkRunnableList(runnables);
return doAdd(checkedRunnables, null);
}
/**
* Submits the runnable for the tool.
* <p>
* The runnable will be added to the queue in the specified section
* and will be run, if it's its turn.
*
* @param runnable the runnable to add
* @param section where to add the runnable, <code>null</code> for current section
* @param strategy flags {@link #IF_ABSENT}
* @return the status of the queue operation.
*/
public Status add(final ToolRunnable runnable,
final @Nullable Section section, final int strategy) {
final ToolRunnable checkedRunnable= checkRunnable(runnable);
return doAdd(checkedRunnable, (SubQueue) section, strategy);
}
/**
* Submits the runnables for the tool.
* <p>
* The runnables will be added en block to the queue in the specified section
* and will be runned, if it's its turn.
*
* @param runnables the runnables to add.
* @param section where to add the runnabler, <code>null</code> for current section
* @return the status of the queue operation.
*/
public Status add(final List<ToolRunnable> runnables,
final @Nullable Section section) {
final ImList<ToolRunnable> checkedRunnables= checkRunnableList(runnables);
return doAdd(checkedRunnables, (SubQueue) section);
}
private synchronized Status doAdd(final ImList<ToolRunnable> runnables,
@Nullable SubQueue section) {
if (section == null) {
section= this.currentSection;
}
final ToolStatus toolStatus= this.process.getToolStatus();
final Status status= acceptSubmit(toolStatus, section);
if (status.getSeverity() < Status.ERROR) {
if (toolStatus.isWaiting()) {
internal_add(runnables, section, true);
notifyAll();
}
else {
internal_add(runnables, section, false);
}
}
return status;
}
private synchronized Status doAdd(final ToolRunnable runnable,
@Nullable SubQueue section, final int strategy) {
if (section == null) {
section= this.currentSection;
}
final ToolStatus toolStatus= this.process.getToolStatus();
final Status status= acceptSubmit(toolStatus, section);
if (status.getSeverity() < Status.ERROR) {
if (strategy != 0) {
checkIOCache();
int idx;
if ((strategy & IF_ABSENT) != 0 && (idx= section.list.indexOf(runnable)) >= 0) {
return new RunnableInfoStatus(0, ALREADY_PRESENT_STATUS.getMessage(),
section.list.get(idx) );
}
}
if (toolStatus.isWaiting()) {
internal_add(ImCollections.newList(runnable), section, true);
notifyAll();
}
else {
internal_add(ImCollections.newList(runnable), section, false);
}
return new RunnableOkStatus(ADDED_STATUS.getMessage(), runnable);
}
return status;
}
@Override
public void remove(final ToolRunnable runnable) {
final ImList<ToolRunnable> checkedRunnables= checkRunnableList(runnable);
synchronized (this) {
internal_remove(checkedRunnables);
internal_fireEvents();
}
}
public void remove(final List<ToolRunnable> runnables) {
final ImList<ToolRunnable> checkedRunnables= checkRunnableList(runnables);
synchronized (this) {
internal_remove(checkedRunnables);
internal_fireEvents();
}
}
public void move(final ImList<ToolRunnable> runnables, final Queue to) {
final ImList<ToolRunnable> checkedRunnables= checkRunnableList(runnables);
if (to == null) {
throw new NullPointerException("to"); //$NON-NLS-1$
}
final ImList<ToolRunnable> finalRunnables;
synchronized (this) {
finalRunnables= internal_moveFrom(checkedRunnables);
}
synchronized (to) {
to.internal_moveTo(finalRunnables, to.currentSection);
to.notifyAll();
}
}
public void moveAll(final Queue to) {
if (to == null) {
throw new NullPointerException("to"); //$NON-NLS-1$
}
final ImList<ToolRunnable> finalRunnables;
synchronized (this) {
checkIOCache();
{ final List<ToolRunnable> removed= new ArrayList<>(internal_getCompleteSize());
for (final Iterator<ToolRunnable> iter= this.topLevelSection.list.iterator(); iter.hasNext();) {
final ToolRunnable runnable= iter.next();
if (runnable.changed(ToolRunnable.MOVING_FROM, this.process)) {
iter.remove();
removed.add(runnable);
}
}
finalRunnables= ImCollections.toList(removed);
}
addContentChangeEvent(new TaskDelta(ToolRunnable.MOVING_FROM, -1, finalRunnables));
internal_fireEvents();
}
synchronized (to) {
to.internal_moveTo(finalRunnables, to.topLevelSection);
to.notifyAll();
}
}
public Status addOnIdle(final SystemRunnable runnable, final int rank) {
final ToolRunnable checkedRunnable= checkRunnable(runnable);
synchronized (this) {
final ToolStatus toolStatus= this.process.getToolStatus();
final Status status= acceptSubmit(toolStatus);
if (status.getSeverity() < Status.ERROR) {
final RankedItem item= new RankedItem(checkedRunnable, rank);
int idx= this.onIdleList.indexOf(item);
if (idx >= 0 && this.onIdleList.get(idx).rank != rank) {
this.onIdleList.remove(idx);
if (!this.resetOnIdle) {
this.currentIdleList.remove(item.runnable);
}
idx= -1;
}
if (idx < 0) {
idx= 0;
for (; idx < this.onIdleList.size(); idx++) {
if (this.onIdleList.get(idx).rank > rank) {
break;
}
}
this.onIdleList.add(idx, item);
if (!this.resetOnIdle) {
if (idx == this.onIdleList.size() - 1) { // last
this.currentIdleList.add(item.runnable);
}
else {
final RankedItem next= this.onIdleList.get(idx + 1);
final int nextIdx= this.currentIdleList.indexOf(next.runnable);
if (nextIdx >= 0) {
this.currentIdleList.add(nextIdx, item.runnable);
}
}
}
}
notifyAll();
}
return status;
}
}
public void removeOnIdle(final ToolRunnable runnable) {
final ToolRunnable checkedRunnable= checkRunnable(runnable);
synchronized (this) {
this.onIdleList.remove(new RankedItem(checkedRunnable, 0));
this.currentIdleList.remove(runnable);
}
}
@Override
public boolean isHotSupported() {
return true;
}
@Override
public Status addHot(final ToolRunnable runnable) {
return addHot(runnable, 0);
}
public Status addHot(final ToolRunnable runnable, final int strategy) {
final ToolRunnable checkedRunnable= checkRunnable(runnable);
synchronized (this) {
final ToolStatus toolStatus= this.process.getToolStatus();
final Status status= acceptSubmit(toolStatus);
if (status.getSeverity() < Status.ERROR) {
if ((strategy & IF_ABSENT) != 0) {
if (this.hotList.contains(checkedRunnable)) {
return ALREADY_PRESENT_STATUS;
}
}
this.hotList.add(checkedRunnable);
if (this.hotList.size() > 0) {
notifyAll();
final ToolController controller= this.process.getController();
if (controller != null) {
controller.scheduleHotMode();
}
}
}
return status;
}
}
@Override
public void removeHot(final ToolRunnable runnable) {
final ToolRunnable checkedRunnable= checkRunnable(runnable);
synchronized (this) {
if (this.hotList.remove(checkedRunnable)) {
runnable.changed(ToolRunnable.REMOVING_FROM, this.process);
}
}
}
private void internal_add(final ImList<ToolRunnable> runnables, final SubQueue section,
final boolean allowCache) {
if (allowCache && this.singleIOCache == null
&& runnables.size() == 1
&& section == this.currentSection && this.currentSection.list.isEmpty() ) {
this.singleIOCache= runnables;
return;
}
checkIOCache();
final int idx= section.getAppendIdx();
section.append(runnables);
addContentChangeEvent(new TaskDelta(ToolRunnable.ADDING_TO, idx, runnables));
internal_fireEvents();
}
private void internal_remove(final ImList<ToolRunnable> runnables) {
checkIOCache();
final ImList<ToolRunnable> finalRunnables;
{ final List<ToolRunnable> removed= new ArrayList<>(runnables.size());
ITER_RUNNABLE: for (final ToolRunnable runnable : runnables) {
for (int iSection= this.sectionStack.size() - 1; iSection >= 0; iSection--) {
final SubQueue section= this.sectionStack.get(iSection);
final int idx= section.list.indexOf(runnable);
if (idx >= 0) {
if (runnable.changed(ToolRunnable.REMOVING_FROM, this.process)) {
section.list.remove(idx);
removed.add(runnable);
}
continue ITER_RUNNABLE;
}
}
}
finalRunnables= ImCollections.toList(removed);
}
addContentChangeEvent(new TaskDelta(ToolRunnable.REMOVING_FROM, -1, finalRunnables));
internal_fireEvents();
}
private ImList<ToolRunnable> internal_moveFrom(final ImList<ToolRunnable> runnables) {
checkIOCache();
final ImList<ToolRunnable> finalRunnables;
{ final List<ToolRunnable> removed= new ArrayList<>(runnables.size());
ITER_RUNNABLE: for (final ToolRunnable runnable : runnables) {
for (int iSection= this.sectionStack.size() - 1; iSection >= 0; iSection--) {
final SubQueue section= this.sectionStack.get(iSection);
final int idx= section.list.indexOf(runnable);
if (idx >= 0) {
if (runnable.changed(ToolRunnable.MOVING_FROM, this.process)) {
section.list.remove(idx);
removed.add(runnable);
}
continue ITER_RUNNABLE;
}
}
}
finalRunnables= ImCollections.toList(removed);
}
addContentChangeEvent(new TaskDelta(ToolRunnable.MOVING_FROM, -1, finalRunnables));
internal_fireEvents();
return finalRunnables;
}
private void internal_moveTo(final ImList<ToolRunnable> runnables, final SubQueue section) {
checkIOCache();
final int idx= section.getAppendIdx();
section.append(runnables);
addContentChangeEvent(new TaskDelta(ToolRunnable.MOVING_TO, idx, runnables));
internal_fireEvents();
}
Section internal_addInsert(final ToolRunnable runnable) {
assert (runnable != null);
checkIOCache();
final SubQueue section= new InsertQueue(runnable);
this.sectionStack.add(section);
this.currentSection.next= section;
this.currentSection= section;
addContentChangeEvent(new TaskDelta(ToolRunnable.ADDING_TO, 0, ImCollections.newList(runnable)));
internal_resetOnIdle();
internal_fireEvents();
return section;
}
void internal_removeInsert(final Section section) {
assert (section != null);
checkIOCache();
final int sectionIdx= this.sectionStack.indexOf(section);
if (sectionIdx < 0) {
return;
}
if (sectionIdx == 0) {
throw new IllegalArgumentException();
}
final ImList<ToolRunnable> finalRunnables;
{ final ToolRunnable[] runnables= new @NonNull ToolRunnable[
internal_getSize(sectionIdx, this.sectionStack.size()) ];
int i= 0;
for (int iSection= this.sectionStack.size() - 1; iSection >= sectionIdx; iSection--) {
final SubQueue removedSection= this.sectionStack.remove(iSection);
i= removedSection.getAll(runnables, i);
removedSection.dispose();
}
this.currentSection= this.sectionStack.get(this.sectionStack.size() - 1);
this.currentSection.next= null;
finalRunnables= ImCollections.newList(runnables);
}
for (final ToolRunnable runnable : finalRunnables) {
// runnable.changed(ToolRunnable.BEING_ABANDONED, this.process);
if (!runnable.changed(ToolRunnable.REMOVING_FROM, this.process)) {
runnable.changed(ToolRunnable.BEING_ABANDONED, this.process);
}
}
addContentChangeEvent(new TaskDelta(ToolRunnable.REMOVING_FROM, -1, finalRunnables));
internal_resetOnIdle();
internal_fireEvents();
}
void internal_scheduleIdle(final ToolRunnable runnable) {
final ToolRunnable checkedRunnable= checkRunnable(runnable);
this.currentIdleList.add(checkedRunnable);
}
void internal_removeIdle(final ToolRunnable runnable) {
final ToolRunnable checkedRunnable= checkRunnable(runnable);
this.currentIdleList.remove(checkedRunnable);
}
void internal_check() {
checkIOCache();
internal_fireEvents();
}
void internal_resetOnIdle() {
this.resetOnIdle= true;
}
private void internal_resetIdleList() {
this.resetOnIdle= false;
this.currentIdleList.clear();
for (int i= 0; i < this.onIdleList.size(); i++) {
this.currentIdleList.add(this.onIdleList.get(i).runnable);
}
}
int internal_next() {
if (!this.hotList.isEmpty()) {
return RUN_HOT;
}
final ToolRunnable runnable;
if (this.singleIOCache != null) {
runnable= this.singleIOCache.get(0);
}
else if (!this.currentSection.list.isEmpty()) {
runnable= this.currentSection.list.peekFirst();
}
else {
if (this.resetOnIdle) {
internal_resetIdleList();
}
if (!this.currentIdleList.isEmpty()) {
runnable= this.currentIdleList.peekFirst();
}
else {
return RUN_NONE;
}
}
return (runnable instanceof SystemRunnable) ?
RUN_OTHER : RUN_DEFAULT;
}
/** After check with {@link #internal_next()} */
ToolRunnable internal_poll() {
final @NonNull ImList<ToolRunnable> finalRunnable;
if (this.singleIOCache != null) {
finalRunnable= this.singleIOCache;
final int idx= this.currentSection.getAppendIdx();
addContentChangeEvent(new TaskDelta(ToolRunnable.ADDING_TO, idx, finalRunnable));
this.singleIOCache= null;
internal_resetOnIdle();
}
else if (!this.currentSection.list.isEmpty()) {
finalRunnable= ImCollections.newList(this.currentSection.list.pollFirst());
internal_resetOnIdle();
}
else {
finalRunnable= ImCollections.newList(this.currentIdleList.pollFirst());
}
addContentChangeEvent(new TaskDelta(ToolRunnable.STARTING, -1, finalRunnable));
internal_fireEvents();
this.finishedExpected.push(finalRunnable);
return finalRunnable.get(0);
}
boolean internal_nextHot() {
return !this.hotList.isEmpty();
}
@Nullable ToolRunnable internal_pollHot() {
return this.hotList.pollFirst();
}
/**
* Not necessary in synchronized block
*/
void internal_onFinished(final ToolRunnable runnable, final int detail) {
assert (runnable == this.finishedExpected.peek().get(0));
addContentChangeEvent(new TaskDelta(detail, -1, this.finishedExpected.poll()));
}
private int internal_getCompleteSize() {
int size= 0;
for (int iSection= this.sectionStack.size() - 1; iSection >= 0; iSection--) {
size+= this.sectionStack.get(iSection).getAllSize();
}
return size;
}
private int internal_getSize(final int startSection, final int endSection) {
int size= 0;
for (int iSection= endSection - 1; iSection >= startSection; iSection--) {
size+= this.sectionStack.get(iSection).getAllSize();
}
return size;
}
private ImList<ToolRunnable> internal_createCompleteList() {
final ToolRunnable[] runnables= new @NonNull ToolRunnable[
internal_getCompleteSize() ];
int i= 0;
for (int iSection= this.sectionStack.size() - 1; iSection >= 0; iSection--) {
i= this.sectionStack.get(iSection).getAll(runnables, i);
}
return ImCollections.newList(runnables);
}
List<ToolRunnable> internal_getList() {
internal_check();
return internal_createCompleteList();
}
List<ToolRunnable> internal_getCurrentList() {
internal_check();
return this.currentSection.list;
}
public synchronized boolean isRequested(final byte state) {
return (this.state == state || this.stateRequest == state);
}
public synchronized boolean pause() {
if (this.state == TERMINATED_STATE) {
return false;
}
final byte oldState= this.stateRequest;
if (oldState != PAUSED_STATE) {
this.stateRequest= PAUSED_STATE;
addStateEvent(DebugEvent.MODEL_SPECIFIC, STATE_REQUEST,
new StateDelta(oldState, PAUSED_STATE));
internal_fireEvents();
notifyAll();
}
return true;
}
public synchronized boolean resume() {
if (this.state == TERMINATED_STATE) {
return false;
}
final byte oldState= this.stateRequest;
if (oldState != PROCESSING_STATE) {
this.stateRequest= PROCESSING_STATE;
addStateEvent(DebugEvent.MODEL_SPECIFIC, STATE_REQUEST,
new StateDelta(oldState, PROCESSING_STATE) );
internal_fireEvents();
notifyAll();
}
return true;
}
void internal_onStatusChanged(final ToolStatus newStatus) {
final byte oldState= this.state;
switch (newStatus) {
case STARTED_IDLING:
case STARTED_SUSPENDED:
this.state= IDLING_STATE;
break;
case STARTED_PAUSED:
this.state= PAUSED_STATE;
break;
case TERMINATED:
this.state= TERMINATED_STATE;
break;
default:
this.state= PROCESSING_STATE;
break;
}
addStateEvent(DebugEvent.CHANGE, DebugEvent.STATE,
new StateDelta(oldState, this.state) );
if (this.stateRequest == PAUSED_STATE && oldState == PAUSED_STATE) {
this.stateRequest= PROCESSING_STATE;
addStateEvent(DebugEvent.MODEL_SPECIFIC, STATE_REQUEST,
new StateDelta(PAUSED_STATE, PROCESSING_STATE) );
}
else if (this.stateRequest == TERMINATED_STATE && this.state == TERMINATED_STATE) {
this.stateRequest= PROCESSING_STATE;
addStateEvent(DebugEvent.MODEL_SPECIFIC, STATE_REQUEST,
new StateDelta(TERMINATED_STATE, PROCESSING_STATE) );
}
}
boolean internal_isPauseRequested() {
return (this.stateRequest == PAUSED_STATE);
}
void internal_dispose() {
checkIOCache();
final byte oldState= this.state;
if (oldState != TERMINATED_STATE) {
this.state= TERMINATED_STATE;
addStateEvent(DebugEvent.CHANGE, DebugEvent.STATE,
new StateDelta(oldState, TERMINATED_STATE) );
}
final ImList<ToolRunnable> finalRunnables;
{ final ToolRunnable[] runnables= new @NonNull ToolRunnable[
internal_getCompleteSize() ];
int i= 0;
for (int iSection= this.sectionStack.size() - 1; iSection >= 1; iSection--) {
final SubQueue removedSection= this.sectionStack.remove(iSection);
i= removedSection.getAll(runnables, i);
removedSection.dispose();
}
{ // iSection == 0
i= this.topLevelSection.getAll(runnables, i);
this.topLevelSection.dispose();
}
this.currentSection= this.topLevelSection;
this.currentSection.next= null;
finalRunnables= ImCollections.newList(runnables);
}
for (final ToolRunnable runnable : finalRunnables) {
runnable.changed(ToolRunnable.BEING_ABANDONED, this.process);
}
addTerminateEvent(new TaskDelta(ToolRunnable.BEING_ABANDONED, -1, finalRunnables));
if (!this.hotList.isEmpty()){
final ImList<ToolRunnable> leftRunnable= ImCollections.toList(this.hotList);
this.hotList.clear();
for (final ToolRunnable runnable : leftRunnable) {
runnable.changed(ToolRunnable.BEING_ABANDONED, this.process);
}
}
if (!this.onIdleList.isEmpty()){
final ImList<RankedItem> leftRunnable= ImCollections.toList(this.onIdleList);
this.onIdleList.clear();
for (final RankedItem item : leftRunnable) {
item.runnable.changed(ToolRunnable.BEING_ABANDONED, this.process);
}
}
internal_fireEvents();
}
private void checkIOCache() {
if (this.singleIOCache != null) {
final int idx= this.currentSection.getAppendIdx();
this.currentSection.append(this.singleIOCache.get(0));
addContentChangeEvent(new TaskDelta(ToolRunnable.ADDING_TO, idx, this.singleIOCache));
this.singleIOCache= null;
}
}
private void addContentChangeEvent(final TaskDelta delta) {
final DebugEvent event= new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT);
event.setData(delta);
this.eventList.add(event);
}
private void addStateEvent(final int kind, final int detail, final StateDelta delta) {
final DebugEvent event= new DebugEvent(this, kind, detail);
event.setData(delta);
this.eventList.add(event);
}
private void addTerminateEvent(final TaskDelta delta) {
final DebugEvent event= new DebugEvent(this, DebugEvent.TERMINATE, DebugEvent.UNSPECIFIED);
event.setData(delta);
this.eventList.add(event);
}
List<DebugEvent> internal_getEventList() {
return this.eventList;
}
void internal_fireEvents() {
if (this.eventList.isEmpty()) {
return;
}
final DebugPlugin manager= DebugPlugin.getDefault();
if (manager != null) {
manager.fireDebugEventSet(this.eventList.toArray(new DebugEvent[this.eventList.size()]));
}
this.eventList.clear();
}
}