From RTSC-Pedia
![]() | [printable version] | offline version generated on 27-May-2010 18:05 UTC | ![]() |
Extending xdc.runtime Gates/Example 1
A posix-based IGateProvider implementation
Contents |
pthread-based IGateProvider Module
In this example we show the implementation of an IGateProvider, named examples.runtime.gates.Lock that was used in a previous example to enable the xdc.runtime package to be used in a pthread-based application.
In the following sections we look under the covers to see how Lock is implemented. Finally, we look at all the other files necessary to create a package to build and house the the Lock module.
The Lock Module
All gate providers must implement the xdc.runtime.IGateProvider interface. In addition, because gate instances are most often created during configuration, gate providers should also support configuration-time instance creation. Fortunately, both of these requirements are easily meet. The Lock.xdc file declares that the Lock module inherits xdc.runtime.IGateProvider and includes an internal section that defines the instance object's state structure. This definition, together with the initialization function instance$static$init, defined in Lock.xs, is sufficient to enable static instances to be created during configuration.
Lock.xdc (Lock module's Specification) | |
| import xdc.runtime.Assert; /*! * ======== Lock ======== * Simple mutex lock */ @InstanceFinalize /* need to pthread_mutex_destroy() all mutexes */ @ModuleStartup /* need to pthread_mutex_init() all statically created mutexes */ module Lock inherits xdc.runtime.IGateProvider { /*! * ======== A_bigEnough ======== */ readonly config Assert.Id A_bigEnough = { msg: "Lock instance not large enough to hold a pthread_mutex_t" }; /*! * ======== A_initSucceeded ======== */ readonly config Assert.Id A_initSucceeded = { msg: "Initialization of pthread_mutex_t failed" }; internal: struct Instance_State { Char mutex_buf[32]; /* buffer big enough to hold a pthread_mutex_t */ } } |
The Lock module works by simply embedding a posix mutex object in the Lock module's instance object. Entering or leaving a gate is simply translated into pthread_mutex_lock or pthread_mutex_lock as necessary. But, in order to initialize the posix mutex objects that are created at configuration time, the Lock.xdc module specification uses the @ModuleStartup attribute to declare that it has a "startup" method that must be called prior to the application's main() entry-point. This startup method, named Lock_Module_startup, is defined in Lock.c and uses the standard posix pthread_mutex_init function to initialize the statically created Lock instance objects. In this case, the statically created Lock instance objects are simply the space necessary to hold a pthread_mutex_t; the real initialization occurs in Lock_Module_startup where we loop over all statically created instances and run the instance initializer, Lock_Instance_init, on each static instance.
This technique, of only allocating space at configuration time and performing instance initialization in a runtime startup function, can be generally applied to "wrap" most existing runtime content in a RTSC module that provides both static and runtime instance creation.
For completeness we need to delete the posix mutex when we delete a lock. To ensure this is done, the Lock.xdc module specification uses the @InstanceFinalize attribute to declare that this module defines an instance finalizer function, in this case Lock_Instance_finalize, that must be called when its instances are deleted.
Lock.c (Lock module's initialization and finalization code) | |
| #include <xdc/runtime/Assert.h> #include <xdc/runtime/Startup.h> #define _XOPEN_SOURCE 500 #include <pthread.h> #include "package/internal/Lock.xdc.h" /* ======== Lock_Module_startup ======== */ Int Lock_Module_startup(Int state) { Int i; /* Make sure size is big enough */ Assert_isTrue(sizeof(Lock_Object) > sizeof(pthread_mutex_t), Lock_A_bigEnough); /* initialize all permanent instances */ for (i = 0; i < Lock_Object_count(); i++) { Lock_Instance_init(Lock_Object_get(NULL, i), NULL); } return (Startup_DONE); } /* ======== Lock_Instance_init ======== */ Void Lock_Instance_init(Lock_Object *lock, const Lock_Params *params) { Int status; pthread_mutexattr_t mattrs; pthread_mutexattr_init(&mattrs); pthread_mutexattr_settype(&mattrs, PTHREAD_MUTEX_RECURSIVE); status = pthread_mutex_init( (pthread_mutex_t *)&lock->mutex_buf, &mattrs); Assert_isTrue(status == 0, Lock_A_initSucceeded); } /* ======== Lock_Instance_finalize ======== */ Void Lock_Instance_finalize(Lock_Object *lock) { pthread_mutex_destroy((pthread_mutex_t *)&lock->mutex_buf); } |
Lock.xs (Lock module's meta-domain implementation) | |
| /* ======== instance$static$init ======== */ function instance$static$init(obj, params) { /* RTSC requires all static data to be initialized */ for (var i = 0; i < obj.mutex_buf.length; i++) { obj.mutex_buf[i] = 0; } } /* ======== queryMeta ======== */ function queryMeta(qual) { /* posix mutexes are both blocking and preemptable */ if (qual == IGateProvider.Q_BLOCKING || qual == IGateProvider.Q_PREEMPTING) { return (true); } return (false); } |
To complete the implementation it is, of course, necessary to define methods that enter and leave the gate (as well as any other methods declared in the xdc.runtime.IGateProvider interface). As shown below, these methods simply use the posix mutex operations pthread_mutex_lock and pthread_mutex_unlock to enter and exit the gate.
Lock.c (remainder of Lock's target implementation) | |
| #include <xdc/runtime/IGateProvider.h> /* ======== Lock_enter ======== */ IArg Lock_enter(Lock_Object *lock) { pthread_mutex_lock((pthread_mutex_t *)&lock->mutex_buf); return (0); } /* ======== Lock_leave ======== */ Void Lock_leave(Lock_Object *lock, IArg key) { pthread_mutex_unlock((pthread_mutex_t *)&lock->mutex_buf); } /* ======== Lock_query ======== */ Bool Lock_query(Int qual) { if (qual == IGateProvider_Q_BLOCKING || qual == IGateProvider_Q_PREEMPTING) { return (TRUE); } return (FALSE); } |
To simplify this example, the implementation of Lock_enter and Lock_leave do not check for possible errors returned by the pthread_mutex methods. This example code should be modified to handle such errors before using this code in production applications. These methods could, for example, simply assert that the return values from the pthread_mutex methods indicate success. Although seemingly draconian, this would be much better than ignoring the error and potentially failing intermittently because a critical section is not being protected.
The Package Files
Since all modules must live in a package we also need to create a two other files:
- package.xdc - a file that define the package's name and identifies the modules it contains, and
- package.bld - a file that specifies which targets should be used to build the sources contained in this package.
|
|
The package.xdc file is largely self explanatory; it defines the name of the package examples.runtime.gates and it specifies the modules contained in the package, Lock.
The package.bld file, on the other hand, requires some explanation. The contents of this package.bld file indicate that the package builds a separate library in the lib/ sub-directory containing the compiled version of Lock.c for every target supported by the producer's build environment whose operating system is "Linux" (and therefore supports pthreads). Remarkably, this package.bld file is sufficient to compile Lock.c for every target, archive it into a library, and create a binary release of this package that supports all targets supported by the producer's build environment.
See also
Using xdc.runtime Gates/Example 1 | Adding concurrency support for pthread-based applications |
C - Instance_init | Module's instance initialization function |
XDCscript - Instance-Body.instance$static$init | Statically initialize a target module's instance object |
C - Instance_finalize | Module's instance finalization function |
![]() | [printable version] | offline version generated on 27-May-2010 18:05 UTC | ![]() |