root/usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/Poold.java
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 * ident        "%Z%%M% %I%     %E% SMI"
 */

package com.sun.solaris.domain.pools;

import java.io.*;
import java.util.*;
import java.util.logging.*;
import java.text.DecimalFormat;
import java.util.concurrent.atomic.*;

import com.sun.solaris.service.locality.*;
import com.sun.solaris.service.logging.*;
import com.sun.solaris.service.pools.*;
import com.sun.solaris.service.exception.*;

/*
 * poold overview
 * ----- --------
 *
 * poold manipulates system resources in accordance with administrator
 * specified constraints and objectives. The "goal" of the application
 * is to maximise the efficiency of available resources within these
 * parameters.
 *
 * Constraints are specified as follows:
 *
 * On a resource set:
 *
 *      - min Is the minimum amount of resource a set should
 *      receive. poold will never elect to move resource so that a set
 *      falls below its minimum value. It is possible for a set to
 *      fall below its minimum as a consequence of administrative
 *      intervention, in which case poold will endeavour to bring a
 *      set back to its minimum level at the earliest opportunity.
 *
 *      - max Is the maximum amount of resource a set should
 *      recieve. poold will never elect to move resource so that a set
 *      rises above its maximum value. It is possible for a set to
 *      rise above its maximum as a consequence of administrative
 *      intervention, in which case poold will endeavour to bring a
 *      set back to its maximum level at the earliest opportunity.
 *
 * On a resource component:
 *
 *      - cpu.pinned Is an indication that a CPU should be ignored by
 *      poold for purposes of reallocation. A pinned CPU will never be
 *      moved by poold from one set to another.
 *
 * In addition to constraints, an administrator may also specify
 * objectives. Currently three types of objectives are supported:
 *
 *      - system.wt-load Is an objective set across an entire
 *      configuration. It attempts to ensure that resource is shared
 *      in accordance with current consumption. Those partitions which
 *      are most heavily utilized are give more resource in an attempt
 *      to lower their utilization levels.
 *
 *      - set.locality Is a locality objective which attempts to
 *      minimize or maximize resource locality for a set.
 *
 *      - set.utilization Is a utilization based objective which an
 *      administrator may use to explicitly dictate the utilization
 *      levels which should be achieved for a set.
 *
 * When executing, poold uses information about the current pools
 * configuration; monitored resource utilization and specified
 * constraints and objectives to determine if a move of resources is
 * likely to lead to an increase in objective satisfaction.
 *
 * Candidate moves are generated by observing resource constraints.
 * These moves are evaluated and scored in terms of their contribution
 * to objective satisfaction (note: objectives are weighted according
 * to their importance) and ranked accordingly. If a move can be
 * identified that delivers an improvement, the move is made. Data is
 * collected about past moves and recorded as "Decision History",
 * before the move is made this data is consulted and if the move is
 * expected not to yield an improvement, it may be cancelled. This
 * refinement is designed to improve the quality of decision making by
 * reflecting upon the past performance of decisions as well as the
 * contribution to objective satisfaction.
 *
 * poold structure
 * ----- ---------
 *
 * The common package name begins with:
 *
 * com.sun.solaris
 *
 * The software is divided into two main functional areas:
 *
 *      service
 *
 *      These packages collaborate to provide services to poold
 *      (typically, they use JNI to access exising
 *      functionality). They are not designed to be extended. For more
 *      details on these classes examine the source files in each
 *      directory.
 *
 *      exception       Stack trace formatter
 *      kstat           Interface to Solaris kstat facility
 *      locality        Interface to Solaris lgrp facility
 *      logging         Interface to Solaris syslog facility
 *      pools           Interface to Solaris libpool facility
 *      timer           High resolution timestamps
 *
 *      domain:
 *
 *      These package reflect problem domain concepts and are
 *      responsible for application logic related to these
 *      concepts.
 *
 *      pools           Dynamic Resource Pools specific functionality.
 *
 * This code block will continue to explain in more detail how poold
 * is organized.
 *
 * poold provides the following basic facilities:
 *
 * Monitoring:
 *
 * Basic statistic access is provided by the
 * com.sun.solaris.service.kstat package. The following interfaces and
 * classes collaborate to provide higher level statistic and
 * monitoring facilities to the application:
 *
 *      INTERFACES
 *
 *      AggregateStatistic
 *      Monitor
 *      Statistic
 *      StatisticListener
 *
 *      CLASSES
 *
 *      AbstractStatistic
 *      DoubleStatistic
 *      LongStatistic
 *      ResourceMonitor
 *      StatisticEvent
 *      StatisticList
 *      StatisticOperations
 *      SystemMonitor
 *      UnsignedInt64Statistic
 *
 * Logging:
 *
 * Logging services are provided by the com.sun.solaris.logging
 * package. In addition, the following class implements Poold's
 * specific logging requirements.
 *
 *      CLASSES
 *
 *      Poold
 *
 * Optimization:
 *
 * lgrp service are provided by the com.sun.solaris.service.lgrp
 * package. pools services are provided by the
 * com.sun.solaris.service.pools package. In addition, optimization is
 * implemented in the following Interfaces and Classes:
 *
 *      INTERFACES
 *
 *      Objective
 *      Solver
 *      WorkloadDependentObjective
 *
 *      CLASSES
 *
 *      AbstractObjective
 *      ComponentMove
 *      DecisionHistory
 *      Expression
 *      KExpression
 *      KVEpression
 *      KVOpExpression
 *      LocalityObjective
 *      Move
 *      Poold
 *      QuantityMove
 *      SystemSolver
 *      UtilizationObjective
 *      WeightedLoadObjective
 *
 * Configuration:
 *
 * pools services are provided by the com.sun.solaris.service.pools
 * package, this is used to read poold configuration details from a
 * libpool configuration. In addition, configuration is implemented in
 * the following Classes:
 *
 *      CLASSES
 *
 *      AbstractObjective
 *      Expression
 *      Poold
 *      SystemSolver
 *
 * (NB: Some classes were mentioned in multiple categories where there
 * responsbilities overlap. Inner classes are not listed as their
 * responsibilities can be clearly inferred from their context.)
 *
 * For more details on any of the packages, classes or interfaces
 * mentioned above, look at the documentation associated with each
 * class.
 */

