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