blob: 40192f7e24906bbd76e90cbbd369d8b92f8b64d1 [file] [log] [blame]
/**********************************************************************
* This file is part of the "Object Teams Runtime Environment"
*
* Copyright 2002-2007 Berlin Institute of Technology, Germany.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Berlin Institute of Technology - Initial API and implementation
**********************************************************************/
package org.objectteams;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.WeakHashMap;
/**
* This is the root class of all team definitions.
* Any class with the <tt>team</tt> modifier implicitly
* inherits from this class.
*
*/
public /* team */ class Team implements ITeam {
// Technical note: comments starting with //$Debug are intended
// for a standalone tool that generates an interface
// which the debugger needs in order to know about line numbers in this class.
/*
* Synchronization: This class supports two levels of synchronization:
* <ul>
* <li>Fields of <code>Team</code> are synchronized
* using <code>this</code> as the monitor.
* <li>Those calls that (un)register a team at its base classes are synchronized
* via <code>registrationLock</code>
* </ul>
* This allows releasing the lock on <code>this</code> before calling to the base class.
* Note, that the synchronized portion of each initial-wrapper is also synchronized
* (against <code>_OT$addTeam/_OT$removeTeam</code>) and that it calls back to
* the registered teams (method <code>isActive()</code>).
* Without <code>registrationLock</code> this situation could easily deadlock:
* Thread1: <pre>t.activate() -> Base._OT$addTeam()</pre>: owns t, waits for Base.
* Thread2: <pre>b.bm() (initial wrapper) -> t.isActive()</pre>: owns Base, waits for t.
*/
/**
* Internal field used by the runtime to install a lifting participant if one is configured.
*/
public static ILiftingParticipant _OT$liftingParticipant = null;
public static ITeamManager _OT$teamManager; // callback into OTDRE, if set
/**
* Default constructor for debugging purpose.
*/
public Team() {} //$Debug(TeamConstructor)
/*
* manual copy-inheritance of a role interface from ITeam.
*/
public interface ILowerable extends ITeam.ILowerable {}
/*
* manual copy-inheritance of a role interface from ITeam.
*/
public interface IConfined extends ITeam.IConfined {}
/**
* Special role type that<ul>
* <li> does not extend java.lang.Object
* <li> may not leak references outside the team.
* </ul>
*/
protected interface Confined {
/* internal method needed for cast and instanceof
* (this method will be generated for role classes) */
ITeam _OT$getTeam();
}
/**
* This class would have been generated by the OT-compiler.
* Don't explicitly use it in client code!
*/
protected class __OT__Confined implements Confined {
// internal method needed for cast and instanceof
public ITeam _OT$getTeam() {
return Team.this; //$Debug(ConfinedGetTeam)
}
}
/**
* Internal function for identifying a Team.
* Should not be called by client code.
*/
public int _OT$getID () {return -1;}
/**
* The constant <code>ALL_THREADS</code> is used for global team (de-)activation.
*/
public static final Thread ALL_THREADS = new Thread();
private static final int _OT$UNREGISTERED = 0;
private static final int _OT$REGISTERED = 1;
private int _OT$registrationState = _OT$UNREGISTERED;
private boolean _OT$globalActive = false;
private ThreadLocal<Integer> _OT$implicitActivationsPerThread = new ThreadLocal<Integer>() {
@Override
protected synchronized Integer initialValue() {
return Integer.valueOf(0);
}
};
private boolean _OT$lazyGlobalActiveFlag = false;
/**
* <code>_OT$activatedThreads</code> contains all threads for which this team instance is active.
* key = activated thread
* value = Boolean(true) for explicit activation | Boolean(false) for implicit activation.
*/
private WeakHashMap<Thread, Boolean> _OT$activatedThreads = new WeakHashMap<Thread, Boolean>();
/** This lock is used to protect activate/deactivate methods <strong>including</strong>
* the calls to doRegistration/doUnregistration.
*/
private Object _OT$registrationLock= new Object();
/**
* {@inheritDoc}
*/
public void activate() {
activate(Thread.currentThread());
}
/**
* {@inheritDoc}
*/
public void deactivate() {
deactivate(Thread.currentThread());
}
/**
* {@inheritDoc}
*/
public void activate(Thread thread) {
// acquire both locks to avoid incomplete execution:
synchronized (this._OT$registrationLock) {
synchronized (this) {
if (thread.equals(ALL_THREADS)) {
_OT$globalActive = true;
_OT$lazyGlobalActiveFlag = true;
TeamThreadManager.addGlobalActiveTeam(this);
} else { // activation only for 'thread':
// register 'thread' as active:
_OT$activatedThreads.put(thread, Boolean.TRUE);
}
} // release this before calling synchronized base class methods
doRegistration(); //$Debug(ActivateMethod)
}
}
/**
* {@inheritDoc}
*/
public void deactivate(Thread thread) {
// acquire both locks to avoid incomplete execution:
synchronized (this._OT$registrationLock) {
boolean shouldUnregister= false;
synchronized(this) {
if (thread.equals(ALL_THREADS)) {
_OT$globalActive = false;
TeamThreadManager.removeGlobalActiveTeam(this);
// unregister all threads:
_OT$activatedThreads.clear();
shouldUnregister= true;
} else { // deactivation only for 'thread':
if (_OT$lazyGlobalActiveFlag) {
// be eager now: activate for all (other) threads:
_OT$activateForAllThreads();
}
// deactivate for 'thread', no longer active:
_OT$activatedThreads.remove(thread);
if (!_OT$lazyGlobalActiveFlag && _OT$activatedThreads.isEmpty()) {
shouldUnregister= true;
}
}
_OT$lazyGlobalActiveFlag = false;
} // release this before calling synchronized base class methods
if (shouldUnregister) //$Debug(DeactivateMethod)
doUnregistration();
}
}
public void deactivateForEndedThread(Thread thread) {
synchronized (_OT$registrationLock) {
boolean shouldUnregister= false;
synchronized (this) {
_OT$activatedThreads.remove(thread);
if (!_OT$lazyGlobalActiveFlag && _OT$activatedThreads.isEmpty())
shouldUnregister= true;
}
if (shouldUnregister)
doUnregistration();
}
}
private void _OT$activateForAllThreads() {
HashSet threads = TeamThreadManager.getExistingThreads();
Iterator it = threads.iterator();
while (it.hasNext()) {
Thread a_thread = (Thread) it.next();
activate(a_thread); // use smaller activate version (no ALL_THREADS, no registerAtBases,...
}
}
/**
* This method is used for implicit activation in team-level methods.
* Implicit activation only applies to the current thread.
* Don't call it from client code.
*/
public void _OT$implicitlyActivate() {
synchronized (this._OT$registrationLock) {
boolean shouldRegister= false;
synchronized (this) {
// this method is used for debugging purpose (team monitor)
Thread currentThread = Thread.currentThread();
if (!_OT$activatedThreads.containsKey(currentThread)) {
// register 'thread' as active:
_OT$activatedThreads.put(currentThread, Boolean.FALSE);
shouldRegister= true;
}
// increment thread local implicit activation counter:
int implActCount = (_OT$implicitActivationsPerThread.get()).intValue();
_OT$implicitActivationsPerThread.set(Integer.valueOf(implActCount + 1 ));
}
if (shouldRegister) //$Debug(ImplicitActivateMethod)
doRegistration();
}
}
/**
* This method is used for implicitly deactivation in team-level methods.
* It respects explicit activation changes and nested calls to team-level methods.
* Implicit deactivation only applies to the current thread.
* Don't call it from client code.
*/
public void _OT$implicitlyDeactivate() {
synchronized (this._OT$registrationLock) {
boolean shouldUnregister= false;
synchronized(this) {
// this method is used for debugging purpose (team monitor)
Thread currentThread = Thread.currentThread();
boolean explicitlyActivated = false;
if (_OT$activatedThreads.containsKey(currentThread)) {
explicitlyActivated = ((Boolean) _OT$activatedThreads.get(currentThread)).booleanValue();
}
if (!explicitlyActivated
&& !_OT$lazyGlobalActiveFlag // no explicit activation overriding the implicit one
&& ((_OT$implicitActivationsPerThread.get()).intValue() == 1)) // this is the last implicit activation
{
_OT$activatedThreads.remove(currentThread);
if (_OT$activatedThreads.isEmpty()) // there are not other threads for which this theam is active
{
shouldUnregister= true;
}
}
// decrement thread local implicit activaion counter:
int implActCount = (_OT$implicitActivationsPerThread.get()).intValue();
_OT$implicitActivationsPerThread.set(Integer.valueOf(implActCount - 1));
}
if (shouldUnregister) //$Debug(ImplicitDeactivateMethod)
doUnregistration();
}
}
/**
* Define whether per-thread activation of this team should be inheritable
* such that the team will be activated automatically for any new threads
* that are spawned from a thread for which the team is already active at that time.
*
* @param inheritable whether or not activation should be inheritable to new threads
*/
public void setInheritableActivation(boolean inheritable) {
if (inheritable)
TeamThreadManager.registerTeamForActivationInheritance(this);
else
TeamThreadManager.unRegisterTeamForActivationInheritance(this);
}
// not API (for use by the TeamThreadManager)
public boolean internalIsActiveSpecificallyFor(Thread t) {
return this._OT$activatedThreads.containsKey(t);
}
/**
* {@inheritDoc}
*/
public final boolean isActive() {
return isActive(Thread.currentThread());
}
/**
* {@inheritDoc}
*/
public final boolean isActive(Thread thread) {
if (thread.equals(ALL_THREADS)) {
return _OT$globalActive;
}
if (_OT$lazyGlobalActiveFlag) {
return true;
} else {
//if (!TeamThreadManager.getExistingThreads().contains(thread)) { // this thread is already finished!
if (!thread.isAlive()) { // this thread is already finished!
throw new IllegalThreadStateException("Called 'isActive(...)' for a thread which is no longer running!");
}
return _OT$activatedThreads.containsKey(thread);
}
}
// ***** for restoring the activation state after a within block: ---->*****
private static final int _OT$INACTIVE = 0;
private static final int _OT$IMPLICIT_ACTIVE = 1;
private static final int _OT$EXPLICIT_ACTIVE = 2;
/**
* {@inheritDoc}
*/
public synchronized int _OT$saveActivationState() {
int old_state = _OT$INACTIVE;
if (_OT$lazyGlobalActiveFlag) {
old_state = _OT$EXPLICIT_ACTIVE;
} else {
Thread current_thread = Thread.currentThread();
if (_OT$activatedThreads.containsKey(current_thread)) {
old_state = _OT$IMPLICIT_ACTIVE;
if (((Boolean)_OT$activatedThreads.get(current_thread)).booleanValue()) {
old_state = _OT$EXPLICIT_ACTIVE;
}
}
}
return old_state;
}
/**
* {@inheritDoc}
*/
public void _OT$restoreActivationState(int old_state) {
synchronized (_OT$registrationLock) {
if (old_state == _OT$INACTIVE) // team was inactive before:
deactivate();
else { // team was active before: has to be reactivated:
boolean explicit = (old_state == _OT$EXPLICIT_ACTIVE);
synchronized (this) {
_OT$activatedThreads.put(Thread.currentThread(), Boolean.valueOf(explicit));
}
doRegistration();
}
}
}
// ***** <----for restoring the activation state after a within block. *****
private void doRegistration() {
if (_OT$registrationState == _OT$UNREGISTERED) {
if (_OT$teamManager != null)
_OT$teamManager.handleTeamStateChange(this, ITeamManager.TeamStateChange.REGISTER);
else
_OT$registerAtBases();
_OT$registrationState = _OT$REGISTERED;
}
}
private void doUnregistration() {
if (_OT$registrationState == _OT$REGISTERED) {
if (_OT$teamManager != null)
_OT$teamManager.handleTeamStateChange(this, ITeamManager.TeamStateChange.UNREGISTER);
else
_OT$unregisterFromBases();
_OT$registrationState = _OT$UNREGISTERED;
}
}
/**
* This method will be implemented by generated code in subteams.
* It registers the team at every base playing one of its roles.
* Don't call it from client code.
*/
public void _OT$registerAtBases() {}
/**
* This method will be implemented by generated code in subteams.
* It unregisters the team from every base playing one of its roles.
* Don't call it from client code.
*/
public void _OT$unregisterFromBases() {}
//public int _OT$activationState = -1; // TODO: remove usage of this from generated code
/**
* {@inheritDoc}
*/
public boolean hasRole(Object aBase) {
// overriding method to be generated by the compiler for each team with bound roles.
return false;
}
/**
* {@inheritDoc}
*/
public boolean hasRole(Object aBase, Class<?> roleType) throws IllegalArgumentException {
// overriding method to be generated by the compiler for each team with bound roles.
throw new IllegalArgumentException("No such bound role type in this team: "+roleType.getName());
}
/**
* {@inheritDoc}
*/
public Object getRole(Object aBase) {
// overriding method to be generated by the compiler for each team with bound roles.
return null;
}
/**
* {@inheritDoc}
*/
public <T> T getRole(Object aBase, Class<T> roleType) throws IllegalArgumentException {
// overriding method to be generated by the compiler for each team with bound roles.
return null;
}
/**
* {@inheritDoc}
*/
public Object[] getAllRoles() {
// overriding method to be generated by the compiler for each team with bound roles.
return new Object[0];
}
/**
* {@inheritDoc}
*/
public <T> T[] getAllRoles(Class<T> roleType) throws IllegalArgumentException {
// overriding method to be generated by the compiler for each team with bound roles.
throw new IllegalArgumentException("Class org.objectteams.Team has no bound roles.");
}
/** Internal variable to be set from generated code. */
private boolean _OT$isExecutingCallin = false;
/**
* Method only for internal use by generated code.
*/
public boolean _OT$setExecutingCallin(boolean newFlag) {
boolean oldVal = _OT$isExecutingCallin;
_OT$isExecutingCallin = newFlag;
return oldVal;
}
/**
* {@inheritDoc}
*/
public boolean isExecutingCallin() {
return _OT$isExecutingCallin;
}
/**
* {@inheritDoc}
*/
public void unregisterRole(Object aRole) {
// overriding method to be generated by the compiler for each team with bound roles.
}
/**
* {@inheritDoc}
*/
public void unregisterRole(Object aRole, Class<?> roleType) throws IllegalArgumentException {
// overriding method to be generated by the compiler for each team with bound roles.
}
@Override
protected void finalize() throws Throwable {
// nop, hook for the debugger
@SuppressWarnings("unused")
int i= 2+3; // Note: body must not be empty for debugger to be able to stop.
} // $Debug(FinalizeMethod)
/**
* If a serializable team wishes to persist its global activation status it must
* call this method from its writeObject() method and correspondingly call
* {@link #readGlobalActivationState(ObjectInputStream)} from its readObject().
*/
protected void writeGlobalActivationState(ObjectOutputStream out) throws IOException {
out.writeBoolean(this._OT$globalActive);
}
/**
* If a serializable team wishes to persist its global activation status it must
* call this method from its readObject() method and correspondingly call
* {@link #writeGlobalActivationState(ObjectOutputStream)} from its writeObject().
* If a team is restored that was globally active when serialized, it will be activated
* correspondingly during deserialization when this method is called.
*/
protected void readGlobalActivationState(ObjectInputStream in) throws IOException {
this._OT$globalActive = in.readBoolean();
if (this._OT$globalActive) {
this._OT$lazyGlobalActiveFlag = true;
this.doRegistration();
}
}
/**
* Serializable teams must invoke this method once from their readObject() method
* in order to re-initialize internal data structures.
*/
protected void restore() { /* empty; implementation will be generated for each serializable sub-class. */ }
/**
* Serializable teams must invoke this method from their readObject() method
* for each role that has been retrieved and shall be re-registered for this team.
*/
protected void restoreRole(Class<?> clazz, Object role) { /* empty; implementation will be generated for each serializable sub-class. */ }
// === BELOW THIS POINT: Methods used by the Object Teams Dynamic Runtime Environment, NOT API ===
public static void registerTeamManager(ITeamManager teamManager) {
if (Team._OT$teamManager != null) throw new IllegalStateException("team manager already defined.");
Team._OT$teamManager = teamManager;
}
/**
* This method is the first part of the new chaining wrapper.
* It should be called from the generated client code.
*
* @param baze the current base object
* @param teams the current team objects
* @param idx the index of the current team in teams
* @param callinIds an array of ids, that are unique in the team
* for a base method in a base class
* @param boundMethodId an unique id for a base method in the base class.
* This id is needed for a base call.
* @param args packed arguments.
* @return possibly boxed result
*/
public Object _OT$callAllBindings(IBoundBase2 baze, ITeam[] teams,int idx,int[] callinIds, int boundMethodId, Object[] args)
{
Object res = null;
if ((boundMethodId & 0x80000000) == 0) { // bit 0x80000000 signals ctor (which has no before/replace bindings)
this._OT$callBefore(baze, callinIds[idx], boundMethodId, args);
res = this._OT$callReplace(baze, teams, idx, callinIds, boundMethodId, args);
}
this._OT$callAfter(baze, callinIds[idx], boundMethodId, args, res); // make result available to param mappings!
return res;
}
/**
* This method calls the next team or a base method,
* if there are no more active teams for a joinpoint
*
* @param baze the current base object
* @param teams the current base object
* @param idx the index of the current team in teams, also points into callinIds, i.e., both lists run synchroneously.
* @param callinIds an array of ids, that are unique in the team
* for a base method in a base class
* @param boundMethodId an unique id for a base method in the base class.
* This id is needed for a base call.
* @param args original packed arguments.
* @param baseCallArgs packed arguments as provided to the base call.
* @param baseCallFlags flags to signal a base call / a base super call
* @return possibly boxed result
*/
public Object _OT$callNext(IBoundBase2 baze, ITeam[] teams, int idx, int[] callinIds, int boundMethodId, Object[] args, Object[] baseCallArgs, int baseCallFlags)
{
return _OT$terminalCallNext(baze, teams, idx, callinIds, boundMethodId, args, baseCallArgs, baseCallFlags);
}
public static Object _OT$terminalCallNext(IBoundBase2 baze, ITeam[] teams, int idx, int[] callinIds, int boundMethodId, Object[] args, Object[] baseCallArgs, int baseCallFlags)
{
// Are there still active teams?
if (idx+1 < teams.length) {
// Yes, so call the next team/callin
return teams[idx+1]._OT$callAllBindings(baze, teams, idx+1, callinIds, boundMethodId, args);
} else {
//No, call the base method
if (baze == null) {
//handle base call to a static base method
return teams[idx]._OT$callOrigStatic(callinIds[idx], boundMethodId, args);
} else {
if (baseCallFlags == 2)
boundMethodId++;
return baze._OT$callOrig(boundMethodId, args);
}
}
}
/**
* Executes all before callins for a given callin id.
* Must be overridden by a team, if the team gets before callins.
*
* @param baze the current base object
* @param callinId the current callin id
* @param boundMethodId an unique id for a base method in the base class.
* This id is needed for a base call.
* @param args packed arguments.
* @return possibly boxed result
*/
public void _OT$callBefore(IBoundBase2 baze, int callinId, int boundMethodId, Object[] args) {
// nop; override with code from before callin bindings.
}
/**
* Executes all after callins for a given callin id.
* Must be overridden by a team, if the team gets after callins.
*
* @param baze the current base object
* @param callinId the current callin id
* @param boundMethodId an unique id for a base method in the base class.
* This id is needed for a base call.
* @param args packed arguments.
* @param result the result of the base method. Could be used by after callins
*/
public void _OT$callAfter(IBoundBase2 baze, int callinId, int boundMethodId, Object[] args, Object result) {
// nop; override with code from after callin bindings.
}
/**
* Execute replace callins of the team for the current callin id.
* Must be overridden by a team, if the team has got replace callins.
*
* @param baze the current base object
* @param teams the current base object
* @param idx the index of the current team in teams
* @param callinIds an array of ids, that are unique in the team
* for a base method in a base class
* @param boundMethodId an unique id for a base method in the base class.
* This id is needed for a base call.
* @param args packed arguments.
* @return possibly boxed result
*/
public Object _OT$callReplace(IBoundBase2 baze, ITeam[] teams, int idx, int[] callinIds, int boundMethodId, Object[] args) {
// default; override with code from replace callin bindings.
return _OT$callNext(baze, teams, idx, callinIds, boundMethodId, args, null, 0);
}
/**
* Calls the method callOrigStatic of a concrete class dependent on the
* given callin id.
* Must be overridden in a team, if the team has got base calls
* to static base methods
* @param callinId
* @param boundMethodId
* @param args
* @return
*/
public Object _OT$callOrigStatic(int callinId, int boundMethodId, Object[] args) {
return null;
}
}