/**
 * The <code>Poold</code> class implements a dynamic resource
 * allocation system for Solaris.
 *
 * <code>Poold</code> is a monitoring daemon, designed to evaluate
 * user specified objectives, monitor workload behaviour and
 * dynamically assign resources in order to satisfy the evaluated
 * objectives. For more details see:
 *
 * <a href="http://sac.eng.sun.com/PSARC/2002/287/">PSARC/2002/287</a>
 */
final class Poold
{
        /**
         * The configuration which is manipulated.
         */
        private Configuration conf;

        /**
         * The monitoring interface.
         */
        private Monitor monitor;

        /**
         * The interface to higher level resource managers.
         */
        private DRM drm;

        /**
         * The interface to the configuration solver.
         */
        private Solver solver;

        /**
         * Default path to the logging properties file.
         */
        public static final String POOLD_PROPERTIES_PATH =
            "/usr/lib/pool/poold.properties";

        /**
         * Logger for records which aren't produced in the Monitoring,
         * Configuration, or Optimization states.  This logger is the
         * parent to the loggers used in those states.
         */
        public static final Logger BASE_LOG = Logger.getLogger(
            "com.sun.solaris.domain.pools.poold");

        /**
         * Logger for records produced in the Configuration state.
         */
        public static final Logger CONF_LOG = Logger.getLogger(
            "com.sun.solaris.domain.pools.poold.Configuration");

        /**
         * Logger for records produced in the Monitoring state.
         */
        public static final Logger MON_LOG = Logger.getLogger(
            "com.sun.solaris.domain.pools.poold.Monitoring");

