StateManager.java
/**
*
*/
package edu.udel.cis.vsl.civl.kripke;
import java.io.PrintStream;
import edu.udel.cis.vsl.civl.err.CIVLExecutionException.Certainty;
import edu.udel.cis.vsl.civl.err.CIVLExecutionException.ErrorKind;
import edu.udel.cis.vsl.civl.err.CIVLInternalException;
import edu.udel.cis.vsl.civl.err.CIVLStateException;
import edu.udel.cis.vsl.civl.err.UnsatisfiablePathConditionException;
import edu.udel.cis.vsl.civl.model.IF.CIVLSource;
import edu.udel.cis.vsl.civl.model.IF.location.Location;
import edu.udel.cis.vsl.civl.model.IF.statement.ChooseStatement;
import edu.udel.cis.vsl.civl.model.IF.statement.Statement;
import edu.udel.cis.vsl.civl.model.common.location.CommonLocation.AtomicKind;
import edu.udel.cis.vsl.civl.model.common.statement.CommonNoopStatement;
import edu.udel.cis.vsl.civl.model.common.statement.StatementList;
import edu.udel.cis.vsl.civl.semantics.Executor;
import edu.udel.cis.vsl.civl.semantics.Executor.StateStatusKind;
import edu.udel.cis.vsl.civl.state.IF.ProcessState;
import edu.udel.cis.vsl.civl.state.IF.State;
import edu.udel.cis.vsl.civl.state.IF.StateFactory;
import edu.udel.cis.vsl.civl.transition.ChooseTransition;
import edu.udel.cis.vsl.civl.transition.SimpleTransition;
import edu.udel.cis.vsl.civl.transition.Transition;
import edu.udel.cis.vsl.civl.util.Pair;
import edu.udel.cis.vsl.civl.util.Printable;
import edu.udel.cis.vsl.gmc.StateManagerIF;
/**
* @author Timothy K. Zirkel (zirkel)
* @author Manchun Zheng (zmanchun)
* @author Stephen F. Siegel (siegel)
*
*/
public class StateManager implements StateManagerIF<State, Transition> {
/* *************************** Instance Fields ************************* */
/**
* The unique executor instance used by the system
*/
private Executor executor;
/**
* The flag to turn on/off printing of debugging information.
*/
private boolean debug = false;
/**
* The maximal number of processes at a state, initialized as 0.
*/
private int maxProcs = 0;
/**
* The output stream to be used in this class to print states, transitions,
* warnings, etc.
*/
private PrintStream out = null;
/**
* Save states during search?
* {@link edu.udel.cis.vsl.civl.run.UserInterface#saveStatesO}
*/
private boolean saveStates = true;
/**
* Print saved states (i.e., canonicalized states)?
* {@link edu.udel.cis.vsl.civl.run.UserInterface#showSavedStatesO}
*/
private boolean showSavedStates = false;
/**
* Print all states (including states that are not saved)?
* {@link edu.udel.cis.vsl.civl.run.UserInterface#showStatesO}
*/
private boolean showStates = false;
/**
* Print transitions?
* {@link edu.udel.cis.vsl.civl.run.UserInterface#showTransitionsO}
*/
private boolean showTransitions = false;
/**
* Simplify state returned by nextState?
* {@link edu.udel.cis.vsl.civl.run.UserInterface#simplifyO}
*/
private boolean simplify = true;
/**
* The unique state factory used by the system.
*/
private StateFactory stateFactory;
/**
* Turn on/off verbose mode.
* {@link edu.udel.cis.vsl.civl.run.UserInterface#verboseO}
*/
private boolean verbose = false;
/**
* The object whose toString() method will be used to print the periodic
* update. The toString method of this object should print a short
* (one-line) message on the state of the search.
*/
private Printable updater;
/**
* If true, print a short one-line update message on the state of the search
* at the next opportunity, and then set this flag back to false. This flag
* is typically set by a separate thread. Access to this thread is protected
* by the lock on this StateManager.
*/
private boolean printUpdate = false;
/**
* Number of calls to method {@link #nextState(State, Transition)}
*/
private int nextStateCalls = 0;
/**
* Keep track of the maximal canonic ID of states. Since
* {@link StateFactory#canonic(State)} is only called when savedState option
* is enabled, this is only updated when savedState option is enabled. The
* motivation to have this field is to allow the state manager to print only
* new states in -savedStates mode, for better user experiences.
*/
private int maxCanonicId = -1;
/* ***************************** Constructor *************************** */
/**
*
* @param executor
* The unique executor to by used in the system.
*/
public StateManager(Executor executor) {
this.executor = executor;
this.stateFactory = executor.stateFactory();
}
/* *************************** Private Methods ************************* */
/**
* Executes an $atom block, supporting nested atom blocks. It requires that
* the whole block is finite, non-blocking and deterministic. Otherwise, a
* warning or an error will be reported.
*
* Precondition:
* <code> location.enterAtom() == true && location == state.getProcessState(pid).getLocation()</code>
*
* @param state
* The current state
* @param pid
* The id of the process being executing
* @param location
* The start location of the atomic block
* @param print
* True iff each step is to be printed.
* @return The resulting state after executing the $atom block
*/
private State executeAtomBlock(State state, int pid, Location location,
boolean print) {
ProcessState p;
CIVLSource atomicStart = location.getSource();
Location newLocation = location;
State newState = state;
int stateCounter = 0;
int atomCount = 0;
while (true) {
boolean statementExecuted = false;
State currentState = newState;
Statement executedStatement = null;
Pair<StateStatusKind, State> temp;
for (Statement s : newLocation.outgoing()) {
temp = executor.executeStatement(currentState, newLocation, s,
pid);
switch (temp.left) {
case NONDETERMINISTIC:
reportError(StateStatusKind.NONDETERMINISTIC, newState,
newLocation);
break;
case NORMAL:
if (statementExecuted) {
reportError(StateStatusKind.NONDETERMINISTIC, newState,
newLocation);
break;
}
statementExecuted = true;
newState = temp.right;
executedStatement = s;
break;
default:// blocked, continue to try executing another
// statement from the same location
continue;
}
}
// current location is blocked
if (!statementExecuted) {
reportError(StateStatusKind.BLOCKED, currentState, newLocation);
}
switch (newLocation.atomicKind()) {
case ATOM_ENTER:
atomCount++;
break;
case ATOM_EXIT:
atomCount--;
default:
}
if (atomCount == 0)// end of the $atom block
return newState;
// warning for possible infinite $atom block
if (stateCounter != 0 && stateCounter % 1024 == 0) {
out.println("Warning: " + (stateCounter)
+ " states in $atom block at "
+ atomicStart.getLocation() + ".");
}
stateCounter++;
p = newState.getProcessState(pid);
if (print && executedStatement != null) {
printStatement(executedStatement, newLocation.atomicKind(),
p.atomicCount(), false);
}
if (p != null && !p.hasEmptyStack())
newLocation = p.getLocation();
else {
throw new CIVLInternalException("Unreachable",
newLocation.getSource());
}
}
}
/**
* Execute the enabled statements from an ATOMIC_ENTER location of an
* $atomic block. When the process is already in atomic execution, i.e.,
* <code>p.inAtomic() == true</code>, then the atomic lock variable assign
* statement <code>$ATOMIC_LOCK_VAR = $self</code> is ignored.
*
* @param pLocation
* The location to work with.
* @param state
* The current state.
* @param pid
* The ID of the current executing process.
* @return A pair of the executed statement and the resulting state.
*/
private Pair<Statement, State> executeAtomicEnter(Location pLocation,
State state, int pid) {
State newState = state;
ProcessState p = state.getProcessState(pid);
Statement executedStatement;
assert !stateFactory.lockedByAtomic(newState)
|| stateFactory.processInAtomic(newState) == pid;
executedStatement = pLocation.getOutgoing(0);
if (!p.inAtomic()) {
newState = executor.executeStatement(newState, pLocation,
executedStatement, pid).right;
} else {
newState = executor.transition(newState,
newState.getProcessState(pid), executedStatement.target());
}
p = newState.getProcessState(pid).incrementAtomicCount();
newState = stateFactory.setProcessState(newState, p, pid);
return new Pair<Statement, State>(executedStatement, newState);
}
/**
* Execute the enabled statements from an ATOMIC_EXIT location of an $atomic
* block. When the process already finishes all active atomic execution,
* i.e., <code>p.inAtomic() == false</code>, then the atomic lock variable
* assign statement <code>$ATOMIC_LOCK_VAR = process<-1></code> will be
* executed; otherwise, it is merely ignored.
*
* @param pLocation
* The location to work with.
* @param state
* The current state.
* @param pid
* The ID of the current executing process.
* @param print
* True iff each step is to be printed.
* @return A pair of the executed statement and the resulting state.
*/
private Pair<Statement, State> executeAtomicExit(Location pLocation,
State state, int pid, boolean print) {
State newState = state;
ProcessState p;
Statement executedStatement;
assert stateFactory.processInAtomic(newState) == pid;
p = newState.getProcessState(pid).decrementAtomicCount();
newState = stateFactory.setProcessState(newState, p, pid);
executedStatement = pLocation.getOutgoing(0);
if (!p.inAtomic()) {
newState = executor.executeStatement(newState, pLocation,
pLocation.getOutgoing(0), pid).right;
if (print) {
printStatement(executedStatement, AtomicKind.ATOMIC_EXIT,
p.atomicCount(), true);
}
} else
newState = executor.transition(newState,
newState.getProcessState(pid), executedStatement.target());
return new Pair<Statement, State>(executedStatement, newState);
}
/**
* Execute the enabled statements from a normal location in an $atomic
* block. The result might be:
* <ol>
* <li>a sudo noop statement and the original state, when the location is
* non-deterministic;</li>
* <li>the unique statement that is enabled and the resulting state, when
* the location is deterministic and non-blocked; or</li>
* <li>NULL, when the location is blocked.</li>
* </ol>
*
* @param pLocation
* The location to work on.
* @param state
* The current state.
* @param pid
* The ID of the currently working process.
* @return A pair of the executed statement and the resulting state.
*/
private Pair<Statement, State> executeAtomicNormal(Location pLocation,
State state, int pid) {
State newState = state;
Statement executedStatement = null;
State oldState = newState;
boolean executed = false;
for (Statement s : pLocation.outgoing()) {
Pair<StateStatusKind, State> temp = executor.executeStatement(
oldState, pLocation, s, pid);
switch (temp.left) {
case NONDETERMINISTIC:
// finds non-determinism, go back to previous state
return new Pair<Statement, State>(new CommonNoopStatement(),
oldState);
case NORMAL:
if (executed) {
// finds non-determinism, go back to previous state
return new Pair<Statement, State>(
new CommonNoopStatement(), oldState);
}
executed = true;
newState = temp.right;
executedStatement = s;
break;
default:// BLOCKED, continue to try executing next statement
continue;
}
}
if (executedStatement != null)
return new Pair<Statement, State>(executedStatement, newState);
return null;
}
/**
* Execute a sequence of purely local statements or statements defined in an
* $atomic block of a certain process
*
* @param state
* The state to start with
* @param pid
* id of the executing process
* @param location
* The start location of the execution
* @param atomic
* True iff executing statements in an atomic block; false iff
* executing purely-local statements in non-atomic context.
* @param print
* True iff each step is to be printed.
* @return The resulting state
*/
private State executeAtomicOrPurelyLocalStatements(State state, int pid,
Location location, boolean atomic, boolean print) {
Location pLocation = location;
ProcessState p = state.getProcessState(pid);
State newState = state;
Statement executedStatement = null;
boolean atomicLockVarChanged = false;
Pair<Statement, State> oneStep;
State oldState = null;
boolean stepExecuted = false;
assert atomic || pLocation.isPurelyLocal();
while ((!atomic && pLocation != null && pLocation.isPurelyLocal())
|| (atomic && pLocation != null)) {
if (pLocation.isLoopPossible()) {
return newState;
}
atomicLockVarChanged = false;
oneStep = null;
stepExecuted = true;
switch (pLocation.atomicKind()) {
case NONE:
oldState = newState;
oneStep = executeAtomicNormal(pLocation, newState, pid);
break;
case ATOM_ENTER:
newState = executeAtomBlock(newState, pid, pLocation, print);
stepExecuted = false;
break;
case ATOMIC_ENTER:
if (atomic) {
if (!p.inAtomic())
atomicLockVarChanged = true;
oneStep = executeAtomicEnter(pLocation, newState, pid);
} else {
newState = executeAtomicOrPurelyLocalStatements(newState,
pid, pLocation, true, print);
stepExecuted = false;
}
break;
case ATOMIC_EXIT:
if (!atomic)
throw new CIVLInternalException("Unreachable",
pLocation.getSource());
oneStep = executeAtomicExit(pLocation, newState, pid, print);
break;
default:
throw new CIVLInternalException("Unreachable",
pLocation.getSource());
}
if (oneStep == null && stepExecuted) {
// location is blocked
if (atomic)
oldState = stateFactory.releaseAtomicLock(oldState);
if (print) {
out.println(" " + pLocation.id()
+ ": ($ATOMIC_LOCK_VAR = process<-1>) at "
+ pLocation.getSource().getSummary() + ";");
}
return oldState;
} else if (oneStep != null) {
executedStatement = oneStep.left;
newState = oneStep.right;
// non-determinism
if (newState == oldState)
return oldState;
if (atomic) {
if (!newState.getProcessState(pid).inAtomic())
return newState;
}
}
p = newState.getProcessState(pid);
if (p != null && print && stepExecuted) {
printStatement(executedStatement, pLocation.atomicKind(),
p.atomicCount(), atomicLockVarChanged);
} else if (print && stepExecuted) {
printStatement(executedStatement, pLocation.atomicKind(), 0,
atomicLockVarChanged);
}
if (p != null && !p.hasEmptyStack())
pLocation = p.peekStack().location();
else
pLocation = null;
}
return newState;
}
/**
* Execute a transition (obtained by the enabler) of a state. When the
* corresponding process is in atomic/atom execution, continue to execute
* more statements as many as possible. Also execute more purely local
* statements if possible.
*
* @param state
* The current state
* @param transition
* The transition to be executed.
* @return the resulting state after execute
* @throws UnsatisfiablePathConditionException
*/
private State nextStateWork(State state, Transition transition)
throws UnsatisfiablePathConditionException {
int pid;
Statement statement;
int numProcs;
ProcessState p;
Location currentLocation;
boolean printTransitions = verbose || debug || showTransitions;
int oldMaxCanonicId = this.maxCanonicId;
assert transition instanceof SimpleTransition;
pid = ((SimpleTransition) transition).pid();
p = state.getProcessState(pid);
currentLocation = p.getLocation();
switch (currentLocation.atomicKind()) {
case ATOMIC_ENTER:
printTransitionPrefix(printTransitions, state, pid);
state = executeAtomicOrPurelyLocalStatements(state, pid,
currentLocation, true, printTransitions);
break;
case ATOMIC_EXIT:
printTransitionPrefix(printTransitions, state, pid);
state = executeAtomicOrPurelyLocalStatements(state, pid,
currentLocation, true, printTransitions);
break;
case ATOM_ENTER:
printTransitionPrefix(printTransitions, state, pid);
state = executeAtomBlock(state, pid, currentLocation,
printTransitions);
break;
case ATOM_EXIT:
throw new CIVLInternalException("Unreachable",
currentLocation.getSource());
default:// execute a normal transition
if (printTransitions) {
out.println();
out.print(state + ", ");
printTransitionLong(out, transition);
out.println(";");
}
state = state.setPathCondition(((SimpleTransition) transition)
.pathCondition());
statement = ((SimpleTransition) transition).statement();
if (transition instanceof ChooseTransition) {
if (statement instanceof StatementList) {
state = executor.executeStatementList(state, pid,
(StatementList) statement,
((ChooseTransition) transition).value());
} else {
assert statement instanceof ChooseStatement;
state = executor.executeChoose(state, pid,
(ChooseStatement) statement,
((ChooseTransition) transition).value());
}
} else {
state = executor.execute(state, pid, statement);
}
// sometimes the execution might allow the process to grab the
// atomic lock
if (executor.stateFactory().lockedByAtomic(state)) {
currentLocation = state.getProcessState(pid).getLocation();
state = executeAtomicOrPurelyLocalStatements(state, pid,
currentLocation, true, printTransitions);
}
}
// do nothing when process pid terminates and is removed from the state
if (!stateFactory.lockedByAtomic(state) && state.numProcs() > pid) {
p = state.getProcessState(pid);
if (p != null && !p.hasEmptyStack()) {
Location newLocation = p.peekStack().location();
// execute purely local statements of the current process
// greedily
if (newLocation != null && newLocation.isPurelyLocal()) {
state = executeAtomicOrPurelyLocalStatements(state, pid,
newLocation, false, printTransitions);
}
}
}
if (printTransitions) {
out.print("--> ");
}
if (saveStates) {
state = stateFactory.canonic(state);
this.maxCanonicId = state.getCanonicId();
} else {
state = stateFactory.collectProcesses(state);
state = stateFactory.collectScopes(state);
state.commit();
}
if (verbose || debug || showTransitions) {
out.println(state);
}
if (debug
|| verbose
|| (!saveStates && showStates)
|| (saveStates && showStates && this.maxCanonicId > oldMaxCanonicId)
|| (saveStates && showSavedStates && this.maxCanonicId > oldMaxCanonicId)) {
// in -savedStates mode, only print new states.
out.println();
state.print(out);
}
numProcs = state.numProcs();
if (numProcs > maxProcs)
maxProcs = numProcs;
return state;
}
/**
* Print a step of a statement, in the following form:
* <code>src->dst: statement at file:location text;</code>For example,<br>
* <code>32->17: sum = (sum+(3*i)) at f0:20.14-24 "sum += 3*i";</code><br>
* When the atomic lock variable is changed during executing the statement,
* then the corresponding information is printed as well. For example,<br>
* <code>13->6: ($ATOMIC_LOCK_VAR = $self) x = 0 at f0:30.17-22 "x = 0";</code>
*
* @param s
* The statement that has been executed in the current step.
* @param atomicKind
* The atomic kind of the source location of the statement.
* @param atomCount
* The atomic/atom count of the process that the statement
* belongs to.
* @param atomicLockVarChanged
* True iff the atomic lock variable is changed during the
* execution of the statement.
*/
private void printStatement(Statement s, AtomicKind atomicKind,
int atomCount, boolean atomicLockVarChanged) {
CIVLSource statementSource = s.getSource();
if (statementSource == null)
statementSource = s.source().getSource();
out.print(" " + s.source().id() + "->");
if (s.target() != null)
out.print(s.target().id() + ": ");
else
out.print("RET: ");
switch (atomicKind) {
case ATOMIC_ENTER:
if (atomicLockVarChanged) {
out.print(s.toString() + " ");
} else
out.print("ENTER_ATOMIC (atomicCount++) ");
out.print(atomCount - 1);
break;
case ATOMIC_EXIT:
if (atomicLockVarChanged) {
out.print(s.toString() + " ");
} else
out.print("LEAVE_ATOMIC (atomicCount--) ");
out.print(atomCount);
break;
case ATOM_ENTER:
out.print(s.toString() + " ");
out.print(atomCount - 1);
break;
case ATOM_EXIT:
out.print(s.toString() + " ");
out.print(atomCount);
break;
default:
out.print(s.toString());
}
if (statementSource != null)
out.println(" at " + statementSource.getSummary() + ";");
}
/**
* Print the prefix of a transition.
*
* @param printTransitions
* True iff each step is to be printed.
* @param state
* The source state of the transition.
* @param pid
* The ID of the process that this transition associates with.
*/
private void printTransitionPrefix(boolean printTransitions, State state,
int pid) {
if (printTransitions) {
out.println();
out.print(state + ", proc ");
out.println(pid + ":");
}
}
/**
* Print the updated status.
*/
private void printUpdateWork() {
updater.print(out);
out.flush();
}
/**
* Report error message for $atom block execution, when
* <ol>
* <li>non-determinism is detected, or</li>
* <li>a blocked location is encountered.</li>
* </ol>
*
* @param kind
* The status kind of the error.
* @param state
* The state that the error occurs.
* @param location
* The location that the error occurs.
*/
private void reportError(StateStatusKind kind, State state,
Location location) {
switch (kind) {
case NONDETERMINISTIC:
executor.evaluator().reportError(
new CIVLStateException(ErrorKind.OTHER, Certainty.CONCRETE,
"Non-determinism is encountered in $atom block.",
state, location.getSource()));
break;
case BLOCKED:
executor.evaluator().reportError(
new CIVLStateException(ErrorKind.OTHER, Certainty.CONCRETE,
"Blocked location is encountered in $atom block.",
state, location.getSource()));
break;
default:
}
}
/*********************** Methods from StateManagerIF *********************/
@Override
public int getDepth(State state) {
return state.getDepth();
}
@Override
public State nextState(State state, Transition transition) {
nextStateCalls++;
if (nextStateCalls % 100 == 0) {
synchronized (this) {
if (printUpdate) {
printUpdateWork();
printUpdate = false;
}
}
}
try {
return nextStateWork(state, transition);
} catch (UnsatisfiablePathConditionException e) {
// problem is the interface requires an actual State
// be returned. There is no concept of executing a
// transition and getting null or an exception.
// since the error has been logged, just stutter:
return state;
}
}
@Override
public boolean onStack(State state) {
return state.onStack();
}
@Override
public void printAllStatesLong(PrintStream arg0) {
}
@Override
public void printAllStatesShort(PrintStream arg0) {
}
@Override
public void printStateLong(PrintStream out, State state) {
state.print(out);
}
@Override
public void printStateShort(PrintStream out, State state) {
out.print(state.toString());
}
@Override
public void printTransitionLong(PrintStream out, Transition transition) {
out.print(transition.toString());
}
@Override
public void printTransitionShort(PrintStream out, Transition transition) {
out.print(transition.toString());
}
@Override
public boolean seen(State state) {
return state.seen();
}
@Override
public void setDepth(State state, int value) {
state.setDepth(value);
}
@Override
public void setOnStack(State state, boolean value) {
state.setOnStack(value);
}
@Override
public void setSeen(State state, boolean value) {
state.setSeen(value);
}
/************************** Other Public Methods *************************/
/**
*
* @return the debugging option, true if under debug mode, otherwise false.
*/
public boolean getDebug() {
return debug;
}
/**
* Returns the number of objects of type State that have been instantiated
* since this JVM started.
*
* @return the number of states instantiated
*/
public long getNumStateInstances() {
return stateFactory.getNumStateInstances();
}
/**
* Returns the number of states saved, i.e., made canonic.
*
* @return the number of canonic states
*/
public int getNumStatesSaved() {
return stateFactory.getNumStatesSaved();
}
/**
* The whole system should be using the same print stream to print
* information in different components.
*
* @return the output stream used by the state manager
*/
public PrintStream getOutputStream() {
return out;
}
/**
* -saveStates is always true in depth first search.
*
* @return the value of the option -saveStates
*/
public boolean getSaveStates() {
return saveStates;
}
/**
* -showSavedStates is false by default
*
* @return the value of the option -showSavedStates
*/
public boolean getShowSavedStates() {
return showSavedStates;
}
/**
* -showStates is false by default
*
* @return the value of the option -showStates
*/
public boolean getShowStates() {
return showStates;
}
/**
* -showTransitions is false by default
*
* @return the value of the option -showTransitions
*/
public boolean getShowTransitions() {
return showTransitions;
}
/**
* -simplify is true by default
*
* @return the value of the option -simplify
*/
public boolean getSimplify() {
return simplify;
}
/**
* The updater, see also {@link #updater}.
*
* @return the updater.
*/
public Printable getUpdater() {
return updater;
}
/**
* -verbose is false by default
*
* @return the value of the option -verbose
*/
public boolean getVerbose() {
return verbose;
}
/**
* @return The maximum number of processes in any state encountered by this
* state manager.
*/
public int maxProcs() {
return maxProcs;
}
/**
* Set the field debug.
*
* @param value
* The value to be used.
*/
public void setDebug(boolean value) {
this.debug = value;
}
/**
* Set the field savedStates.
*
* @param value
* The value to be used.
*/
public void setSaveStates(boolean value) {
this.saveStates = value;
}
/**
* Set the field showSavedStates.
*
* @param value
* The value to be used.
*/
public void setShowSavedStates(boolean value) {
this.showSavedStates = value;
}
/**
* Set the field showStates.
*
* @param value
* The value to be used.
*/
public void setShowStates(boolean value) {
this.showStates = value;
}
/**
* Set the field showTransitions.
*
* @param value
* The value to be used.
*/
public void setShowTransitions(boolean value) {
this.showTransitions = value;
}
/**
* Set the field simplify.
*
* @param value
* The value to be used.
*/
public void setSimplify(boolean value) {
simplify = value;
}
/**
* Set the field savedStates.
*
* @param updater
* The value to be used.
*/
public void setUpdater(Printable updater) {
this.updater = updater;
}
/**
* Set the field out.
*
* @param out
* The output stream to be used.
*/
public void setOutputStream(PrintStream out) {
this.out = out;
}
/**
* Set the field verbose.
*
* @param value
* The value to be used.
*/
public void setVerbose(boolean value) {
this.verbose = value;
}
/**
* Print an update message at your earliest possible convenience.
*/
public synchronized void printUpdate() {
printUpdate = true;
}
}