From RTSC-Pedia

Jump to: navigation, search
revision tip
—— LANDSCAPE orientation
[printable version]  offline version generated on 04-Aug-2010 21:08 UTC  

RTSC Module Primer/Lesson 4

Instance object life-cycle — using the RandGen module

Building upon the instance creation pattern you encountered in Lesson 3, we'll now expand the paradigm to include alternate ways of creating instance objects—in particular, static instances formed by basically moving Mod_create function calls from the target-program back upstream to its corresponding meta-program. We'll also illustrate the general programming pattern for deleting instances, thereby completing the life-cycle of RTSC instance objects.

Contents

Running the program

Deviating a bit from the format of previous lessons, let's start by building and running the program before examining its source code. This example will make much more sense if you can first see its final output—a sequence of random-numbers extracted from three distinct RandGen instance objects, each formed in a slightly different fashion:

 
 
 
 
 
 
 
 
 
>> gmake test
dynamically-created instance:
	6 7 10 1 12 8 10 14 1 15
 
dynamically-constructed instance:
	13 2 1 22 12 5 10 19 17 12
 
statically-created instance:
	12 5 3 11 11 19 5 1 15 0

Reading the program

This example significantly expands prog.c from its counterpart back in Lesson 3, now illustrating several alternative idioms for creating (as well as deleting) RandGen instance objects. Moreover, creation of one particular RandGen instance has actually migrated into prog.cfg where an XDCscript rendition of a target-domain RandGen_create call now appears.

lesson4/prog.c
 
 
 

 
 
 

 
 
 
 
 
 
 
 
 
 
 
 
3a
3b
 
 
 
 
 
 
4a
 
 
 
4b
 
5a
5b
5c
 
6a
6b
 
 
 
#include <bravo/math/RandGen.h>
#include <xdc/runtime/System.h>
 
#include <xdc/cfg/global.h>
 
#define COUNT 10
 
Void printNums(RandGen_Handle rgInst, String label)
{
    Int i;
 
    System_printf("%s:\n\t", label);
    for (i = 0; i < COUNT; i++) {
        System_printf("%d ", RandGen_next(rgInst)); 
    }
    System_printf("\n\n");
}
 
Int main()
{
    RandGen_Handle rgInstHandle;
    RandGen_Struct rgInstStruct;
    RandGen_Params rgParams;
 
    RandGen_Params_init(&rgParams);
 
    rgParams.range = 15;
    rgParams.seed = 3;
    rgInstHandle = RandGen_create(&rgParams, NULL);
 
    rgParams.range = 25;
    rgParams.seed = 2;
    RandGen_construct(&rgInstStruct, &rgParams);
 
    printNums(rgInstHandle, "dynamically-created instance");
    printNums(RandGen_handle(&rgInstStruct), "dynamically-constructed instance");
    printNums(rgInstStatic, "statically-created instance");
 
    RandGen_delete(&rgInstHandle);
    RandGen_destruct(&rgInstStruct);
 
    return 0;
}
 
lesson4/prog.cfg
7
 
 
 
 
8
var Program = xdc.useModule('xdc.cfg.Program'); 
 
var RandGen = xdc.useModule('bravo.math.RandGen');
var System = xdc.useModule('xdc.runtime.System');
 
Program.global["rgInstStatic"] = RandGen.create({range: 20, seed: 4});

Working "middle-out" starting from lines 5a-5c in prog.c, these three successive calls to the printNums helper function (defined at line 2) each input a different RandGen_Handle value that in turn references a distinct instance object; the String label passed as the second argument to printNums suggests something about how we've created these particular instances. In the case of rgInstHandle (used at line 5a), its creation pattern (lines 3a and 4a) tracks identically with what you've already seen in Lesson 3.