        /**
         * Logger for records produced in the Optimization state.
         */
        public static final Logger OPT_LOG = Logger.getLogger(
            "com.sun.solaris.domain.pools.poold.Optimization");

        /**
         * Singleton instance of Poold.
         */
        private static Poold instance;

        /**
         * The main sampling and solving thread.
         */
        private Thread mainThread;

        /**
         * Process exit code indicating a failure.
         */
        private static final int E_PO_FAILURE = 2;

        /**
         * Keep track of whether initialize() has been invoked, to
         * output the "starting" message on the first.
         */
        private AtomicBoolean firstInitialization = new AtomicBoolean(true);

        /**
         * Flags whether poold should run or exit.
         */
        private AtomicBoolean shouldRun = new AtomicBoolean(true);

        private static class logHelper {
                /**
                 * Default logfile location
                 */
                public static final String DEF_LOG_LOC = "/var/log/pool/poold";

                /**
                 * Log location indicating <code>syslog</code>, as
                 * opposed to a file, should be used.
                 */
                public static final String SYSLOG_LOG_LOC = "SYSLOG";

                /**
                 * Default Log severity (if not overridden)
                 */
                public static final Severity DEF_SEVERITY = Severity.INFO;

                /**
                 * Name of configuration property, log location.
                 */
                public static final String PROPERTY_NAME_LOG_LOC =
                    "system.poold.log-location";

                /**
                 * Name of configuration property, log level.
                 */
                public static final String PROPERTY_NAME_LOG_LEVEL =
                    "system.poold.log-level";

                /**
                 * Location of logfile -- an absolute filename, or
                 * "SYSLOG".
                 */
                private static String location;

                /**
                 * Logfile handler, responsible for taking log messages
                 * and exporting them.
                 */
                private static Handler handler;

                /**
                 * Logfile severity, log messages below this severity are
                 * ignored.
                 */
                private static Severity severity;

                /**
                 * Flag recording whether preinitialization has occurred.
                 */
                private static boolean preinitialized = false;

                /**
                 * Flag recording whether the logging Severity has been
                 * overridden with the -l command-line option, which
                 * means the console is the only thing being logged to,
                 * and the configuration's logging properties are
                 * ignored.
                 */
                private static boolean usingConsole;

                /**
                 * Indicates whether logging semantics should be changed
                 * to facilitate debugging.
                 */
                private static final boolean loggingDebugging = false;

