001/*
002 * This file is part of the Kompics component model runtime.
003 *
004 * Copyright (C) 2009 Swedish Institute of Computer Science (SICS) Copyright (C)
005 * 2009 Royal Institute of Technology (KTH)
006 *
007 * Kompics is free software; you can redistribute it and/or modify it under the
008 * terms of the GNU General Public License as published by the Free Software
009 * Foundation; either version 2 of the License, or (at your option) any later
010 * version.
011 *
012 * This program is distributed in the hope that it will be useful, but WITHOUT
013 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
014 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
015 * details.
016 *
017 * You should have received a copy of the GNU General Public License along with
018 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
019 * Place - Suite 330, Boston, MA 02111-1307, USA.
020 */
021package se.sics.kompics;
022
023import java.lang.reflect.Constructor;
024import java.lang.reflect.InvocationTargetException;
025import java.util.concurrent.atomic.AtomicInteger;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028import se.sics.kompics.Component.State;
029import se.sics.kompics.Fault.ResolveAction;
030import se.sics.kompics.config.Config;
031import se.sics.kompics.config.TypesafeConfig;
032import se.sics.kompics.scheduler.ForkJoinScheduler;
033import se.sics.kompics.scheduler.WorkStealingScheduler;
034
035/**
036 * The <code>Kompics</code> class.
037 *
038 * @author Cosmin Arad {@literal <[email protected]>}
039 * @author Jim Dowling {@literal <[email protected]>}
040 * @author Lars Kroll {@literal <[email protected]>}
041 * @version $Id$
042 */
043public final class Kompics {
044
045    public static final long SHUTDOWN_TIMEOUT = 5000;
046    public static final Logger logger = LoggerFactory.getLogger("Kompics");
047    public static final AtomicInteger maxNumOfExecutedEvents = new AtomicInteger(1);
048    private static boolean on = false;
049    private static Scheduler scheduler;
050    private static ComponentCore mainCore;
051    private static final Kompics obj = new Kompics();
052    private static final FaultHandler defaultFaultHandler = new FaultHandler() {
053
054        @Override
055        public Fault.ResolveAction handle(Fault f) {
056            return ResolveAction.ESCALATE;
057        }
058    };
059    private static FaultHandler faultHandler = defaultFaultHandler;
060    private static Config config;
061
062    /**
063     * Set the Kompics runtime scheduler.
064     * 
065     * @param sched
066     *            the scheduler to set
067     * 
068     * @throws java.lang.RuntimeException
069     *             if the Kompics runtime is already running
070     */
071    public static void setScheduler(Scheduler sched) {
072        synchronized (obj) {
073            if (on) {
074                throw new RuntimeException("Kompics already created");
075            }
076            scheduler = sched;
077        }
078    }
079
080    /**
081     * Get the current Kompics runtime scheduler.
082     * 
083     * @return the current scheduler
084     */
085    public static Scheduler getScheduler() {
086        synchronized (obj) {
087            return scheduler;
088        }
089    }
090
091    /**
092     * Set the root fault handler to its default.
093     * 
094     * @param fh
095     *            the new root fault handler
096     * 
097     * @throws java.lang.RuntimeException
098     *             if the Kompics runtime is already running
099     */
100    public static void setFaultHandler(FaultHandler fh) {
101        synchronized (obj) {
102            if (on) {
103                throw new RuntimeException("Kompics already created");
104            }
105            faultHandler = fh;
106        }
107    }
108
109    /**
110     * Reset the root fault handler to its default.
111     * 
112     * @throws java.lang.RuntimeException
113     *             if the Kompics runtime is already running
114     */
115    public static void resetFaultHandler() {
116        synchronized (obj) {
117            if (on) {
118                throw new RuntimeException("Kompics already created");
119            }
120            faultHandler = defaultFaultHandler;
121        }
122    }
123
124    /**
125     * Get the root fault handler.
126     * 
127     * @return the root fault handler
128     */
129    public static FaultHandler getFaultHandler() {
130        synchronized (obj) {
131            return faultHandler;
132        }
133    }
134
135    /**
136     * Set the root config to the default value.
137     * 
138     * @param conf
139     *            the new config to set
140     * 
141     * @throws java.lang.RuntimeException
142     *             if the Kompics runtime is already running
143     */
144    public static void setConfig(Config conf) {
145        synchronized (obj) {
146            if (on) {
147                throw new RuntimeException("Kompics already created");
148            }
149            config = conf;
150        }
151    }
152
153    /**
154     * Reset the root config to the default value.
155     * 
156     * @throws java.lang.RuntimeException
157     *             if the Kompics runtime is already running
158     */
159    public static void resetConfig() {
160        synchronized (obj) {
161            if (on) {
162                throw new RuntimeException("Kompics already created");
163            }
164            config = TypesafeConfig.load();
165        }
166    }
167
168    /**
169     * Get the top-level config instance.
170     * 
171     * Will load a default config, if none was set before.
172     * 
173     * @return the root config
174     */
175    public static Config getConfig() {
176        synchronized (obj) {
177            if (config == null) {
178                config = TypesafeConfig.load();
179            }
180            return config;
181        }
182    }
183
184    /**
185     * Checks if the Kompics runtime is currently running.
186     * 
187     * @return true, if its running
188     */
189    public static boolean isOn() {
190        synchronized (obj) {
191            return on;
192        }
193    }
194
195    /**
196     * Creates the top-level component and starts it together with the Kompics runtime.
197     * 
198     * The value {@code Init.NONE} is passed to the root component.
199     *
200     * @param <C>
201     *            the type of the root component
202     * @param main
203     *            the class instance of the root component
204     */
205    @SuppressWarnings("unchecked")
206    public static <C extends ComponentDefinition> void createAndStart(Class<C> main) {
207        // createAndStart(main, Runtime.getRuntime().availableProcessors());
208        createAndStart(main, (Init<C>) Init.NONE, 1);
209    }
210
211    /**
212     * Creates the top-level component and starts it together with the Kompics runtime.
213     * 
214     * Passes the provided init event to the root component.
215     *
216     * @param <C>
217     *            the type of the root component
218     * @param main
219     *            the class instance of the root component
220     * @param initEvent
221     *            the init event to pass to the root component
222     */
223    public static <C extends ComponentDefinition> void createAndStart(Class<C> main, Init<C> initEvent) {
224        createAndStart(main, initEvent, 1);
225    }
226
227    /**
228     * Creates the top-level component and starts it together with the Kompics runtime.
229     * 
230     * The value {@code Init.NONE} is passed to the root component.
231     *
232     * @param <C>
233     *            the type of the root component
234     * @param main
235     *            the class instance of the root component
236     * @param workers
237     *            the number of workers to use in the runtime's threadpool
238     */
239    @SuppressWarnings("unchecked")
240    public static <C extends ComponentDefinition> void createAndStart(Class<C> main, int workers) {
241        createAndStart(main, (Init<C>) Init.NONE, workers, 1);
242    }
243
244    /**
245     * Creates the top-level component and starts it together with the Kompics runtime.
246     * 
247     * The value {@code Init.NONE} is passed to the root component.
248     *
249     * @param <C>
250     *            the type of the root component
251     * @param main
252     *            the class instance of the root component
253     * @param initEvent
254     *            the init event to pass to the root component
255     * @param workers
256     *            the number of workers to use in the runtime's threadpool
257     */
258    public static <C extends ComponentDefinition> void createAndStart(Class<C> main, Init<C> initEvent, int workers) {
259        createAndStart(main, initEvent, workers, 1);
260    }
261
262    /**
263     * Creates the top-level component and starts it together with the Kompics runtime.
264     * 
265     * The value {@code Init.NONE} is passed to the root component.
266     *
267     * @param <C>
268     *            the type of the root component
269     * @param main
270     *            the class instance of the root component
271     * @param workers
272     *            the number of workers to use in the runtime's threadpool
273     * @param maxEventExecuteNumber
274     *            the maximum number of events to execute before forcing a rescheduling of a component
275     */
276    @SuppressWarnings("unchecked")
277    public static <C extends ComponentDefinition> void createAndStart(Class<C> main, int workers,
278            int maxEventExecuteNumber) {
279        createAndStart(main, (Init<C>) Init.NONE, workers, maxEventExecuteNumber);
280    }
281
282    /**
283     * Creates the top-level component and starts it together with the Kompics runtime.
284     * 
285     * The value {@code Init.NONE} is passed to the root component.
286     *
287     * @param <C>
288     *            the type of the root component
289     * @param main
290     *            the class instance of the root component
291     * @param initEvent
292     *            the init event to pass to the root component
293     * @param workers
294     *            the number of workers to use in the runtime's threadpool
295     * @param maxEventExecuteNumber
296     *            the maximum number of events to execute before forcing a rescheduling of a component
297     */
298    public static <C extends ComponentDefinition> void createAndStart(Class<C> main, Init<C> initEvent, int workers,
299            int maxEventExecuteNumber) {
300        synchronized (obj) {
301            if (on) {
302                throw new RuntimeException("Kompics already created");
303            }
304            on = true;
305
306            if (scheduler == null) {
307                // scheduler = new WorkStealingScheduler(workers);
308                scheduler = new ForkJoinScheduler(workers);
309                // scheduler = new ThreadPoolScheduler(workers);
310            }
311
312            Kompics.maxNumOfExecutedEvents.lazySet(maxEventExecuteNumber);
313
314            try {
315                ComponentDefinition mainComponent;
316                if (initEvent == Init.NONE) { // NONE instance
317                    mainComponent = main.newInstance();
318                } else {
319                    Constructor<C> constr = main.getConstructor(initEvent.getClass());
320                    mainComponent = constr.newInstance(initEvent);
321                }
322                mainCore = mainComponent.getComponentCore();
323                mainCore.setScheduler(scheduler);
324
325                // mainCore.workCount.incrementAndGet();
326                // start Main
327                ((PortCore<ControlPort>) mainCore.getControl()).doTrigger(Start.event, 0, mainCore);
328            } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
329                    | InvocationTargetException | NoSuchMethodException | SecurityException e) {
330                throw new RuntimeException("Cannot create main component " + main.getCanonicalName(), e);
331            }
332
333            scheduler.proceed();
334        }
335    }
336
337    private Kompics() {
338    }
339
340    /**
341     * Asynchronously shut down the Kompics runtime.
342     * <p>
343     * Can be used safely from within components.
344     */
345    public static void asyncShutdown() {
346        Runnable r = new Runnable() {
347
348            @Override
349            public void run() {
350                Kompics.shutdown();
351                ;
352            }
353        };
354        Thread t = new Thread(r);
355        t.start();
356    }
357
358    /**
359     * Synchronously shut down the Kompics runtime.
360     * <p>
361     * Can <b>not</b> be used safely from within Kompics components. Use {@link #asyncShutdown()} instead.
362     */
363    public static void shutdown() {
364        synchronized (obj) {
365            if (mainCore != null) {
366                if (mainCore.state == State.ACTIVE) {
367                    mainCore.control().doTrigger(Kill.event, mainCore.wid, mainCore);
368                }
369                synchronized (mainCore) { // Potential deadlock spot
370                    long start = System.currentTimeMillis();
371                    while (mainCore.state != Component.State.PASSIVE && mainCore.state != Component.State.DESTROYED) {
372                        try {
373                            mainCore.wait(SHUTDOWN_TIMEOUT);
374                        } catch (InterruptedException ex) {
375                            logger.warn("Failed orderly Kompics shutdown", ex);
376                        }
377                        if ((System.currentTimeMillis() - start) > SHUTDOWN_TIMEOUT) {
378                            logger.warn("Failed to shutdown Kompics in time. Forcing shutdown.");
379                            break;
380                        }
381                    }
382                    mainCore.cleanPorts();
383                }
384                mainCore.cleanPorts();
385            }
386            if (scheduler != null) {
387                scheduler.shutdown();
388            }
389            on = false;
390            scheduler = null;
391            obj.notifyAll();
392        }
393    }
394
395    /**
396     * Force an immediate shutdown of the Kompics runtime.
397     * <p>
398     * Can <b>not</b> be used safely from within Kompics components.
399     */
400    public static void forceShutdown() {
401        synchronized (obj) {
402            if (scheduler != null) {
403                scheduler.shutdown();
404            }
405            on = false;
406            scheduler = null;
407
408            obj.notifyAll();
409        }
410    }
411
412    /**
413     * Wait until the Kompics runtime is shut down.
414     * <p>
415     * Can <b>not</b> be used safely from within Kompics components.
416     * 
417     * @throws InterruptedException
418     *             if the waiting is interrupted
419     */
420    public static void waitForTermination() throws InterruptedException {
421        synchronized (obj) {
422            while (on) {
423                obj.wait();
424            }
425        }
426    }
427
428    static void handleFault(final Fault f) {
429        final FaultHandler fh = faultHandler;
430        Thread t = new Thread(new Runnable() {
431
432            @Override
433            public void run() {
434                ResolveAction ra = fh.handle(f);
435                switch (ra) {
436                case RESOLVED:
437                    Kompics.logger.info("Fault {} was resolved by user.", f);
438                    break;
439                case IGNORE:
440                    Kompics.logger.info("Fault {} was declared to be ignored by user. Resuming component...", f);
441                    f.source.markSubtreeAs(Component.State.PASSIVE);
442                    f.source.control().doTrigger(Start.event, 0, mainCore);
443                    break;
444                case DESTROY:
445                    Kompics.logger.info("User declared that Fault {} should quit Kompics...", f);
446                    Kompics.forceShutdown(); {
447                    try {
448                        Kompics.waitForTermination();
449                    } catch (InterruptedException ex) {
450                        Kompics.logger.error("Interrupted while waiting for Kompics termination...");
451                        System.exit(1);
452                    }
453                }
454                    Kompics.logger.info("finished quitting Kompics.");
455                    break;
456                default:
457                    Kompics.logger.info("User declared that Fault {} should quit JVM...", f);
458                    System.exit(1);
459                }
460            }
461
462        });
463        t.start();
464    }
465
466    /**
467     * Log statistics if the @{link WorkStealingScheduler} is used.
468     * 
469     * @deprecated since 1.2.0, don't use the the @{link WorkStealingScheduler}!
470     */
471    @Deprecated
472    public static void logStats() {
473        if (scheduler instanceof WorkStealingScheduler) {
474            ((WorkStealingScheduler) scheduler).logStats();
475        }
476    }
477}