Skip to content

Commit

Permalink
Extend the netcdf API to support programmatic changes to the plugin s…
Browse files Browse the repository at this point in the history
…earch path

re: Unidata#2753

As suggested by Ed Hartnett, This PR extends the netcdf.h API to support programmatic control over the search path used to locate plugins.

I created several different APIs, but finally settled on the following
API as being the simplest possible. It does have the disadvantage that
it requires use of a global lock (not implemented) if used
in a threaded environment.

Specifically, note that modifying the plugin path must be done "atomically".
That is, in a multi-threaded environment, it is important that the sequence of actions involved in setting up the plugin path must be done by a single processor or in some other way as to guarantee that two or more processors are not simultaneously accessing the plugin path get/set operations.

As an example, assume there exists a mutex lock called PLUGINLOCK.
Then any processor accessing the plugin paths should operate as follows:
````
lock(PLUGINLOCK);
nc_plugin_path_get(...);
<rebuild plugin path>
nc_plugin_path_set(...);
unlock(PLUGINLOCK);
````
## Internal Architecture

It is assumed here that there only needs to be a single set of plugin path
directories that is shared by all filter code and is independent of any file descriptor; it is global in other words.
This means, for example, that the path list for NCZarr and for HDF5 will always be the same.

However internally, processing the set of plugin paths depends on the particular
NC_FORMATX value (NC_FORMATX_NC_HDF5 and NC_FORMATX_NCZARR, currently).
So the *nc_plugin_path_set* function, will take the paths it is given and
propagate them to each of the NC_FORMATX dispatchers to store in a way that is
appropriate to the given dispatcher.

There is a complication with respect to the *nc_plugin_path_get* function.
It is possible for users to bypass the netcdf API and modify the HDF5 plugin paths directly. This can result in an inconsistent plugin path between the value
used by HDF5 and the global value used by netcdf-c. Since there is no obvious fix for this, we warn the user of this possibility and otherwise ignore it.

## Test Changes
* A new test -- unit_test/run_pluginpaths.sh -- was created to test this new capability.
* A new test utility has been added as *nczarr_test/ncpluginpath* to return information about the default plugin path list.

## Documentation
* A new file -- docs/pluginpath.md -- provides documentation of the new API.

## Misc. Changes
1. Add some path manipulation utilities to netcf_aux.h
2. Fix the construction of netcdf_json.h as a BUILT_SOURCE
3. Fix some minor bugs in netcdf_json.h
4. Convert netcdf_json.h and netcdf_proplist.h to BUILT_SOURCE.
5. Add NETCDF_ENABLE_HDF5 as synonym for USE_HDF5
6. Fix some size_t <-> int conversion warnings.
7. Encountered and fixed the Windows \r\n problem in tst_pluginpaths.c.
8. Cleanup some minor CMakeLists.txt problems.

## Addendum: Proposed API

The API makes use of a counted vector of strings representing the sequence of directories in the path. The relevant type definition is as follows.
````
typedef struct NCPluginList {size_t ndirs; char** dirs;} NCPluginList;
````

The API proposed in this PR looks like this (from netcdf-c/include/netcdf_filter.h).

````int nc_plugin_path_ndirs(size_t* ndirsp);````

    This function returns the number of directories in the sequence if internal directories of the internal plugin path list.

    The argument is as follows:
    - *ndirsp* store the number of directories in this memory.

* ````int nc_plugin_path_get(NCPluginList* dirs);````

    This function returns the current sequence of directories from the internal plugin path list. Since this function does not modify the plugin path, it does not need to be locked; it is only when used to get the path to be modified that locking is required.

    The argument is as follows:
    - *dirs* counted vector for storing the sequence of directies in the internal path list.

    If the value of *dirs.dirs is NULL (the normal case), then memory is allocated to hold the vector of directories. Otherwise, use the memory of *dirs.dirs* to hold the vector of directories.