                /**
                 * Do the pre-initialization initialization:  install
                 * loggers for reporting errors during initialization.
                 *
                 * @param consoleSeverity If non-null, indicates that
                 * the configuration property-controlled logging behavior
                 * is to be overridden (the <code>-l</code> option was
                 * specified), and messages are to be logged only to the
                 * console, with (at most) the given maximum severity.
                 */
                private static void preinitialize(Severity consoleSeverity) {
                        if (preinitialized)
                                return;

                        /*
                         * Read logging properties affecting the
                         * FileHandler and ConsoleHandler from
                         * <code>poold.properties</code>.
                         */
                        Properties props = new Properties();
                        ByteArrayOutputStream bos = new ByteArrayOutputStream();
                        try {
                                props.load(
                                    new FileInputStream(POOLD_PROPERTIES_PATH));
                                props.store(bos, "");
                                LogManager.getLogManager().readConfiguration(
                                    new ByteArrayInputStream(
                                    bos.toByteArray()));
                        } catch (IOException ioe) {
                                Poold.MON_LOG.log(Severity.WARNING, "could not "
                                    + "read logging properties from "
                                    + "poold.properties: " + ioe);
                        }

                        if (consoleSeverity == null || loggingDebugging) {
                                /*
                                 * Log messages to /var/log/pool/poold,
                                 * the default location, until the pools
                                 * configuration properties are read and
                                 * applied, which may change the logging
                                 * file and severity.
                                 *
                                 * Under normal circumstances, it's
                                 * expected that NO INFO-level messages
                                 * will be emitted until that time; this
                                 * is only a measure to ensure that
                                 * unanticipated errors are reported in
                                 * some log.
                                 */
                                location = SYSLOG_LOG_LOC;
                                handler = SyslogHandler.getInstance("poold",
                                    Facility.DAEMON);
                                severity = DEF_SEVERITY;
                                handler.setLevel(severity);
                                BASE_LOG.addHandler(handler);
                        }

                        if (consoleSeverity != null) {
                                /*
                                 * If -l is specified, log to the
                                 * console.  Unless loggingDebug is
                                 * true, this will also mean that the
                                 * logging properties are ignored.
                                 *
                                 * Determine if the user has specified
                                 * the use of a ConsoleHandler through
                                 * poold.properties.
                                 */
                                Logger root = Logger.getLogger("");
                                Handler[] handler = root.getHandlers();
                                ConsoleHandler ch = null;
                                for (int i = 0; i < handler.length && ch ==
                                    null; i++)
                                        if (handler[i]
                                            instanceof ConsoleHandler)
                                                ch = (ConsoleHandler)handler[i];
                                /*
                                 * If none was previously, install a
                                 * ConsoleHandler.
                                 */
                                if (ch == null) {
                                        ch = new ConsoleHandler();

                                        ch.setFormatter(
                                            new SysloglikeFormatter());
                                        ch.setLevel(consoleSeverity);
                                        root.addHandler(ch);
                                }
                                severity = consoleSeverity;
                                BASE_LOG.log(Severity.DEBUG,
                                    "logging with level " + severity);

                                /**
                                 * Allow logging properties to be
                                 * effective if loggingDebugging is not
                                 * set.
                                 */
                                if (!loggingDebugging)
                                        usingConsole = true;
                        }
                        preinitialized = true;
                }

                /**
                 * Configure loggers based on the logging-related
                 * configuration properties.  Outputs a description of
                 * any changes to the configuration logger.
                 *
                 * @throws ConfigurationException if there is an error
                 * applying libpool configuration properties to
                 * <code>poold</code>
                 */
                public static void initializeWithConfiguration(
                    Configuration conf) throws ConfigurationException
                {
                        String newLogLocation;
                        Severity newLogSeverity;
                        String newLogSeverityName = null;

                        /*
                         * Set the log location as specified by the
                         * configuration's system properties.
                         */
                        try {
                                newLogLocation = conf.getStringProperty(
                                    PROPERTY_NAME_LOG_LOC);
                        } catch (PoolsException e) {
                                newLogLocation = DEF_LOG_LOC;
                        }

                        try {
                                newLogSeverityName = conf.getStringProperty(
                                    PROPERTY_NAME_LOG_LEVEL);
                                newLogSeverity = Severity.getSeverityWithName(
                                    newLogSeverityName);
                                assert(newLogSeverity != null);
                        } catch (PoolsException e) {
                                newLogSeverity = DEF_SEVERITY;
                        } catch (IllegalArgumentException e) {
                                throw(ConfigurationException)
                                    (new ConfigurationException(
                                    "invalid " + PROPERTY_NAME_LOG_LEVEL +
                                    "value: " + newLogSeverityName)
                                    .initCause(e));
                        }

                        Handler newLogHandler = null;

                        /*
                         * (Re)install the logger for the poold class
                         * hierarchy.  This means that only poold
                         * messages are controlled by the pools
                         * configuration properties/command-line
                         * options.
                         */

                        /*
                         * The logfile is always re-opened, in case the
                         * cause for reinitialization is due to SIGHUP
                         * following a log rotation.
                         */
                        if (handler != null) {
                                BASE_LOG.removeHandler(handler);
                                handler.close();
                                handler = null;
                        }

                        if (newLogLocation.toUpperCase().equals(
                            SYSLOG_LOG_LOC.toUpperCase()))
                                newLogHandler =
                                    SyslogHandler.getInstance("poold",
                                    Facility.DAEMON);
                        else {
                                if (!newLogLocation.startsWith("/"))
                                        throw
                                            new ConfigurationException(
                                                PROPERTY_NAME_LOG_LOC +
                                                " value is not an" +
                                                " absolute path");
                                try {
                                        newLogHandler =
                                            new FileHandler(
                                            newLogLocation, 0, 1, true);
                                        newLogHandler.setFormatter(
                                            new SysloglikeFormatter());
                                } catch (java.io.IOException ioe) {
                                        Poold.utility.die(
                                            Poold.CONF_LOG,
                                            new PooldException(
                                            newLogLocation +
                                            ": can't write")
                                            .initCause(ioe), false);
                                }
                        }

                        if (!severity.equals(newLogSeverity) ||
                            !location.equals(newLogLocation))
                                CONF_LOG.log(Severity.DEBUG,
                                    "logging with level " + severity);

                        severity = newLogSeverity;
                        handler = newLogHandler;
                        location = newLogLocation;

                        handler.setLevel(severity);
                        BASE_LOG.addHandler(handler);
                }