Constructed instance.  By contrast, the creation pattern for rgInstStruct—declared, initialized, and used at lines 3b, 4b, and 5b respectively—deviates from the model of Lesson 3 in several important ways:

  • Unlike rgInstHandle, which holds a reference (pointer) to some underlying object dynamically allocated by RandGen_create, line 3b effectively allocates the necessary memory for the instance object at the point of declaration. Variables of type RandGen_Struct are actually C structures of sufficient size to hold an individual RandGen instance object; variables of type RandGen_Handle, by contrast, hold only pointers. Either way, the client has no knowledge of the internal representation of these instance objects—that is to say, both RandGen_Handle and RandGen_Struct remain opaque types.

  • With the necessary memory already allocated in rgInstStruct, we simply pass a pointer to this (uninitialized) structure as the first argument to RandGen_construct at line 4b; this function's second argument is a pointer to a previously initialized RandGen_Params structure (or NULL, to select defaults). Since no internal memory allocation occurs within RandGen_construct—unlike RandGen_create—the caller need not establish an error-handling policy when dynamically-constructing (as opposed to dynamically-creating) new instance objects.

Put another way, RandGen_create allocates and initializes storage for a new instance object; RandGen_construct just initializes storage already allocated in a variable of type RandGen_Struct. Once initialized, you can obtain a corresponding RandGen_Handle that references a particular RandGen_Struct by calling the special conversion function RandGen_handle, as illustrated on line 5b.

Instance termination.  Lines 6a and 6b introduce two more special functions respectively used to terminate the life-cycle of a dynamically-created and a dynamically-constructed instance object:

  • The function RandGen_delete, as you might expect, internally frees storage allocated for this instance object through a prior call to RandGen_create; somewhat unexpected, RandGen_delete takes the address of a RandGen_Handle as its sole argument and will actually overwrite the variable rgInstHandle with the value NULL upon return—a lightweight safety measure to minimize dangling references to inactive instance objects.

  • The function RandGen_destruct—the dual of RandGen_construct—receives the address of a RandGen_Struct as its sole argument and then "finalizes" the instance object; upon return, the variable rgInstStruct effectively returns to that same undefined (and unusable) status it had back at line 3.

Not unlike other resource management patterns involving pairs of functions already familiar to C programmers—such as malloc and free or fopen and fclose—clients of a RTSC instance module must ultimately assume responsibility for properly disposing previously created instance objects if/when necessary, as well as for ensuring that instance handles used in their code do not reference terminated objects.

Static instances.  The third RandGen instance used in this example—referenced through the RandGen_Handle variable rgInstStatic at line 5c of prog.c—requires no prior call to RandGen_create during program execution. Not unlike a statically initialized global variable in C, rgInstStatic references a statically allocated and initialized instance object that is "ready-to-go" when program execution begins. An extern declaration for rgInstStatic appears in a special header file automatically associated with this program and always named xdc/cfg/global.h (see line 1); as the name of this header file suggests, global variables accessible within prog.c are first introduced and initialized within prog.cfg.

Line 8 of prog.cfg illustrates two different RTSC programming idioms that you can use independently or else in combination with one another, as is the case here:

  • The fragment Program.global["rgInstStatic"] names a new global variable whose type in the target-program (here, RandGen_Handle) is inferred from its initial value in the meta-program (here, the result of calling RandGen.create); alternatively, you can assign these globals an XDCscript string, number, or boolean in lieu of an instance object handle. Note that the Program object introduced at line 7 refers to a special module canonically named xdc.cfg.Program whose configs and functions help shape any number of "global" characteristics of the corresponding target-program—placement of memory, debugger integration, program stack size, as well as global variable definitions.

  • The fragment RandGen.create({range: 20, seed: 4}) calls the XDCscript equivalent of RandGen_create, which likewise returns the XDCscript equivalent of a RandGen_Handle. Leveraging the more compact syntax of standard JavaScript versus standard C, we can express the XDCscript equivalent of a RandGen_Params structure as an object literal comprising a comma-separated list of field:value pairs enclosed in curly braces. Any fields not explictly assigned will take on the default value of the corresponding per-instance config; RandGen.create uses all instance config defaults when passed an empty structure {} or else the value null. Unlike its target-domain counterpart, RandGen.create does not require any additional arguments to support error-handling.

Given their status as permanent objects, needless to say you cannot delete statically-created instances at any point during their life-cycle—neither in the prog.c target-program nor even in the prog.cfg meta-program.

Guidelines for usage