* ````int nc_plugin_path_set(const NCPluginList* dirs);````

    This function empties the current internal path sequence and replaces it with the sequence of directories argument. Using an *ndirs* argument of 0 will clear the set of plugin paths.

    The argument are as follows:
    - *dirs* counted vector for storing the sequence of directies in the internal path list.
  • Loading branch information
DennisHeimbigner committed Sep 30, 2024
1 parent 4bf7e6f commit 70111d0
Show file tree
Hide file tree
Showing 86 changed files with 3,720 additions and 2,398 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main-cmake.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: NetCDF-C CMake CI - Windows

on: [pull_request, workflow_dispatch]
on: [ pull_request, workflow_dispatch]

env:
REMOTETESTDOWN: ${{ vars.REMOTETESTDOWN }}
Expand Down
52 changes: 30 additions & 22 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ if (NOT DEFINED NETCDF_IS_TOP_LEVEL)
endif ()
endif ()


################################
# The target
################################
Expand All @@ -72,8 +71,6 @@ endif()
add_library(netcdf)
add_library(netCDF::netcdf ALIAS netcdf)



# Version of the dispatch table. This must match the value in
# configure.ac.
set(NC_DISPATCH_VERSION 5)
Expand Down Expand Up @@ -363,12 +360,12 @@ endif()
set(DEFAULT_CHUNK_SIZE 16777216 CACHE STRING "Default Chunk Cache Size.")
set(DEFAULT_CHUNK_CACHE_SIZE 16777216U CACHE STRING "Default Chunk Cache Size.")
set(DEFAULT_CHUNKS_IN_CACHE 1000 CACHE STRING "Default number of chunks in cache.")
set(DEFAULT_CHUNK_CACHE_PREEMPTION 0.75 CACHE STRING "Default file chunk cache preemption policy (a number between 0 and 1, inclusive.")
set(DEFAULT_CHUNK_CACHE_PREEMPTION 0.75 CACHE STRING "Default file chunk cache preemption policy (a number between 0 and 1, inclusive).")

# HDF5 default cache size values
set(CHUNK_CACHE_SIZE ${DEFAULT_CHUNK_CACHE_SIZE} CACHE STRING "Default HDF5 Chunk Cache Size.")
set(CHUNK_CACHE_NELEMS ${DEFAULT_CHUNKS_IN_CACHE} CACHE STRING "Default maximum number of elements in cache.")
set(CHUNK_CACHE_PREEMPTION ${DEFAULT_CHUNK_CACHE_PREEMPTION} CACHE STRING "Default file chunk cache preemption policy for HDf5 files(a number between 0 and 1, inclusive.")
set(CHUNK_CACHE_PREEMPTION ${DEFAULT_CHUNK_CACHE_PREEMPTION} CACHE STRING "Default file chunk cache preemption policy for HDf5 files(a number between 0 and 1, inclusive.)")

set(NETCDF_LIB_NAME "" CACHE STRING "Default name of the netcdf library.")
set(TEMP_LARGE "." CACHE STRING "Where to put large temp files if large file tests are run.")
Expand Down Expand Up @@ -660,8 +657,29 @@ if(NOT ENABLE_PLUGIN_INSTALL)
unset(NETCDF_PLUGIN_INSTALL_DIR CACHE)
endif()

if(ENABLE_PLUGIN_INSTALL)
if(NOT DEFINED NETCDF_PLUGIN_INSTALL_DIR)
# Internally, the variable HDF5_PLUGIN_PATH
# is always used as a surrogate for the default
# plugin path. If it was not defined by the user,
# then set it internally.
if((NOT DEFINED HDF5_PLUGIN_PATH) OR ("${HDF5_PLUGIN_PATH}" STREQUAL ""))
if(ISMSVC OR ISMINGW)
string(REPLACE "\\" "/" AUP "$ENV{ALLUSERSPROFILE}")
set(HDF5_PLUGIN_PATH "${AUP}/hdfd5/lib/plugin")
else()
set(HDF5_PLUGIN_PATH "/usr/local/hdf5/lib/plugin")
endif()
set(ENV{HDF5_PLUGIN_PATH} "${HDF5_PLUGIN_PATH}")
endif()