                /**
                 * Configure the loggers based on the pool's logging
                 * properties, or, if the -l option was specified on the
                 * command line, continue to use the console.
                 */
                public static void initialize(Configuration conf)
                    throws ConfigurationException
                {
                        if (usingConsole)
                                return;
                        else
                                initializeWithConfiguration(conf);
                }

                /**
                 * Return the current logging level.
                 */
                public static Severity getSeverity()
                {
                        return (severity);
                }

                public static void close()
                {
                        if (handler != null) {
                                BASE_LOG.removeHandler(handler);
                                handler.close();
                        }
                }
        }

        /**
         * Constructor
         *
         * Only one poold instance should be running per system.
         *
         * @param consoleSeverity If non-null, indicates that the
         * configuration property-controlled logging behavior is to be
         * overridden (the <code>-l</code> option was specified), and
         * messages are to be logged only to the console, with (at most)
         * the given maximum severity.
         */
        private Poold(Severity consoleSeverity)
        {
                /*
                 * Establish loggers for recording errors during
                 * initialization.  Under normal circumstances, no
                 * messages will be emitted; this is only a measure to
                 * make sure that unanticipated errors are reported in
                 * some log, or the console, if the -l option is used.
                 */
                logHelper.preinitialize(consoleSeverity);

                /*
                 * Try opening the configuration read-write in hopes the
                 * ability will be possessed henceforth.
                 */
                try {
                        conf = new Configuration(PoolInternal.
                            pool_dynamic_location(), PoolInternal.PO_RDWR);
                        conf.close();
                } catch (PoolsException pe) {
                        Poold.utility.die(CONF_LOG, new PooldException(
                            "cannot open dynamic pools configuration " +
                             "read-write (" + pe.getMessage() + ")")
                             .initCause(pe), false);
                }

                try {
                        conf = new Configuration(PoolInternal.
                            pool_dynamic_location(), PoolInternal.PO_RDONLY);
                } catch (PoolsException pe) {
                        Poold.utility.die(CONF_LOG, pe);
                }

                /*
                 * Create the required sub-components:
                 * - a monitoring object
                 * - a DRM implementer
                 * - a solver
                 */
                monitor = new SystemMonitor();
                drm = new LogDRM();
                solver = new SystemSolver(monitor);
        }

        /**
         * Returns a reference to the singleton <code>Poold</code>,
         * constructing one if necessary.
         *
         * @param consoleSeverity If non-null, indicates that the
         * configuration property-controlled logging behavior is to be
         * overridden (the <code>-l</code> option was specified), and
         * messages are to be logged only to the console, with (at most)
         * the given maximum severity.
         * @throws IllegalArgumentException if the given console
         * severity doesn't match that of an existing instance.
         */
        public static Poold getInstanceWithConsoleLogging(
            Severity consoleSeverity)
        {
                if (instance == null)
                        return (instance = new Poold(consoleSeverity));
                else
                        if (logHelper.usingConsole == false &&
                            consoleSeverity == null || consoleSeverity !=
                            null && logHelper.getSeverity().equals(
                            consoleSeverity))
                                return (instance);
                        else
                                throw new IllegalArgumentException();
        }

