package textbender.a.r.desk; // Copyright 2006, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Textbender Software"), to deal in the Textbender Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Textbender Software, and to permit persons to whom the Textbender Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Textbender Software. THE TEXTBENDER SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TEXTBENDER SOFTWARE OR THE USE OR OTHER DEALINGS IN THE TEXTBENDER SOFTWARE. import java.util.concurrent.*; import java.util.concurrent.atomic.*; import textbender.g.hold.Hold; import textbender.g.lang.ThreadSafe; import textbender.g.util.concurrent.ScheduledThreadPoolExecutorL; /** The desk daemon as a whole. * The single instance of DeskDaemon is available via DeskDaemon.i(). */ public final @ThreadSafe class DeskDaemon { /** The single instance of DeskDaemon. */ public static DeskDaemon i() { return instanceA.get(); } private static final AtomicReference instanceA = new AtomicReference(); /** Creates the single instance of DeskDaemon, * and makes it available via {@linkplain #i() i}(). */ DeskDaemon() { if( !instanceA.compareAndSet( /*expect*/null, DeskDaemon.this )) throw new IllegalStateException(); executor = Executors.unconfigurableScheduledExecutorService ( new ScheduledThreadPoolExecutorL( /*pool size*/1, new ThreadFactory() { private final AtomicInteger threadCountA = new AtomicInteger(); // only an aid, in attempt to keep thread names unique public Thread newThread( Runnable runnable ) { Thread thread = new Thread ( runnable, "desk executor " + threadCountA.incrementAndGet() ); { // regardless of caller's configuration: thread.setDaemon( false ); thread.setPriority( Thread.NORM_PRIORITY ); } executorThreadA.set( thread ); return thread; } }) ); Run.i().spool().add( new Hold() { public @ThreadSafe void release() { executor.shutdown(); } }); } // ------------------------------------------------------------------------------------ /** The general-purpose desk executor, an asynchronous executor * that employs a single thread, the "desk executor" thread. *

* Most of its activity consists of dispatching events * to other processes on the host. Parallel dispatch * helps to avoid distributed deadlock during event handling. * And the buffering provided by the executor's queue * helps to keep other processes unblocked during bursts of activity. *

* * @see #isExecutorThread() */ public ScheduledExecutorService executor() { return executor; } private final ScheduledExecutorService executor; private final AtomicReference executorThreadA = new AtomicReference(); /** Returns true if the calling thread is the desk executor thread. * Use this call to ensure that a given task * is being executed (or not being) on the desk executor thread. */ public boolean isExecutorThread() { return Thread.currentThread().equals( executorThreadA.get() ); } }