001: /*
002: * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
003: * Copyright (C) 2006 - Javolution (http://javolution.org/)
004: * All rights reserved.
005: *
006: * Permission to use, copy, modify, and distribute this software is
007: * freely granted, provided that this notice is preserved.
008: */
009: package javolution.context;
010:
011: import j2me.lang.ThreadLocal;
012: import javolution.lang.Configurable;
013: import javolution.lang.ValueType;
014: import javolution.util.FastMap;
015: import javolution.util.FastTable;
016:
017: /**
018: * <p> This class represents a stack {@link AllocatorContext allocator context};
019: * (using thread-local pools or RTSJ <code>ScopedMemory</code>).</p>
020: *
021: * <p> Stacks allocations reduce heap memory allocation and often result in
022: * faster execution time for almost all objects but the smallest one.</p>
023: *
024: * <p> Stack allocated objects should never be assigned to static members
025: * (see {@link ImmortalContext}). Also, methods entering/exiting stack
026: * contexts should ensure that stack allocated objects do not escape from
027: * their context scope. If necessary, stack objects can be exported using
028: * {@link #outerExecute} or {@link #outerCopy}:[code]
029: * public class LargeInteger implements ValueType, Realtime {
030: * public LargeInteger sqrt() {
031: * StackContext.enter();
032: * try {
033: * LargeInteger result = ZERO;
034: * LargeInteger k = this.shiftRight(this.bitLength() / 2)); // First approximation.
035: * while (true) { // Newton Iteration.
036: * result = (k.plus(this.divide(k))).shiftRight(1);
037: * if (result.equals(k)) return StackContext.outerCopy(result); // Exports result.
038: * k = result;
039: * }
040: * } finally {
041: * StackContext.exit();
042: * }
043: * }
044: * }[/code]</p>
045: *
046: * <p> It should be noted that future versions of the JVM may provide some
047: * limited support for stack allocation through escape analysis.
048: * Users can always {@link #DISABLED turn-off} stack allocation to
049: * revert to standard heap allocation.</p>
050: *
051: * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
052: * @version 5.2, August 19, 2007
053: */
054: public abstract class StackContext extends AllocatorContext {
055:
056: /**
057: * Holds the default implementation. This implementation uses thread-local
058: * pools. RTSJ alternative implementations could use
059: * <code>ScopedMemory</code> for their stack allocations.
060: */
061: public static final Configurable/*<Class<? extends StackContext>>*/DEFAULT = new Configurable(
062: Default.CLASS);
063:
064: /**
065: * Indicates if stack allocations are globally disabled.
066: */
067: public static final Configurable/*<Boolean>*/DISABLED = new Configurable/*<Boolean>*/(
068: new Boolean(false));
069:
070: /**
071: * Indicates if this stack context is disabled.
072: */
073: private boolean _isDisabled = false;
074:
075: /**
076: * Enters the {@link #DEFAULT} stack context.
077: *
078: * @return the statck context being entered.
079: */
080: public static StackContext enter() {
081: StackContext ctx = (StackContext) Context.enter((Class) DEFAULT
082: .get());
083: ctx._isDisabled = ((Boolean) DISABLED.get()).booleanValue();
084: return ctx;
085: }
086:
087: /**
088: * Exits the current stack context.
089: *
090: * @return the stack context being exited.
091: * @throws ClassCastException if the context is not a stack context.
092: */
093: public static/*StackContext*/Context exit() {
094: return (StackContext) Context.exit();
095: }
096:
097: /**
098: * Performs a copy of the specified value allocated outside of the
099: * current stack context.
100: *
101: * @param value the value to be copied.
102: * @return a copy allocated using the outer allocator.
103: */
104: public static/*<T extends ValueType>*/ValueType/*{T}*/outerCopy(
105: ValueType/*{T}*/value) {
106: StackContext ctx = (StackContext) AllocatorContext.getCurrent();
107: boolean isDisabled = ctx.isDisabled();
108: ctx.setDisabled(true);
109: Object copy = value.copy();
110: ctx.setDisabled(isDisabled);
111: return (ValueType/*{T}*/) copy;
112: }
113:
114: /**
115: * Performs a copy of the specified values outside of the
116: * current stack context (convenience methode). This method is
117: * equivalent to:[code]
118: * StackContext.outerExecute(new Runnable() {
119: * public void run() {
120: * for (int i = 0; i < values.length; i++) {
121: * values[i] = {ValueType) values[i].copy();
122: * }
123: * }
124: * });[/code]
125: * @param values the array whose elements are exported.
126: */
127: public static void outerCopy(ValueType[] values) {
128: StackContext ctx = (StackContext) AllocatorContext.getCurrent();
129: boolean isDisabled = ctx.isDisabled();
130: ctx.setDisabled(true);
131: for (int i = 0; i < values.length; i++) {
132: values[i] = (ValueType) values[i].copy();
133: }
134: ctx.setDisabled(isDisabled);
135: }
136:
137: /**
138: * Executes the specified logic outside of the current stack context.
139: *
140: * @param logic the logic to be executed outside of the current stack
141: * context.
142: */
143: public static void outerExecute(Runnable logic) {
144: StackContext ctx = (StackContext) AllocatorContext.getCurrent();
145: boolean isDisabled = ctx.isDisabled();
146: ctx.setDisabled(true);
147: logic.run();
148: ctx.setDisabled(isDisabled);
149: }
150:
151: /**
152: * Indicates if this stack context is disabled. When disabled, allocation
153: * are performed using the outer {@link AllocatorContext}.
154: */
155: public final boolean isDisabled() {
156: return _isDisabled;
157: }
158:
159: /**
160: * Enables/disables this stack context.
161: *
162: * @param isDisabled <code>true</code> if disabled; <code>false</code>
163: * otherwise.
164: */
165: public final void setDisabled(boolean isDisabled) {
166: if (isDisabled == _isDisabled)
167: return; // No change.
168: if (isDisabled) {
169: deactivate();
170: } else {
171: getOuter().getAllocatorContext().deactivate();
172: }
173: _isDisabled = isDisabled;
174: }
175:
176: /**
177: * Default implementation.
178: */
179: private static final class Default extends StackContext {
180:
181: private static final Class CLASS = new Default().getClass();
182:
183: private final ThreadLocal _factoryToAllocator = new ThreadLocal() {
184: protected Object initialValue() {
185: return new FastMap();
186: }
187: };
188:
189: private final ThreadLocal _activeAllocators = new ThreadLocal() {
190: protected Object initialValue() {
191: return new FastTable();
192: }
193: };
194:
195: // All allocators which have been used by the owner
196: // (no synchronization required).
197: private final FastTable _ownerUsedAllocators = new FastTable();
198:
199: // All allocators which have been used by the concurrent threads
200: // (synchronization required).
201: private final FastTable _nonOwnerUsedAllocators = new FastTable();
202:
203: protected void deactivate() {
204: FastTable allocators = (FastTable) _activeAllocators.get();
205: for (int i = 0, n = allocators.size(); i < n;) {
206: ((Allocator) allocators.get(i++)).user = null;
207: }
208: allocators.clear();
209: }
210:
211: protected Allocator getAllocator(ObjectFactory factory) {
212: if (isDisabled()) // Forwards to outer.
213: return getOuter().getAllocatorContext().getAllocator(
214: factory);
215:
216: FastMap factoryToAllocator = (FastMap) _factoryToAllocator
217: .get();
218: StackAllocator allocator = (StackAllocator) factoryToAllocator
219: .get(factory);
220: if (allocator == null) {
221: allocator = new StackAllocator(factory);
222: factoryToAllocator.put(factory, allocator);
223: }
224: if (allocator.user == null) { // Activate.
225: allocator.user = Thread.currentThread();
226: FastTable activeAllocators = (FastTable) _activeAllocators
227: .get();
228: activeAllocators.add(allocator);
229: }
230: if (!allocator._inUse) { // Add to lists of allocators used.
231: allocator._inUse = true;
232: if (Thread.currentThread() == getOwner()) {
233: _ownerUsedAllocators.add(allocator);
234: } else {
235: synchronized (_nonOwnerUsedAllocators) {
236: _nonOwnerUsedAllocators.add(allocator);
237: }
238: }
239: }
240: return allocator;
241: }
242:
243: protected void enterAction() {
244: getOuter().getAllocatorContext().deactivate();
245: }
246:
247: protected void exitAction() {
248: this .deactivate();
249:
250: // Resets all allocators used.
251: for (int i = 0; i < _ownerUsedAllocators.size(); i++) {
252: StackAllocator allocator = (StackAllocator) _ownerUsedAllocators
253: .get(i);
254: allocator.reset();
255: }
256: _ownerUsedAllocators.clear();
257: for (int i = 0; i < _nonOwnerUsedAllocators.size(); i++) {
258: StackAllocator allocator = (StackAllocator) _nonOwnerUsedAllocators
259: .get(i);
260: allocator.reset();
261: }
262: _nonOwnerUsedAllocators.clear();
263: }
264: }
265:
266: // Holds stack allocator implementation.
267: private static final class StackAllocator extends Allocator {
268:
269: private final ObjectFactory _factory;
270:
271: private boolean _inUse;
272:
273: private int _queueLimit;
274:
275: public StackAllocator(ObjectFactory factory) {
276: this ._factory = factory;
277: keepInQueue = true;
278: }
279:
280: protected Object allocate() {
281: if (_queueLimit >= queue.length)
282: resize();
283: Object obj = _factory.create();
284: queue[_queueLimit++] = obj;
285: return obj;
286: }
287:
288: protected void recycle(Object object) {
289: if (_factory.doCleanup()) {
290: _factory.cleanup(object);
291: }
292: for (int i = queueSize; i < _queueLimit; i++) {
293: if (queue[i] == object) { // Found it.
294: queue[i] = queue[queueSize];
295: queue[queueSize++] = object;
296: return;
297: }
298: }
299: throw new j2me.lang.UnsupportedOperationException(
300: "Cannot recycle to the stack an object "
301: + "which has not been allocated from the stack");
302: }
303:
304: protected void reset() {
305: _inUse = false;
306: while (_factory.doCleanup() && (queueSize != _queueLimit)) {
307: Object obj = queue[queueSize++];
308: _factory.cleanup(obj);
309: }
310: queueSize = _queueLimit;
311: }
312:
313: public String toString() {
314: return "Stack allocator for " + _factory.getClass();
315: }
316: }
317:
318: // Allows instances of private classes to be factory produced.
319: static {
320: ObjectFactory.setInstance(new ObjectFactory() {
321: protected Object create() {
322: return new Default();
323: }
324: }, Default.CLASS);
325: }
326: }
|