Repository: Unidata/netcdf-c
Pull request #1472 (2019-08-21 19:40:05)

more unit tests
Part of #1458.
Part of #1457 

Adding more unit tests, this time for the code in nc4internal.c. (These unit tests run for netcdf-4 builds.)

More coming. 

This includes PR #1463 and #1464.


Patch:
diff --git a/.travis.yml b/.travis.yml
index 8636dc5fc..8c2ba6c42 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,21 +12,21 @@ services:
 env:
     matrix:
 # Ubuntu
-        - DOCKIMG=unidata/nctests:serial   USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc CFLAGS='-fsigned-char' AC_COPTS='--disable-netcdf-4 --disable-dap-remote-tests --enable-cdf5 --enable-byterange' COPTS='-DENABLE_NETCDF_4=OFF -DCMAKE_C_FLAGS=-fsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=TRUE -DENABLE_BYTERANGE=TRUE' USECP=FALSE CURHOST=docker-gcc-x64-signed TESTFILTER=FALSE
+        - DOCKIMG=unidata/nctests:serial   USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc   AC_COPTS='CFLAGS=-fsigned-char --disable-netcdf-4 --disable-dap-remote-tests --enable-cdf5 --enable-byterange' COPTS='-DENABLE_NETCDF_4=OFF -DCMAKE_C_FLAGS=-fsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=TRUE -DENABLE_BYTERANGE=TRUE' USECP=FALSE CURHOST=docker-gcc-x64-signed TESTFILTER=FALSE
 
-        - DOCKIMG=unidata/nctests:serial32 USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc CFLAGS='-fsigned-char' AC_COPTS='--disable-netcdf-4 --disable-dap-remote-tests' COPTS='-DENABLE_NETCDF_4=OFF -DCMAKE_C_FLAGS=-fsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=OFF' USECP=FALSE CURHOST=docker-gcc-x86-signed TESTFILTER=FALSE
+        - DOCKIMG=unidata/nctests:serial32 USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc   AC_COPTS='CFLAGS=-fsigned-char --disable-netcdf-4 --disable-dap-remote-tests' COPTS='-DENABLE_NETCDF_4=OFF -DCMAKE_C_FLAGS=-fsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=OFF' USECP=FALSE CURHOST=docker-gcc-x86-signed TESTFILTER=FALSE
 
-        - DOCKIMG=unidata/nctests:serial   USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc CFLAGS='-funsigned-char' AC_COPTS='--disable-netcdf-4 --disable-dap-remote-tests --enable-cdf5' COPTS='-DENABLE_NETCDF_4=OFF -DCMAKE_C_FLAGS=-funsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=TRUE' USECP=FALSE CURHOST=docker-gcc-x64-unsigned TESTFILTER=FALSE
+        - DOCKIMG=unidata/nctests:serial   USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc   AC_COPTS='CFLAGS=-funsigned-char --disable-netcdf-4 --disable-dap-remote-tests --enable-cdf5' COPTS='-DENABLE_NETCDF_4=OFF -DCMAKE_C_FLAGS=-funsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=TRUE' USECP=FALSE CURHOST=docker-gcc-x64-unsigned TESTFILTER=FALSE
 
-        - DOCKIMG=unidata/nctests:serial32 USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc CFLAGS='-funsigned-char' AC_COPTS='--disable-netcdf-4 --disable-dap-remote-tests' COPTS='-DENABLE_NETCDF_4=OFF -DCMAKE_C_FLAGS=-funsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=OFF' USECP=FALSE CURHOST=docker-gcc-x86-unsigned TESTFILTER=FALSE
+        - DOCKIMG=unidata/nctests:serial32 USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc   AC_COPTS='CFLAGS=-funsigned-char --disable-netcdf-4 --disable-dap-remote-tests' COPTS='-DENABLE_NETCDF_4=OFF -DCMAKE_C_FLAGS=-funsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=OFF' USECP=FALSE CURHOST=docker-gcc-x86-unsigned TESTFILTER=FALSE
 
-        - DOCKIMG=unidata/nctests:serial   USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc CFLAGS='-fsigned-char' AC_COPTS='--disable-dap-remote-tests --enable-cdf5' COPTS='-DCMAKE_C_FLAGS=-fsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=TRUE' USECP=FALSE CURHOST=docker-gcc-x64-signed TESTFILTER=TRUE
+        - DOCKIMG=unidata/nctests:serial   USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc   AC_COPTS='CFLAGS=-fsigned-char --disable-dap-remote-tests --enable-cdf5' COPTS='-DCMAKE_C_FLAGS=-fsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=TRUE' USECP=FALSE CURHOST=docker-gcc-x64-signed TESTFILTER=TRUE
 
-        - DOCKIMG=unidata/nctests:serial32 USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc CFLAGS='-fsigned-char' AC_COPTS='--disable-dap-remote-tests' COPTS='-DCMAKE_C_FLAGS=-fsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=OFF' USECP=FALSE CURHOST=docker-gcc-x86-signed TESTFILTER=TRUE
+        - DOCKIMG=unidata/nctests:serial32 USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc   AC_COPTS='CFLAGS=-fsigned-char --disable-dap-remote-tests' COPTS='-DCMAKE_C_FLAGS=-fsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=OFF' USECP=FALSE CURHOST=docker-gcc-x86-signed TESTFILTER=TRUE
 
-        - DOCKIMG=unidata/nctests:serial   USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc CFLAGS='-funsigned-char' AC_COPTS='--disable-dap-remote-tests --enable-cdf5' COPTS='-DCMAKE_C_FLAGS=-funsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=TRUE' USECP=FALSE CURHOST=docker-gcc-x64-unsigned TESTFILTER=TRUE
+        - DOCKIMG=unidata/nctests:serial   USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc   AC_COPTS='CFLAGS=-funsigned-char --disable-dap-remote-tests --enable-cdf5' COPTS='-DCMAKE_C_FLAGS=-funsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=TRUE' USECP=FALSE CURHOST=docker-gcc-x64-unsigned TESTFILTER=TRUE
 
-        - DOCKIMG=unidata/nctests:serial32 USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc CFLAGS='-funsigned-char' AC_COPTS='--disable-dap-remote-tests' COPTS='-DCMAKE_C_FLAGS=-funsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=OFF' USECP=FALSE CURHOST=docker-gcc-x86-unsigned TESTFILTER=TRUE
+        - DOCKIMG=unidata/nctests:serial32 USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc   AC_COPTS='CFLAGS=-funsigned-char --disable-dap-remote-tests' COPTS='-DCMAKE_C_FLAGS=-funsigned-char -DENABLE_DAP_REMOTE_TESTS=OFF -DENABLE_CDF5=OFF' USECP=FALSE CURHOST=docker-gcc-x86-unsigned TESTFILTER=TRUE
 
 # Centos, Fedora
         - DOCKIMG=unidata/nctests:serial.centos USECMAKE=TRUE USEAC=TRUE DISTCHECK=TRUE USE_CC=gcc AC_COPTS='--disable-dap-remote-tests' COPTS='-DENABLE_DAP_REMOTE_TESTS=OFF' USECP=TRUE CURHOST=docker-gcc-x64-centos TESTFILTER=TRUE
@@ -37,4 +37,4 @@ before_install:
 
 script:
 
-    - docker run --privileged --rm -it -h "$CURHOST" -e USEDASH=FALSE -e RUNF=OFF -e RUNCXX=OFF -e RUNP=OFF -e RUNNCO=OFF -e USECMAKE=$USECMAKE -e USEAC=$USEAC -e DISTCHECK=$DISTCHECK -e COPTS="$COPTS" -e AC_OPTS="$AC_OPTS" -e CTEST_OUTPUT_ON_FAILURE=1 -v $(pwd):/netcdf-c -e USE_LOCAL_CP=$USECP -e USE_CC=$USE_CC -e TESTPROC=100  -e ENABLE_FILTER_TESTING=$TESTFILTER -e ENABLE_C_MEMCHECK=OFF -e CFLAGS="$CFLAGS" $DOCKIMG
+    - docker run --privileged --rm -it -h "$CURHOST" -e USEDASH=FALSE -e RUNF=OFF -e RUNCXX=OFF -e RUNP=OFF -e RUNNCO=OFF -e USECMAKE=$USECMAKE -e USEAC=$USEAC -e DISTCHECK=$DISTCHECK -e COPTS="$COPTS" -e AC_OPTS="$AC_OPTS" -e CTEST_OUTPUT_ON_FAILURE=1 -v $(pwd):/netcdf-c -e USE_LOCAL_CP=$USECP -e USE_CC=$USE_CC -e TESTPROC=100  -e ENABLE_FILTER_TESTING=$TESTFILTER -e ENABLE_C_MEMCHECK=OFF $DOCKIMG
diff --git a/Makefile.am b/Makefile.am
index 876725885..61d3f523e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -21,6 +21,11 @@ test-driver-verbose test_common.in
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = netcdf.pc
 
