Next: , Previous: File Format, Up: Top


Appendix D Internal Dispatch Table

Draft 3: 5/15/2010

netCDF Dispatch Mechanism

This document describes the architecture and details of the new netCDF internal dispatch mechanism. The idea is that when a user opens or creates a netcdf file, that a specific dispatch table is chosen. Subsequent netcdf API calls are then channeled through that dispatch table to the appropriate function for implementing that API call.

Currently, the following four dispatch tables are supported.

  1. netcdf classic files (netcdf-3)
  2. netcdf enhanced files (netcdf-4)
  3. OPeNDAP to netcdf-3
  4. OPeNDAP to netcdf-4

The dispatch table represents a distillation of the netcdf API down to a minimal set of internal operations. The format of the dispatch table is defined in the file libdispatch/ncdispatch.h. Every new dispatch table must define this minimal set of operations.

Adding a New Dispatch Table

In order to make this process concrete, let us assume we plan to add an in-memory implementation of netcdf-3.

Step 1.

Define a –enable flag and an AM_CONFIGURE flag in configure.ac. We will use the flags –enable-netcdfm and USE_NETCDFM respectively.

Step 2

Choose some prefix of characters to identify the new dispatch system. In effect we are defining a name-space. For our in-memory system, we will choose "NCM" and "ncm". NCM is used for non-static procedures to be entered into the dispatch table and ncm for all other non-static procedures.

Step 3.

Modify file libdispatch/ncdispatch.h as follows.

Step 4.

Modify file libdispatch/netcdf.c as follows.

Step 5.

Define the functions necessary to fill in the dispatch table. As a rule, we assume that a new directory is defined, libsrcm, say. Within this directory, we need to define Makefile.am, the source files containing the dispatch table and the functions to be placed in the dispatch table – call them ncmdispatch.c and ncmdispatch.h. Look at libsrc/nc3dispatch.[ch] for an example.

As part of the ncmdispatch.c file, you must define the following.

     NC_Dispatch NCM_dispatcher = {
     NC_DISPATCH_NCM,
     NCM_create,
     NCM_open,
     ...
     };
     
     int
     NCM_initialize(void)
     {
         NCM_dispatch_table = &NCM_dispatcher;
         return NC_NOERR;
     }

Assuming that the in-memory library does not require any external libraries, then the Makefile.am will look something like this.

     NCM_SOURCES = ncmdispatch.c ncmdispatch.h ...
     AM_CPPFLAGS +=  -I$(top_srcdir)/libsrc -I$(top_srcdir)/libdispatch
     libnetcdfm_la_SOURCES = $(NCM_SOURCES)
     noinst_LTLIBRARIES = libnetcdfm.la

Step 6.

Provide for the inclusion of this library in the final libnetcdf library. This is accomplished by modifying liblib/Makefile.am by adding something like the following.

     if USE_NETCDFM
        libnetcdf_la_LIBADD += $(top_builddir)/libsrcm/libnetcdfm.la
     endif

Step 7.

Modify the NC_intialize function in liblib/stub.c by adding appropriate references to the NCM dispatch function.

     #ifdef USE_NETCDFM
     extern int NCM_initialize(void);
     #endif
     ...
     int NC_initialize(void)
     {
     ...
     #ifdef USE_DAP
         if((stat = NCM_initialize())) return stat;
     #endif
     ...
     }

Step 8.

Add a directory of tests; ncm_test, say. The file ncm_test/Makefile.am will look something like this.

     # These files are created by the tests.
     CLEANFILES = ...
     # These are the tests which are always run.
     TESTPROGRAMS = test1 test2 ...
     test1_SOURCES = test1.c ...
     ...
     # Set up the tests.
     check_PROGRAMS = $(TESTPROGRAMS)
     TESTS = $(TESTPROGRAMS)
     # Any extra files required by the tests
     EXTRA_DIST = ...

Step 9.

