User Tools

Site Tools


dynamic-loading

Dana is designed to provide a unique level of runtime adaptation for software. Dana programs are built from a collection of strongly-separated components that communicate by abstract interfaces; these components can be seamlessly added to and removed from a running system at any time.

The previous sections have relied upon Dana's automated linking system to make programs quick and easy to write. The next few sections put the loading and linking of components in your hands to carry out as you wish. This adds a little complexity but also provides the basis for very powerful behaviour including self-adaptive systems which can perform advanced duties such as optimising themselves in real-time. As a point of interest, Dana's automated linking system does not have access to any special capablities and is itself written using the same functionality that we introduce in the next few sections of documentation.

The fundamental enabler of runtime adaptation is the ability to load and unload components at runtime and to interact with those components using dynamic interface lookup and dynamic object instantiation. This section covers these topics in detail, while the next sections cover the construction of a complete meta-composer and the use of runtime adaptation mechanics.

We'll start by making the component that we want to load dynamically and the interface that it will provide, through which we'll interact with it. Create a new directory for this example called “loading” and within that directory make a new folder called “resources”.

In the resources folder, download the interface:

Adder.dn
interface Adder{
	int add(int a, int b)
	}

And in the loading folder, download the component:

MyAdder.dn
component provides Adder{
	int Adder:add(int a, int b)
		{
		return a + b
		}
	}

Compile the above component as normal.

Dynamic loading

Components are loaded into memory by a “loader”. This means that the machine code of the component is copied from secondary storage into main memory. Just like with any other component, we use the loader by adding a required interface, in this case of type Loader.

uses Adder
 
component provides App requires io.Output out, data.IntUtil intUtil, Loader loader {
 
	int App:main(AppParam params[])
		{
		IDC com = loader.load("MyAdder.o")
 
		//dynamically instantiate an object using an interface from com (see below)...
 
		return 0
		}
	}

We've also filled in the details of loading another component at runtime using the loader. The loader returns a special interface of type IDC. All components automatically “provide” this special interface. We can use this interface to query the component for other interfaces that it may provide.

Note that in Dana, you can safely load exactly the same component multiple times, and each one will be a completely distinct, separate copy of the component. Loaded components are automatically unloaded when they no longer have any references.

Dynamic interface lookup

Once we've loaded a component into main memory, giving us a generic IDC reference, we can query it for interfaces that it provides as well as interfaces that it requires. Interface querying basically works in the same way for both of these variants but for now we'll just focus on the provided ones.

We query a provided interface using either the notation:

com :< InteraceType

in cases where we want to supply the locally-known type of the interface, or:

com :< "InterfaceType"

in cases where we just want to provide the name of the interface, rather than the complete type.

Both of these operations will return null if no interface of that type / name could be found.

Dynamic instantiation

Putting everything together, we interact with a dynamically loaded component by instantiating an object of a type matching one of that component's provided interfaces. This is very similar to the way in which we normally instantiate objects, except that we specify the interface with which to complete the instantiation.

Adder a = new Adder() from com :< Adder

The “from” notation here enables us to specify the particular interface, of a particular component, with which to instantiate the object.

The complete program then looks like this:

uses Adder
 
component provides App requires io.Output out, data.IntUtil intUtil, Loader loader {
 
	int App:main(AppParam params[])
		{
		IDC com = loader.load("MyAdder.o")
 
		Adder a = new Adder() from com :< Adder
 
		int q = a.add(5, 6)
 
		out.println("result: $(intUtil.intToString(q))")
 
		return 0
		}
	}

Compile and run the program and it should display the correct value from our adder component. Try using different adder components and loading those instead or make your own more complex components to dynamically load.

Loading and linking dependencies

In the above example we dynamically loaded a simple component that had no dependencies (no required interfaces). It is important to understand that components which are dynamically loaded do not benefit from automated linking of any such dependencies and, without loading and linking these, a runtime warning will be generated if a loaded component attempts to use a dependency that isn't linked. This is done to allow the programmer flexibility in how to resolve dependencies, the specific details of which are normally determined by a meta-program such as PAL.

As a quick way to dynamically load a component and resolve all of its dependencies, by loading and linking default components for those dependencies, you can use the composition.RecursiveLoader API. This returns an array of all loaded components, as well as a reference to the “main component” that you requested to load. It is used as follows:

component provides App requires io.Output out, data.IntUtil intUtil, composition.RecursiveLoader loader {
 
	int App:main(AppParam params[])
		{
		LoadedComponents lc = loader.load("MyAdder.o")
		IDC com = lc.mainComponent
 
		//dynamically instantiate an object using an interface from com (see below)...
 
		return 0
		}
	}

The RecursiveLoader is an open-source component in Dana's standard library so you can see for yourself how it works.

dynamic-loading.txt · Last modified: 2017/09/27 05:16 by barryfp

Page Tools