        /**
         * Initializes <code>Poold</code> for operation at startup or
         * in response to a detected libpool configuration change.
         */
        private void initialize()
        {
                try {
                        logHelper.initialize(conf);
                        if (firstInitialization.get())
                                CONF_LOG.log(Severity.INFO, "starting");
                        /*
                         * When a system is extremely busy, it may
                         * prove difficult to initialize poold. Just
                         * keep trying until we succeed.
                         */
                        boolean busy = true;
                        while (busy && shouldRun.get()) {
                                busy = false;
                                try {
                                        monitor.initialize(conf);
                                        CONF_LOG.log(Severity.DEBUG,
                                            "configuring solver...");
                                        solver.initialize(conf);
                                        CONF_LOG.log(Severity.INFO,
                                            "configuration complete");
                                } catch (PoolsException pe) {
                                        CONF_LOG.log(Severity.INFO,
                                            "The system is too busy to " +
                                            "initialize, attempting " +
                                            "initialization again");
                                        /*
                                         * pause for a while before
                                         * re-attempting the
                                         * initialization.
                                         */
                                        try {
                                                Thread.sleep(50);
                                        } catch (InterruptedException ie) {
                                                /*
                                                 * Safe to ignore this
                                                 * exception as we
                                                 * will simply try
                                                 * again sooner.
                                                 */
                                        }
                                        busy = true;
                                } catch (StaleMonitorException sme) {
                                        CONF_LOG.log(Severity.INFO,
                                            "The system is too busy to " +
                                            "initialize, attempting " +
                                            "initialization again");
                                        /*
                                         * pause for a while before
                                         * re-attempting the
                                         * initialization.
                                         */
                                        try {
                                                Thread.sleep(50);
                                        } catch (InterruptedException ie) {
                                                /*
                                                 * Safe to ignore this
                                                 * exception as we
                                                 * will simply try
                                                 * again sooner.
                                                 */
                                        }
                                        busy = true;
                                }
                        }
                        if (firstInitialization.get())
                                firstInitialization.set(false);
                } catch (ConfigurationException ce) {
                        Poold.utility.die(CONF_LOG, ce);
                }
        }

        /**
         * Execute <code>Poold</code> indefinitely. This method is
         * invoked after <code>Poold</code> completes
         * configuration. It will continue to execute until
         * <code>Poold</code> is terminated.
         *
         * @throws Exception If an there is an error in execution.
         */
        private void execute() throws Exception
        {
                int changed = 0;
                boolean confRequired = false;

                while (shouldRun.get()) {
                        try {
                                changed = conf.update();
                                assert(!confRequired || confRequired &&
                                    changed != 0);
                                if (changed != 0) {
                                        CONF_LOG.log(Severity.DEBUG,
                                            "configuration change detected");
                                        if (!confRequired)
                                                CONF_LOG.log(Severity.INFO,
                                                    "configuration changed " +
                                                    "externally");
                                        CONF_LOG.log(Severity.INFO,
                                            "reconfiguring...");
                                }
                                confRequired = false;
                        } catch (PoolsException pe) {
                                Poold.utility.die(CONF_LOG, pe);
                        }
                        if (changed != 0)
                                initialize();

                        boolean gotNext = false;
                        while (shouldRun.get() && !gotNext) {
                                try {
                                        monitor.getNext();
                                        gotNext = true;

                                        /*
                                         * All workload-dependent
                                         * objectives must now be
                                         * checked for violations.  The
                                         * solver holds all objectives
                                         * and it makes the decision
                                         * about whether a
                                         * reconfiguration is required.
                                         */
                                        if (solver.examine(monitor)) {
                                                MON_LOG.log(Severity.INFO,
                                                    "reconfiguration required");
                                                confRequired = solver.solve();
                                        } else {
                                                MON_LOG.log(Severity.INFO,
                                                    "all evaluated objectives"
                                                    + " satisfied");
                                        }
                                } catch (StaleMonitorException e) {
                                        /*
                                         * Later, assert that every
                                         * cause of the
                                         * StaleMonitorException is
                                         * handled by the above
                                         * conf.update().
                                         */
                                        confRequired = true;
                                } catch (InterruptedException ie) {
                                        Poold.MON_LOG.log(Severity.INFO,
                                            "interrupted");
                                        break;
                                }
                        }
                        if (!shouldRun.get())
                                break;
                        System.runFinalization();
                }
                Poold.BASE_LOG.log(Severity.NOTICE, "exiting");
        }