+# Does the user want to build and run unit tests?
+if BUILD_UNIT_TESTS
+UNIT_TEST = unit_test
+endif # BUILD_UNIT_TESTS
+
 # Does the user want to build the V2 API?
 if BUILD_V2
 V2_TEST = nctest
@@ -85,8 +90,8 @@ endif # BUILD_BENCHMARKS
 
 # Define Test directories
 if BUILD_TESTSETS
-TESTDIRS = $(V2_TEST) nc_test $(NC_TEST4) $(BENCHMARKS_DIR)	\
-$(HDF4_TEST_DIR) $(NCDAP2TESTDIR) $(NCDAP4TESTDIR)
+TESTDIRS = $(UNIT_TEST) $(V2_TEST) nc_test $(NC_TEST4)			\
+$(BENCHMARKS_DIR) $(HDF4_TEST_DIR) $(NCDAP2TESTDIR) $(NCDAP4TESTDIR)
 endif
 
 # This is the list of subdirs for which Makefiles will be constructed
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index ba1fc106e..04c7eb960 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -7,6 +7,13 @@ This file contains a high-level description of this package's evolution. Release
 
 ## 4.7.1 - TBD
 
+* [Bug Fix] Remove obsolete _CRAYMPP and LOCKNUMREC macros from
+code. Also brought documentation up to date in man page. These macros
+were used in ancient times, before modern parallel I/O systems were
+developed. Programmers interested in parallel I/O should see
+nc_open_par() and nc_create_par().
+See [GitHub #1436](https://github.com/Unidata/netcdf-c/issues/1459)
+
 * [Enhancement] Remove obsolete and deprecated functions
 nc_set_base_pe() and nc_inq_base_pe() from the dispatch table. (Both
 functions are still supported in the library, this is an internal
diff --git a/configure.ac b/configure.ac
index f311281ee..8ecd51400 100644
--- a/configure.ac
+++ b/configure.ac
@@ -184,6 +184,14 @@ if test "x$enable_jna" = xyes ; then
 AC_DEFINE([JNA], [1], [if true, include jna bug workaround code])
 fi
 
+# Does the user want to turn off unit tests (useful for test coverage
+# analysis).
+AC_ARG_ENABLE([unit-tests],
+  [AS_HELP_STRING([--disable-unit-tests],
+    [Disable tests in unit_test directory. Other tests still run.])])
+test "x$enable_unit_tests" = xno || enable_unit_tests=yes
+AM_CONDITIONAL([BUILD_UNIT_TESTS], [test "x$enable_unit_tests" = xyes])
+
 # Does the user want to build netcdf-4?
 AC_MSG_CHECKING([whether we should build netCDF-4])
 AC_ARG_ENABLE([netcdf-4], [AS_HELP_STRING([--disable-netcdf-4],
@@ -1536,6 +1544,7 @@ AC_CONFIG_FILES([Makefile
                  ncdump/expected/Makefile
                  docs/Makefile
                  docs/images/Makefile
+                 unit_test/Makefile
                  nctest/Makefile
                  nc_test4/Makefile
                  nc_perf/Makefile
diff --git a/docs/netcdf.m4 b/docs/netcdf.m4
index 19dd1d659..13b978928 100644
--- a/docs/netcdf.m4
+++ b/docs/netcdf.m4
@@ -1411,47 +1411,29 @@ May be used to change access to a variable from independent to collective data a
 .HP
 .SH "MPP FUNCTION DESCRIPTIONS"
 .LP
-Additional functions for use on SGI/Cray MPP machines (_CRAYMPP).
-These are used to set and inquire which PE is the base for MPP
-for a particular netCDF. These are only relevant when
-using the SGI/Cray ``global''
-Flexible File I/O layer and desire to have
-only a subset of PEs to open the specific netCDF file.
-For technical reasons, these functions are available on all platforms.
-On a platform other than SGI/Cray MPP, it is as if
-only processor available were processor 0.
+These functions were used on archaic SGI/Cray MPP machines. These
+functions are retained for backward compatibility; the PE arguments
+must all be set to zero.
 .LP
-To use this feature, you need to specify a communicator group and call
-CODE(glio_group_mpi(\|)) or CODE(glio_group_shmem(\|)) prior to the netCDF
-FREF(open) and FREF(create) calls.
 .HP
 FDECL(_create_mp, (IPATH(), ICMODE(), IINITSIZE(), IPE(), OCHUNKSIZE(), ONCID()))
 .sp
-Like FREF(_create) but allows the base PE to be set.
+Like FREF(_create).
 .sp
-The argument ARG(pe) sets the base PE at creation time. In the MPP
-environment, FREF(_create) and FREF(create) set the base PE to processor
-zero by default.
+The argument ARG(pe) must be zero.
 .HP
 FDECL(_open_mp, (IPATH(), IMODE(), IPE(), OCHUNKSIZE(), ONCID()))
 .sp
-Like FREF(_open) but allows the base PE to be set.
-The argument ARG(pe) sets the base PE at creation time. In the MPP
-environment, FREF(_open) and FREF(open) set the base PE to processor
-zero by default.
+Like FREF(_open).
+The argument ARG(pe) must be zero.
 .HP
 FDECL(inq_base_pe, (INCID(), OPE()))
 .sp
-Inquires of the netCDF dataset which PE is being used as the base for MPP use.
-This is safe to use at any time.
+Always returns pe of zero.
 .HP
 FDECL(set_base_pe, (INCID(), IPE()))
 .sp
-Resets the base PE for the netCDF dataset.
-Only perform this operation when the affected communicator group
-synchronizes before and after the call.
-This operation is very risky and should only be contemplated
-under only the most extreme cases.
+This function does nothing.
 .SH "ENVIRONMENT VARIABLES"
 .TP 4
 .B NETCDF_FFIOSPEC
diff --git a/include/nc3internal.h b/include/nc3internal.h
index a0e53bf62..af4f2ea72 100644
--- a/include/nc3internal.h
+++ b/include/nc3internal.h
@@ -221,15 +221,6 @@ NC_lookupvar(NC3_INFO* ncp, int varid, NC_var **varp);
 #define IS_RECVAR(vp)                                           \
     ((vp)->shape != NULL ? (*(vp)->shape == NC_UNLIMITED) : 0 )
 
-#ifdef LOCKNUMREC
-/*
- * typedef SHMEM type
- * for whenever the SHMEM functions can handle other than shorts
- */
-typedef unsigned short int      ushmem_t;
-typedef short int                shmem_t;
-#endif
-
 struct NC3_INFO {
     /* contains the previous NC during redef. */
     NC3_INFO *old;
@@ -258,18 +249,6 @@ struct NC3_INFO {
     NC_dimarray dims;
     NC_attrarray attrs;
     NC_vararray vars;
-#ifdef LOCKNUMREC
-/* size and named indexes for the lock array protecting NC.numrecs */
-#  define LOCKNUMREC_DIM        4
-#  define LOCKNUMREC_VALUE      0
-#  define LOCKNUMREC_LOCK       1
-#  define LOCKNUMREC_SERVING    2
-#  define LOCKNUMREC_BASEPE     3
-    /* Used on Cray T3E MPP to maintain the
-     * integrity of numrecs for an unlimited dimension
-     */
-    ushmem_t lock[LOCKNUMREC_DIM];
-#endif
 };
 
 #define NC_readonly(ncp)                        \
@@ -305,7 +284,6 @@ struct NC3_INFO {
 #define NC_doNsync(ncp)                         \
     fIsSet((ncp)->flags, NC_NSYNC)
 
-#ifndef LOCKNUMREC
 #  define NC_get_numrecs(nc3i)                  \
     ((nc3i)->numrecs)
 
@@ -314,11 +292,6 @@ struct NC3_INFO {
 
 #  define NC_increase_numrecs(nc3i, nrecs)                              \
     {if((nrecs) > (nc3i)->numrecs) ((nc3i)->numrecs = (nrecs));}
-#else
-    size_t NC_get_numrecs(const NC3_INFO *nc3i);
-void   NC_set_numrecs(NC3_INFO *nc3i, size_t nrecs);
-void   NC_increase_numrecs(NC3_INFO *nc3i, size_t nrecs);
-#endif
 
 /* Begin defined in nc.c */
 
diff --git a/include/netcdf.h b/include/netcdf.h
index 6ba8ba48b..c8b00395c 100644
--- a/include/netcdf.h
+++ b/include/netcdf.h
@@ -4,7 +4,7 @@ Main header file for the C API.
 
 Copyright 2018, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014,
-2015, 2016, 2017, 2018
+2015, 2016, 2017, 2018, 2019
 University Corporation for Atmospheric Research/Unidata.
 
 See \ref copyright file for more info.
@@ -1760,19 +1760,17 @@ nc_show_metadata(int ncid);
 
 /* End {put,get}_var */
 
-/* #ifdef _CRAYMPP */
-/*
- * Public interfaces to better support
- * CRAY multi-processor systems like T3E.
- * A tip of the hat to NERSC.
- */
+/* Delete a file. */
+EXTERNL int
+nc_delete(const char *path);
+
 /*
- * It turns out we need to declare and define
- * these public interfaces on all platforms
- * or things get ugly working out the
- * FORTRAN interface. On !_CRAYMPP platforms,
- * these functions work as advertised, but you
- * can only use "processor element" 0.
+ * The following functions were written to accomodate the old Cray
+ * systems. Modern HPC systems do not use these functions any more,
+ * but use the nc_open_par()/nc_create_par() functions instead. These
+ * functions are retained for backward compatibibility. These
+ * functions work as advertised, but you can only use "processor
+ * element" 0.
  */
 
 EXTERNL int
@@ -1783,9 +1781,6 @@ EXTERNL int
 nc__open_mp(const char *path, int mode, int basepe,
         size_t *chunksizehintp, int *ncidp);
 
-EXTERNL int
-nc_delete(const char *path);
-
 EXTERNL int
 nc_delete_mp(const char *path, int basepe);
 
@@ -1795,8 +1790,6 @@ nc_set_base_pe(int ncid, int pe);
 EXTERNL int
 nc_inq_base_pe(int ncid, int *pe);
 
-/* #endif _CRAYMPP */
-
 /* This v2 function is used in the nc_test program. */
 EXTERNL int
 nctypelen(nc_type datatype);
diff --git a/libdispatch/nc.c b/libdispatch/nc.c
index ba07c6538..0c600c73a 100644
--- a/libdispatch/nc.c
+++ b/libdispatch/nc.c
@@ -1,31 +1,39 @@
 /*
- *	Copyright 2018, University Corporation for Atmospheric Research
+ *      Copyright 2018, University Corporation for Atmospheric Research
  *      See netcdf/COPYRIGHT file for copying and redistribution conditions.
  */
+/**
+ * @file
+ * @internal
+ *
+ * This file contains functions that work with the NC struct. There is
+ * an NC struct for every open netCDF file.
+ *
+ * @author Glenn Davis
+ */
 
 #include "config.h"
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
-#if defined(LOCKNUMREC) /* && _CRAYMPP */
-#  include <mpp/shmem.h>
-#  include <intrinsics.h>
-#endif
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
-
 #include "ncdispatch.h"
 
-/* This is the default create format for nc_create and nc__create. */
+/** This is the default create format for nc_create and nc__create. */
 static int default_create_format = NC_FORMAT_CLASSIC;
 
-/* These have to do with version numbers. */
-#define MAGIC_NUM_LEN 4
-#define VER_CLASSIC 1
-#define VER_64BIT_OFFSET 2
-#define VER_HDF5 3
-
+/**
+ * Find the NC struct for an open file, using the ncid.
+ *
+ * @param ncid The ncid to find.
+ * @param ncpp Pointer that gets a pointer to the NC.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID ncid not found.
+ * @author Glenn Davis, Dennis Heimbigner
+ */
 int
 NC_check_id(int ncid, NC** ncpp)
 {
@@ -35,25 +43,46 @@ NC_check_id(int ncid, NC** ncpp)
     return NC_NOERR;
 }
 
+/**
+ * Free an NC struct and its related resources. Before this is done,
+ * be sure to remove the NC from the open file list with
+ * del_from_NCList().
+ *
+ * @param ncp Pointer to the NC struct to be freed.
+ *
+ * @author Glenn Davis, Dennis Heimbigner
+ */
 void
 free_NC(NC *ncp)
 {
     if(ncp == NULL)
-	return;
+        return;
     if(ncp->path)
-	free(ncp->path);
+        free(ncp->path);
     if(ncp->model)
-	free(ncp->model);
+        free(ncp->model);
     /* We assume caller has already cleaned up ncp->dispatchdata */
-#if _CRAYMPP && defined(LOCKNUMREC)
-    shfree(ncp);
-#else
     free(ncp);
-#endif /* _CRAYMPP && LOCKNUMREC */
 }
 
+/**
+ * Create and initialize a new NC struct. The ncid is assigned later.
+ *
+ * @param dispatcher An pointer to the NC_Dispatch table that should
+ * be used by this NC.
+ * @param path The name of the file.
+ * @param mode The open or create mode.
+ * @param model An NCmodel instance, provided by NC_infermodel().
+ * @param ncpp A pointer that gets a pointer to the newlly allocacted
+ * and initialized NC struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ENOMEM Out of memory.
+ * @author Glenn Davis, Dennis Heimbigner
+ */
 int
-new_NC(const NC_Dispatch* dispatcher, const char* path, int mode, NCmodel* model, NC** ncpp)
+new_NC(const NC_Dispatch* dispatcher, const char* path, int mode,
+       NCmodel* model, NC** ncpp)
 {
     NC *ncp = (NC*)calloc(1,sizeof(NC));
     if(ncp == NULL) return NC_ENOMEM;
@@ -61,31 +90,40 @@ new_NC(const NC_Dispatch* dispatcher, const char* path, int mode, NCmodel* model
     ncp->path = nulldup(path);
     ncp->mode = mode;
     if((ncp->model = malloc(sizeof(NCmodel)))==NULL)
-	return NC_ENOMEM;
+        return NC_ENOMEM;
     *ncp->model = *model; /* Make a copy */
     if(ncp->path == NULL) { /* fail */
         free_NC(ncp);
-	return NC_ENOMEM;
+        return NC_ENOMEM;
     }
     if(ncpp) {
-      *ncpp = ncp;
+        *ncpp = ncp;
     } else {
-      free_NC(ncp);
+        free_NC(ncp);
     }
     return NC_NOERR;
 }
 
-/* This function sets a default create flag that will be logically
-   or'd to whatever flags are passed into nc_create for all future
-   calls to nc_create.
-   Valid default create flags are NC_64BIT_OFFSET, NC_CLOBBER,
-   NC_LOCK, NC_SHARE. */
+/**
+ * This function sets a default create flag that will be logically
+ *  or'd to whatever flags are passed into nc_create for all future
+ *  calls to nc_create.
+ *
+ * @param format The format that should become the default.
+ * @param old_formatp Pointer that gets the previous default. Ignored
+ * if NULL.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ENOTBUILD Requested format not built with this install.
+ * @return ::NC_EINVAL Invalid input.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
 int
 nc_set_default_format(int format, int *old_formatp)
 {
     /* Return existing format if desired. */
     if (old_formatp)
-      *old_formatp = default_create_format;
+        *old_formatp = default_create_format;
 
     /* Make sure only valid format is set. */
 #ifndef ENABLE_CDF5
@@ -95,7 +133,7 @@ nc_set_default_format(int format, int *old_formatp)
 #ifdef USE_HDF5
     if (format != NC_FORMAT_CLASSIC && format != NC_FORMAT_64BIT_OFFSET &&
         format != NC_FORMAT_NETCDF4 && format != NC_FORMAT_NETCDF4_CLASSIC &&
-	format != NC_FORMAT_CDF5)
+        format != NC_FORMAT_CDF5)
         return NC_EINVAL;
 #else
     if (format == NC_FORMAT_NETCDF4 || format == NC_FORMAT_NETCDF4_CLASSIC)
@@ -108,6 +146,12 @@ nc_set_default_format(int format, int *old_formatp)
     return NC_NOERR;
 }
 
+/**
+ * Get the current default format.
+ *
+ * @return the default format.
+ * @author Ed Hartnett
+ */
 int
 nc_get_default_format(void)
 {
diff --git a/libdispatch/nclistmgr.c b/libdispatch/nclistmgr.c
index 043addb6e..e05b7a3a4 100644
--- a/libdispatch/nclistmgr.c
+++ b/libdispatch/nclistmgr.c
@@ -1,7 +1,15 @@
 /*********************************************************************
    Copyright 2018, UCAR/Unidata See netcdf/COPYRIGHT file for
    copying and redistribution conditions.
- *********************************************************************/
+*********************************************************************/
+/**
+ * @file
+ *
+ * Functions to manage the list of NC structs. There is one NC struct
+ * for each open file.
+ *
+ * @author Dennis Heimbigner
+*/
 
 #include "config.h"
 #include <stdlib.h>
@@ -9,22 +17,40 @@
 #include <assert.h>
 #include "ncdispatch.h"
 
+/** This shift is applied to the ext_ncid in order to get the index in
+ * the array of NC. */
 #define ID_SHIFT (16)
+
+/** This is the length of the NC list - the number of files that can
+ * be open at one time. We use 2^16 = 65536 entries in the array, but
+ * slot 0 is not used, so only 65535 files may be open at one
+ * time. */
 #define NCFILELISTLENGTH 0x10000
 
-/* Version one just allocates the max space (sizeof(NC*)*2^16)*/
+/** This is the pointer to the array of NC, one for each open file. */
 static NC** nc_filelist = NULL;
 
+/** The number of files currently open. */
 static int numfiles = 0;
 
-/* Common */
+/**
+ * How many files are currently open?
+ *
+ * @return number of open files.
+ * @author Dennis Heimbigner
+ */
 int
 count_NCList(void)
 {
     return numfiles;
 }
 
-
+/**
+ * Free an empty NCList. @note If list is not empty, or has not been
+ * allocated, function will silently exit.
+ *
+ * @author Dennis Heimbigner
+ */
 void
 free_NCList(void)
 {
@@ -33,86 +59,156 @@ free_NCList(void)
     nc_filelist = NULL;
 }
 
+/**
+ * Add an already-allocated NC to the list. It will be assigned an
+ * ncid in this function.
+ *
+ * If this is the first file to be opened, the nc_filelist will be
+ * allocated and set to all 0.
+ *
+ * The ncid is assigned by finding the first open index in the
+ * nc_filelist array (skipping index 0). The ncid is this index
+ * left-shifted ID_SHIFT bits (16). This puts the file ID in the first
+ * two bytes of the 4-byte integer, and leaves the last two bytes for
+ * group IDs for netCDF-4 files.
+ *
+ * @param ncp Pointer to already-allocated and initialized NC struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ENOMEM Out of memory.
+ * @author Dennis Heimbigner
+ */
 int
 add_to_NCList(NC* ncp)
 {
     int i;
     int new_id;
     if(nc_filelist == NULL) {
-	if (!(nc_filelist = calloc(1, sizeof(NC*)*NCFILELISTLENGTH)))
-	    return NC_ENOMEM;
-	numfiles = 0;
+        if (!(nc_filelist = calloc(1, sizeof(NC*)*NCFILELISTLENGTH)))
+            return NC_ENOMEM;
+        numfiles = 0;
     }
 
     new_id = 0; /* id's begin at 1 */
     for(i=1; i < NCFILELISTLENGTH; i++) {
-	if(nc_filelist[i] == NULL) {new_id = i; break;}
+        if(nc_filelist[i] == NULL) {new_id = i; break;}
     }
     if(new_id == 0) return NC_ENOMEM; /* no more slots */
     nc_filelist[new_id] = ncp;
     numfiles++;
-    new_id = (new_id << ID_SHIFT);
-    ncp->ext_ncid = new_id;
+    ncp->ext_ncid = (new_id << ID_SHIFT);
     return NC_NOERR;
 }
 
+/**
+ * Delete an NC struct from the list. This happens when the file is
+ * closed. Relies on all memory in the NC being deallocated after this
+ * function with freeNC().
+ *
+ * @note If the file list is empty, or this NC can't be found in the
+ * list, this function will silently exit.
+ *
+ * @param ncp Pointer to NC to be removed from list.
+ *
+ * @author Dennis Heimbigner
+ */
 void
 del_from_NCList(NC* ncp)
 {
-   unsigned int ncid = ((unsigned int)ncp->ext_ncid) >> ID_SHIFT;
-   if(numfiles == 0 || ncid == 0 || nc_filelist == NULL) return;
-   if(nc_filelist[ncid] != ncp) return;
+    unsigned int ncid = ((unsigned int)ncp->ext_ncid) >> ID_SHIFT;
+    if(numfiles == 0 || ncid == 0 || nc_filelist == NULL) return;
+    if(nc_filelist[ncid] != ncp) return;
 
-   nc_filelist[ncid] = NULL;
-   numfiles--;
+    nc_filelist[ncid] = NULL;
+    numfiles--;
 
-   /* If all files have been closed, release the filelist memory. */
-   if (numfiles == 0)
-      free_NCList();
+    /* If all files have been closed, release the filelist memory. */
+    if (numfiles == 0)
+        free_NCList();
 }
 
+/**
+ * Find an NC in the list, given an ext_ncid. The NC list is indexed
+ * with the first two bytes of ext_ncid. (The last two bytes specify
+ * the group for netCDF4 files, or are zeros for classic files.)
+ *
+ * @param ext_ncid The ncid of the file to find.
+ *
+ * @return pointer to NC or NULL if not found.
+ * @author Dennis Heimbigner, Ed Hartnett
+ */
 NC *
 find_in_NCList(int ext_ncid)
 {
-   NC* f = NULL;
-   unsigned int ncid = ((unsigned int)ext_ncid) >> ID_SHIFT;
-   if(numfiles > 0 && nc_filelist != NULL && ncid < NCFILELISTLENGTH)
-	f = nc_filelist[ncid];
+    NC* f = NULL;
+    /* Discard the first two bytes of ext_ncid to get ncid. */
+    unsigned int ncid = ((unsigned int)ext_ncid) >> ID_SHIFT;
+
+    /* If we have a filelist, there will be an entry, possibly NULL,
+     * for this ncid. */
+    if (nc_filelist)
+    {
+        assert(numfiles);
+        f = nc_filelist[ncid];
+    }
 
-   /* for classic files, ext_ncid must be a multiple of (1<<ID_SHIFT) */
-   if (f != NULL && f->model->impl == NC_FORMATX_NC3 && (ext_ncid % (1<<ID_SHIFT)))
-       return NULL;
+    /* For classic files, ext_ncid must be a multiple of
+     * (1<<ID_SHIFT). That is, the group part of the ext_ncid (the
+     * last two bytes) must be zero. If not, then return NULL, which
+     * will eventually lead to an NC_EBADID error being returned to
+     * user. */
+    if (f != NULL && f->model->impl == NC_FORMATX_NC3 && (ext_ncid % (1<<ID_SHIFT)))
+        return NULL;
 
-   return f;
+    return f;
 }
 
-/*
-Added to support open by name
-*/
+/**
+ * Find an NC in the list using the file name.
+ *
+ * @param path Name of the file.
+ *
+ * @return pointer to NC or NULL if not found.
+ * @author Dennis Heimbigner
+ */
 NC*
 find_in_NCList_by_name(const char* path)
 {
-   int i;
-   NC* f = NULL;
-   if(nc_filelist == NULL)
-	return NULL;
-   for(i=1; i < NCFILELISTLENGTH; i++) {
-	if(nc_filelist[i] != NULL) {
-	    if(strcmp(nc_filelist[i]->path,path)==0) {
-		f = nc_filelist[i];
-		break;
-	    }
-	}
-   }
-   return f;
+    int i;
+    NC* f = NULL;
+    if(nc_filelist == NULL)
+        return NULL;
+    for(i=1; i < NCFILELISTLENGTH; i++) {
+        if(nc_filelist[i] != NULL) {
+            if(strcmp(nc_filelist[i]->path,path)==0) {
+                f = nc_filelist[i];
+                break;
+            }
+        }
+    }
+    return f;
 }
 
+/**
+ * Find an NC in list based on its index. The index is ((unsigned
+ * int)ext_ncid) >> ID_SHIFT. This is the two high bytes of the
+ * ext_ncid. (The other two bytes are used for the group ID for
+ * netCDF-4 files.)
+ *
+ * @param index The index in the NC list.
+ * @param ncp Pointer that gets pointer to the next NC. Igored if
+ * NULL.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ERANGE Index out of range.
+ * @author Dennis Heimbigner
+ */
 int
 iterate_NCList(int index, NC** ncp)
 {
     /* Walk from 0 ...; 0 return => stop */
     if(index < 0 || index >= NCFILELISTLENGTH)
-	return NC_ERANGE;
+        return NC_ERANGE;
     if(ncp) *ncp = nc_filelist[index];
     return NC_NOERR;
 }
diff --git a/libsrc/nc3internal.c b/libsrc/nc3internal.c
index 063f6864e..36afe710d 100644
--- a/libsrc/nc3internal.c
+++ b/libsrc/nc3internal.c
@@ -10,10 +10,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
-#if defined(LOCKNUMREC) /* && _CRAYMPP */
-#  include <mpp/shmem.h>
-#  include <intrinsics.h>
-#endif
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -47,11 +43,7 @@ free_NC3INFO(NC3_INFO *nc3)
 	free_NC_dimarrayV(&nc3->dims);
 	free_NC_attrarrayV(&nc3->attrs);
 	free_NC_vararrayV(&nc3->vars);
-#if _CRAYMPP && defined(LOCKNUMREC)
-	shfree(nc3);
-#else
 	free(nc3);
-#endif /* _CRAYMPP && LOCKNUMREC */
 }
 
 static NC3_INFO *
@@ -948,21 +940,6 @@ NC_endef(NC3_INFO *ncp,
 	return ncio_sync(ncp->nciop);
 }
 
-#ifdef LOCKNUMREC
-static int
-NC_init_pe(NC *ncp, int basepe) {
-	if (basepe < 0 || basepe >= _num_pes()) {
-		return NC_EINVAL; /* invalid base pe */
-	}
-	/* initialize common values */
-	ncp->lock[LOCKNUMREC_VALUE] = 0;
-	ncp->lock[LOCKNUMREC_LOCK] = 0;
-	ncp->lock[LOCKNUMREC_SERVING] = 0;
-	ncp->lock[LOCKNUMREC_BASEPE] =  basepe;
-	return NC_NOERR;
-}
-#endif
-
 
 /*
  * Compute the expected size of the file.
@@ -1017,11 +994,7 @@ int NC3_new_nc(NC3_INFO** ncpp)
 	NC *nc;
 	NC3_INFO* nc3;
 
-#if _CRAYMPP && defined(LOCKNUMREC)
-	ncp = (NC *) shmalloc(sizeof(NC));
-#else
 	ncp = (NC *) malloc(sizeof(NC));
-#endif /* _CRAYMPP && LOCKNUMREC */
 	if(ncp == NULL)
 		return NC_ENOMEM;
 	(void) memset(ncp, 0, sizeof(NC));
@@ -1058,20 +1031,13 @@ NC3_create(const char *path, int ioflags, size_t initialsz, int basepe,
 	fSet(ioflags, NC_SHARE);
 #endif
 
-#if defined(LOCKNUMREC) /* && _CRAYMPP */
-	if (status = NC_init_pe(nc3, basepe)) {
-		return status;
-	}
-#else
 	/*
-	 * !_CRAYMPP, only pe 0 is valid
+	 * Only pe 0 is valid
 	 */
 	if(basepe != 0) {
-      if(nc3) free(nc3);
-      return NC_EINVAL;
-    }
-#endif
-
+            if(nc3) free(nc3);
+            return NC_EINVAL;
+        }
 	assert(nc3->flags == 0);
 
 	/* Now we can set min size */
@@ -1193,23 +1159,17 @@ NC3_open(const char *path, int ioflags, int basepe, size_t *chunksizehintp,
 	fSet(ioflags, NC_SHARE);
 #endif
 
-#if defined(LOCKNUMREC) /* && _CRAYMPP */
-	if (status = NC_init_pe(nc3, basepe)) {
-	    goto unwind_alloc;
-	}
-#else
 	/*
-	 * !_CRAYMPP, only pe 0 is valid
+	 * Only pe 0 is valid.
 	 */
 	if(basepe != 0) {
-      if(nc3) {
-        free(nc3);
-        nc3 = NULL;
-      }
-      status = NC_EINVAL;
-      goto unwind_alloc;
-    }
-#endif
+            if(nc3) {
+                free(nc3);
+                nc3 = NULL;
+            }
+            status = NC_EINVAL;
+            goto unwind_alloc;
+        }
 
 #ifdef ENABLE_BYTERANGE
     /* If the model specified the use of byte-ranges, then signal by
@@ -1577,37 +1537,6 @@ NC3_set_fill(int ncid,
 	return NC_NOERR;
 }
 
-#ifdef LOCKNUMREC
-
-/* create function versions of the NC_*_numrecs macros */
-size_t
-NC_get_numrecs(const NC *nc3) {
-	shmem_t numrec;
-	shmem_short_get(&numrec, (shmem_t *) nc3->lock + LOCKNUMREC_VALUE, 1,
-		nc3->lock[LOCKNUMREC_BASEPE]);
-	return (size_t) numrec;
-}
-
-void
-NC_set_numrecs(NC *nc3, size_t nrecs)
-{
-    shmem_t numrec = (shmem_t) nrecs;
-    /* update local value too */
-    nc3->lock[LOCKNUMREC_VALUE] = (ushmem_t) numrec;
-    shmem_short_put((shmem_t *) nc3->lock + LOCKNUMREC_VALUE, &numrec, 1,
-    nc3->lock[LOCKNUMREC_BASEPE]);
-}
-
-void NC_increase_numrecs(NC *nc3, size_t nrecs)
-{
-    /* this is only called in one place that's already protected
-     * by a lock ... so don't worry about it */
-    if (nrecs > NC_get_numrecs(nc3))
-	NC_set_numrecs(nc3, nrecs);
-}
-
-#endif /* LOCKNUMREC */
-
 /**
  * Return the file format.
  *
@@ -1719,7 +1648,18 @@ NC3_inq_type(int ncid, nc_type typeid, char *name, size_t *size)
    return NC_NOERR;
 }
 
-/**************************************************/
+/**
+ * This is an obsolete form of nc_delete(), supported for backwards
+ * compatibility.
+ *
+ * @param path Filename to delete.
+ * @param basepe Must be 0.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EIO Couldn't delete file.
+ * @return ::NC_EINVAL Invaliod basepe. Must be 0.
+ * @author Glenn Davis, Ed Hartnett
+ */
 int
 nc_delete_mp(const char * path, int basepe)
 {
@@ -1733,17 +1673,11 @@ nc_delete_mp(const char * path, int basepe)
 	status = NC_check_id(ncid,&nc);
         if(status) return status;
 
-#if defined(LOCKNUMREC) /* && _CRAYMPP */
-	if (status = NC_init_pe(nc3, basepe)) {
-		return status;
-	}
-#else
 	/*
-	 * !_CRAYMPP, only pe 0 is valid
+	 * Only pe 0 is valid.
 	 */
 	if(basepe != 0)
 		return NC_EINVAL;
-#endif
 
 	(void) nc_close(ncid);
 	if(unlink(path) == -1) {
diff --git a/libsrc/putget.m4 b/libsrc/putget.m4
index 644a7dd8f..ebd0fc04a 100644
--- a/libsrc/putget.m4
+++ b/libsrc/putget.m4
@@ -28,13 +28,6 @@ dnl
 #include "ncx.h"
 #include "fbits.h"
 #include "onstack.h"
-#ifdef LOCKNUMREC
-#  include <mpp/shmem.h>	/* for SGI/Cray SHMEM routines */
-#  ifdef LN_TEST
-#    include <stdio.h>
-#  endif
-#endif
-
 
 #undef MIN  /* system may define MIN somewhere and complain */
 #define MIN(mm,nn) (((mm) < (nn)) ? (mm) : (nn))
@@ -403,40 +396,6 @@ static int
 NCvnrecs(NC3_INFO* ncp, size_t numrecs)
 {
 	int status = NC_NOERR;
-#ifdef LOCKNUMREC
-	ushmem_t myticket = 0, nowserving = 0;
-	ushmem_t numpe = (ushmem_t) _num_pes();
-
-	/* get ticket and wait */
-	myticket = shmem_short_finc((shmem_t *) ncp->lock + LOCKNUMREC_LOCK,
-		ncp->lock[LOCKNUMREC_BASEPE]);
-#ifdef LN_TEST
-		fprintf(stderr,"%d of %d : ticket = %hu\n",
-			_my_pe(), _num_pes(), myticket);
-#endif
-	do {
-		shmem_short_get((shmem_t *) &nowserving,
-			(shmem_t *) ncp->lock + LOCKNUMREC_SERVING, 1,
-			ncp->lock[LOCKNUMREC_BASEPE]);
-#ifdef LN_TEST
-		fprintf(stderr,"%d of %d : serving = %hu\n",
-			_my_pe(), _num_pes(), nowserving);
-#endif
-		/* work-around for non-unique tickets */
-		if (nowserving > myticket && nowserving < myticket + numpe ) {
-			/* get a new ticket ... you've been bypassed */
-			/* and handle the unlikely wrap-around effect */
-			myticket = shmem_short_finc(
-				(shmem_t *) ncp->lock + LOCKNUMREC_LOCK,
-				ncp->lock[LOCKNUMREC_BASEPE]);
-#ifdef LN_TEST
-				fprintf(stderr,"%d of %d : new ticket = %hu\n",
-					_my_pe(), _num_pes(), myticket);
-#endif
-		}
-	} while(nowserving != myticket);
-	/* now our turn to check & update value */
-#endif
 
 	if(numrecs > NC_get_numrecs(ncp))
 	{
@@ -519,11 +478,6 @@ NCvnrecs(NC3_INFO* ncp, size_t numrecs)
 
 	}
 common_return:
-#ifdef LOCKNUMREC
-	/* finished with our lock - increment serving number */
-	(void) shmem_short_finc((shmem_t *) ncp->lock + LOCKNUMREC_SERVING,
-		ncp->lock[LOCKNUMREC_BASEPE]);
-#endif
 	return status;
 }
 
diff --git a/libsrc4/nc4internal.c b/libsrc4/nc4internal.c
index bc02d1cd0..ad4d3a70f 100644
--- a/libsrc4/nc4internal.c
+++ b/libsrc4/nc4internal.c
@@ -89,7 +89,7 @@ nc4_check_name(const char *name, char *norm_name)
  * nc4_nc4f_list_add(), except it takes an ncid instead of an NC *,
  * and also passes back the dispatchdata pointer.
  *
- * @param ncid The ncid of the file (aka ext_ncid).
+ * @param ncid The (already-assigned) ncid of the file (aka ext_ncid).
  * @param path The file name of the new file.
  * @param mode The mode flag.
  * @param dispatchdata Void * that gets pointer to dispatch data,
@@ -125,10 +125,35 @@ nc4_file_list_add(int ncid, const char *path, int mode, void **dispatchdata)
     return NC_NOERR;
 }
 
+/* /\** */
+/*  * @internal Change the ncid of an open file. This is needed for PIO */
+/*  * integration. */
+/*  * */
+/*  * @param ncid The ncid of the file (aka ext_ncid). */
+/*  * @param new_ncid The new ncid to use. */
+/*  * */
+/*  * @return ::NC_NOERR No error. */
+/*  * @return ::NC_EBADID No NC struct with this ext_ncid. */
+/*  * @return ::NC_ENOMEM Out of memory. */
+/*  * @author Ed Hartnett */
+/*  *\/ */
+/* int */
+/* nc4_file_change_ncid(int ncid, int new_ncid) */
+/* { */
+/*     NC *nc; */
+/*     int ret; */
+
+/*     /\* Find NC pointer for this file. *\/ */
+/*     if ((ret = NC_check_id(ncid, &nc))) */
+/*         return ret; */
+
+/*     return NC_NOERR; */
+/* } */
+
 /**
- * @internal Get info about a file on the list of libsrc4 open files. This is
- * used by dispatch layers that wish to use the libsrc4 metadata
- * model, but don't know about struct NC.
+ * @internal Get info about a file on the list of libsrc4 open
+ * files. This is used by dispatch layers that wish to use the libsrc4
+ * metadata model, but don't know about struct NC.
  *
  * @param ncid The ncid of the file (aka ext_ncid).
  * @param path A pointer that gets file name (< NC_MAX_NAME). Igored
@@ -212,7 +237,7 @@ nc4_nc4f_list_add(NC *nc, const char *path, int mode)
 
     /* There's always at least one open group - the root
      * group. Allocate space for one group's worth of information. Set
-     * its hdf id, name, and a pointer to it's file structure. */
+     * its grp id, name, and allocate associated empty lists. */
     if ((retval = nc4_grp_list_add(h5, NULL, NC_GROUP_NAME, &h5->root_grp)))
         return retval;
 
diff --git a/ncgen/main.c b/ncgen/main.c
index 51524db20..178729ce7 100644
--- a/ncgen/main.c
+++ b/ncgen/main.c
@@ -244,11 +244,6 @@ main(
 #endif
     memset(&globalspecials,0,sizeof(GlobalSpecialData));
 
-#if _CRAYMPP && 0
-    /* initialize CRAY MPP parallel-I/O library */
-    (void) par_io_init(32, 32);
-#endif
-
     while ((c = getopt(argc, argv, "134567bB:cdD:fhHk:l:M:no:Pv:xL:N:")) != EOF)
       switch(c) {
 	case 'd':
diff --git a/ncgen3/main.c b/ncgen3/main.c
index 0d861ad7a..830508885 100644
--- a/ncgen3/main.c
+++ b/ncgen3/main.c
@@ -114,11 +114,6 @@ main(
     cmode_modifier = 0;
     nofill_flag = 0;
 
-#if _CRAYMPP && 0
-    /* initialize CRAY MPP parallel-I/O library */
-    (void) par_io_init(32, 32);
-#endif
-
     while ((c = getopt(argc, argv, "bcfk:l:no:v:x")) != EOF)
       switch(c) {
 	case 'c':		/* for c output, old version of "-lc" */


Test patch:
diff --git a/unit_test/Makefile.am b/unit_test/Makefile.am
new file mode 100644
index 000000000..65b545a60
--- /dev/null
+++ b/unit_test/Makefile.am
@@ -0,0 +1,25 @@
+## This is a automake file, part of Unidata's netCDF package.
+# Copyright 2019, see the COPYRIGHT file for more information.
+
+# This file builds and runs the unit tests. These tests are not run in
+# the CMake build, because we would then have to extern these internal
+# functions, to allow Windows to work. Since we have not extern'd
+# these functions, they will only be run under the autotools build.
+
+# Ed Hartnett 8/9/19
+
+# Put together AM_CPPFLAGS and AM_LDFLAGS.
+include $(top_srcdir)/lib_flags.am
+
+# Find and link to the netcdf-c library.
+LDADD = ${top_builddir}/liblib/libnetcdf.la
+
+if USE_NETCDF4
+NC4_TESTS = tst_nc4internal
+endif # USE_NETCDF4
+
+check_PROGRAMS = tst_nclist $(NC4_TESTS)
+TESTS = tst_nclist $(NC4_TESTS)
+
+# If valgrind is present, add valgrind targets.
+@VALGRIND_CHECK_RULES@
diff --git a/unit_test/tst_nc4internal.c b/unit_test/tst_nc4internal.c
new file mode 100644
index 000000000..3f752986e
--- /dev/null
+++ b/unit_test/tst_nc4internal.c
@@ -0,0 +1,231 @@
+/* This is part of the netCDF package. Copyright 2005-2019 University
+   Corporation for Atmospheric Research/Unidata. See COPYRIGHT file
+   for conditions of use.
+
+   Test list functions in nc4internal.c.
+
+   Ed Hartnett, 8/21/19
+*/
+
+#include "config.h"
+#include <nc_tests.h>
+#include "nc.h"
+#include "nc4internal.h"
+#include "ncdispatch.h"
+#include "err_macros.h"
+
+/* An integer value to use in testing. */
+#define TEST_VAL_42 42
+
+#define FILE_NAME "tst_nc4internal.nc"
+#define VAR_NAME "Hilary_Duff"
+
+int
+main(int argc, char **argv)
+{
+    printf("\n*** Testing netcdf nc4internal functions.\n");
+    printf("Testing adding new file to nc4internal file lists with "
+           "nc4_file_list_add()...");
+    {
+        NC *ncp;
+        NCmodel model;
+        char *path;
+        /* The NC3_dispatch_table is defined in nc3dispatch.c and
+         * externed in ncdispatch.h. But it will be 0 because we have
+         * not yet called NC3_initialize(). */
+        const void *dispatcher = NC3_dispatch_table;
+        void *dispatchdata_in;
+        int mode = 0, mode_in;
+
+        /* This won't work because there is no NC in the NC list which
+         * has an ncid of TEST_VAL_42. */
+        if (nc4_file_list_add(TEST_VAL_42, FILE_NAME, 0, NULL) != NC_EBADID) ERR;
+
+        /* Create the NC* instance and insert its dispatcher and
+         * model. */
+        if (new_NC(dispatcher, FILE_NAME, mode, &model, &ncp)) ERR;
+
+        /* Add to array of open files nc_filelist and define
+         * ext_ncid by left-shifting the index 16 bits. */
+        add_to_NCList(ncp);
+
+        /* Create the NC_FILE_INFO_T instance associated empty lists
+         * to hold dims, types, groups, and the root group. */
+        if (nc4_file_list_add(ncp->ext_ncid, FILE_NAME, mode, NULL)) ERR;
+
+        /* Find the file in the list. */
+        if (!(path = malloc(NC_MAX_NAME + 1))) ERR;
+        if (nc4_file_list_get(ncp->ext_ncid, &path, &mode_in, &dispatchdata_in)) ERR;
+        if (strcmp(path, FILE_NAME)) ERR;
+        free(path);
+        if (mode_in != mode) ERR;
+
+        /* This won't work. */
+        if (nc4_file_list_del(TEST_VAL_42) != NC_EBADID) ERR;
+
+        /* Delete the NC_FILE_INFO_T and related storage. */
+        if (nc4_file_list_del(ncp->ext_ncid)) ERR;
+
+        /* Delete the ncp from the list. (In fact, just null out its
+         * entry in the array of file slots.) */
+        del_from_NCList(ncp); /* Will free empty list. */
+
+        /* Now free the NC struct. */
+        free_NC(ncp);
+    }
+    SUMMARIZE_ERR;
+    printf("Testing adding new file to nc4internal file lists with "
+           "nc4_nc4f_list_add()...");
+    {
+        NC *ncp, *ncp_in, *ncp_in2;
+        NCmodel model;
+        char *path;
+        void *dispatchdata_in;
+        int mode = 0, mode_in;
+        NC_GRP_INFO_T *grp, *grp2;
+        NC_FILE_INFO_T *h5, *h52;
+
+        /* Create the NC* instance and insert its dispatcher and
+         * model. */
+        if (new_NC(NC3_dispatch_table, FILE_NAME, mode, &model, &ncp)) ERR;
+
+        /* Add to array of open files nc_filelist and define
+         * ext_ncid by left-shifting the index 16 bits. */
+        add_to_NCList(ncp);
+
+        /* Create the NC_FILE_INFO_T instance associated empty lists
+         * to hold dims, types, groups, and the root group. */
+        if (nc4_nc4f_list_add(ncp, FILE_NAME, mode)) ERR;
+
+        /* Find the file in the list. */
+        if (!(path = malloc(NC_MAX_NAME + 1))) ERR;
+        if (nc4_file_list_get(ncp->ext_ncid, &path, &mode_in, &dispatchdata_in)) ERR;
+        if (strcmp(path, FILE_NAME)) ERR;
+        free(path);
+        if (mode_in != mode) ERR;
+
+        /* Find it again. */
+        if (nc4_find_nc_grp_h5(ncp->ext_ncid, &ncp_in, &grp, &h5)) ERR;
+        if (ncp_in->ext_ncid != ncp->ext_ncid) ERR;
+        if (grp->nc4_info->controller->ext_ncid != ncp->ext_ncid) ERR;
+        if (h5->controller->ext_ncid != ncp->ext_ncid) ERR;
+
+        /* Any of the pointer parameters may be NULL. */
+        if (nc4_find_nc_grp_h5(ncp->ext_ncid, NULL, NULL, NULL)) ERR;
+        if (nc4_find_nc_grp_h5(ncp->ext_ncid, &ncp_in2, NULL, NULL)) ERR;
+        if (ncp_in2->ext_ncid != ncp->ext_ncid) ERR;
+        if (nc4_find_nc_grp_h5(ncp->ext_ncid, NULL, &grp2, NULL)) ERR;
+        if (grp2->nc4_info->controller->ext_ncid != ncp->ext_ncid) ERR;
+        if (nc4_find_nc_grp_h5(ncp->ext_ncid, NULL, NULL, &h52)) ERR;
+        if (h52->controller->ext_ncid != ncp->ext_ncid) ERR;
+
+        /* There are additional functions which use the NULL
+         * parameters of nc4_find_nc_grp_h5(). */
+        grp2 = NULL;
+        h52 = NULL;
+        if (nc4_find_grp_h5(ncp->ext_ncid, NULL, NULL)) ERR;
+        if (nc4_find_grp_h5(ncp->ext_ncid, &grp2, NULL)) ERR;
+        if (grp2->nc4_info->controller->ext_ncid != ncp->ext_ncid) ERR;
+        if (nc4_find_grp_h5(ncp->ext_ncid, NULL, &h52)) ERR;
+        if (h52->controller->ext_ncid != ncp->ext_ncid) ERR;
+        grp2 = NULL;
+        if (nc4_find_nc4_grp(ncp->ext_ncid, NULL)) ERR;
+        if (nc4_find_nc4_grp(ncp->ext_ncid, &grp2)) ERR;
+        if (grp2->nc4_info->controller->ext_ncid != ncp->ext_ncid) ERR;
+
+        /* Delete the NC_FILE_INFO_T and related storage. */
+        if (nc4_file_list_del(ncp->ext_ncid)) ERR;
+
+        /* Delete the ncp from the list. (In fact, just null out its
+         * entry in the array of file slots.) */
+        del_from_NCList(ncp); /* Will free empty list. */
+
+        /* Now free the NC struct. */
+        free_NC(ncp);
+    }
+    SUMMARIZE_ERR;
+    printf("Testing adding new var to nc4internal file...");
+    {
+        NC *ncp, *ncp_in;
+        NCmodel model;
+        NC_GRP_INFO_T *grp;
+        NC_VAR_INFO_T *var, *var_in;
+        NC_FILE_INFO_T *h5;
+
+        /* Create the NC* instance and insert its dispatcher and
+         * model. */
+        if (new_NC(NC3_dispatch_table, FILE_NAME, 0, &model, &ncp)) ERR;
+
+        /* Add to array of open files nc_filelist and define
+         * ext_ncid by left-shifting the index 16 bits. */
+        add_to_NCList(ncp);
+
+        /* Create the NC_FILE_INFO_T instance associated empty lists
+         * to hold dims, types, groups, and the root group. */
+        if (nc4_file_list_add(ncp->ext_ncid, FILE_NAME, 0, NULL)) ERR;
+
+        /* Find the file in the list. */
+        if (nc4_find_nc_grp_h5(ncp->ext_ncid, &ncp_in, &grp, &h5)) ERR;
+        if (ncp_in->ext_ncid != ncp->ext_ncid) ERR;
+
+        /* Add a var to the varlist. */
+        if (nc4_var_list_add(grp, VAR_NAME, 0, &var)) ERR;
+
+        /* Find the var. */
+        if (nc4_find_var(grp, VAR_NAME, &var_in)) ERR;
+        if (strcmp(var_in->hdr.name, var->hdr.name)) ERR;
+
+        /* Find it again. */
+        h5 = NULL;
+        grp = NULL;
+        var_in = NULL;
+        if (nc4_find_grp_h5_var(ncp->ext_ncid, 0, &h5, &grp, &var_in)) ERR;
+        if (h5->controller->ext_ncid != ncp->ext_ncid) ERR;
+        if (grp->nc4_info->controller->ext_ncid != ncp->ext_ncid) ERR;
+        if (strcmp(var_in->hdr.name, var->hdr.name)) ERR;
+
+        /* Delete the NC_FILE_INFO_T and related storage, including
+         * all vars, dims, types, etc. */
+        if (nc4_file_list_del(ncp->ext_ncid)) ERR;
+
+        /* Delete the ncp from the list. (In fact, just null out its
+         * entry in the array of file slots.) */
+        del_from_NCList(ncp); /* Will free empty list. */
+
+        /* Now free the NC struct. */
+        free_NC(ncp);
+    }
+    SUMMARIZE_ERR;
+    /* printf("Testing changing ncid..."); */
+    /* { */
+    /*     NC *ncp, *ncp2; */
+    /*     int mode = 0; */
+    /*     NCmodel model; */
+    /*     int ret; */
+
+    /*     /\* Create the NC* instance and insert its dispatcher and model. *\/ */
+    /*     if ((ret = new_NC(NULL, FILE_NAME, mode, &model, &ncp))) ERR; */
+
+    /*     /\* Add to list of known open files and define ext_ncid. *\/ */
+    /*     add_to_NCList(ncp); */
+
+    /*     /\* Find it in the list. *\/ */
+    /*     if (!(ncp2 = find_in_NCList(ncp->ext_ncid))) ERR; */
+    /*     if (!(ncp2 = find_in_NCList_by_name(FILE_NAME))) ERR; */
+    /*     if ((ret = iterate_NCList(1, &ncp2))) ERR; */
+    /*     if (count_NCList() != 1) ERR; */
+
+    /*     /\* Change the ncid. *\/ */
+    /*     if (nc4_file_change_ncid(ncp->ext_ncid, TEST_VAL_42)) ERR; */
+
+    /*     /\* Delete it. *\/ */
+    /*     del_from_NCList(ncp); /\* Will free empty list. *\/ */
+    /*     free_NC(ncp); */
+
+    /*     /\* Ensure it is no longer in list. *\/ */
+    /*     /\* if (find_in_NCList(ncp->ext_ncid)) ERR; *\/ */
+
+    /* } */
+    /* SUMMARIZE_ERR; */
+    FINAL_RESULTS;
+}
diff --git a/unit_test/tst_nclist.c b/unit_test/tst_nclist.c
new file mode 100644
index 000000000..0daae16a9
--- /dev/null
+++ b/unit_test/tst_nclist.c
@@ -0,0 +1,117 @@
+/* This is part of the netCDF package. Copyright 2005-2019 University
+   Corporation for Atmospheric Research/Unidata. See COPYRIGHT file
+   for conditions of use.
+
+   Test list functions in nclistmgr.c.
+
+   Ed Hartnett, 8/10/19
+*/
+
+#include "config.h"
+#include <nc_tests.h>
+#include "nc.h"
+#include "ncdispatch.h"
+#include "err_macros.h"
+
+/* An integer value to use in testing. */
+#define TEST_VAL_42 42
+
+#define FILE_NAME "tst_nclist.nc"
+
+int
+main(int argc, char **argv)
+{
+    printf("\n*** Testing netcdf internal NC list functions.\n");
+    printf("Testing NC list functions with no open files...");
+    {
+        /* These are cases where there are no files, or NULL
+         * params. */
+        if (count_NCList()) ERR;
+        free_NCList();
+        if (find_in_NCList_by_name("nope")) ERR;
+        if (iterate_NCList(0, NULL)) ERR;
+    }
+    SUMMARIZE_ERR;
+    printf("Testing adding to NC list...");
+    {
+        int ncid;
+        NC *ncp, *ncp2;
+        int mode = 0;
+        NCmodel model;
+        int ret;
+
+        /* Create the NC* instance and insert its dispatcher and model. */
+        if ((ret = new_NC(NULL, FILE_NAME, mode, &model, &ncp))) ERR;
+
+        /* Nothing to find yet. */
+        if (find_in_NCList(TEST_VAL_42)) ERR;
+
+        /* Add to list of known open files and define ext_ncid. To get
+         * the ncid, we find the first open index > 1 in the
+         * nc_filelist array, which has a size of 65536. Then we
+         * left-shift that index 16 bits to put it in the first
+         * 2-bytes of the 4-byte ncid. (The other two bytes are
+         * reserved for grpid of netCDF-4 groups.) */
+        add_to_NCList(ncp);
+
+        /* These won't work! */
+        if (find_in_NCList(TEST_VAL_42)) ERR;
+        if (find_in_NCList_by_name("nope")) ERR;
+        if ((ret = iterate_NCList(2, &ncp2))) ERR;
+
+        /* Find it in the list. */
+        if (!(ncp2 = find_in_NCList(ncp->ext_ncid))) ERR;
+        if (!(ncp2 = find_in_NCList_by_name(FILE_NAME))) ERR;
+        if ((ret = iterate_NCList(1, &ncp2))) ERR;
+        if (count_NCList() != 1) ERR;
+
+        /* Won't do anything because list contains an entry. */
+        free_NCList();
+
+        /* Delete it. */
+        ncid = ncp->ext_ncid;
+        del_from_NCList(ncp); /* Will free empty list. */
+        free_NC(ncp);
+
+        /* Ensure it is no longer in list. */
+        if (find_in_NCList(ncid)) ERR;
+    }
+    SUMMARIZE_ERR;
+#ifdef LARGE_FILE_TESTS
+    /* This test is slow, only run it on large file test builds. */
+    printf("Testing maxing out NC list...");
+    {
+        NC *ncp;
+        int mode = 0;
+        NCmodel model;
+        int max_num_nc = 65535;
+        int i;
+        int ret;
+
+        /* Fill the NC list. */
+        for (i = 0; i < max_num_nc; i++)
+        {
+            if ((ret = new_NC(NULL, FILE_NAME, mode, &model, &ncp))) ERR;
+            if (add_to_NCList(ncp)) ERR;
+        }
+
+        /* Check the count. */
+        if (count_NCList() != max_num_nc) ERR;
+
+        /* Try and add another. It will fail. */
+        if (add_to_NCList(ncp) != NC_ENOMEM) ERR;
+
+        /* Delete them all. */
+        for (i = 0; i < max_num_nc; i++)
+        {
+            if (iterate_NCList(i + 1, &ncp)) ERR;
+            if (!ncp) ERR;
+            del_from_NCList(ncp);
+            free_NC(ncp);
+        }
+    }
+    SUMMARIZE_ERR;
+#endif /* LARGE_FILE_TESTS */
+
+    FINAL_RESULTS;
+}