# The --with-plugin-dir gives the user control of the plugin
# directory. If set to 'yes' (the default), then the last directory in
# HDF5_PLUGIN_PATH is used (see above).
# If a specific directory is provided, it will be used.
# If 'no', then plugins will not be installed.

if(NETCDF_ENABLE_PLUGIN_INSTALL)
if(DEFINED NETCDF_PLUGIN_INSTALL_DIR)
set(NETCDF_PLUGIN_INSTALL_DIR "${HDF5_PLUGIN_PATH}")
# Default to HDF5_PLUGIN_PATH or its default directories
if(DEFINED ENV{HDF5_PLUGIN_PATH})
set(NETCDF_PLUGIN_INSTALL_DIR "$ENV{HDF5_PLUGIN_PATH}")
Expand All @@ -682,15 +700,7 @@ endif(ENABLE_PLUGIN_INSTALL)

if(ENABLE_PLUGIN_INSTALL)
# Use the lowest priority dir in the path
if(NOT ISMSVC AND NOT ISMINGW)
string(REPLACE ":" ";" PATH_LIST ${NETCDF_PLUGIN_INSTALL_DIR})
else()
set(PATH_LIST ${NETCDF_PLUGIN_INSTALL_DIR})
endif()

# Get last element
list(GET PATH_LIST -1 NETCDF_PLUGIN_INSTALL_DIR)
set(PLUGIN_INSTALL_DIR_SETTING "${NETCDF_PLUGIN_INSTALL_DIR}")
getlastdir("${HDF5_PLUGIN_PATH}" "NETCDF_PLUGIN_INSTALL_DIR")
message(STATUS "Final value of-DPLUGIN_INSTALL_DIR=${NETCDF_PLUGIN_INSTALL_DIR}")
else() # No option specified
unset(NETCDF_PLUGIN_INSTALL_DIR)
Expand Down Expand Up @@ -753,10 +763,10 @@ if(NOT WIN32)
endif()

# Options for S3 Support
#option(NETCDF_ENABLE_S3 "Enable S3 support." OFF)
option(NETCDF_ENABLE_S3_AWS "Enable S3 support via AWS-CPP-SDK" OFF)
option(NETCDF_ENABLE_S3_INTERNAL "Enable S3 Internal support." OFF)

#option(NETCDF_ENABLE_S3 "Enable S3 support." OFF)
cmake_dependent_option(NETCDF_ENABLE_S3 "Enable S3 Support" ON "NETCDF_ENABLE_S3_AWS OR NETCDF_ENABLE_S3_INTERNAL" OFF)

option(NETCDF_ENABLE_NCZARR_S3 "Enable NCZarr S3 support; Deprecated in favor of NETCDF_ENABLE_S3" ${NETCDF_ENABLE_S3})
Expand Down Expand Up @@ -877,7 +887,7 @@ if(NETCDF_ENABLE_TESTS)
# See https://github.com/Unidata/netcdf-c/issues/2627 for more information.
###
option(NETCDF_ENABLE_BENCHMARKS "Run benchmark Tests." OFF)

set(BUILD_BENCHMARKS ${NETCDF_ENABLE_BENCHMARKS} CACHE BOOL "alias for NETCDF_ENABLE_BENCHMARKS")

###
# End known-failures.
Expand Down Expand Up @@ -1380,8 +1390,8 @@ endif(NETCDF_ENABLE_MMAP)
# Used in the `configure_file` calls below
set(ISCMAKE "yes")
if(MSVC)
set(ISMSVC ON CACHE BOOL "" FORCE)
set(REGEDIT ON CACHE BOOL "" FORCE)
set(ISMSVC yes CACHE BOOL "" FORCE)
set(REGEDIT yes CACHE BOOL "" FORCE)
# Get windows major version and build number
execute_process(COMMAND "systeminfo" OUTPUT_VARIABLE WININFO)
if(WININFO STREQUAL "")
Expand Down Expand Up @@ -1564,8 +1574,6 @@ endif()
# STATIC_DEFINE netcdf_BUILT_AS_STATIC
#)



