[otj-users] Teams, Threads and Within: how to deal with them; how to coordinate them?

Henry Sudhof hsudhof at cs.tu-berlin.de
Wed Oct 12 10:32:08 CEST 2005


Hi,
For a change I won't use this list to report a bug, but to discuss a 
feature instead. The within.  
 In fact it is indeed rather harmless, as long as only a single thread 
is using it: contrary to what one (or rather: I) might expect from a 
block statement, its effect is global and not limited to the enclosed 
statements. While any thread is executing code inside a "within" block 
the corresponding team is active for _all_ threads.
The language definition leaves no room for speculation about this:


------------------------------------snip

§5.2. Explicit Team activation
(a) Activation block
A Team can be activated by the block construct within (myTeam) { stmts } 
If stmts has only one statement this can be abbreviated to within 
(myTeam) stmt In these statements, myTeam must denote a Team instance. 
For the time of executing this block, this Team instance is activated.

The within block statement guarantees that it leaves the team in exactly 
the same activation state as it was in when entering this block. This 
includes the cases of exceptions, meaning that deactivation will also 
occur if the execution of the block terminates abnormally.
(b) Imperative activation
Each team class inherits the methods activate() and deactivate() from 
the predefined class org.objectteams.Team (super class of all team 
classes). These methods allow to control Team activation disregarding 
the block structure of the program.
Note, that methods activate() and deacivate() make no guarantees with 
respect to exceptions.    
(c) Multiple and mixed activations

    * If activate() is invoked on a team instance that has been 
explicitly activated before, this statement has no effect at all (note 
the difference in §5.3(a) below).
      The same applies to deactivating an inactive team.
    * If a team was already active when entering a within block, it will 
remain active after leaving the block.
    * If the team was active on entry of a within block and if 
deactivate() is invoked on the same team instance from within the within 
block, leaving the block will re-activate the team.

------------------------------------snap


Note the sentence : "For the time of executing this block, this Team 
instance is activated."
Hence, the described behaviour is intended .  



I argue that this definition and implementation has serious side-effects 
in multi-threaded environments, which are not trivial to workaround.
To make that case, I'll throw in my findings from a row of smaller 
experiments (I am aware that this might reach  down to the very core of 
team activation/deactivation).

A)
Issue #1: §5.2(c)-2 the implementation behaves exactly as it should; but 
that might cause interesting behaviour:

 Assume a program running in two threads: t1 and t2 sharing one team X.
The events occur in this order:
Thread t1 enters a within block for X
Thread t2 enters a within block for X. Note that the team is already 
active because t1 is executing a within block.
Thread t1 leaves the within block. (trick-)Question #1: should X be 
deactivated? It was inactive when t1 entered the block.
Thread t2 leaves the within block. Question #2: t2 entered the within 
block when X was active, should X stay active/ be re-activated?
 
OT indeed does answer both questions with "yes", causing a race condition.




Issue #2: §5.2(c)-3: The team has not to be deactivated from code 
within  the "within" block to create the described behaviour. 
Deactivation from another thread (possibly unaware of the "within" 
block) will(and should!) have the very same result.
I concede the point that technically the whole program is inside the 
within-block, while any thread is inside it.   

But the simple problem is nonetheless present: if another thread 
activates/deactivates a team while another thread is inside a "within" 
block, the team will revert globally to the state it held before 
entering the within.  This constitutes a race condition requiring manual 
synchronization, which is not explicitly covered by the language 
definition. Alas, this is probably more appropriate for a "programmer's 
guide" than the definition.


C) The constructive part (not quite yet).
The question arises: How to handle the situation.
People  might try to  write something like this, as synchronizing within 
blocks appears to be a good idea. This basically takes out any danger of 
another thread changing the team's activation state.
--------------------------------------
    synchronized(testTeam){    
            within(testTeam){
                 ....
            }
    }
--------------------------------------
Which naturally raises a new issue entirely: the "totally unanticipated 
team activation" or "oops, sorry about being late" effect (so far my 
proposals to name this).
 

For those wondering "where is the problem": consider this
--------------------------------------
    synchronized(testTeam){          
        
            within(testTeam){
                 ... //we *know* that the team is active here
            }
          
    }
    //but here all bets are off. <-!!!
--------------------------------------

Apparently this guarantees the desired thread safety at the cost of some 
performance. No other thread will mess with the activation(*) _while_ a 
thread is inside the synchronized block. But that doesn't take out the 
global semantics of the team activation:

Assume a program running in two threads: t1 and t2 sharing one team x of 
the type X. A has a bound role with a callin on a base class B's method 
m. Let b be an instance of B.
 
The events occur in this order:
Thread t1 enters a synchronized block for x Thread t1 enters a within 
block for x Thread t2 calls b.m . As t1 has the lock on x, neither the 
callin nor the method will  be executed yet Thread t1 leaves the within 
block. x will be deactivated Thread t1 leaves the synchronized block. t2 
obtains the lock on x The callin for b.m gets executed in t2, despite x 
being no longer active. This causes implicit activation of x (!).

This shows  somewhat of a grey area regarding  §5.3. : implicit 
activation can happen without externalized roles and/or team-level 
methods as well.
Also, this means that a team might become become active, despite no 
other thread activating it explicitely (not even implicitly).


D) The real constructive part (;-) )

A few ways to deal with threads I found useful:
The graceful way to temporarily deactivate a team without causing undue 
trouble for other threads.

--------------------------------------
    synchronized(testTeam){    
        
          //we don't know anything here
            within(testTeam){
             testTeam.deactivate();
                 .... //we *know* that the team is inactive here
            }//gracefully return to the previous state
          //the team should be in the same state as before again
    }// from here on all bets are off
--------------------------------------

Guard predicates can be used to limit Teams to certain threads. Not the 
most elegant solution, but it does work.
--------------------------------------
    public class team OneThreadTeam
         base when(Thread.currentThread() == myThread){
    
    Thread myThread = ...

--------------------------------------

Also, threadlocal variables holding a team can be quite powerful, when 
used with a guard predicate, as the example above.
--------------------------------------
ThreadLocal threadLocalTeam = new ThreadLocal(); Team t = 
(Team)(threadBase.threadLocalTeam .get());
within(t){ // note that t should be chained to its thread(s) by a   
guard predicate
...

--------------------------------------







So:
- mixing within and activate/deactivate, especially in multi-threaded 
programs,  may lead to unexpected behaviour
- using "within" in multi-threaded programs may require synchronization
- synchronizing "within" blocks may lead to unexpected behaviour (...)
- reusable Teams (*cough*) need to address thread safety/ be robust 
enough to be used in a multi-threaded environment


I wonder, if others had/have good ideas to deal with threads in OT/J. 
Especially beyond the mere of dealing with their management, but to 
actually have a benefit of using them (one such area is/might be the 
asynchronous execution of callins).
But also it would be interesting for me to know, if there are any plans 
for future developments in this area.

Thanks in advance for your feedback;
Regards,
Henry


* Actually  that  is only half the truth: The statements only holds true 
_while_ inside the synchronized block. Other, unsynchronized,  within 
blocks might still have side-effects on the state after leaving the block.





More information about the otj-users mailing list