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