The example at hand illustrates three distinctive idioms for creating RTSC instance objects, each taking a somewhat different path to obtain the same result—a Mod_Handle that you can pass to other public functions of a RTSC instance module. Here, then, are some guidelines to help you choose which instance creation idiom to employ in a particular application setting.

Target-domain Mod_create (dynamically-created instances).  You should consider this path as the norm, with the others representing variations that typically yield performance gains at the expense of some additional programming complexity. During the early phases of application development—when program correctness and robustness should always trump program speed and size—you should utilize target-domain Mod_create calls by default until proven otherwise.

In practice, most target-domain Mod_create calls occur soon after execution enters the program's main function, usually well before any time-critical processing begins. Since dynamically-created instances require runtime allocation as well as initialization of object storage—a potentially lengthy and non-deterministic operation that may impact real-time performance—limiting target-domain Mod_create calls to program startup often becomes mandatory. Along these lines, you may also find that (once created during startup) these instances can effectively live forever in many embedded applications.

Recognizing the prevalence of "create-only" target programs that never call any instance Mod_delete functions, RTSC enables you to configure simpler, more deterministic memory allocators used internally by Mod_create—resulting in non-trivial reductions in program footprint.

Target-domain Mod_construct (dynamically-constructed instances).  With allocation of space for the instance object under explicit client control, target-domain Mod_construct calls certainly have a degree of flexibility not shared by Mod_create—you can declare the Mod_Struct passed to Mod_construct as a local or global variable, or even allocate the space by directly calling a memory allocator of your choice. The Mod_construct function simply initializes the object, and hence can potentially find use in situations where the internal memory allocation performed as part of Mod_create proves too costly.

One common scenario for preferring Mod_construct over Mod_create occurs when an instance object will have a very short lifetime—and the object itself needs to come and go with minimal impact on real-time performance. Local Mod_Struct variables coupled with matching calls to Mod_construct and Mod_destruct—all appearing within the scope of a single client function—serve this purpose nicely, essentially by eliminating the cost of memory allocation. While this approach certainly scales to (long-lived) global Mod_Struct variables, efficiency advantages over Mod_create begin to diminish in this scenario; statically-created instance objects (reviewed next) in fact represent a better choice for optimizing performance in these situations.

Though outside the scope of this Primer, suffice it to say that constructed instances may have less diagnostic support than created instances—another trade-off between robustness and performance. In particular, RTSC analysis tooling used to view the set of instance objects currently associated with an individual module may not account for any dynamically-constructed instances—only instances resulting from Mod_create calls, either dynamically in the target-domain or else statically in the meta-domain. This limitation reinforces our suggestion to restrict use of Mod_construct to short-lived, transient instance objects.

Meta-domain Mod.create (statically-created instances).  This idiom can offer significant performance gains in time and (especially) space whenever your application uses instance objects that effectively remain live throughout program execution. Applications that follow the model of prog.c from Lesson 3—instance Mod_create calls in main, but no instance Mod_delete calls within prog.c. Starting with a working target program—functionally correct, but with a larger memory footprint than desired—you can better focus on translating Mod_create calls from standard C into the less familiar world of XDCscript; after a while, this translation process will seem almost mechanical to you.

By moving all target-domain Mod_create calls on a particular instance module upstream to the meta-domain, the target program need not carry runtime code supplied by that module for initializing (and finalizing) dynamically-created instances—often, a significant percentage of a module's internal implementation. And by moving all target-domain Mod_create calls on all instance modules into the meta-domain, the target program need not carry a runtime memory allocator—another significant portion of C code.

Besides a consequential reduction in target-program footprint, the virtually unlimited resources of the host-based meta-domain enable module suppliers to validate the correctness of each statically-created instance—starting with its per-instance configs and expanding to its interrelationship with other program elements. Though theoretically possible to implement equivalent runtime checks in the target-program, the practical constraints of embedded programming in C often suggest otherwise.

See also

xdc.cfg.Program.global Client documentation for xdc.cfg.Program.global

[printable version]  offline version generated on 04-Aug-2010 21:08 UTC  
Copyright © 2008 The Eclipse Foundation. All Rights Reserved
Views
Personal tools
package reference