Initializing help system before first use

Compatibility checks: Handling versions and restrictions

Topics covered in this chapter:

The Mosel Native Interface, any modules using the NI, and also Mosel models using module functionality all are likely to evolve over time—most often via the addition of new functionality. In a development context, the components of an application typically are compiled and run using the same Mosel version. However, this may not be the case for deployment, resulting in issues of version compatibility, in particular when deploying (partial) updates to older platforms. The following sections show how such backwards compatibility can be achieved for Mosel modules.

Furthermore, the use of Mosel in protected environments, usually in the context of remote model execution (e.g., via mmjobs, XPRD, Xpress Insight), requires modules to be compliant with access restrictions that are imposed by the environment. Section Restrictions shows how to implement the necessary checks.

Mosel version

By default, Mosel modules will require (at least) the Mosel version that has been used for compiling them. That is, they cannot be run on older versions of Mosel. However, if we know that a module is not using any recent features of Mosel (e.g. it has been developed some time ago using an older version of Mosel and we are now simply recompiling it with some newer release) we can set the Mosel NI compatibility flag XPRM_NICOMPAT to some older version number.

#define XPRM_NICOMPAT 3002000           /* Compatibility level: Mosel 3.2.0 */ 

Module version

Modules that are developed and distributed over a longer period of time most likely will have gone through a number of versions— the reader is reminded that the module version number is returned in the argument libver of the module initialization function and can be generated with the help of the NI macro XPRM_MKVER. Functionality added by a given module version will obviously not be available from older versions, and inversely, models written for older module versions will not require this new functionality. So, depending on the functionality used by a given model, we may be required to use a more or less recent version of a given DSO (NB: the expected module version is stored in the BIM file).

Furthermore, depending on the conventions used for numbering a particular module, the default module version compatibility rules applied by Mosel may have to be modified for a particular module.

'Update version' service

The service XPRM_SRV_UPDVERS makes it possible to determine which module version is required by a model by inspecting the module functionality used in this particular model. This services is used by the Mosel compiler (whereas most other services are used at runtime). The DSO will be loaded with the lowest version number that satisfies the functionality required be the model. As a consequence, it is possible update the DSO to some newer version (containing additional functionality and maintaining all existing) without having to recompile the model source.

Example: Assume that we have implemented a new version 0.0.2 of the example 'solarray' from Chapter User-defined subroutines that defines an additional overloaded form of the subroutine 'getsol' returning solution values rounded to integers. That is, we now have the following two entries in the list of subroutines:

static XPRMdsofct tabfct[]=
    {
     {"solarray",1000,XPRM_TYP_NOT,2,"A.vA.r",ar_getsol},
     {"solarray",1001,XPRM_TYP_NOT,2,"A.vA.i",ar_getintsol}
    }; 

To implement detailed module version checking, we define the list of services with a single entry for the XPRM_SRV_UPDVERS service, and we update the main interface definition structure correspondingly:

/* Table of services */
static XPRMdsoserv tabserv[]=
    {
     {XPRM_SRV_UPDVERS,(void*)updvers},      /* Module version check */
    };
	
/* Interface structure */
static XPRMdsointer dsointer=
    {
     0,NULL, sizeof(tabfct)/sizeof(XPRMdsofct),tabfct, 0,NULL,
     sizeof(tabserv)/sizeof(mm_dsoserv),tabserv
    }; 

The function updvers returns the required module version depending on the input it receives in its arguments event (XPRM_UPDV_INIT = called at module initialization, returns the lowest version counter for this module; XPRM_UPDV_FUNC = checking the module version required by a particular subroutine) and what (identification number of the object for subroutines, types, parameters).

static void updvers(int event, int what, int *version)
{
 if (event==XPRM_UPDV_INIT)
   *version=XPRM_MKVER(0,0,1);               /* First version of this module */
 else if (event==XPRM_UPDV_FUNC)
 {
   switch (what) {
    case 1000: *version=XPRM_MKVER(0,0,1);   /* Works with 1st module version */
               break;
    case 1001: *version=XPRM_MKVER(0,0,2);   /* Requires 2nd module version */
   }
 }
} 