        /**
         * Cleanup any resources when the application terminates.
         */
        private void cleanup()
        {
                conf.close();
                logHelper.close();
                instance = null;
        }

        /**
         * Invoke <code>Poold</code>. This main function is provided
         * so that <code>poold</code> can be executed.  Execution will
         * continue indefinitely unless there is an error, in which case
         * when execute() terminates.
         */
        public void run()
        {
                mainThread = Thread.currentThread();

                Runtime.getRuntime().addShutdownHook(new Thread() {
                        public void run() {
                                try {
                                        cleanup();
                                } catch (Throwable t) {
                                }
                        }
                });

                try {
                        initialize();
                        execute();
                } catch (Throwable t) {
                        Poold.utility.die(BASE_LOG, t);
                }
        }

        /**
         * Stops <code>Poold</code>.  Sets a flag indicating that run()
         * should break out of the monitor/solve loop and clean up.
         */
        public void shutdown() {
                /*
                 * Flag that the main thread should break out of the
                 * sample/solve loop as soon as possible.
                 */
                shouldRun.set(false);

                /*
                 * Interrupt the main thread, which will cause the
                 * monitor to break out of its sleep if it's waiting for
                 * the sample time to arrive, and cause the sample/solve
                 * loop condition to be evaluated sooner.  But make some
                 * effort not to cause an InterruptedIOException if
                 * we're not even through initialization yet; we'll get
                 * around to shutting down soon enough.
                 */
                if (!firstInitialization.get() && mainThread != null)
                        mainThread.interrupt();
        }

        public static void main(String args[]) throws IllegalArgumentException
        {
                Severity severity = null;

                if (args.length > 0) {
                        if (args[0].compareTo("-l") == 0 && args.length == 2) {
                                severity = (Severity) Severity.parse(args[1]);
                        } else
                                throw new IllegalArgumentException(
                                    "usage: poold [-l level]");
                }
                Poold p = getInstanceWithConsoleLogging(severity);
                p.run();
        }

        /**
         * The <code>utility</code> class provides various
         * <code>Poold</code> related static utility methods that
         * don't naturally reside on any class.
         */
        static class utility {
                /**
                 * Outputs a near-final message corresponding
                 * to an exception (or other throwable) to the named
                 * logger before causing the VM to exit.  The message
                 * will have ERROR severity unless an instance of
                 * PoolsException is given, in which case the message
                 * will adopt the PoolsException's severity.  Similarly,
                 * if the PoolsException specifies an exit code, it will
                 * be used; otherwise the default of
                 * <code>E_PO_FAILURE</code> will be used.
                 *
                 * @param logger Logger used to log the message
                 * @param t The cause.  A stack trace will be affixed to
                 * the message.
                 */
                public static void die(Logger logger, Throwable t)
                {
                        die(logger, t, true);
                }

