1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 *
26 * ident	"%Z%%M%	%I%	%E% SMI"
27 */
28
29package com.sun.solaris.domain.pools;
30
31import java.io.*;
32import java.util.*;
33import java.util.logging.*;
34import java.text.DecimalFormat;
35import java.util.concurrent.atomic.*;
36
37import com.sun.solaris.service.locality.*;
38import com.sun.solaris.service.logging.*;
39import com.sun.solaris.service.pools.*;
40import com.sun.solaris.service.exception.*;
41
42/*
43 * poold overview
44 * ----- --------
45 *
46 * poold manipulates system resources in accordance with administrator
47 * specified constraints and objectives. The "goal" of the application
48 * is to maximise the efficiency of available resources within these
49 * parameters.
50 *
51 * Constraints are specified as follows:
52 *
53 * On a resource set:
54 *
55 *	- min Is the minimum amount of resource a set should
56 *	receive. poold will never elect to move resource so that a set
57 *	falls below its minimum value. It is possible for a set to
58 *	fall below its minimum as a consequence of administrative
59 *	intervention, in which case poold will endeavour to bring a
60 *	set back to its minimum level at the earliest opportunity.
61 *
62 *	- max Is the maximum amount of resource a set should
63 *	recieve. poold will never elect to move resource so that a set
64 *	rises above its maximum value. It is possible for a set to
65 *	rise above its maximum as a consequence of administrative
66 *	intervention, in which case poold will endeavour to bring a
67 *	set back to its maximum level at the earliest opportunity.
68 *
69 * On a resource component:
70 *
71 *	- cpu.pinned Is an indication that a CPU should be ignored by
72 *	poold for purposes of reallocation. A pinned CPU will never be
73 *	moved by poold from one set to another.
74 *
75 * In addition to constraints, an administrator may also specify
76 * objectives. Currently three types of objectives are supported:
77 *
78 *	- system.wt-load Is an objective set across an entire
79 *	configuration. It attempts to ensure that resource is shared
80 *	in accordance with current consumption. Those partitions which
81 *	are most heavily utilized are give more resource in an attempt
82 *	to lower their utilization levels.
83 *
84 *	- set.locality Is a locality objective which attempts to
85 *	minimize or maximize resource locality for a set.
86 *
87 *	- set.utilization Is a utilization based objective which an
88 *	administrator may use to explicitly dictate the utilization
89 *	levels which should be achieved for a set.
90 *
91 * When executing, poold uses information about the current pools
92 * configuration; monitored resource utilization and specified
93 * constraints and objectives to determine if a move of resources is
94 * likely to lead to an increase in objective satisfaction.
95 *
96 * Candidate moves are generated by observing resource constraints.
97 * These moves are evaluated and scored in terms of their contribution
98 * to objective satisfaction (note: objectives are weighted according
99 * to their importance) and ranked accordingly. If a move can be
100 * identified that delivers an improvement, the move is made. Data is
101 * collected about past moves and recorded as "Decision History",
102 * before the move is made this data is consulted and if the move is
103 * expected not to yield an improvement, it may be cancelled. This
104 * refinement is designed to improve the quality of decision making by
105 * reflecting upon the past performance of decisions as well as the
106 * contribution to objective satisfaction.
107 *
108 * poold structure
109 * ----- ---------
110 *
111 * The common package name begins with:
112 *
113 * com.sun.solaris
114 *
115 * The software is divided into two main functional areas:
116 *
117 *	service
118 *
119 *	These packages collaborate to provide services to poold
120 *	(typically, they use JNI to access exising
121 *	functionality). They are not designed to be extended. For more
122 *	details on these classes examine the source files in each
123 *	directory.
124 *
125 *	exception	Stack trace formatter
126 *	kstat		Interface to Solaris kstat facility
127 *	locality	Interface to Solaris lgrp facility
128 *	logging		Interface to Solaris syslog facility
129 *	pools		Interface to Solaris libpool facility
130 *	timer		High resolution timestamps
131 *
132 *	domain:
133 *
134 *	These package reflect problem domain concepts and are
135 *	responsible for application logic related to these
136 *	concepts.
137 *
138 *	pools		Dynamic Resource Pools specific functionality.
139 *
140 * This code block will continue to explain in more detail how poold
141 * is organized.
142 *
143 * poold provides the following basic facilities:
144 *
145 * Monitoring:
146 *
147 * Basic statistic access is provided by the
148 * com.sun.solaris.service.kstat package. The following interfaces and
149 * classes collaborate to provide higher level statistic and
150 * monitoring facilities to the application:
151 *
152 *	INTERFACES
153 *
154 *	AggregateStatistic
155 *	Monitor
156 *	Statistic
157 *	StatisticListener
158 *
159 *	CLASSES
160 *
161 *	AbstractStatistic
162 *	DoubleStatistic
163 *	LongStatistic
164 *	ResourceMonitor
165 *	StatisticEvent
166 *	StatisticList
167 *	StatisticOperations
168 *	SystemMonitor
169 *	UnsignedInt64Statistic
170 *
171 * Logging:
172 *
173 * Logging services are provided by the com.sun.solaris.logging
174 * package. In addition, the following class implements Poold's
175 * specific logging requirements.
176 *
177 *	CLASSES
178 *
179 *	Poold
180 *
181 * Optimization:
182 *
183 * lgrp service are provided by the com.sun.solaris.service.lgrp
184 * package. pools services are provided by the
185 * com.sun.solaris.service.pools package. In addition, optimization is
186 * implemented in the following Interfaces and Classes:
187 *
188 *	INTERFACES
189 *
190 *	Objective
191 *	Solver
192 *	WorkloadDependentObjective
193 *
194 *	CLASSES
195 *
196 *	AbstractObjective
197 *	ComponentMove
198 *	DecisionHistory
199 *	Expression
200 *	KExpression
201 *	KVEpression
202 *	KVOpExpression
203 *	LocalityObjective
204 *	Move
205 *	Poold
206 *	QuantityMove
207 *	SystemSolver
208 *	UtilizationObjective
209 *	WeightedLoadObjective
210 *
211 * Configuration:
212 *
213 * pools services are provided by the com.sun.solaris.service.pools
214 * package, this is used to read poold configuration details from a
215 * libpool configuration. In addition, configuration is implemented in
216 * the following Classes:
217 *
218 *	CLASSES
219 *
220 *	AbstractObjective
221 *	Expression
222 *	Poold
223 *	SystemSolver
224 *
225 * (NB: Some classes were mentioned in multiple categories where there
226 * responsbilities overlap. Inner classes are not listed as their
227 * responsibilities can be clearly inferred from their context.)
228 *
229 * For more details on any of the packages, classes or interfaces
230 * mentioned above, look at the documentation associated with each
231 * class.
232 */
233
234/**
235 * The <code>Poold</code> class implements a dynamic resource
236 * allocation system for Solaris.
237 *
238 * <code>Poold</code> is a monitoring daemon, designed to evaluate
239 * user specified objectives, monitor workload behaviour and
240 * dynamically assign resources in order to satisfy the evaluated
241 * objectives. For more details see:
242 *
243 * <a href="http://sac.eng.sun.com/PSARC/2002/287/">PSARC/2002/287</a>
244 */
245final class Poold
246{
247	/**
248	 * The configuration which is manipulated.
249	 */
250	private Configuration conf;
251
252	/**
253	 * The monitoring interface.
254	 */
255	private Monitor monitor;
256
257	/**
258	 * The interface to higher level resource managers.
259	 */
260	private DRM drm;
261
262	/**
263	 * The interface to the configuration solver.
264	 */
265	private Solver solver;
266
267	/**
268	 * Default path to the logging properties file.
269	 */
270	public static final String POOLD_PROPERTIES_PATH =
271	    "/usr/lib/pool/poold.properties";
272
273	/**
274	 * Logger for records which aren't produced in the Monitoring,
275	 * Configuration, or Optimization states.  This logger is the
276	 * parent to the loggers used in those states.
277	 */
278	public static final Logger BASE_LOG = Logger.getLogger(
279	    "com.sun.solaris.domain.pools.poold");
280
281	/**
282	 * Logger for records produced in the Configuration state.
283	 */
284	public static final Logger CONF_LOG = Logger.getLogger(
285	    "com.sun.solaris.domain.pools.poold.Configuration");
286
287	/**
288	 * Logger for records produced in the Monitoring state.
289	 */
290	public static final Logger MON_LOG = Logger.getLogger(
291	    "com.sun.solaris.domain.pools.poold.Monitoring");
292
293	/**
294	 * Logger for records produced in the Optimization state.
295	 */
296	public static final Logger OPT_LOG = Logger.getLogger(
297	    "com.sun.solaris.domain.pools.poold.Optimization");
298
299	/**
300	 * Singleton instance of Poold.
301	 */
302	private static Poold instance;
303
304	/**
305	 * The main sampling and solving thread.
306	 */
307	private Thread mainThread;
308
309	/**
310	 * Process exit code indicating a failure.
311	 */
312	private static final int E_PO_FAILURE = 2;
313
314	/**
315	 * Keep track of whether initialize() has been invoked, to
316	 * output the "starting" message on the first.
317	 */
318	private AtomicBoolean firstInitialization = new AtomicBoolean(true);
319
320	/**
321	 * Flags whether poold should run or exit.
322	 */
323	private AtomicBoolean shouldRun = new AtomicBoolean(true);
324
325	private static class logHelper {
326		/**
327		 * Default logfile location
328		 */
329		public static final String DEF_LOG_LOC = "/var/log/pool/poold";
330
331		/**
332		 * Log location indicating <code>syslog</code>, as
333		 * opposed to a file, should be used.
334		 */
335		public static final String SYSLOG_LOG_LOC = "SYSLOG";
336
337		/**
338		 * Default Log severity (if not overridden)
339		 */
340		public static final Severity DEF_SEVERITY = Severity.INFO;
341
342		/**
343		 * Name of configuration property, log location.
344		 */
345		public static final String PROPERTY_NAME_LOG_LOC =
346		    "system.poold.log-location";
347
348		/**
349		 * Name of configuration property, log level.
350		 */
351		public static final String PROPERTY_NAME_LOG_LEVEL =
352		    "system.poold.log-level";
353
354		/**
355		 * Location of logfile -- an absolute filename, or
356		 * "SYSLOG".
357		 */
358		private static String location;
359
360		/**
361		 * Logfile handler, responsible for taking log messages
362		 * and exporting them.
363		 */
364		private static Handler handler;
365
366		/**
367		 * Logfile severity, log messages below this severity are
368		 * ignored.
369		 */
370		private static Severity severity;
371
372		/**
373		 * Flag recording whether preinitialization has occurred.
374		 */
375		private static boolean preinitialized = false;
376
377		/**
378		 * Flag recording whether the logging Severity has been
379		 * overridden with the -l command-line option, which
380		 * means the console is the only thing being logged to,
381		 * and the configuration's logging properties are
382		 * ignored.
383		 */
384		private static boolean usingConsole;
385
386		/**
387		 * Indicates whether logging semantics should be changed
388		 * to facilitate debugging.
389		 */
390		private static final boolean loggingDebugging = false;
391
392		/**
393		 * Do the pre-initialization initialization:  install
394		 * loggers for reporting errors during initialization.
395		 *
396		 * @param consoleSeverity If non-null, indicates that
397		 * the configuration property-controlled logging behavior
398		 * is to be overridden (the <code>-l</code> option was
399		 * specified), and messages are to be logged only to the
400		 * console, with (at most) the given maximum severity.
401		 */
402		private static void preinitialize(Severity consoleSeverity) {
403			if (preinitialized)
404				return;
405
406			/*
407			 * Read logging properties affecting the
408			 * FileHandler and ConsoleHandler from
409			 * <code>poold.properties</code>.
410			 */
411			Properties props = new Properties();
412			ByteArrayOutputStream bos = new ByteArrayOutputStream();
413			try {
414				props.load(
415				    new FileInputStream(POOLD_PROPERTIES_PATH));
416				props.store(bos, "");
417				LogManager.getLogManager().readConfiguration(
418				    new ByteArrayInputStream(
419				    bos.toByteArray()));
420			} catch (IOException ioe) {
421				Poold.MON_LOG.log(Severity.WARNING, "could not "
422				    + "read logging properties from "
423				    + "poold.properties: " + ioe);
424			}
425
426			if (consoleSeverity == null || loggingDebugging) {
427				/*
428				 * Log messages to /var/log/pool/poold,
429				 * the default location, until the pools
430				 * configuration properties are read and
431				 * applied, which may change the logging
432				 * file and severity.
433				 *
434				 * Under normal circumstances, it's
435				 * expected that NO INFO-level messages
436				 * will be emitted until that time; this
437				 * is only a measure to ensure that
438				 * unanticipated errors are reported in
439				 * some log.
440				 */
441				location = SYSLOG_LOG_LOC;
442				handler = SyslogHandler.getInstance("poold",
443				    Facility.DAEMON);
444				severity = DEF_SEVERITY;
445				handler.setLevel(severity);
446				BASE_LOG.addHandler(handler);
447			}
448
449			if (consoleSeverity != null) {
450				/*
451				 * If -l is specified, log to the
452				 * console.  Unless loggingDebug is
453				 * true, this will also mean that the
454				 * logging properties are ignored.
455				 *
456				 * Determine if the user has specified
457				 * the use of a ConsoleHandler through
458				 * poold.properties.
459				 */
460				Logger root = Logger.getLogger("");
461				Handler[] handler = root.getHandlers();
462				ConsoleHandler ch = null;
463				for (int i = 0; i < handler.length && ch ==
464				    null; i++)
465					if (handler[i]
466					    instanceof ConsoleHandler)
467						ch = (ConsoleHandler)handler[i];
468				/*
469				 * If none was previously, install a
470				 * ConsoleHandler.
471				 */
472				if (ch == null) {
473					ch = new ConsoleHandler();
474
475					ch.setFormatter(
476					    new SysloglikeFormatter());
477					ch.setLevel(consoleSeverity);
478					root.addHandler(ch);
479				}
480				severity = consoleSeverity;
481				BASE_LOG.log(Severity.DEBUG,
482				    "logging with level " + severity);
483
484				/**
485				 * Allow logging properties to be
486				 * effective if loggingDebugging is not
487				 * set.
488				 */
489				if (!loggingDebugging)
490					usingConsole = true;
491			}
492			preinitialized = true;
493		}
494
495		/**
496		 * Configure loggers based on the logging-related
497		 * configuration properties.  Outputs a description of
498		 * any changes to the configuration logger.
499		 *
500		 * @throws ConfigurationException if there is an error
501		 * applying libpool configuration properties to
502		 * <code>poold</code>
503		 */
504		public static void initializeWithConfiguration(
505		    Configuration conf) throws ConfigurationException
506		{
507			String newLogLocation;
508			Severity newLogSeverity;
509			String newLogSeverityName = null;
510
511			/*
512			 * Set the log location as specified by the
513			 * configuration's system properties.
514			 */
515			try {
516				newLogLocation = conf.getStringProperty(
517				    PROPERTY_NAME_LOG_LOC);
518			} catch (PoolsException e) {
519				newLogLocation = DEF_LOG_LOC;
520			}
521
522			try {
523				newLogSeverityName = conf.getStringProperty(
524				    PROPERTY_NAME_LOG_LEVEL);
525				newLogSeverity = Severity.getSeverityWithName(
526				    newLogSeverityName);
527				assert(newLogSeverity != null);
528			} catch (PoolsException e) {
529				newLogSeverity = DEF_SEVERITY;
530			} catch (IllegalArgumentException e) {
531				throw(ConfigurationException)
532				    (new ConfigurationException(
533				    "invalid " + PROPERTY_NAME_LOG_LEVEL +
534				    "value: " + newLogSeverityName)
535				    .initCause(e));
536			}
537
538			Handler newLogHandler = null;
539
540			/*
541			 * (Re)install the logger for the poold class
542			 * hierarchy.  This means that only poold
543			 * messages are controlled by the pools
544			 * configuration properties/command-line
545			 * options.
546			 */
547
548			/*
549			 * The logfile is always re-opened, in case the
550			 * cause for reinitialization is due to SIGHUP
551			 * following a log rotation.
552			 */
553			if (handler != null) {
554				BASE_LOG.removeHandler(handler);
555				handler.close();
556				handler = null;
557			}
558
559			if (newLogLocation.toUpperCase().equals(
560			    SYSLOG_LOG_LOC.toUpperCase()))
561				newLogHandler =
562				    SyslogHandler.getInstance("poold",
563				    Facility.DAEMON);
564			else {
565				if (!newLogLocation.startsWith("/"))
566					throw
567					    new ConfigurationException(
568						PROPERTY_NAME_LOG_LOC +
569						" value is not an" +
570						" absolute path");
571				try {
572					newLogHandler =
573					    new FileHandler(
574					    newLogLocation, 0, 1, true);
575					newLogHandler.setFormatter(
576					    new SysloglikeFormatter());
577				} catch (java.io.IOException ioe) {
578					Poold.utility.die(
579					    Poold.CONF_LOG,
580					    new PooldException(
581					    newLogLocation +
582					    ": can't write")
583					    .initCause(ioe), false);
584				}
585			}
586
587			if (!severity.equals(newLogSeverity) ||
588			    !location.equals(newLogLocation))
589				CONF_LOG.log(Severity.DEBUG,
590				    "logging with level " + severity);
591
592			severity = newLogSeverity;
593			handler = newLogHandler;
594			location = newLogLocation;
595
596			handler.setLevel(severity);
597			BASE_LOG.addHandler(handler);
598		}
599
600		/**
601		 * Configure the loggers based on the pool's logging
602		 * properties, or, if the -l option was specified on the
603		 * command line, continue to use the console.
604		 */
605		public static void initialize(Configuration conf)
606		    throws ConfigurationException
607		{
608			if (usingConsole)
609				return;
610			else
611				initializeWithConfiguration(conf);
612		}
613
614		/**
615		 * Return the current logging level.
616		 */
617		public static Severity getSeverity()
618		{
619			return (severity);
620		}
621
622		public static void close()
623		{
624			if (handler != null) {
625				BASE_LOG.removeHandler(handler);
626				handler.close();
627			}
628		}
629	}
630
631	/**
632	 * Constructor
633	 *
634	 * Only one poold instance should be running per system.
635	 *
636	 * @param consoleSeverity If non-null, indicates that the
637	 * configuration property-controlled logging behavior is to be
638	 * overridden (the <code>-l</code> option was specified), and
639	 * messages are to be logged only to the console, with (at most)
640	 * the given maximum severity.
641	 */
642	private Poold(Severity consoleSeverity)
643	{
644		/*
645		 * Establish loggers for recording errors during
646		 * initialization.  Under normal circumstances, no
647		 * messages will be emitted; this is only a measure to
648		 * make sure that unanticipated errors are reported in
649		 * some log, or the console, if the -l option is used.
650		 */
651		logHelper.preinitialize(consoleSeverity);
652
653		/*
654		 * Try opening the configuration read-write in hopes the
655		 * ability will be possessed henceforth.
656		 */
657		try {
658			conf = new Configuration(PoolInternal.
659			    pool_dynamic_location(), PoolInternal.PO_RDWR);
660			conf.close();
661		} catch (PoolsException pe) {
662			Poold.utility.die(CONF_LOG, new PooldException(
663			    "cannot open dynamic pools configuration " +
664			     "read-write (" + pe.getMessage() + ")")
665			     .initCause(pe), false);
666		}
667
668		try {
669			conf = new Configuration(PoolInternal.
670			    pool_dynamic_location(), PoolInternal.PO_RDONLY);
671		} catch (PoolsException pe) {
672			Poold.utility.die(CONF_LOG, pe);
673		}
674
675		/*
676		 * Create the required sub-components:
677		 * - a monitoring object
678		 * - a DRM implementer
679		 * - a solver
680		 */
681		monitor = new SystemMonitor();
682		drm = new LogDRM();
683		solver = new SystemSolver(monitor);
684	}
685
686	/**
687	 * Returns a reference to the singleton <code>Poold</code>,
688	 * constructing one if necessary.
689	 *
690	 * @param consoleSeverity If non-null, indicates that the
691	 * configuration property-controlled logging behavior is to be
692	 * overridden (the <code>-l</code> option was specified), and
693	 * messages are to be logged only to the console, with (at most)
694	 * the given maximum severity.
695	 * @throws IllegalArgumentException if the given console
696	 * severity doesn't match that of an existing instance.
697	 */
698	public static Poold getInstanceWithConsoleLogging(
699	    Severity consoleSeverity)
700	{
701		if (instance == null)
702			return (instance = new Poold(consoleSeverity));
703		else
704			if (logHelper.usingConsole == false &&
705			    consoleSeverity == null || consoleSeverity !=
706			    null && logHelper.getSeverity().equals(
707			    consoleSeverity))
708				return (instance);
709			else
710				throw new IllegalArgumentException();
711	}
712
713	/**
714	 * Initializes <code>Poold</code> for operation at startup or
715	 * in response to a detected libpool configuration change.
716	 */
717	private void initialize()
718	{
719		try {
720			logHelper.initialize(conf);
721			if (firstInitialization.get())
722				CONF_LOG.log(Severity.INFO, "starting");
723			/*
724			 * When a system is extremely busy, it may
725			 * prove difficult to initialize poold. Just
726			 * keep trying until we succeed.
727			 */
728			boolean busy = true;
729			while (busy && shouldRun.get()) {
730				busy = false;
731				try {
732					monitor.initialize(conf);
733					CONF_LOG.log(Severity.DEBUG,
734					    "configuring solver...");
735					solver.initialize(conf);
736					CONF_LOG.log(Severity.INFO,
737					    "configuration complete");
738				} catch (PoolsException pe) {
739					CONF_LOG.log(Severity.INFO,
740					    "The system is too busy to " +
741					    "initialize, attempting " +
742					    "initialization again");
743					/*
744					 * pause for a while before
745					 * re-attempting the
746					 * initialization.
747					 */
748					try {
749						Thread.sleep(50);
750					} catch (InterruptedException ie) {
751						/*
752						 * Safe to ignore this
753						 * exception as we
754						 * will simply try
755						 * again sooner.
756						 */
757					}
758					busy = true;
759				} catch (StaleMonitorException sme) {
760					CONF_LOG.log(Severity.INFO,
761					    "The system is too busy to " +
762					    "initialize, attempting " +
763					    "initialization again");
764					/*
765					 * pause for a while before
766					 * re-attempting the
767					 * initialization.
768					 */
769					try {
770						Thread.sleep(50);
771					} catch (InterruptedException ie) {
772						/*
773						 * Safe to ignore this
774						 * exception as we
775						 * will simply try
776						 * again sooner.
777						 */
778					}
779					busy = true;
780				}
781			}
782			if (firstInitialization.get())
783				firstInitialization.set(false);
784		} catch (ConfigurationException ce) {
785			Poold.utility.die(CONF_LOG, ce);
786		}
787	}
788
789	/**
790	 * Execute <code>Poold</code> indefinitely. This method is
791	 * invoked after <code>Poold</code> completes
792	 * configuration. It will continue to execute until
793	 * <code>Poold</code> is terminated.
794	 *
795	 * @throws Exception If an there is an error in execution.
796	 */
797	private void execute() throws Exception
798	{
799		int changed = 0;
800		boolean confRequired = false;
801
802		while (shouldRun.get()) {
803			try {
804				changed = conf.update();
805				assert(!confRequired || confRequired &&
806				    changed != 0);
807				if (changed != 0) {
808					CONF_LOG.log(Severity.DEBUG,
809					    "configuration change detected");
810					if (!confRequired)
811						CONF_LOG.log(Severity.INFO,
812						    "configuration changed " +
813						    "externally");
814					CONF_LOG.log(Severity.INFO,
815					    "reconfiguring...");
816				}
817				confRequired = false;
818			} catch (PoolsException pe) {
819				Poold.utility.die(CONF_LOG, pe);
820			}
821			if (changed != 0)
822				initialize();
823
824			boolean gotNext = false;
825			while (shouldRun.get() && !gotNext) {
826				try {
827					monitor.getNext();
828					gotNext = true;
829
830					/*
831					 * All workload-dependent
832					 * objectives must now be
833					 * checked for violations.  The
834					 * solver holds all objectives
835					 * and it makes the decision
836					 * about whether a
837					 * reconfiguration is required.
838					 */
839					if (solver.examine(monitor)) {
840						MON_LOG.log(Severity.INFO,
841						    "reconfiguration required");
842						confRequired = solver.solve();
843					} else {
844						MON_LOG.log(Severity.INFO,
845						    "all evaluated objectives"
846						    + " satisfied");
847					}
848				} catch (StaleMonitorException e) {
849					/*
850					 * Later, assert that every
851					 * cause of the
852					 * StaleMonitorException is
853					 * handled by the above
854					 * conf.update().
855					 */
856					confRequired = true;
857				} catch (InterruptedException ie) {
858					Poold.MON_LOG.log(Severity.INFO,
859					    "interrupted");
860					break;
861				}
862			}
863			if (!shouldRun.get())
864				break;
865			System.runFinalization();
866		}
867		Poold.BASE_LOG.log(Severity.NOTICE, "exiting");
868	}
869
870	/**
871	 * Cleanup any resources when the application terminates.
872	 */
873	private void cleanup()
874	{
875		conf.close();
876		logHelper.close();
877                instance = null;
878	}
879
880	/**
881	 * Invoke <code>Poold</code>. This main function is provided
882	 * so that <code>poold</code> can be executed.  Execution will
883	 * continue indefinitely unless there is an error, in which case
884	 * when execute() terminates.
885	 */
886	public void run()
887	{
888		mainThread = Thread.currentThread();
889
890		Runtime.getRuntime().addShutdownHook(new Thread() {
891			public void run() {
892				try {
893					cleanup();
894				} catch (Throwable t) {
895				}
896			}
897		});
898
899		try {
900			initialize();
901			execute();
902		} catch (Throwable t) {
903			Poold.utility.die(BASE_LOG, t);
904		}
905	}
906
907	/**
908	 * Stops <code>Poold</code>.  Sets a flag indicating that run()
909	 * should break out of the monitor/solve loop and clean up.
910	 */
911	public void shutdown() {
912		/*
913		 * Flag that the main thread should break out of the
914		 * sample/solve loop as soon as possible.
915		 */
916		shouldRun.set(false);
917
918		/*
919		 * Interrupt the main thread, which will cause the
920		 * monitor to break out of its sleep if it's waiting for
921		 * the sample time to arrive, and cause the sample/solve
922		 * loop condition to be evaluated sooner.  But make some
923		 * effort not to cause an InterruptedIOException if
924		 * we're not even through initialization yet; we'll get
925		 * around to shutting down soon enough.
926		 */
927		if (!firstInitialization.get() && mainThread != null)
928			mainThread.interrupt();
929	}
930
931	public static void main(String args[]) throws IllegalArgumentException
932	{
933		Severity severity = null;
934
935		if (args.length > 0) {
936			if (args[0].compareTo("-l") == 0 && args.length == 2) {
937				severity = (Severity) Severity.parse(args[1]);
938			} else
939				throw new IllegalArgumentException(
940				    "usage: poold [-l level]");
941		}
942		Poold p = getInstanceWithConsoleLogging(severity);
943		p.run();
944	}
945
946	/**
947	 * The <code>utility</code> class provides various
948	 * <code>Poold</code> related static utility methods that
949	 * don't naturally reside on any class.
950	 */
951	static class utility {
952		/**
953		 * Outputs a near-final message corresponding
954		 * to an exception (or other throwable) to the named
955		 * logger before causing the VM to exit.  The message
956		 * will have ERROR severity unless an instance of
957		 * PoolsException is given, in which case the message
958		 * will adopt the PoolsException's severity.  Similarly,
959		 * if the PoolsException specifies an exit code, it will
960		 * be used; otherwise the default of
961		 * <code>E_PO_FAILURE</code> will be used.
962		 *
963		 * @param logger Logger used to log the message
964		 * @param t The cause.  A stack trace will be affixed to
965		 * the message.
966		 */
967		public static void die(Logger logger, Throwable t)
968		{
969			die(logger, t, true);
970		}
971
972		/**
973		 * Outputs a near-final message corresponding
974		 * to an exception (or other throwable) to the named
975		 * logger before causing the VM to exit.  The message
976		 * will have ERROR severity unless an instance of
977		 * PoolsException is given, in which case the message
978		 * will adopt the PoolsException's severity.  Similarly,
979		 * if the PoolsException specifies an exit code, it will
980		 * be used; otherwise the default of
981		 * <code>E_PO_FAILURE</code> will be used.
982		 *
983		 * @param logger Logger used to log the message
984		 * @param t The cause.
985		 * @param showStackTrace If true, a stack trace will be
986		 * affixed to the message.
987		 */
988		public static void die(Logger logger, Throwable t,
989		    boolean showStackTrace)
990		{
991			try {
992				Severity severity;
993				/*
994				 * Configure the message's exception and
995				 * severity.
996				 */
997				LogRecord record;
998				if (t instanceof PooldException)
999					record = new LogRecord(
1000					    ((PooldException)t).getSeverity(),
1001					    t.getMessage());
1002				else
1003					record = new LogRecord(Severity.ERR,
1004					    t.getMessage());
1005				if (record.getMessage() == null)
1006					record.setMessage("exception " +
1007					    t.getClass().getName());
1008				if (showStackTrace)
1009					record.setThrown(t);
1010
1011				record.setLoggerName(logger.getName());
1012				logger.log(record);
1013
1014				if (logHelper.handler != null)
1015					logHelper.handler.flush();
1016				if (t instanceof PooldException)
1017					System.exit(((PooldException)t)
1018					    .getExitCode());
1019				else
1020					System.exit(E_PO_FAILURE);
1021			} catch (Exception e) {
1022				SuccinctStackTraceFormatter.printStackTrace(e);
1023				System.exit(-1);
1024			}
1025		}
1026
1027		/**
1028		 * Outputs a warning-level message to the named logger.
1029		 *
1030		 * @param logger Logger used to log the message
1031		 * @param t The cause.
1032		 * @param showStackTrace If true, a stack trace will be
1033		 * affixed to the message.
1034		 */
1035		public static void warn(Logger logger, Throwable t,
1036		    boolean showStackTrace)
1037		{
1038			try {
1039				Severity severity;
1040				/*
1041				 * Configure the message's exception and
1042				 * severity.
1043				 */
1044				LogRecord record;
1045				if (t instanceof PooldException)
1046					record = new LogRecord(
1047					    ((PooldException)t).getSeverity(),
1048					    t.getMessage());
1049				else
1050					record = new LogRecord(Severity.WARNING,
1051					    t.getMessage());
1052				if (record.getMessage() == null)
1053					record.setMessage("exception " +
1054					    t.getClass().getName());
1055				if (showStackTrace)
1056					record.setThrown(t);
1057
1058				record.setLoggerName(logger.getName());
1059				logger.log(record);
1060
1061				if (logHelper.handler != null)
1062					logHelper.handler.flush();
1063			} catch (Exception e) {
1064				SuccinctStackTraceFormatter.printStackTrace(e);
1065				System.exit(-1);
1066			}
1067		}
1068	}
1069}
1070
1071class ConfigurationException extends Exception
1072{
1073	public ConfigurationException(String message)
1074	{
1075		super(message);
1076	}
1077}
1078
1079class IllegalOFValueException extends RuntimeException
1080{
1081	public IllegalOFValueException(String message)
1082	{
1083		super(message);
1084	}
1085}
1086
1087class PooldException extends Exception {
1088	/**
1089	 * The exit code which the virtual machine should exit at if
1090	 * this exception cannot be handled.
1091	 */
1092	private int exitCode;
1093
1094	/**
1095	 * The severity of this message.  See <code>Severity</code>.
1096	 */
1097	private Severity severity;
1098
1099	/**
1100	 * Constructs a message with default exit code and
1101	 * <code>System.ERR</code> severity.
1102	 */
1103	public PooldException(String message)
1104	{
1105		this(message, 1, Severity.ERR);
1106	}
1107
1108	/**
1109	 * Constructs a message with given exit code and
1110	 * <code>System.ERR</code> severity.
1111	 */
1112	public PooldException(String message, int exitCode)
1113	{
1114		this(message, exitCode, Severity.ERR);
1115	}
1116
1117	/**
1118	 * Constructs a message with a given exit code and severity.
1119	 */
1120	public PooldException(String message, int exitCode, Severity severity)
1121	{
1122		super(message);
1123		this.exitCode = exitCode;
1124		this.severity = severity;
1125	}
1126
1127	/**
1128	 * The exit code which the virtual machine should exit at if
1129	 * this exception cannot be handled.
1130	 */
1131	public int getExitCode()
1132	{
1133		return (exitCode);
1134	}
1135
1136	public Severity getSeverity()
1137	{
1138		return (severity);
1139	}
1140}
1141