001    /*
002    Copyright (c) 1996-2012, Damon Hart-Davis
003    All rights reserved.
004    
005    Redistribution and use in source and binary forms, with or without
006    modification, are permitted provided that the following conditions are
007    met:
008    
009      * Redistributions of source code must retain the above copyright
010        notice, this list of conditions and the following disclaimer.
011    
012      * Redistributions in binary form must reproduce the above copyright
013        notice, this list of conditions and the following disclaimer in the
014        documentation and/or other materials provided with the
015        distribution.
016    
017    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
018    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
019    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
020    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
021    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
022    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
023    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
024    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
025    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
026    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
027    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028    */
029    package org.hd.d.pg2k.svrCore;
030    
031    import java.lang.management.OperatingSystemMXBean;
032    import java.util.concurrent.ArrayBlockingQueue;
033    import java.util.concurrent.ExecutorService;
034    import java.util.concurrent.Executors;
035    import java.util.concurrent.Future;
036    import java.util.concurrent.SynchronousQueue;
037    import java.util.concurrent.ThreadFactory;
038    import java.util.concurrent.ThreadPoolExecutor;
039    import java.util.concurrent.TimeUnit;
040    import java.util.concurrent.atomic.AtomicInteger;
041    
042    import org.hd.d.pg2k.svrCore.props.LocalProps;
043    
044    
045    /**Utilities to assist with threading/concurrency.
046     */
047    public final class ThreadUtils
048        {
049        /**Prevent creation of instances. */
050        private ThreadUtils() { }
051    
052    
053        /**A factory for creating daemon pool threads.
054         * Adapted (purloined) from Doug Lea / Sun JVM code.
055         */
056        public static final class DaemonThreadFactory implements ThreadFactory
057            {
058            private final ThreadGroup group;
059            private final AtomicInteger threadNumber = new AtomicInteger(1);
060            private final String namePrefix;
061            private final boolean lowPriority;
062    
063            public DaemonThreadFactory(final String poolName,
064                                       final boolean lowPriority)
065                {
066                final SecurityManager s = System.getSecurityManager();
067                group = (s != null)? s.getThreadGroup() :
068                                     Thread.currentThread().getThreadGroup();
069                namePrefix = "pool-" + poolName + "-thread-";
070                this.lowPriority = lowPriority;
071                }
072    
073            public Thread newThread(final Runnable r)
074                {
075                final Thread t = new Thread(group, r,
076                                      namePrefix + threadNumber.getAndIncrement(),
077                                      0);
078                t.setDaemon(true);
079                if(lowPriority)
080                    { t.setPriority(Thread.MIN_PRIORITY); }
081                else if(t.getPriority() != Thread.NORM_PRIORITY)
082                    { t.setPriority(Thread.NORM_PRIORITY); }
083                return(t);
084                }
085            }
086    
087    
088        /**If this system property is set and parsable, then the system behaves as if the specified number of CPUs is available at start-up.
089         * This can be used to stress-test the system,
090         * or to avoid grabbing all the CPUs in a shared system,
091         * or when the JVM/system notion of CPU is not very suitable for us, eg with early HyperThreading.
092         * <p>
093         * Any value set for the property should be strictly positive
094         * when parsed as if by Integer.getInteger().
095         */
096        public static final String PROCESSOR_COUNT_OVERRIDE_SYSPNAME = "org.hd.d.pg2k.CPUs";
097    
098        /**If non-null (ie the system property is set and parsable and we are allowed access), then the system behaves as if the specified number of CPUs is available at start-up. */
099        private static final Integer FORCED_CPU_COUNT;
100        static
101            {
102            Integer count = null;
103            try { count = Integer.getInteger(PROCESSOR_COUNT_OVERRIDE_SYSPNAME, null); }
104            catch(final Exception e) { /* Silently absorb error, eg due to security exception. */ }
105            FORCED_CPU_COUNT = count;
106            }
107    
108        /**Estimated number of CPUs available; strictly positive.
109         * Usually the JVM/system count of available processors when this class is initialised,
110         * but can be overridden manually.
111         */
112        public static final int AVAILABLE_PROCESSORS = (FORCED_CPU_COUNT == null) ?
113                Runtime.getRuntime().availableProcessors() :
114                Math.max(1, FORCED_CPU_COUNT.intValue());
115    
116        /**Approximate maximum concurrent low-priority pool threads; strictly positive.
117         * We may refuse to queue some (discardable) threads
118         * when at least this many of our threads are running.
119         */
120        private static final int MAX_LO_PRI_THREADS = Math.max(1, (AVAILABLE_PROCESSORS/2));
121    
122        /**Maximum number of threads that may run in nonCPUThreadPool(); strictly positive.
123         * We limit the amount of threading by:
124         * <ul>
125         * <li>The likely limit on back-end connectivity (eg HTTP-limited).
126         * <li>The likely strain on other resources, such as disc and memory.
127         * </ul>
128         * <p>
129         * Observation suggests that up to 8 threads should be deployed
130         * to extract maximum throughput from a single magnetic disc spindle
131         * given I/O delays from seeking and from I/O bandwidth delays,
132         * so we should always deploy something like that
133         * (though solid-state storage is essentially seek-free with less latency),
134         * rising slowly with the number of CPUs because
135         * I/O capacity probably grows somewhat with CPU count.
136         * <p>
137         * Note also that we may place network-based work
138         * and other I/O blocked items in this pool.
139         * <p>
140         * This starts at 4 for uniprocessors.
141         */
142        private static final int MAX_THREADS_NCTP = 4 + (AVAILABLE_PROCESSORS/4);
143    
144        /**Shared non-reconfigurable thread pool for compute-bound activities.
145         * Suitable for almost-entirely CPU-bound threads
146         * (though we allow for a little parallel slackness),
147         * and so the pool size is bounded to a value roughly proportional to
148         * the number of CPUs available to this JVM (at creation of this pool).
149         * <p>
150         * Work cannot be queued for this pool
151         * (ie tasks are always executed immediately),
152         * and excess work (with no free pool threads available)
153         * will be run directly in the caller's thread.
154         * <p>
155         * This puts an upper bound on Thread resources (eg memory for stack)
156         * consumed by threads in this pool.
157         * <p>
158         * The threads in the pool are daemon threads,
159         * so should not prevent the JVM from exiting.
160         */
161        private static final ThreadPoolExecutor _computeIntensiveThreadPool = new ThreadPoolExecutor(1, 1 + ((5*AVAILABLE_PROCESSORS)/4),
162                        600L, TimeUnit.SECONDS, // Keep worker threads alive for 10 minutes...
163                        new SynchronousQueue<Runnable>(),
164                        new DaemonThreadFactory("ThreadUtils.computeIntensiveThreadPool", false),
165                        new ThreadPoolExecutor.CallerRunsPolicy());
166        /**Allow these threads to timeout and release all resources. */
167        static { _computeIntensiveThreadPool.allowCoreThreadTimeOut(true); }
168    
169        /**Returns approximate space (spare threads) currently available in the computeIntensivePool; non-negative. */
170        public static int computeIntensiveThreadPoolSpace()
171            { return(_computeIntensiveThreadPool.getMaximumPoolSize() - _computeIntensiveThreadPool.getActiveCount()); }
172    
173        /**Shared non-reconfigurable thread pool for compute-bound activities.
174         * Suitable for almost-entirely CPU-bound threads
175         * (though we allow for a little parallel slackness),
176         * and so the pool size is bounded to a value roughly proportional to
177         * the number of CPUs available to this JVM (at creation of this pool).
178         * <p>
179         * By preference, this should be used for tasks that may block user interaction,
180         * or that really need to use every available CPU for some critical work;
181         * anything else should use a lower-priority pool if possible.
182         * <p>
183         * Work cannot be queued for this pool
184         * (ie tasks are always executed immediately),
185         * and excess work (with no free pool threads available)
186         * will be run directly in the caller's thread.
187         * <p>
188         * This puts an upper bound on Thread resources (eg memory for stack)
189         * consumed by threads in this pool.
190         * <p>
191         * The threads in the pool are daemon threads,
192         * so should not prevent the JVM from exiting.
193         */
194        public static final ExecutorService computeIntensiveThreadPool =
195            Executors.unconfigurableExecutorService(_computeIntensiveThreadPool);
196    
197        /**Shared thread pool for I/O-bound activities. */
198        private static final ThreadPoolExecutor _nonCPUThreadPool = new ThreadPoolExecutor(1, MAX_THREADS_NCTP,
199                        300L, TimeUnit.SECONDS, // Keep worker threads alive for 5 minutes...
200                        new SynchronousQueue<Runnable>(),
201                        new ThreadUtils.DaemonThreadFactory("ThreadUtils.nonCPUThreadPool", false),
202                        new ThreadPoolExecutor.CallerRunsPolicy());
203        /**Allow these threads to timeout and release all resources. */
204        static { _nonCPUThreadPool.allowCoreThreadTimeOut(true); }
205    
206        /**Shared non-reconfigurable thread pool for I/O-bound activities.
207         * Suitable for relatively short-lived and/or I/O-bound threads,
208         * thus we have a fixed thread/resource limit.
209         * <p>
210         * Work cannot be queued for this pool
211         * (ie tasks are always executed immediately),
212         * and excess work (with no free pool threads available)
213         * will be run directly in the caller's thread.
214         * <p>
215         * We always try to keep one or two worker threads alive for fast response.
216         * <p>
217         * The threads in the pool are daemon threads,
218         * so should not prevent the JVM from exiting.
219         */
220        public static final ExecutorService nonCPUThreadPool =
221            Executors.unconfigurableExecutorService(_nonCPUThreadPool);
222    
223        /**Returns approximate space (spare threads) currently available in the nonCPUThreadPool; non-negative. */
224        public static int nonCPUThreadPoolSpace()
225            { return(_nonCPUThreadPool.getMaximumPoolSize() - _nonCPUThreadPool.getActiveCount()); }
226    
227        /**Shared non-reconfigurable thread pool for discardable low-priority I/O-bound activities.
228         * Suitable for non-critical relatively I/O-bound threads,
229         * thus we have a fixed thread/resource limit.
230         * <p>
231         * Work cannot be queued for this pool
232         * (ie tasks are always executed immediately or silently discarded).
233         * <p>
234         * The threads in the pool are daemon threads,
235         * so should not prevent the JVM from exiting.
236         */
237        private static final ThreadPoolExecutor _nonCPUThreadPoolDiscardable =
238            (new ThreadPoolExecutor(1, Math.max(2, MAX_THREADS_NCTP/2),
239                300L, TimeUnit.SECONDS, // Keep worker threads alive for 5 minutes...
240                new SynchronousQueue<Runnable>(),
241                new ThreadUtils.DaemonThreadFactory("ThreadUtils.nonCPUThreadPoolDiscardable", true),
242                new ThreadPoolExecutor.DiscardPolicy()) {
243                /**Only allow new task here if CPU not overloaded or pool empty, else silently discard. */
244                @Override public void execute(final Runnable command)
245                    { if(nonCPUThreadPoolDiscardableSpace() > 0) { super.execute(command); } }
246            });
247        /**Allow these threads to timeout and release all resources. */
248        static { _nonCPUThreadPoolDiscardable.allowCoreThreadTimeOut(true); }
249        /**Shared non-reconfigurable thread pool for discardable I/O-bound activities.
250         * Suitable for non-critical relatively I/O-bound threads,
251         * thus we have a fixed thread/resource limit.
252         * <p>
253         * Work cannot be queued for this pool
254         * (ie tasks are always executed immediately or silently discarded).
255         * <p>
256         * The threads in the pool are daemon threads,
257         * so should not prevent the JVM from exiting.
258         */
259        public static final ExecutorService nonCPUThreadPoolDiscardable =
260            Executors.unconfigurableExecutorService(_nonCPUThreadPoolDiscardable);
261    
262        /**Returns approximate space (spare threads) currently available in the nonCPUThreadPoolDiscardable; non-negative.
263         * Pool size is approximately capped at 1 if CPU is heavily loaded.
264         */
265        public static int nonCPUThreadPoolDiscardableSpace()
266            { return(Math.max(0, (isCPUHeavilyLoaded() ? 1 : _nonCPUThreadPoolDiscardable.getMaximumPoolSize()) - _nonCPUThreadPoolDiscardable.getActiveCount())); }
267    
268    
269        /**Mutable implementation of thread pool for low-priority compute-bound activities. */
270        private static final ThreadPoolExecutor _lowPriorityThreadPool =
271             new ThreadPoolExecutor(1, MAX_LO_PRI_THREADS,
272                120L, TimeUnit.SECONDS, // Keep worker threads alive for 2 minutes...
273                new SynchronousQueue<Runnable>(),
274                new DaemonThreadFactory("ThreadUtils.lowPriorityThreadPool : ExecutorService", true),
275                new ThreadPoolExecutor.CallerRunsPolicy());
276        /**Allow these threads to timeout and release all resources. */
277        static { _lowPriorityThreadPool.allowCoreThreadTimeOut(true); }
278    
279        /**Returns approximate space (spare threads) currently available in the lowPriorityThreadPool; non-negative. */
280        public static int lowPriorityThreadPoolSpace()
281            { return(_lowPriorityThreadPool.getMaximumPoolSize() - _lowPriorityThreadPool.getActiveCount()); }
282    
283        /**Shared non-reconfigurable thread pool for low-priority compute-bound activities.
284         * Suitable for almost-entirely CPU-bound threads
285         * and so the pool size is bounded to a value roughly proportional to
286         * (less than) the number of CPUs available to this JVM (at creation of this pool).
287         * <p>
288         * Work cannot be queued for this pool
289         * (ie tasks are always executed immediately),
290         * and excess work (with no free pool threads available)
291         * will be run directly in the caller's thread.
292         * <p>
293         * These threads run with minimum priority.
294         * <p>
295         * Tasks submitted to this pool <em>should not</em>
296         * consume heavy resources (such as memory) other than CPU
297         * so that we can run many of them at once,
298         * though we try not to use more than half the CPUs at once
299         * when there are multiple CPUs available.
300         * <p>
301         * This puts an upper bound on Thread resources (eg memory for stack)
302         * consumed by threads in this pool.
303         * <p>
304         * The threads in the pool are daemon threads,
305         * so should not prevent the JVM from exiting.
306         */
307        public static final ExecutorService lowPriorityThreadPool =
308            Executors.unconfigurableExecutorService(_lowPriorityThreadPool);
309    
310    
311        /**Mutable implementation of thread pool for discardable low-priority compute-bound activities. */
312        private static final ThreadPoolExecutor _lowPriorityThreadPoolDiscardable =
313            (new ThreadPoolExecutor(1, Math.max(1, MAX_LO_PRI_THREADS),
314                60L, TimeUnit.SECONDS, // Keep worker threads alive for 1 minute...
315                ((MAX_LO_PRI_THREADS > 1) ? new ArrayBlockingQueue<Runnable>(MAX_LO_PRI_THREADS-1) : new SynchronousQueue<Runnable>()), // Possibly allow some queueing to try to minimise discards.
316                new DaemonThreadFactory("ThreadUtils.lowPriorityThreadPoolDiscardable : ExecutorService", true),
317                new ThreadPoolExecutor.DiscardPolicy()) {
318                    /**Only allow new task here if not already too many CPU-bond threads running, else silently discard. */
319                    @Override public void execute(final Runnable command)
320                        { if(_dontDiscardLowPriCPUBoundTask()) { super.execute(command); } }
321                });
322        /**Allow these threads to timeout and release all resources. */
323        static { _lowPriorityThreadPoolDiscardable.allowCoreThreadTimeOut(true); }
324    
325        /**If true then a low-priority CPU-bound task need not be discarded if the thread pool has room and the system is not too busy.
326         * Conversely, when this is false, discard such tasks even when the pool could take them.
327         * <p>
328         * Note that the test of system load may be too expensive.
329         */
330        private static boolean _dontDiscardLowPriCPUBoundTask()
331            { return(_cpuIntensiveThreadsRunningNotOverLimit(MAX_LO_PRI_THREADS - 1) && !ThreadUtils.isCPUHeavilyLoaded()); }
332    
333        /**Returns approximate space (spare threads) currently available in the lowPriorityThreadPoolDiscardable; non-negative. */
334        public static int lowPriorityThreadPoolDiscardableSpace()
335            { return(_lowPriorityThreadPoolDiscardable.getMaximumPoolSize() - _lowPriorityThreadPoolDiscardable.getActiveCount()); }
336    
337        /**Returns true if it may be possible to have a low-priority discardable task run immediately.
338         * This also indicates in general that the thread pools are not very busy
339         * and a true return can be regarded as an <em>indication</em> to that effect.
340         * <p>
341         * This returning true is no guarantee that a low-priority discardable task
342         * that is immediately submitted will be run, however,
343         * since other threads may attempt to (and manage to) start a task
344         * that would make this false.
345         */
346        public static boolean couldRunLowPriorityDiscardableTask()
347            { return((lowPriorityThreadPoolDiscardableSpace() > 0) && _dontDiscardLowPriCPUBoundTask()); }
348    
349        /**Shared non-reconfigurable thread pool for discardable low-priority compute-bound activities.
350         * Most suitable for almost-entirely CPU-bound threads,
351         * and so the pool size is bounded to a value roughly proportional to
352         * (but less than) the number of CPUs available to this JVM (at creation of this pool).
353         * <p>
354         * This prefers to queue tasks and execute them on a small number of CPUs,
355         * only potentially increasing the pool size if the queue fills.
356         * (This will only queue tasks on a machine with a significant number of CPUs,
357         * where it could run more than one of these tasks at once, eg not on a uniprocessor.)
358         * <p>
359         * When this pool and its queue are full,
360         * or other CPU-bound pools appear to be using all available CPUs,
361         * then <em>this silently discards new tasks submitted</em>.
362         * <p>
363         * These threads run with minimum priority.
364         * <p>
365         * Tasks submitted to this pool <em>should not</em> generally
366         * consume heavy resources (such as memory) other than CPU
367         * so that we can safely run many of them at once.
368         * This pool tries not to use more than half the CPUs at once
369         * when there are multiple CPUs available.
370         * <p>
371         * A bounded (small) amount of work can be queued for this pool
372         * (ie tasks are usually executed immediately or should not wait long),
373         * and excess work (with no free pool threads or queue space available)
374         * will be <em>silently discarded</em>.
375         * <p>
376         * This puts an upper bound on Thread resources (eg memory for stack)
377         * consumed by threads in this pool.
378         * <p>
379         * The threads in the pool are daemon threads,
380         * so should not prevent the JVM from exiting.
381         */
382        public static final ExecutorService lowPriorityThreadPoolDiscardable =
383            Executors.unconfigurableExecutorService(_lowPriorityThreadPoolDiscardable);
384    
385        /**Returns true if the count of (non-I/O-bound) threads running in our pools is no higher than the specified limit.
386         * This is only likely to be an approximation
387         * since there is no synchronisation, etc,
388         * and statistical parameters are being combined here.
389         *
390         * @param limit  expected to be non-negative
391         *     (but may be negative resulting in always returning false)
392         */
393        private static boolean _cpuIntensiveThreadsRunningNotOverLimit(final int limit)
394            {
395            int count = 0;
396            // Order these to minimise run-time.
397            if(limit < (count += _computeIntensiveThreadPool.getActiveCount())) { return(false); }
398            if(limit < (count += _lowPriorityThreadPool.getActiveCount())) { return(false); }
399            if(limit < (count += _lowPriorityThreadPoolDiscardable.getActiveCount())) { return(false); }
400            return(true);
401            }
402    
403        /**Dump some diagnostics during startup. */
404        static
405                {
406                /* if(IsDebug.isDebug) */
407                        {
408                        System.out.println("INFO: ThreadUtils: init: available processors="+AVAILABLE_PROCESSORS+
409                                                                            ", MAX_LO_PRI_THREADS="+MAX_LO_PRI_THREADS+
410                                                                            ", MAX_THREADS_NCTP="+MAX_THREADS_NCTP);
411                        }
412                }
413    
414        /**Return a Future that already has its result computed. */
415        public static <T> Future<T> makeCompletedFuture(final T r)
416            {
417            return new Future<T>(){
418                public boolean cancel(final boolean mayInterruptIfRunning)
419                    { return(false); }
420                public T get() // throws InterruptedException, ExecutionException
421                    { return(r); }
422                public T get(final long timeout, final TimeUnit unit) // throws InterruptedException, ExecutionException, TimeoutException
423                    { return(r); }
424                public boolean isCancelled()
425                    { return(false); }
426                public boolean isDone()
427                    { return(true); }
428                };
429            }
430    
431    
432        /**If false, then it seems to be unsafe to try to check system load.
433         * Avoid uptime check on some early 1.6.0 pre-releases that crash...
434         */
435        private static final boolean CHECK_UPTIME = !System.getProperty("java.version").startsWith("1.6.0-d");
436    
437        /**Get load fraction in queued jobs per CPU over last minute (like UNIX load average, but normalised); -1 if unavailable otherwise non-negative.
438         * A value of 0 indicates an idle system in CPU terms,
439         * a value of 1 indicates fully-utilised CPUs,
440         * and a value greater than one indicates more work available than CPUs to run it.
441         * <p>
442         * Like the UNIX load average, but divided by the number of CPUs to normalise it.
443         */
444        public static float loadFraction()
445            {
446            if(!CHECK_UPTIME) { return(-1f); }
447            final OperatingSystemMXBean operatingSystemMXBean = java.lang.management.ManagementFactory.getOperatingSystemMXBean();
448            final int cpus = operatingSystemMXBean.getAvailableProcessors();
449            final float loadAverage = (float) operatingSystemMXBean.getSystemLoadAverage();
450            if(loadAverage < 0) { return(-1f); }
451            if(cpus == 1) { return(loadAverage); }
452            return(loadAverage / cpus);
453            }
454    
455        /**Private flag for cpuHeavilyLoaded() to note time when we last sampled.
456         * Given that the underlying value is on the scale of a minute,
457         * we need only sample on a similar timescale,
458         * though to detect and respond quickly to overload a few seconds is probably best.
459         * <p>
460         * Initially zero.
461         * <p>
462         * Marked volatile for thread-safe lock-free access.
463         */
464        private static volatile long _lastHeavilyLoadSampleTime;
465    
466        /**Private cache for cpuHeavilyLoaded(): True if system was heavily loaded at last sample, False if lightly loaded.
467         * Initially null.
468         * <p>
469         * Marked volatile for thread-safe lock-free access.
470         */
471        private static volatile Boolean _lastHeavilyLoadSample;
472    
473        /**Returns TRUE if CPU known to be heavily loaded over the last minute or so, FALSE if lightly loaded, else null if load 'normal'.
474         * May be cached for efficiency, eg to avoid expensive system calls and floating-point arithmetic.
475         * <p>
476         * When running well, the system should over around the normal / light-load boundary.
477         * <p>
478         * May retain/cache 'heavily loaded' value longer (ie asymmetrically).
479         */
480        public static Boolean cpuHeavyLoad()
481            {
482            final long now = System.currentTimeMillis();
483            final Boolean wasHeavilyLoaded = _lastHeavilyLoadSample;
484            if(_lastHeavilyLoadSampleTime + ((Boolean.TRUE == wasHeavilyLoaded) ? 8192 : 2048) > now) { return(wasHeavilyLoaded); } // From cache.
485            _lastHeavilyLoadSampleTime = now; // Postpone next sampling (to try to avoid race with this one).
486            final Boolean nowHeavilyLoaded; // New sample.
487            final float loadFraction = loadFraction();
488            if(loadFraction >= LocalProps.getHeavyLoadMin()) { nowHeavilyLoaded = Boolean.TRUE; }
489            else if(loadFraction < LocalProps.getLightLoadMax()) { nowHeavilyLoaded = Boolean.FALSE; }
490            else { nowHeavilyLoaded = null; }
491            _lastHeavilyLoadSample = nowHeavilyLoaded; // Cache result...
492            return(nowHeavilyLoaded);
493            }
494    
495        /**Returns true if CPU known to be heavily loaded over the last minute or so, else false.
496         * May be cached for efficiency, eg to avoid expensive system calls and floating-point arithmetic.
497         */
498        public static boolean isCPUHeavilyLoaded()
499            { return(Boolean.TRUE == cpuHeavyLoad()); }
500    
501        /**Returns true if CPU known to be lightly loaded over the last minute or so, else false.
502         * May be cached for efficiency, eg to avoid expensive system calls and floating-point arithmetic.
503         */
504        public static boolean isCPULightlyLoaded()
505            { return(Boolean.FALSE == cpuHeavyLoad()); }
506        }