You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xalan.apache.org by jk...@apache.org on 2001/04/13 23:43:26 UTC
cvs commit: xml-xalan/java/src/org/apache/xml/dtm CoroutineSAXParser_Xerces.java CoroutineManager.java CoroutineParser.java
jkesselm 01/04/13 14:43:26
Modified: java/src/org/apache/xml/dtm Tag: DTM_EXP
CoroutineManager.java CoroutineParser.java
Added: java/src/org/apache/xml/dtm Tag: DTM_EXP
CoroutineSAXParser_Xerces.java
Log:
Abstracting the CoroutineParser to an interface. May wind up with a CoroutineSAXParser layer as well.
One initial implementation, demonstrating the Xerces parseSome() approach;
Need to do an equivalent for pure SAX; that will be messier.
Still need to abstract CoroutineManager as well.
Revision Changes Path
No revision
No revision
1.1.2.4 +140 -68 xml-xalan/java/src/org/apache/xml/dtm/Attic/CoroutineManager.java
Index: CoroutineManager.java
===================================================================
RCS file: /home/cvs/xml-xalan/java/src/org/apache/xml/dtm/Attic/CoroutineManager.java,v
retrieving revision 1.1.2.3
retrieving revision 1.1.2.4
diff -u -r1.1.2.3 -r1.1.2.4
--- CoroutineManager.java 2001/04/13 16:02:14 1.1.2.3
+++ CoroutineManager.java 2001/04/13 21:43:24 1.1.2.4
@@ -60,12 +60,13 @@
/**
* <meta name="usage" content="internal"/>
- * Support the coroutine design pattern.
- * <p>
- * A coroutine set is a very simple cooperative non-preemptive
+ * <p>Support the coroutine design pattern.</p>
+ *
+ * <p>A coroutine set is a very simple cooperative non-preemptive
* multitasking model, where the switch from one task to another is
* performed via an explicit request. Coroutines interact according to
- * the following rules:
+ * the following rules:</p>
+ *
* <ul>
* <li>One coroutine in the set has control, which it retains until it
* either exits or resumes another coroutine.</li>
@@ -76,13 +77,14 @@
* -- so that if/when it is resumed, it will proceed from the point at which
* it last gave up control.</li>
* </ul>
- * Coroutines can be thought of as falling somewhere between pipes and
+ *
+ * <p>Coroutines can be thought of as falling somewhere between pipes and
* subroutines. Like call/return, there is an explicit flow of control
* from one coroutine to another. Like pipes, neither coroutine is
* actually "in charge", and neither must exit in order to transfer
- * control to the other.
- * <p>
- * One classic application of coroutines is in compilers, where both
+ * control to the other. </p>
+ *
+ * <p>One classic application of coroutines is in compilers, where both
* the parser and the lexer are maintaining complex state
* information. The parser resumes the lexer to process incoming
* characters into lexical tokens, and the lexer resumes the parser
@@ -94,55 +96,100 @@
* can optimize the coroutine metaphor), isn't necessarily as clear in
* intent, may have trouble handling cases where data flows in both
* directions, and may not handle some of the more complex cases where
- * more than two coroutines are involved.
- * <p>
- * Most coroutine systems also provide a way to pass data between the
+ * more than two coroutines are involved.</p>
+ *
+ * <p>Most coroutine systems also provide a way to pass data between the
* source and target of a resume operation; this is sometimes referred
* to as "yielding" a value. Others rely on the fact that, since only
* one member of a coroutine set is running at a time and does not
* lose control until it chooses to do so, data structures may be
- * directly shared between them with only minimal precautions.
- * <p>
- * "Note: This should not be taken to mean that producer/consumer
+ * directly shared between them with only minimal precautions.</p>
+ *
+ * <p>"Note: This should not be taken to mean that producer/consumer
* problems should be always be done with coroutines." Queueing is
* often a better solution when only two threads of execution are
* involved and full two-way handshaking is not required. It's a bit
* difficult to find short pedagogical examples that require
- * coroutines for a clear solution.
- * <p>
- * The fact that only one of a group of coroutines is running at a
+ * coroutines for a clear solution.</p>
+ *
+ * <p>The fact that only one of a group of coroutines is running at a
* time, and the control transfer between them is explicit, simplifies
* their possible interactions, and in some implementations permits
* them to be implemented more efficiently than general multitasking.
* In some situations, coroutines can be compiled out entirely;
* in others, they may only require a few instructions more than a
- * simple function call.
- * <p>
- * This version is built on top of standard Java threading, since
+ * simple function call.</p>
+ *
+ * <p>This version is built on top of standard Java threading, since
* that's all we have available right now. It's been encapsulated for
- * code clarity and possible future optimization.
- * <p>
- * (Two possible approaches: wait-notify based and queue-based. Some
+ * code clarity and possible future optimization.</p>
+ *
+ * <p>(Two possible approaches: wait-notify based and queue-based. Some
* folks think that a one-item queue is a cleaner solution because it's
* more abstract -- but since coroutine _is_ an abstraction I'm not really
* worried about that; folks should be able to switch this code without
- * concern.)
- * <p>
- * %TBD% THIS SHOULD BE AN INTERFACE. Arguably Coroutine itself should be an
- * interface much like Runnable, but I think that can be built on top of this.
- */
+ * concern.)</p>
+ *
+ * <p>%TBD% THIS SHOULD BE AN INTERFACE, to facilitate building other
+ * implementations... perhaps including a true coroutine system
+ * someday, rather than controlled threading. Arguably Coroutine
+ * itself should be an interface much like Runnable, but I think that
+ * can be built on top of this.</p>
+ * */
public class CoroutineManager
{
- /** %TBD% */
- BitSet activeIDs=new BitSet();
- /** %TBD% */
- static final int m_unreasonableId=1024;
+ /** "Is this coroutine ID number already in use" lookup table.
+ * Currently implemented as a bitset as a compromise between
+ * compactness and speed of access, but obviously other solutions
+ * could be applied.
+ * */
+ BitSet m_activeIDs=new BitSet();
+
+ /** Limit on the coroutine ID numbers accepted. I didn't want the
+ * in-use table to grow without bound. If we switch to a more efficient
+ * sparse-array mechanism, it may be possible to raise or eliminate
+ * this boundary.
+ */
+ static int m_unreasonableId=1024;
- /** %TBD% */
+ /** Internal field used to hold the data being explicitly passed
+ * from one coroutine to another during a co_resume() operation.
+ * (Of course implicit data sharing may also occur; one of the reasons
+ * for using coroutines is that you're guaranteed that none of the
+ * other coroutines in your set are using shared structures at the time
+ * you access them.)
+ *
+ * %REVIEW% It's been proposed that we be able to pass types of data
+ * other than Object -- more specific object types, or
+ * lighter-weight primitives. This would seem to create a potential
+ * explosion of "pass x recieve y back" methods (or require
+ * fracturing resume into two calls, resume-other and
+ * wait-to-be-resumed), and the weight issue could be managed by
+ * reusing a mutable buffer object to contain the primitive
+ * (remember that only one coroutine runs at a time, so once the
+ * buffer's set it won't be walked on). Typechecking objects is
+ * interesting from a code-robustness point of view, but it's
+ * unclear whether it makes sense to encapsulate that in the
+ * coroutine code or let the callers do it, since it depends on RTTI
+ * either way. Restricting the parameters to objects implementing a
+ * specific CoroutineParameter interface does _not_ seem to be a net
+ * win; applications can do so if they want via front-end code, but
+ * there seem to be too many use cases involving passing an existing
+ * object type that you may not have the freedom to alter and may
+ * not want to spend time wrapping another object around.
+ * */
Object m_yield=null;
- /** %TBD% */
- int m_nextCoroutine=-1;
+ // Expose???
+ final static int NOBODY=-1;
+ final static int ANYBODY=-1;
+
+ /** Internal field used to confirm that the coroutine now waking up is
+ * in fact the one we intended to resume. Some such selection mechanism
+ * is needed when more that two coroutines are operating within the same
+ * group.
+ */
+ int m_nextCoroutine=NOBODY;
/** <p>Each coroutine in the set managed by a single
* CoroutineManager is identified by a small positive integer. This
@@ -170,7 +217,7 @@
{
if(coroutineID>=0)
{
- if(coroutineID>=m_unreasonableId || activeIDs.get(coroutineID))
+ if(coroutineID>=m_unreasonableId || m_activeIDs.get(coroutineID))
return -1;
}
else
@@ -180,7 +227,7 @@
coroutineID=0;
while(coroutineID<m_unreasonableId)
{
- if(activeIDs.get(coroutineID))
+ if(m_activeIDs.get(coroutineID))
++coroutineID;
else
break;
@@ -189,29 +236,30 @@
return -1;
}
- activeIDs.set(coroutineID);
+ m_activeIDs.set(coroutineID);
return coroutineID;
}
- /** Internal subroutine: Check whether an ID is active. In fact I'll
- * probably inline this test, but...
- */
- private boolean idExists(int coroutineID)
- {
- return (coroutineID>=m_unreasonableId || activeIDs.get(coroutineID));
- }
-
/** In the standard coroutine architecture, coroutines are
* identified by their method names and are launched and run up to
* their first yield by simply resuming them; its's presumed that
* this recognizes the not-already-running case and does the right
- * thing. We need a way to achieve that same threadsafe run-up...
- * start the coroutine with a wait or some such.
+ * thing. We seem to need a way to achieve that same threadsafe
+ * run-up... eg, start the coroutine with a wait.
*
* %TBD% whether this makes any sense...
+ *
+ * @param thisCoroutine the identifier of this coroutine, so we can
+ * recognize when we are being resumed.
+ * @exception java.lang.NoSuchMethodException if thisCoroutine isn't
+ * a registered member of this group. %REVIEW% whether this is the
+ * best choice.
* */
- Object co_entry_pause(int thisCoroutine)
+ Object co_entry_pause(int thisCoroutine) throws java.lang.NoSuchMethodException
{
+ if(!m_activeIDs.get(thisCoroutine))
+ throw new java.lang.NoSuchMethodException();
+
while(m_nextCoroutine != thisCoroutine)
{
try
@@ -221,7 +269,7 @@
catch(java.lang.InterruptedException e)
{
// %TBD% -- Declare? Encapsulate? Ignore? Or
- // dance deasil and widdershins about the instruction cache?
+ // dance widdershins about the instruction cache?
}
}
@@ -239,16 +287,21 @@
* ID we watch for to see if we're the ones being resumed.
* @param toCoroutine. Integer identifier for the coroutine we wish to
* invoke.
+ * @exception java.lang.NoSuchMethodException if toCoroutine isn't a
+ * registered member of this group. %REVIEW% whether this is the best choice.
* */
- public synchronized Object co_resume(Object arg_object,int thisCoroutine,int toCoroutine)
+ public synchronized Object co_resume(Object arg_object,int thisCoroutine,int toCoroutine) throws java.lang.NoSuchMethodException
{
+ if(!m_activeIDs.get(toCoroutine))
+ throw new java.lang.NoSuchMethodException("Coroutine not available, id="+toCoroutine);
+
// We expect these values to be overwritten during the notify()/wait()
// periods, as other coroutines in this set get their opportunity to run.
m_yield=arg_object;
m_nextCoroutine=toCoroutine;
notify();
- while(m_nextCoroutine != thisCoroutine)
+ while(m_nextCoroutine != thisCoroutine || m_nextCoroutine==ANYBODY || m_nextCoroutine==NOBODY)
{
try
{
@@ -257,45 +310,64 @@
catch(java.lang.InterruptedException e)
{
// %TBD% -- Declare? Encapsulate? Ignore? Or
- // dance deasil and widdershins about the instruction cache?
+ // dance deasil about the program counter?
}
}
+
+ if(m_nextCoroutine==NOBODY)
+ {
+ // Pass it along
+ co_exit(thisCoroutine);
+ // And inform this coroutine that its partners are Going Away
+ // %REVIEW% Should this throw/return something more useful?
+ throw new java.lang.NoSuchMethodException("CoroutineManager recieved co_exit() request");
+ }
return m_yield;
}
- /** Make the ID available for reuse and terminate this coroutine,
- * transferring control to... %TBD%
+ /** Terminate this entire set of coroutines. The others will be
+ * deregistered and have exceptions thrown at them. Note that this
+ * is intended as a panic-shutdown operation; under normal
+ * circumstances a coroutine should always end with co_exit_to() in
+ * order to politely inform at least one of its partners that it is
+ * going away.
*
- * %TBD% What should we do if toCoroutine isn't registered? Exception?
+ * %TBD% This may need significantly more work.
*
- * %TBD% who gets control, or do we just release one at random?
- * What does yield get set to? Or does this cause _all_ coroutines
- * in this set to exit -- that actually sounds more useful.
- */
+ * %TBD% Should this just be co_exit_to(,,CoroutineManager.PANIC)?
+ *
+ * @param thisCoroutine Integer identifier for the coroutine requesting exit.
+ * */
public synchronized void co_exit(int thisCoroutine)
{
- activeIDs.clear(thisCoroutine);
- m_nextCoroutine=-1; // %REVIEW% Someone said Exit!
+ m_activeIDs.clear(thisCoroutine);
+ m_nextCoroutine=NOBODY; // %REVIEW%
notify();
}
/** Make the ID available for reuse and terminate this coroutine,
* transferring control to the specified coroutine.
*
- * %TBD% What should we do if toCoroutine isn't registered? Exception?
- */
- public synchronized void co_exit_to(Object arg_object,int thisCoroutine,int toCoroutine)
+ * @param arg_object A value to be passed to the other coroutine.
+ * @param thisCoroutine Integer identifier for the coroutine leaving the set.
+ * @param toCoroutine. Integer identifier for the coroutine we wish to
+ * invoke.
+ * @exception java.lang.NoSuchMethodException if toCoroutine isn't a
+ * registered member of this group. %REVIEW% whether this is the best choice.
+ * */
+ public synchronized void co_exit_to(Object arg_object,int thisCoroutine,int toCoroutine) throws java.lang.NoSuchMethodException
{
+ if(!m_activeIDs.get(toCoroutine))
+ throw new java.lang.NoSuchMethodException("Coroutine not available, id="+toCoroutine);
+
// We expect these values to be overwritten during the notify()/wait()
// periods, as other coroutines in this set get their opportunity to run.
m_yield=arg_object;
m_nextCoroutine=toCoroutine;
- activeIDs.clear(thisCoroutine);
+ m_activeIDs.clear(thisCoroutine);
notify();
}
}
-
-
1.1.2.2 +22 -150 xml-xalan/java/src/org/apache/xml/dtm/Attic/CoroutineParser.java
Index: CoroutineParser.java
===================================================================
RCS file: /home/cvs/xml-xalan/java/src/org/apache/xml/dtm/Attic/CoroutineParser.java,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -r1.1.2.1 -r1.1.2.2
--- CoroutineParser.java 2001/04/13 16:03:27 1.1.2.1
+++ CoroutineParser.java 2001/04/13 21:43:24 1.1.2.2
@@ -63,117 +63,31 @@
import java.io.IOException;
import org.apache.xml.dtm.CoroutineManager;
-/** <p>CoroutineParser illustrates how to run Xerces's incremental
- * parsing feature (parseSome()) in a coroutine. Output from Xerces
- * will still be issued via callbacks, which will need to be recieved
- * and acted upon by an appopriate handler.</p>
+/** <p>CoroutineParser is an API for parser threads that operate as
+ * coroutines. See CoroutineSAXParser and CoroutineSAXParser_Xerces
+ * for examples.</p>
+ *
+ * <p><grumble> I'd like the interface to require a specific form
+ * for either the base constructor or a static factory method. Java
+ * doesn't allow us to specify either, so I'll just document them
+ * here:
+ *
+ * <ul>
+ * <li>public CoroutineParser(CoroutineManager co, int appCoroutine);</li>
+ * <li>public CoroutineParser createCoroutineParser(CoroutineManager co, int appCoroutine);</li>
+ * </ul>
*
- * Usage is something like this:
- * <code>
- * CoroutineManager co = new CoroutineManager()
- * int appCoroutine = co.co_joinCoroutineSet(-1);
- * if (appCoroutine == -1) { [[error handling]] }
- * CoroutineParser parser = new CoroutineParser(co, appCoroutine);
- * int parserCoroutine = parser.getParserCoroutine();
- * [[register typical SAX handlers with the parser]]
+ * </grumble></p>
*
- * ...
- *
- * InputSource source = [[document source]];
- * Object result = co.co_resume_to(source, appCoroutine, parserCoroutine);
- * if (result == null) {
- * [[cannot happen here, only if we ask parser to terminate]]
- * }
- * else if (result instanceof Boolean) {
- * if (((Boolean)result).booleanValue()) {
- * [[document open, ready to proceed]]
- * }
- * else {
- * [[document not open, but no exception? I think that this might
- * relate to the continue-on-fatal-error feature, but obviously
- * we cannot continue in this case...]]
- * }
- * }
- * else if (result instanceof Exception) {
- * [[process error]]
- * }
- *
- * ...
- *
- * [[nothing in queue to process, so run the parser coroutine]]
- * Object result = co.co_resume_to(Boolean.TRUE, appCoroutine, parserCoroutine);
- * if (result == null) {
- * [[cannot happen here, only if we ask parser to terminate]]
- * }
- * else if (result instanceof Boolean) {
- * if (((Boolean)result).booleanValue()) {
- * [[some parsing has been performed and
- * the end of the document has not been seen]]
- * }
- * else {
- * [[some parsing might have been performed and
- * the end of the document has been seen]]
- * }
- * }
- * else if (result instanceof Exception) {
- * [[process error during parsing]]
- * }
- *
- * ...
- *
- * [[reset the parser coroutine]]
- * Object result = co.co_resume_to(Boolean.FALSE, appCoroutine, parserCoroutine);
- * [[returns Boolean.FALSE, expect next InputSource]]
- *
- * ...
- *
- * [[terminate the parser coroutine]]
- * Object result = co.co_resume_to(null, appCoroutine, parserCoroutine);
- * [[returns null]]
- * </code>
- *
* <p>Status: In progress</p>
* */
-public class CoroutineParser
-extends org.apache.xerces.parsers.SAXParser
-implements Runnable {
-
- //
- // Data
- //
-
- private CoroutineManager fCoroutineManager = null;
- private int fAppCoroutine = -1;
- private int fParserCoroutine = -1;
- private boolean fParseInProgress=false;
-
- //
- // Constructors
- //
-
- public CoroutineParser(CoroutineManager co, int appCoroutine) {
-
- initHandlers(true, this, this);
-
- fCoroutineManager = co;
- fAppCoroutine = appCoroutine;
- fParserCoroutine = co.co_joinCoroutineSet(-1);
- if (fParserCoroutine == -1)
- throw new RuntimeException("co_joinCoroutineSet() failed");
- Thread t = new Thread(this);
- t.setDaemon(false);
- t.start();
- }
-
- //
- // Public methods
- //
-
- // coroutine support
+public interface CoroutineParser extends Runnable {
- public int getParserCoroutine() {
- return fParserCoroutine;
- }
+ /** @return the coroutine ID number for this CoroutineParser object.
+ * Note that this isn't useful unless you know which CoroutineManager
+ * you're talking to.
+ * */
+ public int getParserCoroutine();
/**
* This coroutine (thread) can be resumed with the following arguments:
@@ -200,48 +114,6 @@
* resumes with:
* co_resume(Boolean.FALSE, ...) always.
*/
- public void run() {
- Object arg = null;
- while (true) {
- arg = fCoroutineManager.co_resume(arg, fParserCoroutine, fAppCoroutine);
- if (arg == null) {
- fCoroutineManager.co_exit_to(arg, fParserCoroutine, fAppCoroutine);
- break;
- }
- if (arg instanceof InputSource) {
- if (fParseInProgress) {
- arg = new SAXException("parse may not be called while parsing.");
- }
- else {
- try {
- boolean ok = parseSomeSetup((InputSource)arg);
- arg = ok ? Boolean.TRUE : Boolean.FALSE;
- }
- catch (Exception ex) {
- arg = ex;
- }
- }
- }
- else if (arg instanceof Boolean) {
- boolean keepgoing = ((Boolean)arg).booleanValue();
- if (!keepgoing) {
- fParseInProgress = false;
- arg = Boolean.FALSE;
- }
- else {
- try {
- keepgoing = parseSome();
- arg = keepgoing ? Boolean.TRUE : Boolean.FALSE;
- } catch (SAXException ex) {
- arg = ex;
- } catch (IOException ex) {
- arg = ex;
- } catch (Exception ex) {
- arg = new SAXException(ex);
- }
- }
- }
- }
- }
+ public void run();
-} // class CoroutineParser
+} // class CoroutineSAXParser_Xerces
No revision
No revision
1.1.2.1 +266 -0 xml-xalan/java/src/org/apache/xml/dtm/Attic/CoroutineSAXParser_Xerces.java
---------------------------------------------------------------------
To unsubscribe, e-mail: xalan-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: xalan-cvs-help@xml.apache.org