Isolatepublic final class Isolate extends Object
Last modified: 05/03/31 13:22:49.
Note: this document is still a draft. Details in the API are
subject to change.
The Isolate class provides the means of creating and managing
isolated computations and arranging for their communication with each
other.
Terminology
Each isolated computation is called a Task. An Isolate
object is a Java representation of the task. Multiple
Isolate objects may be created to represent the same task. Where
the context is clear, the words "task", "isolate" and "isolate
object" may be used interchangeably. When a distinction needs to be
made, the word "task" is used to describe the underlying
computation, and the word "isolate" is used to describe the Java
object(s) that represent the computation.
When two Isolate objects represent the same task, they are said to
be equivalent to each other. Equivalent Isolate objects are
created to avoid the sharing of Isolate objects across task
boundaries. For example, in the following program, task A launches
task B using the object b1 , and task B gets a
reference to itself using the object
b2 . b1 and b2 are two
distinct objects:
class TaskA {
void launchB() {
Isolate b1 = new Isolate("TaskB", ....);
b1.start();
}
}
class TaskB {
public static void main(String args[]) {
Isolate b2 = Isolate.currentIsolate();
}
}
Degree of Isolation
Tasks in the CLDC environment are isolated in the following sense:
- Each task has a separate namespace for loading Java classes.
- Each task has a separate set of static variables for Java classes
- A
synchronized static method uses a different
monitor object inside each task.
- Typically no Java objects are shared across task boundaries.
(see Object Sharing below for
exceptions).
- Resource quotas are controlled by the runtime environment to
prevent tasks to use excessive amount of resources. The
implementation currently supports resource control for
memory or CPU cycles.
Class path
Part of the definition of an isolate is its
classpath, where the basic classes for the isolate are found.
The CLDC runtime searches for classes in three sets of
locations:
- The romized system classes.
- The isolate system class path.
- The isolate application class path.
System and application class paths are specified separately
for an isolate when it is created. Classes on system and application class
paths have different access rights:
- Only classes loaded from the system class path can access
hidden classes.
- Only classes on the system class path can be loaded to restricted
packages.
For the definition of hidden and restricted packages, see
doc/misc/Romizer.html.
When an isolate requests a class, the class is first looked up the
romized system classes, then searched in the isolate system class path,
and then searched in the isolate application class path.
User application classes should be put on the application class path,
system class path can contain only trusted system classes.
WARNING: UNTRUSTED USER APPLICATION CLASSES MUST NEVER BE PUT ON THE
SYSTEM CLASS PATH, AS IT GRANTS THEM ACCESS TO SYSTEM INTERNALS AND BREAKS
SYSTEM SECURITY.
Object Sharing
The Isolate API in CLDC does not support the sharing of arbitrary
Java object across Isolate boundaries. The only exception is String
objects: String objects may be passed as arguments from a parent
isolate to a child isolate's main() method. Such
Strings are passed by reference instead of by value in order to
conserve resource. Also, interned Strings (such as literal Strings
that appear inside Java source code) may be shared across Isolate
boundaries.
Even though String objects may be shared across isolates, different
isolates should not attempt to coordinate their activities by
synchronizing on these Strings. Specifically, an interned Strings cannot
be synchronized across isolate boundaries because it uses a different
monitor object in each Isolate.
Inter-Isolate Communication
Isolates may need to communicate with each to coordinate their
activities. The recommended method of inter-isolate communication
is a native event queue. On many CLDC/MIDP environments a native
event queue already exists. Such event queues can be extended for
one isolate to send events to another isolate.
Some CLDC/MIDP implementors may be tempted to use native code to
pass shared objects from one isolate to another, and use
traditional Java object-level synchronization to perform
inter-isolate communication. In our experience this could easily
lead to inter-isolate deadlock that could be exploited by
downloaded malicious Midlets. For example, if a shared object is
used to synchronize screen painting, a malicious Midlet may stop
other Midlets from painting by not returning from its
paint() method.
In our experience, with shared objects, it would take significant
effort to design a system that can prevent such attacks. In
contrast, event queues are much more easily understood to design
a safe environment. Thus, we strongly recommend against using
shared objects for inter-isolate communication.
The following code is an example of how an isolate can create other
isolates:
import com.sun.cldc.isolate.*;
class HelloWorld {
// Usage: cldc_vm -classpath HelloWorld HelloWorld2
public static void main(String [] argv) {
System.out.println("HelloWorld");
for (int i = 0; i < 6; i++) {
try {
// pass i as an argument to the isolate just for fun
String[] isoArgs = {Integer.toString(i)};
Isolate iso = new Isolate(argv[0], isoArgs);
iso.start();
} catch (Exception e) {
System.out.println("caught exception " + e);
e.printStackTrace();
}
System.out.println("HelloWorld: Iso " + i + " started.");
}
}
}
class HelloWorld2 {
static String st = "HelloWorld2[";
public static void main(String [] argv) {
st = st.concat(argv[0]);
st = st.concat("]");
System.out.println("st is " + st);
System.exit(42);
}
}
|
Fields Summary |
---|
private static int | _API_access_okControls access to each public API entry point. The
isolate creator can grant API access to the child.
Note this is a static so it is private to each Isolate | private int | _priorityPriority level of this Isolate that was set using setPriority before
the isolate has started. | private Isolate | _nextLinks to the next Isolate in a task's _seen_isolates list. See
Task.cpp for more information. | private long | _uniqueIdA number that uniquely identifies the task represented by this
Isolate object. | private int | _terminatedCalled by native code when the task corresponding to this Isolate
has terminated | private int | _saved_exit_codeIf this isolate has terminated, this variable saves the exit code --
Normally the exitCode() method would retrieve the exit code from
the Task. _saved_exit_code is used only if this Isolate object
has been dis-associated from the Task (i.e., the Task has
exited). | private String | _mainClassSaves the mainClass parameter passed to Isolate() constructor. | private String[] | _mainArgsSaves the mainArgs parameter passed to Isolate() constructor. | private String[] | _app_classpathSaves app_classpath[] parameter passed to Isolate() constructor. | private String[] | _sys_classpathSaves sys_classpath[] parameter passed to Isolate() constructor. | private String[] | _hidden_packagesPackages we want to be hidden in this Isolate. See definition of hidden package in
doc/misc/Romizer.html | private String[] | _restricted_packagesPackages we want to be restricted in this Isolate. See definition of restricted package in
doc/misc/Romizer.html | private int | _memoryReserveAmount of memory reserved for this isolate
The isolate cannot get OutOfMemory exception until it
allocates at least memoryReserve bytes.
If the system cannot reserve the requested amount,
the isolate will not start. | private int | _memoryLimitMemory allocation limit for this isolate
OutOfMemoryError exception is thrown if
- the isolate exceeds its memory allocation limit
- excess over the reserved amount for this isolate
cannot be allocated in the heap or
conflicts with reserves of other isolates | private int | _APIAccessUsed during bootstrap of new Isolate to store API access value | private int | _ConnectDebugger | private int | _UseVerifier | private int | _profileId | private static final int | DEFAULT_PROFILE_IDID of default profile. | private static final int | SUSPENDA special priority level to indicate that an Isolate is suspended. | public static final int | MIN_PRIORITYThe minimum priority that an Isolate can have. | public static final int | NORM_PRIORITYThe default priority that is assigned to an Isolate. | public static final int | MAX_PRIORITYThe maximum priority that an Isolate can have. | static final int | INVALID_TASK_ID | static final int | NEW | static final int | STARTED | static final int | STOPPING | static final int | STOPPED | private static final int | EXIT_REASON_IMPLICIT_EXITThe last non-daemon thread returned from main. | private static final int | EXIT_REASON_UNCAUGHT_EXCEPTThe last non-daemon thread exited due to an uncaught exception.
Note that if a daemon thread dies with an uncaught exception,
that will not cause the containing isolate to die. Additionally,
only if the last non-daemon thread dies with
an uncaught exception will this reason be noted. Uncaught exceptions
in shutdown hooks do not count, either. | private static final int | EXIT_REASON_SELF_EXITThe isolate invoked {@link System#exit System.exit},
{@link Runtime#exit Runtime.exit}, or
{@link Isolate#exit Isolate.exit} on itself. | private static final int | EXIT_REASON_SELF_HALTThe isolate invoked
{@link Runtime#halt Runtime.halt} or
{@link Isolate#halt Isolate.halt} on itself. | private static final int | EXIT_REASON_OTHER_EXITSome other isolate invoked {@link Isolate#exit Isolate.exit}
on the isolate. | private static final int | EXIT_REASON_OTHER_HALTSome other isolate invoked {@link Isolate#halt Isolate.halt}
on the isolate. |
Constructors Summary |
---|
public Isolate(String mainClass, String[] mainArgs)Creates a new isolated java application with a default configuration.
This constructor has the same effect as invoking
{@link #Isolate(String,String[],String[])}
and passing null for the app_classpath
and sys_classpath parameters.
See the long constructor documentation for more details.
this(mainClass, mainArgs, (String[])null);
| public Isolate(String mainClass, String[] mainArgs, String[] app_classpath)Creates a new isolated java application with a default configuration.
This constructor has the same effect as invoking
{@link #Isolate(String,String[],String[], String[])}
and passing null for the sys_classpath
parameter.
See the long constructor documentation for more details.
this(mainClass, mainArgs, app_classpath, (String[])null);
| public Isolate(String mainClass, String[] mainArgs, String[] app_classpath, String[] sys_classpath)Creates a new Isolate with the specified arguments and
classpath.
The new isolate will execute the main method of
class mainClass with arguments
mainArgs . The mainClass parameter
must reference a class present in the romized system classes,
or in one of the classpath elements specified by the
sys_classpath and app_classpath parameters.
When the constructor returns, the new isolate is not yet
running. The new isolate does not start execution until the
{@link #start start} method is invoked. The {@link #halt halt}
and {@link #exit exit} methods will fail if before
{@link #start start} is invoked.
Class resolution and loading are performed in the new task
represented by this new isolate. Any loading exceptions (such as
ClassNotFoundException ), including the loading
exception of the specified mainClass , will occur
inside the new task when it is started, not the creator task.
Changes made to any of the constructor's parameters after
control returns from this constructor will have no effect on the
newly created isolate.
If mainArgs is null , a zero-length
String array will be provided to the main method
of mainClass .
User application classes should be put on app_classpath ,
while sys_classpath can contain only trusted system
classes.
WARNING: UNTRUSTED USER APPLICATION CLASSES MUST NEVER BE PUT ON THE
SYSTEM CLASS PATH, AS IT GRANTS THEM ACCESS TO SYSTEM INTERNALS AND
BREAKS SYSTEM SECURITY.
securityCheck();
if (mainClass == null) {
throw new IllegalArgumentException("specified class name is null");
}
registerNewIsolate();
_priority = NORM_PRIORITY;
_mainClass = mainClass;
_mainArgs = argCopy(mainArgs);
_app_classpath = argCopy(app_classpath);
_sys_classpath = argCopy(sys_classpath);
/*
* <p>WARNING: DO NOT REMOVE THIS MESSAGE UNLESS YOU HAVE READ AND
* UNDERSTOOD THE SECURITY IMPLICATIONS: HAVING UNTRUSTED USER
* APPLICATION CLASSES ON THE SYSTEM CLASS PATH GRANTS THEM ACCESS TO
* SYSTEM INTERNALS AND BREAKS SYSTEM SECURITY.
*/
if (_sys_classpath.length != 0) {
System.err.println();
System.err.println("****warning****");
System.err.println("****Untrusted user classes must never be put");
System.err.println("****on the system class path");
System.err.println("****warning****");
System.err.println();
}
|
Methods Summary |
---|
private java.lang.String[] | argCopy(java.lang.String[] args)
if (args == null) {
return new String[0];
}
String[] result = new String[args.length];
JVM.unchecked_obj_arraycopy(args, 0, result, 0, args.length);
return result;
| public void | attachDebugger()
securityCheck();
attachDebugger0(this);
| private native void | attachDebugger0(com.sun.cldc.isolate.Isolate obj)
| public static com.sun.cldc.isolate.Isolate | currentIsolate()Returns the Isolate object corresponding to the currently executing
task.
This method never returns null .
securityCheck();
return currentIsolate0();
| private static native com.sun.cldc.isolate.Isolate | currentIsolate0()
| public void | exit(int status)Requests normal termination of this Isolate .
Invocation of this method is equivalent to causing the isolate
to invoke {@link java.lang.Runtime#exit(int)}. If this method
invocation is, in fact, the cause of the isolate's termination,
the status supplied will be the isolate's
termination status.
No exception is thrown if this isolate is already
terminated. Even if {@link #isTerminated()} returns false prior
to invoking exit , an invocation of exit may
occur after the isolate exits on its own or is terminated by
another isolate. In these cases, the actual exit code reported by
the isolate may be different from status .
If this isolate is not yet started, it will be marked as
already terminated. A subsequent invocation to {@link #start()} would
result in an IsolateStartupException.
If this isolate is suspended, it will be terminated without
being resumed.
try {
stop(status, this == currentIsolate() ?
EXIT_REASON_SELF_EXIT :
EXIT_REASON_OTHER_EXIT);
} catch (SecurityException se) {
stop(status, EXIT_REASON_SELF_EXIT);
}
| public int | exitCode()Returns the exit code of the isolate. If this Isolate has terminated,
this method returns the exit code parameter to the first invocation of
System.exit(), Isolate.exit() or Isolate.halt() that caused the Isolate
to terminate. If this Isolate has terminated without calling
System.exit(), Isolate.exit() or Isolate.halt(), then 0 is returned.
If this Isolate has not started or has not terminated, 0 is returned.
return exitCode0();
| private native int | exitCode0()
| public java.lang.String[] | getClassPath()Returns the classpath the Isolate was started with.
return argCopy(_app_classpath);
| public static com.sun.cldc.isolate.Isolate[] | getIsolates()Returns an array of Isolate objects representing
all tasks that have been started but have not terminated.
New tasks may have been constructed or existing ones
terminated by the time this method returns.
securityCheck();
return getIsolates0();
| private static native com.sun.cldc.isolate.Isolate[] | getIsolates0()
| public int | getPriority()Returns the priority of this isolate.
return _priority;
| private native int | getStatus()Returns the current status of the task represented by this Isolate.
| public void | halt(int status)Forces termination of this Isolate .
If this method invocation is in fact the cause of the isolate's
termination, the status supplied will be the
isolate's termination status.
No exception is thrown if this isolate is already
terminated. Even if {@link #isTerminated()} returns false prior
to invoking halt , an invocation of halt may
occur after the isolate exits on its own or is terminated by
another isolate. In these cases, the actual exit code reported by
the isolate may be different from status .
If this isolate is not yet started, it will be marked as
already terminated. A subsequent invocation to {@link #start()} would
result in an IsolateStartupException.
If this isolate is suspended, it will be terminated without
being resumed.
Implementation Note
Implementations should strive to implement "quick" termination
with as little coordination with the target isolate as possible.
The only information required of a terminated isolate is the exit
code it was terminated with.
try {
stop(status, this == currentIsolate() ?
EXIT_REASON_SELF_HALT :
EXIT_REASON_OTHER_HALT);
} catch (SecurityException se) {
stop(status, EXIT_REASON_SELF_HALT);
}
| public int | id()Returns a small integer ID that uniquely identifies this
Isolate among the current set of active Isolates. The returned
ID will remain unchanged and reserved for this Isolate during its
entire lifetime. However, after this Isolate is terminated, the ID may
be resumed for a new Isolate. // isolate was terminated --
// see IsolateEvent.TERMINATED
return id0();
| private native int | id0()
| synchronized boolean | isStarted()
return getStatus() <= NEW;
| public boolean | isSuspended()Returns if this isolate has been suspended.
return (isSuspended0() != 0 ? true : false);
| private native int | isSuspended0()
| public boolean | isTerminated()Returns true if this Isolate is terminated.
int state = getStatus();
return (state >= STOPPED);
| private native void | nativeStart()
| private native void | notifyStatus()Notify all threads that are waiting on the status of any Isolate object
that represent the same task as this Isolate object.
To simplify VM design, this method does NOT need to be called
while holding a lock of such Isolate objects. To avert race conditions,
the waiting threads must be blocked using waitStatus() instead
of wait(). See comments inside waitForExit() for details.
| private native void | registerNewIsolate()Add this Isolate to the TaskDesc::_seen_isolates list of the
current task and return the globally unique isolate identifier.
| public int | reservedMemory()
return _memoryReserve;
| public void | resume()The opposite of the suspend method.
This method will resume the isolate only if the isolate is
currently started, suspended and not terminated. Otherwise this
method has no effect.
resume0();
| private native void | resume0()
| private static void | securityCheck()Determine if this isolate has permission to access the API
If not, throw runtime exception
if (_API_access_ok == 0) {
throw new SecurityException("Access to Isolate API not allowed");
}
| public void | setAPIAccess(boolean access)Sets the access to Isolate API for this Isolate. This method
should be used by the AMS, before the Isolate is started, to
control whether or not a created Isolate is able to call the
Isolate API. The default for all but the first Isolate is
false . If the AMS calls this method after the Isolate
has started, it has no effect.
In additional, after an Isolate has started, if it has access
to the Isolate API, it can call this method to disable
it. However, once it loses the access, attempts to call this
method would result in a SecurityException.
_APIAccess = (access == true ? 1 : 0);
// Only allow access to be degraded after starting.
if (!access && equals(currentIsolate())) {
_API_access_ok = 0;
}
| public void | setDebug(boolean mode)
_ConnectDebugger = (mode == true ? 1 : 0);
| public void | setHiddenPackages(java.lang.String[] package_names)Sets the packages which will be hidden. See definition of hidden package in
doc/misc/Romizer.html. Note, that this function call overrides previous settings.
If isolate is already started the method throws an
IllegalIsolateStateException .
if (getStatus() > NEW) {
throw new IllegalIsolateStateException("Can only set hidden packages before Isolate starts");
}
_hidden_packages = package_names;
| public void | setMemoryQuota(int reserved)Sets the object heap memory reserved and maximum limits to the
same value. Note that if the system does not have sufficient
resources to guaranteed the reserved amount, the start() method
of this Isolate would fail. This method should only be called
before the Isolate is started. Calling it after the isolate
has started will cause undetermined behavior.
setMemoryQuota(reserved, reserved);
| public void | setMemoryQuota(int reserved, int total)Sets the object heap memory quota for this Isolate. Note that
if the system does not have sufficient resources to guaranteed
the reserved amount, the start() method of this Isolate would
fail.
This method should only be called before the Isolate is
started. Calling it after the isolate has started will cause
undetermined behavior.
if (reserved < 0 || reserved > total) {
throw new IllegalArgumentException();
}
_memoryReserve = reserved;
_memoryLimit = total;
| public void | setPriority(int new_priority)Adjust the priority of this Isolate. The priority controls the
amount of CPU time that VM allocates to execute threads in this
Isolate.
Note: thread scheduling and task scheduling use separate mechanisms.
In the current imeplentation, each task is guaranteed execution time
relative to its priority.
if (new_priority >= MIN_PRIORITY && new_priority <= MAX_PRIORITY) {
_priority = new_priority;
setPriority0(new_priority);
}
| private native void | setPriority0(int new_priority)
| public native void | setProfile(java.lang.String profile)Sets active profile name for isolate. This method must be
called before the isolate is started.
If isolate is already started the method throws an
IllegalIsolateStateException .
The method also determines if profile
is a name of existing profile which is defined in ROM
configuration file. If not, throws runtime
IllegalArgumentException .
| public void | setRestrictedPackages(java.lang.String[] package_names)Sets the packages which will be restricted. See definition of restricted package in
doc/misc/Romizer.html. Note, that this function call overrides previous settings.
If isolate is already started the method throws an
IllegalIsolateStateException .
if (getStatus() > NEW) {
throw new IllegalIsolateStateException("Can only set restricted packages before Isolate starts");
}
_restricted_packages = package_names;
| public void | setUseVerifier(boolean verify)Controls whether or not classes for this isolate need to be
verified. When creating a new Isolate, the AMS may waive
verification for classes that have already been verified. The
default is false . This method should be called
before the Isolate is started.
_UseVerifier = (verify == true ? 1 : 0);
| public synchronized void | start()Start execution of this Isolate . Any code that belongs
to this Isolate (including static initializers)
is executed only after this method is called.
Control will return from this method when the new isolate's
first user level thread starts executing, or if an error occurs
during the initialization of the new isolate.
If any exception is thrown by this method, no code in the
Isolate will have executed.
Errors such as the main class being invalid or not visible in
the classpath will occur handled within the new isolate.
if (getStatus() > NEW) {
throw new IsolateStartupException("Isolate has already started");
}
try {
nativeStart();
} catch (IsolateResourceError e) {
throw e;
} catch (OutOfMemoryError e) {
throw e;
} catch (Throwable t) {
// To be somewhat compilant to JSR-121, we do not pass any
// other errors back to the caller of start(). Instead,
// the caller can use Isolate.exitCode() to discover that
// the isolate has exited. See CR 6270554.
}
// Wait till the fate of the started isolate is known
// (STARTED or STOPPED...)
while (getStatus() <= NEW) {
try {
// Note: do NOT use wait(). See comments inside waitForExit().
waitStatus(NEW);
} catch (InterruptedException e) {
throw new IsolateStartupException(
"Exception was thrown while Isolate was starting");
}
}
| private native void | stop(int exit_code, int exit_reason)Stopping execution of an Isolate. Used by implementation of exit
and halt.
If this isolate is not yet started, it will be marked as
already terminated. A subsequent invocation to {@link #start()} would
result in an IsolateStartupException.
If this isolate is suspended, it will be terminated without
being resumed.
| public void | suspend()Suspends all threads in this isolate from execution. This
method should be used carefully if objects shared between isolates
(passed via native methods) are used for synchornization. A
suspended isolate holding a lock on such an object will stop other
tasks from ever receiving that lock.
See introduction for better ways of communicating between isolates.
This method will suspend the isolate only if the isolate is currently
started, not suspended and not terminated. Otherwise this method
has no effect.
suspend0();
| private native void | suspend0()
| public int | totalMemory()
return _memoryLimit;
| public int | usedMemory()This function returns the approximate amount of object heap
memory currently used by this Isolate. The approximate value
may not be accurate: it may not include recent allocations
made by the Isolate, and it may count objects allocated by the
Isolate that have since become unreachable.
return usedMemory0();
| private native int | usedMemory0()
| public synchronized void | waitForExit()Blocks the execution of the calling thread until this Isolate
has exited. If waitForExit() is called on the
current Isolate, the result is undefined.
while (getStatus() <= STOPPING) {
try {
// Note: do NOT use wait(): When notifyStatus() is
// called, the calling thread may not hold the monitor
// of this object, so if we wrote the code like this
// we may get into a race condition
// while (getStatus() <= STOPPING) {
// <thread switch/race condition may happen here>
// wait();
// }
// waitStatus() performs the getStatus() <= STOPPING check in
// native code again, where thread switch is guaranteed to
// not happen. Hence we won't have a race condition.
waitStatus(STOPPING);
} catch (InterruptedException e) {
// IMPL_NOTE: this method should throw InterruptedException!
throw new Error();
}
}
| private native void | waitStatus(int maxStatus)Blocks the current thread until getStatus() would return a value
greater than maxStatus, or (CLDC Spec 1.1 only) until this
thread is interrupted.
See comments inside waitForExit() to see why this method method
must be used instead of wait() to avert race conditions.
|
|