##
# Brute force, grab all of the dlls from the dependency directory,
# install them in the binary dir. Grab all of the .libs, put them
Expand Down
8 changes: 4 additions & 4 deletions cmake/dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -226,13 +226,13 @@ if(USE_HDF5)
endif(USE_HDF5)

################################
# Curl Libraryies
# Curl Libraries
# Only needed for DAP (DAP2 or DAP4)
# and NCZARR with S3 Support
# and NCZARR S3 support
# and byterange support
################################

if( (NETCDF_ENABLE_DAP AND (NETCDF_ENABLE_DAP2 OR NETCDF_ENABLE_DAP4 OR NETCDF_ENABLE_BYTERANGE_SUPPORT)) OR (NETCDF_ENABLE_NCZARR AND NETCDF_ENABLENCZARR_S3))

if( NETCDF_ENABLE_DAP2 OR NETCDF_ENABLE_DAP4 OR NETCDF_ENABLE_BYTERANGE_SUPPORT OR NETCDF_ENABLE_NCZARR_S3)
# See if we have libcurl
find_package(CURL)
#target_compile_options(netcdf
Expand Down
14 changes: 13 additions & 1 deletion cmake/netcdf_functions_macros.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -324,4 +324,16 @@ function(is_disabled feature ret_val)
set(${ret_val} "yes" PARENT_SCOPE)
set("NC_${ret_val}" 1 PARENT_SCOPE)
endif(${feature})
endfunction()
endfunction()

# Extract the last element from a path string
function(getlastdir s ret_val)
if(NOT ISMSVC AND NOT ISMINGW)
string(REPLACE ":" ";" list "${s}")
else()
set(list ${s})
endif()
list(GET list -1 last)
set(${ret_val} "${last}" PARENT_SCOPE)
endfunction()

3 changes: 3 additions & 0 deletions config.h.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ are set when opening a binary file on Windows. */
#define ERANGE_FILL 1
#endif

/* if true, use hdf5 S3 virtual file reader */
#cmakedefine NETCDF_ENABLE_HDF5 1

/* if true, use hdf5 S3 virtual file reader */
#cmakedefine NETCDF_ENABLE_HDF5_ROS3 1

Expand Down
86 changes: 53 additions & 33 deletions configure.ac
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
o# -*- Autoconf -*-
# -*- Autoconf -*-
## Process this file with autoconf to produce a configure script.

# This is part of Unidata's netCDF package. Copyright 2005-2018, see
Expand Down Expand Up @@ -936,8 +936,6 @@ else
AC_MSG_RESULT([${have_zip}])
enable_nczarr_zip=${have_zip} # alias



AC_MSG_CHECKING([whether nczarr zip support is enabled])
AC_MSG_RESULT([${enable_nczarr_zip}])

Expand All @@ -949,15 +947,10 @@ else
AC_MSG_CHECKING([whether netcdf S3 support should be enabled])
AC_ARG_ENABLE([s3],
[AS_HELP_STRING([--enable-s3],
[enable netcdf S3 support])])
[enable netcdf S3 support (default off)])])
test "x$enable_s3" = xyes || enable_s3=no
AC_MSG_RESULT($enable_s3)

if test "x$enable_remote_functionality" = xno ; then
AC_MSG_WARN([--disable-remote-functionality => --disable-s3])
enable_s3=no
fi

# --enable-nczarr-s3 is synonym for --enable-s3 (but...)
AC_MSG_CHECKING([whether netcdf NCZarr S3 support should be enabled])
AC_ARG_ENABLE([nczarr-s3],
Expand All @@ -966,11 +959,16 @@ else
AC_MSG_RESULT([$enable_nczarr_s3 (Deprecated) Please use --enable-s3)])

