ERights Home e 
Back to: Deconstructing E On to: ECDAF Startup

Starting E


EBoot, EEnvironment, startup, and all that

Last updated: [98/06/10 Chip]

This page was written by Chip, based on Arturo's excellent template.

Introduction

This document is an attempt to explain the E program startup process in general and the startup of Microcosm in particular.

Requirements

The procedure for starting up an E application has to solve two problems. First, it needs to get the E execution environment and run queue set up prior to actually launching into the application per se, thus it needs to insinuate itself in between the usual Java program launch interface and the application. Second, it needs to establish the initial set of capabilities and package them in a way suitable for being passed as parameters to the highest level of the application, thus it needs to be in cahoots with the whole mechanism for handling the E capability environment.

Architecture and Non-architecture

EBoot, In Theory

A Java program is started up by giving the Java interpreter the name of a startup class. The interpreter looks for a static main() method in this class and calls it, passing it a single argument consisting of the command line arguments stuffed into an array of strings. The startup of an E program is similar, except that the startup class implements the einterface ELaunchable, which declares a single method:

public einterface ELaunchable {
    emethod go(EEnvironment env);
}

The go() emethod is passed the initial E execution environment (which includes, among other things, the command line arguments). Note that whereas in the case of starting a Java program the initial step is a call to a static method on the startup class, in E the initial go() message is sent to an instance of the startup class which the startup process creates.

We get from the Java startup process to the E startup process via the Java class EBoot. This class provides a one-size-fits-all Java main() method that invokes the E startup procedure. The first Java command line argument to EBoot is the name of the E startup eclass, with additional command line parameters then following on in the traditional fashion. EBoot in turn creates an instance of the specified E startup class and sends it the go() message along with the initial E execution environment which EBoot also creates.

EBoot, In Reality

The above simple and straightforward story represents the original architectural intent. Naturally, the real story of what actually happens is way more complicated. EBoot.main() actually just calls the static method callEMain() on the class org.erights.e.elib.util.EThreadGroup, passing its own class name (i.e., the string "EBoot") and the command line arguments array. EThreadGroup.callEMain() in turn creates new instances of EThreadGroup (a subclass of Java's ThreadGroup class) and org.erights.e.elib.util.EMainThread (a subclass of Java's Thread class). The EMainThread object constructor takes three arguments: the newly created EThreadGroup (which it passes to its super constructor), the name of the E boot class that was passed to the EThreadGroup constructor, and the command line args array. It uses CRAPI to extract a Method object for the method EMain() from the named E boot class. EThreadGroup.callEMain() then launches the EMainThread thread in the standard Java manner by calling its start() method. The start() method calls EMainThread.run() which then uses CRAPI to invoke the remembered EMain() Method, passing it the remembered args array. The EBoot.EMain() method then does the actual work of initializing the E environment, instantiating the E startup object, and sending the go() message to it.

All of this indirection and fiddling around with CRAPI is so that (1) the E run queue gets launched as its own special thread in its own special thread group, and (2) there can be a choice of the actual boot class. In addition to EBoot, there are several alternative boot classes, including ELogin, e.quake.Revive, and IFCBoot. These all end up doing essentially the same job as EBoot, except under different startup circumstances:

  • EBoot -- vanilla E computation startup
  • ELogin -- startup for Microcosm, does a bunch of extra initialization work
  • IFCBoot -- starts up IFC and then uses EBoot to actually start E computation
  • Revive -- startup from checkpoint

The actual work associated with starting up is done in the EMain() method. In the case of EBoot it:

  • Builds a Java Properties table, including parsing any property setting arguments from the command line (and this parsing removes these from the args array that the application will eventually see).
  • Creates an instance of the startup class and verifies that it is indeed an ELaunchable.
  • Creates a new Vat for the E computation to take place in.
  • Creates and initializes a new EEnvironment object, giving it the args array, the Properties table, the Vat, and a reference to the system class loader.
  • Initializes timers, clocks, entropy collection, the tracing package (if enabled), the Inspector (if enabled), the initial set of crew capabilities, and the EStdio class.
  • Sends the go() message to the startup object.

The startup object which EMain() invokes is presumed to be fully trusted. The EEnvironment object which is passed in the go() message contains all the capabilities needed to be in total command of the Vat. It is the responsibility of the startup object to hold these capabilities closely and to start up the less privileged computation which will actually run the application.

ELogin does essentially all the same work as EBoot, and then some. It handles class preloading (if enabled), opening up the crew Repository, starting up various timers that are used to collect performance statistics, and invoking the login UI to get a user name and password. The ELogin code is currently quite a bit more complicated than EBoot, in part because it has a much larger quantity of diagnostic and tracing code than EBoot and in part because it contains a number of performance enhancing tweaks and JVM bug workarounds. It also contains thread handshaking in support of the login UI, which, as a handcoded AWT thing, runs asynchronously.

IFCBoot is really just a startup wrapper rather than an entirely different boot model. It starts an IFC thread, waits for it to get going, and then simply calls EBoot.EMain() directly.