                /**
                 * Outputs a near-final message corresponding
                 * to an exception (or other throwable) to the named
                 * logger before causing the VM to exit.  The message
                 * will have ERROR severity unless an instance of
                 * PoolsException is given, in which case the message
                 * will adopt the PoolsException's severity.  Similarly,
                 * if the PoolsException specifies an exit code, it will
                 * be used; otherwise the default of
                 * <code>E_PO_FAILURE</code> will be used.
                 *
                 * @param logger Logger used to log the message
                 * @param t The cause.
                 * @param showStackTrace If true, a stack trace will be
                 * affixed to the message.
                 */
                public static void die(Logger logger, Throwable t,
                    boolean showStackTrace)
                {
                        try {
                                Severity severity;
                                /*
                                 * Configure the message's exception and
                                 * severity.
                                 */
                                LogRecord record;
                                if (t instanceof PooldException)
                                        record = new LogRecord(
                                            ((PooldException)t).getSeverity(),
                                            t.getMessage());
                                else
                                        record = new LogRecord(Severity.ERR,
                                            t.getMessage());
                                if (record.getMessage() == null)
                                        record.setMessage("exception " +
                                            t.getClass().getName());
                                if (showStackTrace)
                                        record.setThrown(t);

                                record.setLoggerName(logger.getName());
                                logger.log(record);

                                if (logHelper.handler != null)
                                        logHelper.handler.flush();
                                if (t instanceof PooldException)
                                        System.exit(((PooldException)t)
                                            .getExitCode());
                                else
                                        System.exit(E_PO_FAILURE);
                        } catch (Exception e) {
                                SuccinctStackTraceFormatter.printStackTrace(e);
                                System.exit(-1);
                        }
                }

                /**
                 * Outputs a warning-level message to the named logger.
                 *
                 * @param logger Logger used to log the message
                 * @param t The cause.
                 * @param showStackTrace If true, a stack trace will be
                 * affixed to the message.
                 */
                public static void warn(Logger logger, Throwable t,
                    boolean showStackTrace)
                {
                        try {
                                Severity severity;
                                /*
                                 * Configure the message's exception and
                                 * severity.
                                 */
                                LogRecord record;
                                if (t instanceof PooldException)
                                        record = new LogRecord(
                                            ((PooldException)t).getSeverity(),
                                            t.getMessage());
                                else
                                        record = new LogRecord(Severity.WARNING,
                                            t.getMessage());
                                if (record.getMessage() == null)
                                        record.setMessage("exception " +
                                            t.getClass().getName());
                                if (showStackTrace)
                                        record.setThrown(t);

                                record.setLoggerName(logger.getName());
                                logger.log(record);

                                if (logHelper.handler != null)
                                        logHelper.handler.flush();
                        } catch (Exception e) {
                                SuccinctStackTraceFormatter.printStackTrace(e);
                                System.exit(-1);
                        }
                }
        }
}

class ConfigurationException extends Exception
{
        public ConfigurationException(String message)
        {
                super(message);
        }
}

class IllegalOFValueException extends RuntimeException
{
        public IllegalOFValueException(String message)
        {
                super(message);
        }
}

class PooldException extends Exception {
        /**
         * The exit code which the virtual machine should exit at if
         * this exception cannot be handled.
         */
        private int exitCode;

        /**
         * The severity of this message.  See <code>Severity</code>.
         */
        private Severity severity;

        /**
         * Constructs a message with default exit code and
         * <code>System.ERR</code> severity.
         */
        public PooldException(String message)
        {
                this(message, 1, Severity.ERR);
        }

        /**
         * Constructs a message with given exit code and
         * <code>System.ERR</code> severity.
         */
        public PooldException(String message, int exitCode)
        {
                this(message, exitCode, Severity.ERR);
        }

        /**
         * Constructs a message with a given exit code and severity.
         */
        public PooldException(String message, int exitCode, Severity severity)
        {
                super(message);
                this.exitCode = exitCode;
                this.severity = severity;
        }

        /**
         * The exit code which the virtual machine should exit at if
         * this exception cannot be handled.
         */
        public int getExitCode()
        {
                return (exitCode);
        }

        public Severity getSeverity()
        {
                return (severity);
        }
}