Home News People Projects Publications Technical Pages
   
Object Network toolkit
MTON
GUI
PLUG
 
 
 

Multi-Threaded Object Network Architecture

Main MTON Features

  • It can instantiate, simulate, and debug any port based object network.
  • About 70 classes in ~10K lines (only the simulation engine).
  • Designed to be executed as a remote daemon process, accessible from a TCP socket. But it also can run locally as a full-featured shell system, apart from the Graphical User Interface.
  • Support to dynamic class loading through remote class servers.
  • Each network place runs on its own thread.
  • Concurrent control is intrinsically implemented into the iteration sequence, even with external Java classes.
  • Ordinary Java classes can be imported, and then be used inside the MTON engine as if they were pure object network classes. In this case a set of Java interfaces and abstract classes is provided.
  • Appropriate exception handling for exceptions thrown from user code (user exceptions)  and thrown from internal MTON code (critical exceptions).

How the MTON engine works

The MTON engine is composed, basically, by a huge set of abstract classes, interfaces and support classes. The first thing you need to enable the MTON engine is the compiled Java package of the desired object network. These classes contain information about topology, classes (including their Java implementation) and kernel objects. All this stuff is generated by means of the Graphical User Interface.

Direct accessing the MTON engine is a very complicated task, requiring several protocol requirements. In order to facilitate this communication we developed an interactive console, with a textual interface. This console can be controlled locally by the user or be redirected to a TCP socket, allowing remote control. When it runs as a remote console server it acts like a daemon, allowing it be started in the same virtual machine of the GUI (default setting) or in another remote JVM.

The engine is needed only when simulating a network, because the  process of edition and compilation can be totally carried out under the front-end.

Loading a compiled network into the console
 
Whenever the user wants to simulate the network, which is supposed to be already compiled, it does so by executing the appropriate option in the GUI. This causes the loading of the network into the console. However it's almost sure that the classes that define the network are not accessible to the console class loader. This way, it relies on its on-demand remote class loader, which is responsible for loading all needed Java classes it may need to instantiate the network. It does so through successive connections to an available file server. This file server runs as a daemon on the GUI JVM, which, in turn, has direct access to the files that define the desired package. This process is totally transparent to the user. You will need to be aware of this fact only when working with the textual console interface.

The figure bellow shows the entire process. The ONSL compiler generates all compiled Java classes that implement the network and make them accessible through its local class server. Then the user requests the start of the simulation, causing the console to load the supplied network through the class server and instantiate it in its local JVM.



Console Command Summary

server HOSTNAME:PORT set the current class server. The default value is set to localhost:5000.
load NETWORK instantiates a network. The supplied network must be readable by the current class server.
down performs a shutdown and remove the current running network from the VM.
step [N] performs N iterations on the current network. If omitted N=1.
watch PAGE COMMAND adds a watch entry on the specified page.
watch PAGE (on|off) turns on/off the specified watch page.
unwatch PAGE ENTRY removes a watch entry from the specified watch page.
[OBJECT].METHOD invokes a debugging method  on the specified object. If the object name is omitted then will be used the current namespace. Depending on the method can be necessary one or more arguments.
cd (..|DIRECTORY) changes the current directory. The directory specification does not support inline use of '..' aggregated to another name.
pwd prints the current namespace directory.
buffer [on|off] sets buffering on/off or prints the current state.
echo [on|off] sets echoing on/of or prints the current state.
debug [on|off] sets internal debugging on/off or prints the current state.
config prints all current available configurations. 
help shows short descriptions of all commands.
dump (l|f) dumps the entire network namespace tree as a string (or collection of strings). The 'list' format shows a listing like 'ls -lR' unix command, with one entry for each nameable. The 'functional' option generates a listing in a PROLOG like format, packing everything in one string. This command is used only by the inspector when it wants to discover all details of the current network at one shot.


Exception Handling

During simulation several pieces of code inside the MTON engine and the user code may throw exceptions at any time. The engine considers all exceptions to be of two different types:
    1. - User exceptions: these exceptions were thrown either directly from the code the user has typed in the code editor or some stub method the user code has invoqued. This type of exception is very common during debugging. The default action performed by the engine when it catches an user exception is to kill all MTON engine threads. The console (and the console server) unload all network classes and keeps itself on-line, waiting more command requests. From the simulation dialog side the exception is reported.
    2. - Engine exceptions: these exceptions are thrown intrinsically by the internal code of the engine, and has nothing to do with the user code. But they may also be thrown due incorrect implementations of external Java classes on the Mutable and Immutable interfaces. The default action performed is abort the Java Virtual Machine, because it implies in a serious MTON error. Whenever this happens one should send an error report to the maintainer of the code, as described in the report that the failure generates on the console the Console Server was started.
    It is recommended that all possible throwable exceptions be appropriately treated inside the user code, even in known error conditions, in order to free any previous allocated resource.

