Home | History | Annotate | Download | only in ref
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one
      3  * or more contributor license agreements. See the NOTICE file
      4  * distributed with this work for additional information
      5  * regarding copyright ownership. The ASF licenses this file
      6  * to you under the Apache License, Version 2.0 (the  "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 /*
     19  * $Id: CoroutineManager.java 468653 2006-10-28 07:07:05Z minchau $
     20  */
     21 package org.apache.xml.dtm.ref;
     22 
     23 import java.util.BitSet;
     24 
     25 import org.apache.xml.res.XMLErrorResources;
     26 import org.apache.xml.res.XMLMessages;
     27 
     28 
     29 /**
     30  * <p>Support the coroutine design pattern.</p>
     31  *
     32  * <p>A coroutine set is a very simple cooperative non-preemptive
     33  * multitasking model, where the switch from one task to another is
     34  * performed via an explicit request. Coroutines interact according to
     35  * the following rules:</p>
     36  *
     37  * <ul>
     38  * <li>One coroutine in the set has control, which it retains until it
     39  * either exits or resumes another coroutine.</li>
     40  * <li>A coroutine is activated when it is resumed by some other coroutine
     41  * for the first time.</li>
     42  * <li>An active coroutine that gives up control by resuming another in
     43  * the set retains its context -- including call stack and local variables
     44  * -- so that if/when it is resumed, it will proceed from the point at which
     45  * it last gave up control.</li>
     46  * </ul>
     47  *
     48  * <p>Coroutines can be thought of as falling somewhere between pipes and
     49  * subroutines. Like call/return, there is an explicit flow of control
     50  * from one coroutine to another. Like pipes, neither coroutine is
     51  * actually "in charge", and neither must exit in order to transfer
     52  * control to the other. </p>
     53  *
     54  * <p>One classic application of coroutines is in compilers, where both
     55  * the parser and the lexer are maintaining complex state
     56  * information. The parser resumes the lexer to process incoming
     57  * characters into lexical tokens, and the lexer resumes the parser
     58  * when it has reached a point at which it has a reliably interpreted
     59  * set of tokens available for semantic processing. Structuring this
     60  * as call-and-return would require saving and restoring a
     61  * considerable amount of state each time. Structuring it as two tasks
     62  * connected by a queue may involve higher overhead (in systems which
     63  * can optimize the coroutine metaphor), isn't necessarily as clear in
     64  * intent, may have trouble handling cases where data flows in both
     65  * directions, and may not handle some of the more complex cases where
     66  * more than two coroutines are involved.</p>
     67  *
     68  * <p>Most coroutine systems also provide a way to pass data between the
     69  * source and target of a resume operation; this is sometimes referred
     70  * to as "yielding" a value.  Others rely on the fact that, since only
     71  * one member of a coroutine set is running at a time and does not
     72  * lose control until it chooses to do so, data structures may be
     73  * directly shared between them with only minimal precautions.</p>
     74  *
     75  * <p>"Note: This should not be taken to mean that producer/consumer
     76  * problems should be always be done with coroutines." Queueing is
     77  * often a better solution when only two threads of execution are
     78  * involved and full two-way handshaking is not required. It's a bit
     79  * difficult to find short pedagogical examples that require
     80  * coroutines for a clear solution.</p>
     81  *
     82  * <p>The fact that only one of a group of coroutines is running at a
     83  * time, and the control transfer between them is explicit, simplifies
     84  * their possible interactions, and in some implementations permits
     85  * them to be implemented more efficiently than general multitasking.
     86  * In some situations, coroutines can be compiled out entirely;
     87  * in others, they may only require a few instructions more than a
     88  * simple function call.</p>
     89  *
     90  * <p>This version is built on top of standard Java threading, since
     91  * that's all we have available right now. It's been encapsulated for
     92  * code clarity and possible future optimization.</p>
     93  *
     94  * <p>(Two possible approaches: wait-notify based and queue-based. Some
     95  * folks think that a one-item queue is a cleaner solution because it's
     96  * more abstract -- but since coroutine _is_ an abstraction I'm not really
     97  * worried about that; folks should be able to switch this code without
     98  * concern.)</p>
     99  *
    100  * <p>%TBD% THIS SHOULD BE AN INTERFACE, to facilitate building other
    101  * implementations... perhaps including a true coroutine system
    102  * someday, rather than controlled threading. Arguably Coroutine
    103  * itself should be an interface much like Runnable, but I think that
    104  * can be built on top of this.</p>
    105  * */
    106 public class CoroutineManager
    107 {
    108   /** "Is this coroutine ID number already in use" lookup table.
    109    * Currently implemented as a bitset as a compromise between
    110    * compactness and speed of access, but obviously other solutions
    111    * could be applied.
    112    * */
    113   BitSet m_activeIDs=new BitSet();
    114 
    115   /** Limit on the coroutine ID numbers accepted. I didn't want the
    116    * in-use table to grow without bound. If we switch to a more efficient
    117    * sparse-array mechanism, it may be possible to raise or eliminate
    118    * this boundary.
    119    * @xsl.usage internal
    120    */
    121   static final int m_unreasonableId=1024;
    122 
    123   /** Internal field used to hold the data being explicitly passed
    124    * from one coroutine to another during a co_resume() operation.
    125    * (Of course implicit data sharing may also occur; one of the reasons
    126    * for using coroutines is that you're guaranteed that none of the
    127    * other coroutines in your set are using shared structures at the time
    128    * you access them.)
    129    *
    130    * %REVIEW% It's been proposed that we be able to pass types of data
    131    * other than Object -- more specific object types, or
    132    * lighter-weight primitives.  This would seem to create a potential
    133    * explosion of "pass x recieve y back" methods (or require
    134    * fracturing resume into two calls, resume-other and
    135    * wait-to-be-resumed), and the weight issue could be managed by
    136    * reusing a mutable buffer object to contain the primitive
    137    * (remember that only one coroutine runs at a time, so once the
    138    * buffer's set it won't be walked on). Typechecking objects is
    139    * interesting from a code-robustness point of view, but it's
    140    * unclear whether it makes sense to encapsulate that in the
    141    * coroutine code or let the callers do it, since it depends on RTTI
    142    * either way. Restricting the parameters to objects implementing a
    143    * specific CoroutineParameter interface does _not_ seem to be a net
    144    * win; applications can do so if they want via front-end code, but
    145    * there seem to be too many use cases involving passing an existing
    146    * object type that you may not have the freedom to alter and may
    147    * not want to spend time wrapping another object around.
    148    * */
    149   Object m_yield=null;
    150 
    151   // Expose???
    152   final static int NOBODY=-1;
    153   final static int ANYBODY=-1;
    154 
    155   /** Internal field used to confirm that the coroutine now waking up is
    156    * in fact the one we intended to resume. Some such selection mechanism
    157    * is needed when more that two coroutines are operating within the same
    158    * group.
    159    */
    160   int m_nextCoroutine=NOBODY;
    161 
    162   /** <p>Each coroutine in the set managed by a single
    163    * CoroutineManager is identified by a small positive integer. This
    164    * brings up the question of how to manage those integers to avoid
    165    * reuse... since if two coroutines use the same ID number, resuming
    166    * that ID could resume either. I can see arguments for either
    167    * allowing applications to select their own numbers (they may want
    168    * to declare mnemonics via manefest constants) or generating
    169    * numbers on demand.  This routine's intended to support both
    170    * approaches.</p>
    171    *
    172    * <p>%REVIEW% We could use an object as the identifier. Not sure
    173    * it's a net gain, though it would allow the thread to be its own
    174    * ID. Ponder.</p>
    175    *
    176    * @param coroutineID  If >=0, requests that we reserve this number.
    177    * If <0, requests that we find, reserve, and return an available ID
    178    * number.
    179    *
    180    * @return If >=0, the ID number to be used by this coroutine. If <0,
    181    * an error occurred -- the ID requested was already in use, or we
    182    * couldn't assign one without going over the "unreasonable value" mark
    183    * */
    184   public synchronized int co_joinCoroutineSet(int coroutineID)
    185   {
    186     if(coroutineID>=0)
    187       {
    188         if(coroutineID>=m_unreasonableId || m_activeIDs.get(coroutineID))
    189           return -1;
    190       }
    191     else
    192       {
    193         // What I want is "Find first clear bit". That doesn't exist.
    194         // JDK1.2 added "find last set bit", but that doesn't help now.
    195         coroutineID=0;
    196         while(coroutineID<m_unreasonableId)
    197           {
    198             if(m_activeIDs.get(coroutineID))
    199               ++coroutineID;
    200             else
    201               break;
    202           }
    203         if(coroutineID>=m_unreasonableId)
    204           return -1;
    205       }
    206 
    207     m_activeIDs.set(coroutineID);
    208     return coroutineID;
    209   }
    210 
    211   /** In the standard coroutine architecture, coroutines are
    212    * identified by their method names and are launched and run up to
    213    * their first yield by simply resuming them; its's presumed that
    214    * this recognizes the not-already-running case and does the right
    215    * thing. We seem to need a way to achieve that same threadsafe
    216    * run-up...  eg, start the coroutine with a wait.
    217    *
    218    * %TBD% whether this makes any sense...
    219    *
    220    * @param thisCoroutine the identifier of this coroutine, so we can
    221    * recognize when we are being resumed.
    222    * @exception java.lang.NoSuchMethodException if thisCoroutine isn't
    223    * a registered member of this group. %REVIEW% whether this is the
    224    * best choice.
    225    * */
    226   public synchronized Object co_entry_pause(int thisCoroutine) throws java.lang.NoSuchMethodException
    227   {
    228     if(!m_activeIDs.get(thisCoroutine))
    229       throw new java.lang.NoSuchMethodException();
    230 
    231     while(m_nextCoroutine != thisCoroutine)
    232       {
    233         try
    234           {
    235             wait();
    236           }
    237         catch(java.lang.InterruptedException e)
    238           {
    239             // %TBD% -- Declare? Encapsulate? Ignore? Or
    240             // dance widdershins about the instruction cache?
    241           }
    242       }
    243 
    244     return m_yield;
    245   }
    246 
    247   /** Transfer control to another coroutine which has already been started and
    248    * is waiting on this CoroutineManager. We won't return from this call
    249    * until that routine has relinquished control.
    250    *
    251    * %TBD% What should we do if toCoroutine isn't registered? Exception?
    252    *
    253    * @param arg_object A value to be passed to the other coroutine.
    254    * @param thisCoroutine Integer identifier for this coroutine. This is the
    255    * ID we watch for to see if we're the ones being resumed.
    256    * @param toCoroutine  Integer identifier for the coroutine we wish to
    257    * invoke.
    258    * @exception java.lang.NoSuchMethodException if toCoroutine isn't a
    259    * registered member of this group. %REVIEW% whether this is the best choice.
    260    * */
    261   public synchronized Object co_resume(Object arg_object,int thisCoroutine,int toCoroutine) throws java.lang.NoSuchMethodException
    262   {
    263     if(!m_activeIDs.get(toCoroutine))
    264       throw new java.lang.NoSuchMethodException(XMLMessages.createXMLMessage(XMLErrorResources.ER_COROUTINE_NOT_AVAIL, new Object[]{Integer.toString(toCoroutine)})); //"Coroutine not available, id="+toCoroutine);
    265 
    266     // We expect these values to be overwritten during the notify()/wait()
    267     // periods, as other coroutines in this set get their opportunity to run.
    268     m_yield=arg_object;
    269     m_nextCoroutine=toCoroutine;
    270 
    271     notify();
    272     while(m_nextCoroutine != thisCoroutine || m_nextCoroutine==ANYBODY || m_nextCoroutine==NOBODY)
    273       {
    274         try
    275           {
    276             // System.out.println("waiting...");
    277             wait();
    278           }
    279         catch(java.lang.InterruptedException e)
    280           {
    281             // %TBD% -- Declare? Encapsulate? Ignore? Or
    282             // dance deasil about the program counter?
    283           }
    284       }
    285 
    286     if(m_nextCoroutine==NOBODY)
    287       {
    288         // Pass it along
    289         co_exit(thisCoroutine);
    290         // And inform this coroutine that its partners are Going Away
    291         // %REVIEW% Should this throw/return something more useful?
    292         throw new java.lang.NoSuchMethodException(XMLMessages.createXMLMessage(XMLErrorResources.ER_COROUTINE_CO_EXIT, null)); //"CoroutineManager recieved co_exit() request");
    293       }
    294 
    295     return m_yield;
    296   }
    297 
    298   /** Terminate this entire set of coroutines. The others will be
    299    * deregistered and have exceptions thrown at them. Note that this
    300    * is intended as a panic-shutdown operation; under normal
    301    * circumstances a coroutine should always end with co_exit_to() in
    302    * order to politely inform at least one of its partners that it is
    303    * going away.
    304    *
    305    * %TBD% This may need significantly more work.
    306    *
    307    * %TBD% Should this just be co_exit_to(,,CoroutineManager.PANIC)?
    308    *
    309    * @param thisCoroutine Integer identifier for the coroutine requesting exit.
    310    * */
    311   public synchronized void co_exit(int thisCoroutine)
    312   {
    313     m_activeIDs.clear(thisCoroutine);
    314     m_nextCoroutine=NOBODY; // %REVIEW%
    315     notify();
    316   }
    317 
    318   /** Make the ID available for reuse and terminate this coroutine,
    319    * transferring control to the specified coroutine. Note that this
    320    * returns immediately rather than waiting for any further coroutine
    321    * traffic, so the thread can proceed with other shutdown activities.
    322    *
    323    * @param arg_object    A value to be passed to the other coroutine.
    324    * @param thisCoroutine Integer identifier for the coroutine leaving the set.
    325    * @param toCoroutine   Integer identifier for the coroutine we wish to
    326    * invoke.
    327    * @exception java.lang.NoSuchMethodException if toCoroutine isn't a
    328    * registered member of this group. %REVIEW% whether this is the best choice.
    329    * */
    330   public synchronized void co_exit_to(Object arg_object,int thisCoroutine,int toCoroutine) throws java.lang.NoSuchMethodException
    331   {
    332     if(!m_activeIDs.get(toCoroutine))
    333       throw new java.lang.NoSuchMethodException(XMLMessages.createXMLMessage(XMLErrorResources.ER_COROUTINE_NOT_AVAIL, new Object[]{Integer.toString(toCoroutine)})); //"Coroutine not available, id="+toCoroutine);
    334 
    335     // We expect these values to be overwritten during the notify()/wait()
    336     // periods, as other coroutines in this set get their opportunity to run.
    337     m_yield=arg_object;
    338     m_nextCoroutine=toCoroutine;
    339 
    340     m_activeIDs.clear(thisCoroutine);
    341 
    342     notify();
    343   }
    344 }
    345