Revive handles the revival of an orthogonally persisted E computation. It departs from the standard boot model entirely, the Revive class merely serving to insinuate the restore-from-checkpoint operation into the standard startup command sequence. Revive.EMain() processes the command line arguments and properties in the standard fashion, starts up tracing and entropy collection, then punts to another static method, Revive.doRevival(). This doRevival() method is also called by code in ELogin when the latter detects the presence of a checkpoint file during login (this code is actually commented out at the moment, as a first step toward removing orthogonal persistence support from the Microcosm application; however the scaffolding remains in place). The doRevival() method restores the Vat from the checkpoint file and sets it to running. No go() message is sent, of course, since the computation in the checkpoint is presumed to already be started (else it couldn't have gotten into the checkpoint in the first place).

Login

Describe login here.

Microcosm Startup

The canonical startup class (that is, the one that receives the go() message) for our application is Agency. Nominally this is the class that starts up the Pluribus runtime, but in reality it is the class that starts up the Microcosm application (as a result of various unfortunate historical twists of fate, these two components have gotten inextricably mixed up with each other).

Agency has two startup pathways, because it is both an ELaunchable and a Seismologist. In the ELaunchable case it starts up via a go() message from EBoot or equivalent. In the Seismologist case it starts up via a noticeCommit() message from the Vat. In normal operation, the go() method and the noticeCommit() method each simply send the Agency object an initialStartup() message, which is where the two startup pathways merge. However, the go() method actually has two startup modes, regulated by the makeTemplate property. If makeTemplate is false (the normal case) it sends itself the initialStartup() message. However, if makeTemplate is true, it immediately checkpoints and then exits, leaving behind a template checkpoint file.

Assuming the normal startup pathway, Agency eventually finds its way to the initialStartup() method. This method performs some additional general-purpose initialization steps, principally filling out the AgentInfo struct which the Agency object holds onto: it summons the directory root maker and the UI framework maker magic powers, creates an UnumMaster object, and establishes a Registrar. It then looks for the Agent property and uses this as the name of an Agent class which it creates and sends a go() message of its own (the Agent go() message takes two parameters, the EEnvironemnt and the AgentInfo).

Although the choice of Agent class is parameterized via a property setting, there is actually only one class which implements the Agent interface. This is MicrocosmText. The MicrocosmText.go() method does some initialization of its own: it does some state bundle loading, inits the Repository, hub event log and member database, does a bunch of fiddling with timers and tracing, then falls into MicrocosmText.initRealm(). This in turn extracts the name of a realmText file from the property RealmTextFile and calls MicrocosmText.createRealm() with this name as a parameter. If the soulTest property is true and the checkpoint property names a checkpoint file, then createRealm restores a state-bundled-persisted realm from the checkpoint file. Otherwise, it reads the realmText file and creates a new realm from scratch as described by the contained realmText.

Current implementation

As there is little architecture per se in this subsystem, the text above describes the structure of the current implementation rather than the plan it was supposed to conform to.

Is it Javadoc'ed?

Not surprisingly, given that this code spans a number of different packages and classes written and maintained by a large number of different programmers, the degree of Javadoc'ing varies considerably. Some of it is thoroughly Javadoc'd, some not at all, and some has Javadoc comments that are present but out of date. Cleanup is warranted.

Design Issues

Open Issues

The current startup code is designed to support a degree of generality that is not currently exploited. There is some question as to whether this degree of generality ever will be exploited. In any case, the startup pathway is extremely long and complicated, with initialization of various components of the system happening at a variety of levels in the code.

The present scheme has three levels of class parameterization:

  1. The boot class
    • specified via the first command line argument
    • controls E runtime startup
    • vectored via JVM startup convention: internal class load followed by static call to main(args)
  2. The startup class
    • specified via the second command line argument
    • controls Agency startup
    • vectored via E message send: newInstance() call followed by send of go(env)
  3. The agent class
    • specified via the "Agent" property
    • controls application startup
    • vectored via E message send: newInstance() call followed by send of go(env,info)

However, the choices are much more fixed than this pattern might suggest. The boot class is really always ELogin or EBoot, the startup class is always Agency, and the agent class is always MicrocosmText. Within the space of these fixed choices, however, are a number of other startup variations controlled by other means:

ELogin and EBoot have to perform essentially the same sets of initialization, but they do so with code that is sufficiently different between the two classes that it is hard to see the points of commonality. In addition, ELogin runs the account manager to take the user through the login dialog -- but does so before the rest of the runtime environment is established and so invokes some 5000 lines of custom UI code. The boot classes are additionally complicated by the logic to funnel through a common body of thread initialization code, which is in turn complicated by having the boot methods be static (which thus entails fiddling around with CRAPI).

Agency supports two different entry paths, depending on whether it is started from the normal pattern or as a result of checkpoint recovery. However, we are phasing out orthogonal persistence and so much of the other code needed for the latter path to work has already been removed or commented out. Furthermore, in its normal startup mode, Agency can either startup the program or it can fork and hibernate -- additional support for orthogonal persistence that is on its way out.

MicrocosmText also has two startup paths. Depending on parameter settings, it will either restore from a state bundle checkpoint or it will create a new realm from a realmText file. And in any case having realmText processing be embedded in the system startup process seems perverse at best.

A number of other minor initializations are parameterized by property settings. These conditionally enable or disable various subsystems, such as the Inspector, tracing, logging, and so forth. We probably want to retain this sort of control. However, different subsystems are regulated at different levels in the multi-level model, with no particular rhyme or reason as to why a particular subsystem is controlled from a particular place. The same comment applies to initializations in general. Some systems are initialized in more than one place, in some cases with complex conditional logic to make sure the program doesn't step on its own toes (the Repository, in particular, seems to be initialized in three different places, but the three sets of initialzations are all very different), and in other cases have their initialization partially on one place and partially in another..

The overall organization of the startup process reflects the organic history of this code rather than a coherent plan. By rationalizing some of this and reducing the number of levels of indirection, it should be possible to reduce the bulk of the code considerably, and to make it significantly simpler, clearer, and easy to maintain. A significant refactoring of the startup code is warranted.

 

 
Unless stated otherwise, all text on this page which is either unattributed or by Mark S. Miller is hereby placed in the public domain.
ERights Home e 
Back to: Deconstructing E On to: ECDAF Startup
Download    FAQ    API    Mail Archive    Donate

report bug (including invalid html)

Golden Key Campaign Blue Ribbon Campaign