Importing external Java classes

Introduction
The MTON engine has the ability to import ordinary Java classes into its execution system, allowing reuse of previously available code.  In order to be imported a Java class must implement the ExternalObject interface. This interface defines the basic behavior of an external object.

Previous releases of the simulation engine treated data safety internally, however this feature has changed significantly. We have restructured this process to avoid racing conditions that could be raised from the engine during simulation. Now the engine leaves almost all necessary runtime checking to the external Java class itself. This may seem a poor approach, but it brings more flexibility to the system itself, because now the user is free to adopt all, some, or none of the safety recommendations at all.

The ExternalObject interface was designed to provide some minimum concurrent control over objects running in several threads. For convenience, we provide an abstract default implementation. Meanwhile, due the fact that the Java language does not support multiple class inheritance (only one class and zero or more interfaces) it's not guaranteed that you always will be able to use this default implementation. This means that sometimes will be necessary apply a kind of "cut and paste" of the code from the default implementation to your code.

Native network objects do not need special treatment, because their concurrence control is embedded in their code.
 

Becoming MTON-able: initial view

The iteration sequence may be primarily divided into two fundamental stages. The first is the match, in which all possible combinations are tried by all interested consumers. The point here is that NO, I said no, state modification - on any object - can be performed during this phasis because no-one can predict the order in which interested consumers will try their match. The second is the perform phase, after all conflicts being solved enabled objects will perform assigned operations. Even now we need to be aware of which kind of modifications - and over which objects - are to be done, because objects may be consumed in several different ways by consumers. When an object is shared it cannot have its state changed.

We are aware that concurrency control is one of the hardest things in computer programming. In this project we have done a deal, in the sense that we provide the user with basic information needed to perform the checking for invalid state changes. On the other hand the user can play with this trade depending on the costs of this control. These problems will be commented and treated later on this section.

The initial step to adapt an external Java class is to pay some bureucracy, implementing the ExternalObject interface. It will be necessary the implementation of three methods (one is due the inheritance from NetCloneable). The code must import the package as:

import netobj.MTON.*;
The main interfaces are:
 
public interface ExternalObject extends NetCloneable {
    public void setOwner(ExternalNetObject owner);
    public ExternalNetObject getOwner();
}

and
 

public interface NetCloneable {
    public NetCloneable createClone();
}

ExternalNetObject getOwner()

Returns the owner of this external object instance. Actually this value is the same previous value set by setOwner, or null if this object has no owner.
void setOwner(ExternalNetObject owner)
Sets the owner of this object. If this object is being removed the owner should be set to null. The value of owner should be stored internally in the current instance.
NetCloneable createClone()
Creates a new instance that has the same values of this one (derived from the NetCloneable interface).
Meanwhile, all this process may be cumbersome if you need to recode it several times in your classes. As a matter of convenience we supply a default implementation for the ExternalObject interface. Note that this implementation takes into account thread synchronization.
 
abstract public class DefaultExternalObject
    implements ExternalObject, Cloneable {

    /** Owner of this object. */
    protected ExternalNetObject __owner;

    /**
     * Gets the owner of this object.
     *
     * @return owner of this object.
     *         If this object has no owner it will
     *         return <code>null</null>.
     */
    public ExternalNetObject getOwner() {
      synchronized(this) {
        return __owner;
      }
    }

    /**
     * Sets the owner of this object.
     *
     * @param owner new owner of this object.
     */
    public void setOwner(ExternalNetObject owner) {
      synchronized(this) {
        __owner = owner;
      }
    }

    /**
     * Creates a clone of this object.
     * @return clone of this object.
     */
    public NetCloneable createClone() {
      try {
        return (NetCloneable)clone();
      } catch (CloneNotSupportedException e){
        Throwable re = 
           new RuntimeException("could not create clone.");
        throw (RuntimeException)re.fillInStackTrace();
      }
    }

}//DefaultExternalObject

 

This implementation provides a wrapper from the method netobj.MTON.createClone to  java.lang.Object.clone, which is better undertood by Java programmers (but in this release they have equivalent functionalities). This way you can forget createClone and implement only clone.

So we have that:

protected Object clone() throws CloneNotSupportedException

This method is derived from the Cloneable interface, and returns a copy of this object.
Even with this default implementation we may be in trouble with the limitation of the single inheritance imposed by the Java language. This will be clearly perceived when your candidate external class is already a sub-class of another class, making impossible put DefaultExternalObject as a base class of it. This situation can be solved simply copying the internal code of the default implementation to your class and implementing the clone method.

It's very important to note that we still did not perform any state tracking. We just studied basic features.
 

Becoming MTON-able: ensuring data safety

The next step is to evaluate how states are arranged, how they change, and who can trigger state changes (who is invoking which methods?).

