/* * Javolution - Java(TM) Solution for Real-Time and Embedded Systems * Copyright (C) 2006 - Javolution (http://javolution.org/) * All rights reserved. * * Permission to use, copy, modify, and distribute this software is * freely granted, provided that this notice is preserved. */ package javolution.context; import javolution.lang.Configurable; import javolution.lang.MathLib; import javolution.lang.Reflection; /** * <p> This class represents a context to take advantage of concurrent * algorithms on multi-processors systems.</p> * * <p> When a thread enters a concurrent context, it may performs concurrent * executions by calling the {@link #execute(Runnable)} static method. * The logic is then executed by a concurrent thread or by the current * thread itself if there is no concurrent thread immediately available * (the number of concurrent threads is limited, see * <a href="{@docRoot}/overview-summary.html#configuration"> * Javolution Configuration</a> for details).</p> * * <p> Only after all concurrent executions are completed, is the current * thread allowed to exit the scope of the concurrent context * (internal synchronization).</p> * * <p> Concurrent logics always execute within the same {@link Context} as * the calling thread. For example, if the main thread runs in a * {@link StackContext}, concurrent executions are performed in the * same {@link StackContext} as well.</p> * * <p> Concurrent contexts ensure the same behavior whether or not the execution * is performed by the current thread or a concurrent thread. Any exception * raised during the concurrent logic executions is propagated to the * current thread.</p> * * <p> Concurrent contexts are easy to use, and provide automatic * load-balancing between processors with almost no overhead. Here is * an example of <b>concurrent/recursive</b> implementation of the * Karatsuba multiplication for large integers:[code] * public LargeInteger multiply(LargeInteger that) { * if (that._size <= 1) { * return multiply(that.longValue()); // Direct multiplication. * } else { // Karatsuba multiplication in O(n^log2(3)) * int bitLength = this.bitLength(); * int n = (bitLength >> 1) + (bitLength & 1); * * // this = a + 2^n b, that = c + 2^n d * LargeInteger b = this.shiftRight(n); * LargeInteger a = this.minus(b.shiftLeft(n)); * LargeInteger d = that.shiftRight(n); * LargeInteger c = that.minus(d.shiftLeft(n)); * Multiply ac = Multiply.valueOf(a, c); * Multiply bd = Multiply.valueOf(b, d); * Multiply abcd = Multiply.valueOf(a.plus(b), c.plus(d)); * ConcurrentContext.execute(ac, bd, abcd); * // a*c + ((a+b)*(c+d)-a*c-b*d) 2^n + b*d 2^2n * return ac.value().plus( * abcd.value().minus(ac.value().plus(bd.value())).shiftWordLeft(n)).plus( * bd.value().shiftWordLeft(n << 1)); * } * } * private static class Multiply implements Runnable { * LargeInteger _left, _right, _value; * static Multiply valueOf(LargeInteger left, LargeInteger right) { * Multiply multiply = new Multiply(); // Or use an ObjectFactory (to allow stack allocation). * multiply._left = left; * multiply._right = right; * return multiply; * } * public void run() { * _value = _left.times(_right); // Recursive. * } * public LargeInteger value() { * return _result; * } * };[/code] * * Here is a concurrent/recursive quick/merge sort using anonymous inner * classes (the same method is used for * <a href="http://javolution.org/doc/benchmark.html">benchmark</a>):[code] * private void quickSort(final FastTable<? extends Comparable> table) { * final int size = table.size(); * if (size < 100) { * table.sort(); // Direct quick sort. * } else { * // Splits table in two and sort both part concurrently. * final FastTable<? extends Comparable> t1 = FastTable.newInstance(); * final FastTable<? extends Comparable> t2 = FastTable.newInstance(); * ConcurrentContext.enter(); * try { * ConcurrentContext.execute(new Runnable() { * public void run() { * t1.addAll(table.subList(0, size / 2)); * quickSort(t1); // Recursive. * } * }); * ConcurrentContext.execute(new Runnable() { * public void run() { * t2.addAll(table.subList(size / 2, size)); * quickSort(t2); // Recursive. * } * }); * } finally { * ConcurrentContext.exit(); * } * // Merges results. * for (int i=0, i1=0, i2=0; i < size; i++) { * if (i1 >= t1.size()) { * table.set(i, t2.get(i2++)); * } else if (i2 >= t2.size()) { * table.set(i, t1.get(i1++)); * } else { * Comparable o1 = t1.get(i1); * Comparable o2 = t2.get(i2); * if (o1.compareTo(o2) < 0) { * table.set(i, o1); * i1++; * } else { * table.set(i, o2); * i2++; * } * } * } * FastTable.recycle(t1); * FastTable.recycle(t2); * } * }[/code] * * <p> {@link #getConcurrency() Concurrency} can be {@link LocalContext locally} * adjusted. For example:[code] * LocalContext.enter(); * try { // Do not use more than half of the processors during analysis. * ConcurrentContext.setConcurrency((Runtime.getRuntime().availableProcessors() / 2) - 1); * runAnalysis(); // Use concurrent contexts internally. * } finally { * LocalContext.exit(); * }[/code] </p> * It should be noted that the concurrency cannot be increased above the * configurable {@link #MAXIMUM_CONCURRENCY maximum concurrency}. * In other words, if the maximum concurrency is <code>0</code>, * concurrency is disabled regardless of local concurrency settings.</p> * * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> * @version 5.1, July 2, 2007 */ public abstract class ConcurrentContext extends Context { /** * Holds the maximum number of concurrent executors * (see <a href="{@docRoot}/overview-summary.html#configuration"> * Javolution Configuration</a> for details). */ public static final Configurable/*<Integer>*/ MAXIMUM_CONCURRENCY = new Configurable( new Integer(availableProcessors() - 1)) { protected void notifyChange(Object oldValue, Object newValue) { // The maximum concurrency is also the default concurrency. CONCURRENCY.setDefault(newValue); } }; private static int availableProcessors() { Reflection.Method availableProcessors = Reflection.getInstance().getMethod("java.lang.Runtime.availableProcessors()"); if (availableProcessors != null) { Integer processors = (Integer) availableProcessors.invoke(Runtime.getRuntime()); return processors.intValue(); } else // J2ME. return 1; } /** * Holds the default implementation. Concurrent executions are performed * in the same memory area and at the same priority as the calling thread. * This implementation uses <code>javax.realtime.RealtimeThread</code> * for concurrent threads. Alternative (RTSJ) implementations could also use * <code>javax.realtime.NoHeapRealtimeThread</code>. */ public static final Configurable/*<Class<? extends ConcurrentContext>>*/ DEFAULT = new Configurable(Default.class) {}; /** * Holds the current concurrency. */ private static final LocalContext.Reference CONCURRENCY = new LocalContext.Reference( MAXIMUM_CONCURRENCY.get()); /** * Default constructor. */ protected ConcurrentContext() { } /** * Enters a concurrent context (instance of {@link #DEFAULT}). */ public static void enter() { Context.enter((Class) DEFAULT.get()); } /** * Enters a concurrent context only if the specified condition is verified. * * @param condition <code>true</code> to enter a concurrent context; * <code>false</code> otherwise. */ public static void enter(boolean condition) { if (condition) { ConcurrentContext.enter(); } } /** * Exits the current concurrent context. * * @throws ClassCastException if the context is not a concurrent context. */ public static void exit() { Context.exit(ConcurrentContext.class); } /** * Exits a concurrent context only if the specified condition is verified. * * @param condition <code>true</code> to exit a concurrent context; * <code>false</code> otherwise. */ public static void exit(boolean condition) { if (condition) { ConcurrentContext.exit(); } } /** * Set the {@link LocalContext local} concurrency. Concurrency is * hard limited by {@link #MAXIMUM_CONCURRENCY}. * * @param concurrency the new concurrency (<code>0</code> or negative * number to disable concurrency). */ public static void setConcurrency(int concurrency) { concurrency = MathLib.max(0, concurrency); concurrency = MathLib.min(((Integer) MAXIMUM_CONCURRENCY.get()).intValue(), concurrency); CONCURRENCY.set(new Integer(concurrency)); } /** * Returns the {@link LocalContext local} concurrency. * * @return the maximum number of concurrent thread. */ public static int getConcurrency() { return ((Integer) CONCURRENCY.get()).intValue(); } /** * Executes the specified logic by a concurrent thread if * one available; otherwise the logic is executed by the current thread. * Any exception or error occurring during concurrent executions is * propagated to the current thread upon {@link #exit} * of the concurrent context. * * @param logic the logic to execute concurrently if possible. * @throws ClassCastException if the current context is not a * {@link ConcurrentContext}. */ public static void execute(Runnable logic) { ConcurrentContext ctx = (ConcurrentContext) Context.getCurrentContext(); ctx.executeAction(logic); } /** * Executes the specified logics concurrently. This method is * equivalent to:[code] * ConcurrentContext.enter(); * try { * ConcurrentContext.execute(logics[0]); * ConcurrentContext.execute(logics[1]); * ... * } finally { * ConcurrentContext.exit(); * } * [/code] * * @param logics the logics to execute concurrently if possible. *@JVM-1.5+@ public static void execute(Runnable... logics) { ConcurrentContext.enter(); ConcurrentContext ctx = (ConcurrentContext) ConcurrentContext.getCurrentContext(); try { for (int i=0; i < logics.length; i++) { ctx.executeAction(logics[i]); } } finally { ConcurrentContext.exit(); } } /**/ /** * Executes the specified logic concurrently if possible. * * @param logic the logic to execute. */ protected abstract void executeAction(Runnable logic); /** * Default implementation using {@link ConcurrentThread} executors. */ static final class Default extends ConcurrentContext { /** * Holds the concurrent executors (created during class initialization). * The maximum concurrency cannot be changed afterward. */ private static final ConcurrentThread[] _Executors = new ConcurrentThread[((Integer) MAXIMUM_CONCURRENCY.get()).intValue()]; static { for (int i = 0; i < Default._Executors.length; i++) { Default._Executors[i] = new ConcurrentThread(); Default._Executors[i].start(); } } /** * Holds the concurrency. */ private int _concurrency; /** * Holds any error occurring during concurrent execution. */ private volatile Throwable _error; /** * Holds the number of concurrent execution initiated. */ private int _initiated; /** * Holds the number of concurrent execution completed. */ private int _completed; // Implements Context abstract method. protected void enterAction() { _concurrency = ConcurrentContext.getConcurrency(); } // Implements ConcurrentContext abstract method. protected void executeAction(Runnable logic) { if (_error != null) return; // No point to continue (there is an error). for (int i = _concurrency; --i >= 0;) { if (_Executors[i].execute(logic, this)) { _initiated++; return; // Done concurrently. } } // Execution by current thread. logic.run(); } // Implements Context abstract method. protected void exitAction() { try { if (_initiated != 0) synchronized (this) { while (_initiated != _completed) { try { this.wait(); } catch (InterruptedException e) { throw new ConcurrentException(e); } } } if (_error != null) { if (_error instanceof RuntimeException) throw ((RuntimeException) _error); if (_error instanceof Error) throw ((Error) _error); throw new ConcurrentException(_error); // Wrapper. } } finally { _error = null; _initiated = 0; _completed = 0; } } // Called when a concurrent execution starts. void started() { Context.setConcurrentContext(this); } // Called when a concurrent execution finishes. void completed() { synchronized (this) { _completed++; this.notify(); } AllocatorContext.getCurrentAllocatorContext().deactivate(); } // Called when an error occurs. void error(Throwable error) { synchronized (this) { if (_error == null) // First error. _error = error; } } } // Allows instances of private classes to be factory produced. static { ObjectFactory.setInstance(new ObjectFactory() { protected Object create() { return new Default(); } }, Default.class); } }