Provide for libnetcdfm to be constructed by adding the following to the top-level Makefile.am.

     if USE_NETCDFM
     NCM=libsrcm
     NCMTESTDIR=ncm_test
     endif
     ...
     SUBDIRS = ... $(DISPATCHDIR)  $(NCM) ... $(NCMTESTDIR)

D.0.1 Choosing a Dispatch Table

The dispatch table is chosen in the NC_create and the NC_open procedures in libdispatch/netcdf.c. The decision is currently based on the following pieces of information.

In addition to the above, there is one additional mechanism to force the use of a specific dispatch table. The procedure "NC_set_dispatch_override()" can be invoked to specify a dispatch table.

When adding a new dispatcher, it is necessary to modify NC_create and NC_open in libdispatch/netcdf.c to detect when it is appropriate to use the NCM dispatcher. Some possibilities are as follows.

  1. Add a new mode flag: say NC_NETCDFM.
  2. Use an environment variable.
  3. Define a special file path format that indicates the need to use a special dispatch table.

D.0.2 Special Dispatch Table Signatures.

Several of the entries in the dispatch table are significantly different than those of the external API.

D.0.2.1 Create/Open

The create table entry and the open table entry have the following signatures respectively.

     int (*create)(const char *path, int cmode,
                size_t initialsz, int basepe, size_t *chunksizehintp,
                int useparallel, MPI_Comm comm, MPI_Info info,
                struct NC_Dispatch*, struct NC** ncp);
     
     int (*open)(const char *path, int mode,
              int basepe, size_t *chunksizehintp,
              int use_parallel, MPI_Comm comm, MPI_Info info,
              NC_Dispatch*, NC** ncp);

The key difference is that these are the union of all the possible create/open signatures from the netcdf.h API. Note especially the last two parameters. The dispatch table is included in case the create function (e.g. NCM_create) needs to invoke other dispatch functions. The very last parameter is a pointer to a pointer to an NC instance. It is expected that the create function will allocate and fill in an instance of an "NC" object and return a pointer to it in the ncp parameter.

D.0.2.2 Notes:
D.0.2.3 put_vara/get_vara
     int (*put_vara)(int ncid, int varid, const size_t *start, const size_t *count,
                          const void *value, nc_type memtype);
     
     int (*get_vara)(int ncid, int varid, const size_t *start, const size_t *count,
                     void *value, nc_type memtype);

Most of the parameters are similar to the netcdf API parameters. The last parameter, however, is the type of the data in memory. Additionally, instead of using an "int islong" parameter, the memtype will be either NC_INT or NC_INT64, depending on the value of sizeof(long). This means that even netcdf-3 code must be prepared to encounter the NC_INT64 type.

D.0.2.4 put_attr/get_attr
     int (*get_att)(int ncid, int varid, const char *name,
                         void *value, nc_type memtype);
     
     int (*put_att)(int ncid, int varid, const char *name, nc_type datatype, size_t len,
                    const void *value, nc_type memtype);

Again, the key difference is the memtype parameter. As with put/get_vara, it used NC_INT64 to encode the long case.

D.0.3 NetCDF Library Assembly

The assembly of the final libnetcdf library occurs in the directory liblib. The Makefile uses all of the available configuration flags to decide which component libraries will be added to libnetcdf to produce the final library. In addition, the proper version of netcdf.h will have been placed in liblib: either the version from libsrc or the version from libsrc4 depending on the USE_NETCDF4 flag.

D.0.4 Utility Construction

All of the utilities and the test directories (nctest, nc_test, ...) are expected to obtain their libnetcdf library and their netcdf.h from the ones in liblib.

D.0.5 Miscellaneous Notes

  1. It may be desirable to include a few test cases in the libsrcm directory. Libsrc4, for example, has quite a number of such tests. In order to do this, it is necessary to create a number of stub definitions so that the library will compile and load with the test cases. The file libsrc/stub3.c shows a typical stub file.