A model that is compiled using the new module version 0.0.2, but that just uses the original real-valued getsol function will load the module as version 0.0.1.

'Check version' service

The service XPRM_SRV_CHKVER allows a module to override Mosel's default version compatibility rules that are checked at runtime when loading the module: module version numbers use a code with 3 numbers (major, minor, release); by default, a module version A can be used in place of module version B if the following conditions apply

major(A) = major(B)
minor(A) = minor(B)
release(A) ≥ release(B)

Example: Instead of numbering our extension of the 'solarray' example described in the previous section as version 0.0.2, we wish to give it the version number 0.1.0. That is, we now have the following module initialization function:

DSO_INIT solarray_init(XPRMnifct nifct, int *interver,int *libver, XPRMdsointer **interf)
{
 ...
 *libver=XPRM_MKVER(0,1,0);             /* Module version */
 ...
}

The compilation of models requiring the previous version 0.0.1 will work correctly with the corresponding definition of the 'update version' service. However, loading of the generated BIM file will fail with the error message 'wrong version for module solarray' if the default compatibility rules are applied. The following definition of the XPRM_SRV_CHKVER service will make it possible to use a module version 0.1.0 with a model that expects a DSO version 0.0.*, for completeness' sake we also show the definition of function updvers:

/* Table of services */
static XPRMdsoserv tabserv[]=
    {
     {XPRM_SRV_UPDVERS,(void*)updvers},
     {XPRM_SRV_CHKVER,(void*)chkvers},
    };
	
static void updvers(int event, int what, int *version)
{
 if (event==XPRM_UPDV_INIT)
   *version=XPRM_MKVER(0,0,1);               /* First version of this module */
 else if (event==XPRM_UPDV_FUNC)
 {
   switch (what) {
    case 1000: *version=XPRM_MKVER(0,0,1);   /* Works with 1st module version */
               break;
    case 1001: *version=XPRM_MKVER(0,1,0);   /* Requires 2nd module version */
   }
 }
}

static int chkvers(int reqvers)
{
 /* This module version accepts to run with models expecting any version
    from 0.0.1 to 0.1.0 inclusive */
  return (reqvers>MM_MKVER(0,1,0))||(reqvers<MM_MKVER(0,0,1));
} 

Restrictions

Restrictions are implemented via the service XPRM_SRV_CHRES that expects a function of the form int chkres(int restr), where the restrictions to be checked are passed in the bit-encoded parameter restr.

static int chkres(int);

static XPRMdsoserv tabserv[]=
    {
     {XPRM_SRV_CHKRES,(void*)chkres},
    }; 

None of the example modules presented in this guide involve external file access or calls to external commands and therefore do not require any detailed checks of access restrictions. In all examples, we can simply add a function chkres that returns the value '0' to indicate that the module is compliant with all access restrictions, without any need for further modifications to the module definitions.

static int chkres(int r)
{
 return 0;
} 

An example module that employs external file access functions is the data compression module zlib described in the whitepaper 'Generalized file handling in Mosel'. In this example, the 'CHKRES' service and function chkres are defined as shown above and in addition, we need to check for restrictions wherever external file access functions are being used, such as calls to file opening through an external compression library.

The relevant part of the `open a compressed file' function gzip_open is shown below (the complete code of this example is provided in the file zlib.c, located in the directory examples/mosel/Modules of the Xpress distribution). Notice the use of the NI function pathcheck to expand the file name and check whether it can be accessed given the current restrictions. NB: Mosel NI file access functions such as fopen automatically perform the necessary tests for restrictions and hence do not require the addition of any tests to achieve compliance with restrictions.

static void *gzip_open(XPRMcontext ctx,int *mode,const char *fname)
{
 char cfname[MM_MAXPATHLEN];
 char cmode[16];

 if((fname==NULL)||
    (mm->pathcheck(ctx,fname,cfname,MM_MAXPATHLEN,
    ((*mode)&MM_F_WRITE)?MM_RCHK_WRITE:MM_RCHK_READ)!=0))
 {
  errno=EACCES;
  return NULL;
 }
 else
 {
  ...                                /* Build up 'cmode' */
  return gzopen(cfname,cmode);       /* Call external file access function */
 }
} 

© 2001-2024 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.