/* * 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 j2me.lang.ThreadLocal; import javolution.lang.Configurable; import javolution.lang.ValueType; import javolution.util.FastMap; import javolution.util.FastTable; /** *
This class represents a stack {@link AllocatorContext allocator context};
* (using thread-local pools or RTSJ ScopedMemory
).
Stacks allocations reduce heap memory allocation and often result in * faster execution time for almost all objects but the smallest one.
* *Stack allocated objects should never be assigned to static members * (see {@link ImmortalContext}). Also, methods entering/exiting stack * contexts should ensure that stack allocated objects do not escape from * their context scope. If necessary, stack objects can be exported using * {@link #outerExecute} or {@link #outerCopy}:[code] * public class LargeInteger implements ValueType, Realtime { * public LargeInteger sqrt() { * StackContext.enter(); * try { * LargeInteger result = ZERO; * LargeInteger k = this.shiftRight(this.bitLength() / 2)); // First approximation. * while (true) { // Newton Iteration. * result = (k.plus(this.divide(k))).shiftRight(1); * if (result.equals(k)) return StackContext.outerCopy(result); // Exports result. * k = result; * } * } finally { * StackContext.exit(); * } * } * }[/code]
* *It should be noted that future versions of the JVM may provide some * limited support for stack allocation through escape analysis. * Users can always {@link #DISABLED turn-off} stack allocation to * revert to standard heap allocation.
* * @author Jean-Marie Dautelle * @version 5.2, August 19, 2007 */ public abstract class StackContext extends AllocatorContext { /** * Holds the default implementation. This implementation uses thread-local * pools. RTSJ alternative implementations could use *ScopedMemory
for their stack allocations.
*/
public static final Configurable/*true
if disabled; false
* otherwise.
*/
public final void setDisabled(boolean isDisabled) {
if (isDisabled == _isDisabled) return; // No change.
if (isDisabled) {
deactivate();
} else {
getOuter().getAllocatorContext().deactivate();
}
_isDisabled = isDisabled;
}
/**
* Default implementation.
*/
private static final class Default extends StackContext {
private static final Class CLASS = new Default().getClass();
private final ThreadLocal _factoryToAllocator = new ThreadLocal() {
protected Object initialValue() {
return new FastMap();
}
};
private final ThreadLocal _activeAllocators = new ThreadLocal() {
protected Object initialValue() {
return new FastTable();
}
};
// All allocators which have been used by the owner
// (no synchronization required).
private final FastTable _ownerUsedAllocators = new FastTable();
// All allocators which have been used by the concurrent threads
// (synchronization required).
private final FastTable _nonOwnerUsedAllocators = new FastTable();
protected void deactivate() {
FastTable allocators = (FastTable) _activeAllocators.get();
for (int i = 0, n = allocators.size(); i < n;) {
((Allocator) allocators.get(i++)).user = null;
}
allocators.clear();
}
protected Allocator getAllocator(ObjectFactory factory) {
if (isDisabled()) // Forwards to outer.
return getOuter().getAllocatorContext().getAllocator(factory);
FastMap factoryToAllocator = (FastMap) _factoryToAllocator.get();
StackAllocator allocator = (StackAllocator) factoryToAllocator.get(factory);
if (allocator == null) {
allocator = new StackAllocator(factory);
factoryToAllocator.put(factory, allocator);
}
if (allocator.user == null) { // Activate.
allocator.user = Thread.currentThread();
FastTable activeAllocators = (FastTable) _activeAllocators.get();
activeAllocators.add(allocator);
}
if (!allocator._inUse) { // Add to lists of allocators used.
allocator._inUse = true;
if (Thread.currentThread() == getOwner()) {
_ownerUsedAllocators.add(allocator);
} else {
synchronized (_nonOwnerUsedAllocators) {
_nonOwnerUsedAllocators.add(allocator);
}
}
}
return allocator;
}
protected void enterAction() {
getOuter().getAllocatorContext().deactivate();
}
protected void exitAction() {
this.deactivate();
// Resets all allocators used.
for (int i=0; i < _ownerUsedAllocators.size(); i++) {
StackAllocator allocator = (StackAllocator) _ownerUsedAllocators.get(i);
allocator.reset();
}
_ownerUsedAllocators.clear();
for (int i=0; i < _nonOwnerUsedAllocators.size(); i++) {
StackAllocator allocator = (StackAllocator) _nonOwnerUsedAllocators.get(i);
allocator.reset();
}
_nonOwnerUsedAllocators.clear();
}
}
// Holds stack allocator implementation.
private static final class StackAllocator extends Allocator {
private final ObjectFactory _factory;
private boolean _inUse;
private int _queueLimit;
public StackAllocator(ObjectFactory factory) {
this._factory = factory;
keepInQueue = true;
}
protected Object allocate() {
if (_queueLimit >= queue.length) resize();
Object obj = _factory.create();
queue[_queueLimit++] = obj;
return obj;
}
protected void recycle(Object object) {
if (_factory.doCleanup()) {
_factory.cleanup(object);
}
for (int i= queueSize; i < _queueLimit; i++) {
if (queue[i] == object) { // Found it.
queue[i] = queue[queueSize];
queue[queueSize++] = object;
return;
}
}
throw new j2me.lang.UnsupportedOperationException(
"Cannot recycle to the stack an object " +
"which has not been allocated from the stack");
}
protected void reset() {
_inUse = false;
while (_factory.doCleanup() && (queueSize != _queueLimit)) {
Object obj = queue[queueSize++];
_factory.cleanup(obj);
}
queueSize = _queueLimit;
}
public String toString() {
return "Stack allocator for " + _factory.getClass();
}
}
// Allows instances of private classes to be factory produced.
static {
ObjectFactory.setInstance(new ObjectFactory() {
protected Object create() {
return new Default();
}
}, Default.CLASS);
}
}