# Set enable_s3 instead of enable_nczarr_s3
if test "x$enable_s3" = xno && test "x$enable_nczarr_s3" = xyes && test "x$enable_remote_functionality" = xyes; then
if test "x$enable_s3" = xno && test "x$enable_nczarr_s3" = xyes ; then
enable_s3=yes # back compatibility
fi
unset enable_nczarr_s3

if test "x$enable_remote_functionality" = xno ; then
AC_MSG_WARN([--disable-remote-functionality => --disable-s3])
enable_s3=no
fi

# Note we check for the library after checking for enable_s3
# because for some reason this fails if we unconditionally test for sdk
# and it is not available. Fix someday
Expand Down Expand Up @@ -1166,7 +1164,7 @@ AC_ARG_ENABLE([benchmarks],
are timed. We use these tests to check netCDF performance.])])
test "x$enable_benchmarks" = xyes || enable_benchmarks=no
AC_MSG_RESULT($enable_benchmarks)
if test "x$enable_HDF5" = xno -a "x$enable_benchmarks" = xyes; then
if test "x$enable_hdf5" = xno -a "x$enable_benchmarks" = xyes; then
AC_MSG_ERROR([Can't use benchmarks if HDF5 is disabled.])
fi
AM_CONDITIONAL(BUILD_BENCHMARKS, [test x$enable_benchmarks = xyes])
Expand Down Expand Up @@ -1645,7 +1643,7 @@ enable_hdf5_szip=no
has_hdf5_ros3=no

if test "x$enable_hdf5" = xyes; then

AC_DEFINE([NETCDF_ENABLE_HDF5], [1], [if true, use HDF5])
AC_DEFINE([USE_HDF5], [1], [if true, use HDF5])
AC_DEFINE([H5_USE_16_API], [1], [use HDF5 1.6 API])

Expand Down Expand Up @@ -2145,25 +2143,46 @@ fi

AC_SUBST(STD_FILTERS,[$std_filters])

# Internally, the variable HDF5_PLUGIN_PATH
# is always used as a surrogate for the default
# plugin path. If it was not defined by the user,
# then set it internally.
if test "x${HDF5_PLUGIN_PATH}" = x ; then
if test "x$ISMSVC" = xyes || test "x$ISMINGW" = xyes; then
HDF5_PLUGIN_PATH="${ALLUSERSPROFILE}\\hdfd5\\lib\\plugin"
else
HDF5_PLUGIN_PATH="/usr/local/hdf5/lib/plugin"
fi
fi

# The --with-plugin-dir gives the user control of the plugin
# directory. If set to 'yes' (the default), then first directory in
# the HDF5_PLUGIN_PATH, or /usr/local/hdf5/lib/plugin (the default
# HDF5 plugin dir) will be used. If another directory is provided, it
# will be used. If 'no', then plugins will not be installed.
# directory. If set to 'yes' (the default), then the last directory in
# HDF5_PLUGIN_PATH is used (see above).
# If a specific directory is provided, it will be used.
# If 'no', then plugins will not be installed.

AC_MSG_CHECKING([whether and where we should install plugins])
AC_ARG_WITH([plugin-dir], [AS_HELP_STRING([--with-plugin-dir=<absolute directory>|yes|no|--without-plugin-dir],
[Install selected standard filters in specified or default directory])],
[],[with_plugin_dir=no])
AC_MSG_RESULT([$with_plugin_dir])
# Using selected filter options forces installation
if test "x$have_zstd" = xyes; then
if test "x$enable_plugins" = xyes && test "x$with_plugin_dir" = xno; then
AC_MSG_WARN([--enable_filter-zstd => --with-plugin-dir.])
with_plugin_dir=yes
fi
fi
AC_MSG_RESULT([$with_plugin_dir])
if test "x$with_plugin_dir" = xno ; then # option missing|disabled
with_plugin_dir=no
with_plugin_dir_setting="N.A."
enable_plugin_dir=no
<<<<<<< HEAD
elif test "x$with_plugin_dir" != x ; then
if test "x$with_plugin_dir" != xyes && test "x$with_plugin_dir" != xno ; then
# Presume true directory
HDF5_PLUGIN_PATH="${with_plugin_dir}"
=======
elif test "x$with_plugin_dir" = xyes ; then # --with-plugin-dir, no argument
# Default to last dir (lowest search priority) in HDF5_PLUGIN_PATH
PLUGIN_PATH="$HDF5_PLUGIN_PATH"
Expand All @@ -2178,24 +2197,25 @@ elif test "x$with_plugin_dir" = xyes ; then # --with-plugin-dir, no argument
fi

fi
>>>>>>> Unidata/main
fi
# Use the lowest priority dir in the path
if test "x$ISMSVC" = xyes || test "x$ISMINGW" = xyes; then
PLUGIN_DIR=`echo "$PLUGIN_PATH" | tr ';' ' '`
else
PLUGIN_DIR=`echo "$PLUGIN_PATH" | tr ':' ' '`
fi
for pp in ${PLUGIN_DIR} ; do last="$pp"; done
PLUGIN_DIR="$last"
with_plugin_dir_setting="$PLUGIN_DIR"
# canonical form is all forward slashes
with_plugin_dir=`echo "$PLUGIN_DIR" | tr '\\\\' '/'`
enable_plugin_dir=yes
AC_MSG_NOTICE([Defaulting to --with-plugin-dir=$with_plugin_dir])
else # --with-plugin-dir=<dir|path>
with_plugin_dir_setting="$with_plugin_dir"
enable_plugin_dir=yes
fi

# Use the last dir (lowest search priority) in HDF5_PLUGIN_PATH
PLUGIN_PATH="$HDF5_PLUGIN_PATH"
if test "x$ISMSVC" = xyes || test "x$ISMINGW" = xyes; then
PLUGIN_DIR=`echo "$PLUGIN_PATH" | tr ';' ' '`
else
PLUGIN_DIR=`echo "$PLUGIN_PATH" | tr ':;' ' '`
fi
last=
for pp in ${PLUGIN_DIR} ; do last="$pp"; done
PLUGIN_DIR="$last"
with_plugin_dir_setting="$PLUGIN_DIR"
# canonical form is all forward slashes
with_plugin_dir=`echo "$PLUGIN_DIR" | tr '\\\\' '/'`
enable_plugin_dir=yes
AC_MSG_NOTICE([Final --with-plugin-dir=$with_plugin_dir])
AM_CONDITIONAL([ENABLE_PLUGIN_DIR], [test "x$enable_plugin_dir" = xyes])
AC_SUBST([PLUGIN_INSTALL_DIR], [$with_plugin_dir])
# Better value for libnetcdf.settings
Expand Down
1 change: 1 addition & 0 deletions docs/Doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,7 @@ INPUT = @abs_top_srcdir@/docs/mainpage.dox \
@abs_top_srcdir@/docs/all-error-codes.md \
@abs_top_srcdir@/docs/filters.md \
@abs_top_srcdir@/docs/quickstart_filters.md \
@abs_top_srcdir@/docs/pluginpath.md \
@abs_top_srcdir@/docs/quickstart_paths.md \
@abs_top_srcdir@/docs/quickstart_env.md \
@abs_top_srcdir@/include/netcdf.h \
Expand Down
2 changes: 1 addition & 1 deletion docs/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ known_problems.md COPYRIGHT.md inmeminternal.dox testserver.dox \
byterange.md nczarr.md quantize.md all-error-codes.md \
quickstart_paths.md cloud.md header.html attribute_conventions.md \
file_format_specifications.md quickstart_filters.md quickstart_env.md \
doxygen-awesome-css netcdf-50x50.png
doxygen-awesome-css netcdf-50x50.png pluginpath.md

# Turn off parallel builds in this directory.
.NOTPARALLEL:
Expand Down
Loading

0 comments on commit 70111d0

Please sign in to comment.