KESO allows you to spatially isolate different applications or parts of one application
by putting the parts to be isolated from another in different domains. This is done by
simply defining multiple Domain blocks in the $KESORC file. Normally, the
isolated applications or application components will need to communicate with each other.
KESO provides two OS-independant mechanisms for exchanging data between different domains,
portals and shared memory, the usage of which will be explained in this howto. The
CiAO
backend additionally offers KESO APIs to use
CiAO's
native inter-application communication mechanisms, which are primarily intended to
exchange data between Java applications and native applications. These mechanisms will not
be discussed in this howto.
Portals are the primary inter-domain communication mechanism. The concept is similar to that
of Java RMI. The portal mechanism enable a domain (the service domain) to export a
service to other domains (the client domains). The service is described by a
Java interface definition, and manifests in the service domain in the form of a service
object, that is an instance of a class implementing the service interface. The portal
mechanism allows control flows in the client domains to invoke the methods of the service
interface in the protection context of the service domain.
The example in the figure shows four domains. The Srv domain exports a
service named TickerService, described by the Java interface
TickerService, that is imported by
the client domains A and B. Domain C does not import the
service and hence will not be able to use it at runtime.
The TickerService defines one method,
roundtrip(), which constitutes our example service.
This method is implemented by the class TickerServiceImpl,
which contains an additional method foo(). Since
foo() is not defined by the service interface, it
does not become part of the exported service and can thus not be (directly) invoked by control
flows of the client domains.
jino statically creates an instance of the TickerServiceImpl
in the service domain. In addition, a proxy object (the portal) is allocated; the proxy object is
an instance of an automatically generated class that also implements the
TickerService interface. Calling the
roundtrip() method on the proxy object switches the
execution domain context to the service domain, calls the roundtrip()
on the service object and upon return restores the original execution domain context.
Exported services need to be defined in the Domain block of the service
domain in the $KESORC file. Likewise, client domains need to declare
which services they want to access by defining an Import block for each
imported service.
World (TickerService) {
System (AVR) {
Domain (Srv) {
# export service named TickerService
Service (TickerService) {
# interface describing the service
ServiceInterface="TickerService";
# class implementing the service (must implement the above interface)
ServiceClass="TickerServiceImpl";
}
}
Domain (A) {
# import the TickerService
# makes this domain a client domain that service
Import (TickerService) { }
}
Domain (B) {
Import (TickerService) { }
}
Domain (C) {
}
}
}
The above example shows the relevant portions of a $KESORC configuration
file for the TickerService example. Irrelevant parts such as heap and task
definitions are omitted to keep the example clear.
The TickerService is provided by the domain Srv by defining a Service
block in the Domain block of that domain. The name of the service block
becomes the name of the service, which will be used when referring to this service
from both the code and other statements of the configuration file. Thus, the name
chosen for the service must not be reused in another service definition. The service
block needs to contain two attributes:
The ServiceInterface attribute contains the fully qualified name of Java
interface describing the service. The service interface needs to extend the
Portal marker interface.
The ServiceClass attribute contains the fully qualified name of the class
that shall provide the implementation for the service. The service object created
for the service will be an instance of this class. The service class needs to implement the
Service marker interface.
All domains that want to use a service need to import that service using an
Import block. The name of the block refers to the service that is to be
imported. In our example, the domains A and B use this service, thus
their Domain blocks contain the appropriate import statements. Domain C
does not import the service, and consequently will not be able to access it at runtime.
To access the service from the Java code, the control flow that want to invoke a portal
call needs to get hold of the proxy object for that service. For this, it uses the
lookup() method of the
PortalService:
The definitions of the service interface and the service class do not differ
from other Java class definition, except that the Portal marker interface
needs to be extended by the service interface and the
Service marker interface must
be implemented by the service class:
// TickerService.java
public interface TickerService extends keso.core.Portal {
public void roundtrip();
}
// TickerServiceImpl.java
public class TickerService implements keso.core.Service,TickerService {
public void roundtrip() { /* do something /* }
private void foo() { /* do something else */ }
}
The spatial isolation in KESO relies on a logical separation of the domains' object
heaps (implemented as physical heap separation in KESO). This means that each
dynamically allocated object is visible to exactly one domain at any time. To
retain this separation, objects cannot be passed by reference in the invocation of
portal calls, since this would make the passed object visible in both the client
and the service domain. The same issue also affects return values of portal calls.
To avoid this problem, KESO passes both primitive values and objects by
value when given as parameters to or returned by a portal call. For
passed objects, the entire transitive closure of the object is cloned to the
heap of the service domain, i.e. the the directly referenced object is cloned,
all objects referenced by this objects, all objects referenced by these
objects, and so on. In some cases, this may lead to a large portion of the
object heap being cloned. Also, changes to the object clones in the service
domain are not propagated back to the client domain. The programmer needs to
keep this in mind when using portal calls. It is best to avoid using
non-primitive parameters or return values in service interfaces if possible. To
enable the programmer to constrain the objects being copied, KESO provides the
NonCopyable marker
interface. Objects of classes that implement this interface are not cloned
during a portal call. Instead, the reference is replaced by a null reference.
The by-value parameter passing of portals may be expensive if two domains need
to cooperate on a large amount of data. For this special case, there is a second
mechanism for sharing data between domains in KESO, shared memory. KESO's shared
memory is based on
Memory objects, that provide
primitive data access to an area of raw memory. The
howto on programming device drivers discusses
the use of Memory objects.
Contrary to the programming of device drivers, the shared memory area needs to be
a regular memory area rather than an address region in that memory-mapped device
registers reside. To allocate such an area of memory, use the
allocMemory() or
allocStaticMemory() services of the
MemoryService class.
Now after having allocated the shared memory region, the other domain(s) that
the domain wants to share the data with also need to be able to access the area.
This happens by providing the Memory object via a service or passing it as
parameter to a portal call in the other domain(s). The by-value invocation
and return value semantic of the portal call will clone the
Memory object in the target
domain, but it will still refer to the same area of memory. Thus, the
participating domains each use a different
Memory object, but all
memory accesses through any of these objects happen on the same area of
memory - the raw memory area allocated originally allocated by one domain has
thus become a shared memory region.
In the same manner as with Memory objects
referring to device memory areas, the abstraction of
memory-mapped objects may also
be used based on raw memory regions of regular memory. To create such a mapping from an
existing Memory object, use the
mapMemoryToStaticObject() service provided by the
MemoryService class.