The MTON engine can handle two types of instances regarding state behavior.
  • Mutable, instances that can change have its state changed.
  • Immutable, instances that, once created, will retain that same value until it be destroyed by the garbage collector.
The simplest case is the immutable one, as they cannot change anyway, no concurrent sequence of events can raise problems at all. As a rule of thumb, just sub-class the DefaultExternalObject (or DefaultMutable if you are one of those purists, because they are the same).

The mutable case is certainly painful due the very fact that now we depend on the way the user has designed the external class and the particular way in which the JVM schedules concurrent threads. We have no fully guaranteed recipe to solve all these problems. The MTON engine supplies the external class designer with some runtime information to avoid at least pathological cases, e.g., those ones that happens when some state change is to be performed when:

  • the network is inside the matching phase;
  • the target object has one or more weak references (attachments) to it.
We stress that the internal simulation engine does not care about all this stuff if you don't. This is just an advice to prevent, with the greatest probability, any possible misbehaviour. We can, in fact, design fully predictable classes that will never raise data safety problems. Sometimes this will be in fact the only possible solution (see the Matrix class), where references to the internal array may be used from other object making verification virtually impossible.

We provide a basic default class to help with mutable classes. This class makes use of the information supplied by its wrapper (an instance of ExternalNetObject) that sinalizes if the state can or cannot change depending on the type of reference is being realized over it.
 

abstract public class DefaultMutable extends DefaultExternalObject
    implements Mutable {

    /**
     * Determines if this object can change its internal
     * state. This information is obtained from the 
     * parent, invoking its <code>canModifyState</code>
     * method. Notice that invocations to this method
     * should be synchronized on this object.
     * @return true(free to modify state), false(denied).
     */
    public boolean canModifyState() {
      if (__owner != null)
        synchronized(__owner) {
          synchronized (this) {
            return __owner.canModifyState();
          }
        }
      return true;
    }
 

    /**
     * Throws an exception if this object cannot have 
     * its internal state changed.
     * @exception ConcurrentException if this object
     *            cannot be change.
     */
    public void assertModifiable() 
      throws ConcurrentException{
      if (!canModifyState())
        throw new ConcurrentException(CANNOT_BE_MODIFIED);
    }

} //DefaultMutable

Remember our discussion on the problem of the single inheritance of Java. Sometimes will be required that you use the cut and paste technique.

The following listing shows a possible implementation of a class designed to hold an integer value, allowing some operations on it. In this case we are free to sub-class DefaultMutable.

import netobj.MTON.*;

public class xInteger extends DefaultMutable {

    private int value;

    public xInteger(int value){
      this.value = value;
    }

    synchronized public int getValue() {
      return value;
    }

    synchronized public void setValue(int i) {
      assertModifiable();
      value = i;
    }

    synchronized public void add(int value) {
      assertModifiable();
      this.value += value;
    }

    protected Object clone() {
      return new xInteger(value);
    }

} //xInteger

Notice that in this simple example we have simply made all methods synchronized just to avoid some complications. Not all cases are simple as this, because they eventually may fall into deadlock.

Network dynamics

Object Encapsulation:  Fundamental concepts
    One of the most important things regarding dynamics is the object encapsulation. It specifies how objects are structured, and how their fields access other objects.

    The first concepy you need to know is that there are two kinds of reference, connecting an object to a field of another object: structural and weak references.

    Structural references indicate that a given object A in fact belongs to a object B, allowing it to change the internal state of A. Any other object even cannot hold a reference to it. Weak references state that a given object A is referenced by one or more objects B1, ..., Bn, but no one can change the internal state of A, as its value is shared between all participants.

    Putting these ideas more formally:

    • An object O1 is said to be the owner of O2 iff O1 is the unique object that has a  reference to O2 and the permission to change the internal state of it.
    • An object Oa is said to be attached to O1, O2,.., On iff each object in O1, O2,.., On contains a reference to Oa, but no permission to change the state of it.


    Weak references (attachments) are intensively used during the match process, when several combinations are evaluated concurrently in several threads. As it does not allow state changes during the existence of weak references, candidate consumers cannot, in any way, alter the internal state of incoming objects. This reference was mainly designed in order to avoid unnecessary object cloning when objects are shared by consumers. Another useful application takes place when an object is consumed in shared mode, does not requiring unnecessary duplications.

    Structural references indicates that the object is virtually contained in the field of another one. As no other object has a reference (weak or structural) its state can be freely changed by the owner at no data safety risk.

    The following figure shows this process.

The simulation engine takes into account the type of reference an object has to decide whether it can or cannot haver it internal state modified (see the section: importing external classes).

 



If you have any suggestions click here.
last update: 04-aug-1999
$Id: mton.html,v 1.1.1.1 1999/09/23 01:16:35 asrgomes Exp $


 

Send a mail to our webmaster for any questions or comments about this web site.

Copyright © 1999 Computational Semiotics Group

Last modified: 23th November, 1999 .