All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.org.apache.xml.internal.dtm.ref.CoroutineManager Maven / Gradle / Ivy

The newest version!
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 *
 *
 * This file incorporates work covered by the following copyright and
 * permission notice:
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * $Id: CoroutineManager.java,v 1.8 2010-11-01 04:34:37 joehw Exp $
 */
package com.sun.org.apache.xml.internal.dtm.ref;

import java.util.BitSet;

import com.sun.org.apache.xml.internal.res.XMLErrorResources;
import com.sun.org.apache.xml.internal.res.XMLMessages;


/**
 * 

Support the coroutine design pattern.

* *

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:

* *
    *
  • One coroutine in the set has control, which it retains until it * either exits or resumes another coroutine.
  • *
  • A coroutine is activated when it is resumed by some other coroutine * for the first time.
  • *
  • An active coroutine that gives up control by resuming another in * the set retains its context -- including call stack and local variables * -- so that if/when it is resumed, it will proceed from the point at which * it last gave up control.
  • *
* *

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.

* *

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 * when it has reached a point at which it has a reliably interpreted * set of tokens available for semantic processing. Structuring this * as call-and-return would require saving and restoring a * considerable amount of state each time. Structuring it as two tasks * connected by a queue may involve higher overhead (in systems which * 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.

* *

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.

* *

"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.

* *

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.

* *

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.

* *

(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.)

* *

%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.

* */ public class CoroutineManager { /** "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. * @xsl.usage internal */ static final int m_unreasonableId=1024; /** 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; // 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; /**

Each coroutine in the set managed by a single * CoroutineManager is identified by a small positive integer. This * brings up the question of how to manage those integers to avoid * reuse... since if two coroutines use the same ID number, resuming * that ID could resume either. I can see arguments for either * allowing applications to select their own numbers (they may want * to declare mnemonics via manefest constants) or generating * numbers on demand. This routine's intended to support both * approaches.

* *

%REVIEW% We could use an object as the identifier. Not sure * it's a net gain, though it would allow the thread to be its own * ID. Ponder.

* * @param coroutineID If >=0, requests that we reserve this number. * If <0, requests that we find, reserve, and return an available ID * number. * * @return If >=0, the ID number to be used by this coroutine. If <0, * an error occurred -- the ID requested was already in use, or we * couldn't assign one without going over the "unreasonable value" mark * */ public synchronized int co_joinCoroutineSet(int coroutineID) { if(coroutineID>=0) { if(coroutineID>=m_unreasonableId || m_activeIDs.get(coroutineID)) return -1; } else { // What I want is "Find first clear bit". That doesn't exist. // JDK1.2 added "find last set bit", but that doesn't help now. coroutineID=0; while(coroutineID=m_unreasonableId) return -1; } m_activeIDs.set(coroutineID); return 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 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. * */ public synchronized 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 { wait(); } catch(java.lang.InterruptedException e) { // %TBD% -- Declare? Encapsulate? Ignore? Or // dance widdershins about the instruction cache? } } return m_yield; } /** Transfer control to another coroutine which has already been started and * is waiting on this CoroutineManager. We won't return from this call * until that routine has relinquished control. * * %TBD% What should we do if toCoroutine isn't registered? Exception? * * @param arg_object A value to be passed to the other coroutine. * @param thisCoroutine Integer identifier for this coroutine. This is the * 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) throws java.lang.NoSuchMethodException { if(!m_activeIDs.get(toCoroutine)) throw new java.lang.NoSuchMethodException(XMLMessages.createXMLMessage(XMLErrorResources.ER_COROUTINE_NOT_AVAIL, new Object[]{Integer.toString(toCoroutine)})); //"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 || m_nextCoroutine==ANYBODY || m_nextCoroutine==NOBODY) { try { // System.out.println("waiting..."); wait(); } catch(java.lang.InterruptedException e) { // %TBD% -- Declare? Encapsulate? Ignore? Or // 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(XMLMessages.createXMLMessage(XMLErrorResources.ER_COROUTINE_CO_EXIT, null)); //"CoroutineManager recieved co_exit() request"); } return m_yield; } /** 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% This may need significantly more work. * * %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) { 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. Note that this * returns immediately rather than waiting for any further coroutine * traffic, so the thread can proceed with other shutdown activities. * * @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(XMLMessages.createXMLMessage(XMLErrorResources.ER_COROUTINE_NOT_AVAIL, new Object[]{Integer.toString(toCoroutine)})); //"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; m_activeIDs.clear(thisCoroutine); notify(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy