diff --git a/.appveyor.yml b/.appveyor.yml
index 2652a7810..2cdc51128 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -48,6 +48,7 @@ environment:
TOOLCHAIN: 2017
- TOOLCHAIN: cygwin
- TOOLCHAIN: mingw
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
# Platform: architecture
platform:
@@ -60,6 +61,10 @@ matrix:
# VS Express installs don't have the 64 bit compiler
- platform: x64
TOOLCHAIN: 10.0
+ # Cygwin static-debug has compiler problems
+ - configuration: static-debug
+ TOOLCHAIN: cygwin
+
#---------------------------------#
# building & testing #
@@ -81,6 +86,9 @@ test_script:
notifications:
- - provider: Slack
- incoming_webhook:
- secure: RYOm3FIUYeZGjWKaeTVKwq+C3fzK54AKwbmAoECED45mex3lN+8HmrC845a6mg9xPUJ/ND51RopWVaKDD9/UzaM0SO195RQLKqUTIUafiuM=
+ - provider: Email
+ to:
+ - core-talk@aps.anl.gov
+ on_build_success: false
+
+ - provider: GitHubPullRequest
diff --git a/.ci/appveyor-make.bat b/.ci/appveyor-make.bat
index a489d80ee..9cd8fe684 100644
--- a/.ci/appveyor-make.bat
+++ b/.ci/appveyor-make.bat
@@ -42,13 +42,13 @@ if "%TOOLCHAIN%"=="mingw" (
set "MAKE=mingw32-make"
if "%OS%"=="64BIT" (
set "EPICS_HOST_ARCH=windows-x64-mingw"
- set "INCLUDE=C:\tools\mingw64\include;%INCLUDE%"
- set "PATH=C:\tools\mingw64\bin;%PATH%"
+ set "INCLUDE=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\include;%INCLUDE%"
+ set "PATH=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;%PATH%"
echo [INFO] MinGW Toolchain 64bit
) else (
set "EPICS_HOST_ARCH=win32-x86-mingw"
- set "INCLUDE=C:\tools\mingw32\include;%INCLUDE%"
- set "PATH=C:\tools\mingw32\bin;%PATH%"
+ set "INCLUDE=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\include;%INCLUDE%"
+ set "PATH=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin;%PATH%"
echo [INFO] MinGW Toolchain 32bit
)
echo [INFO] Compiler Version
diff --git a/.ci/appveyor-prepare.bat b/.ci/appveyor-prepare.bat
index 26ca8118a..9f105ee96 100644
--- a/.ci/appveyor-prepare.bat
+++ b/.ci/appveyor-prepare.bat
@@ -55,16 +55,9 @@ if "%TOOLCHAIN%"=="mingw" (
) || (
echo [INFO] EPICS set up for optimized build
)
- if "%OS%"=="64BIT" (
- echo [INFO] Installing MinGW 64bit
- cinst mingw || cinst mingw
- ) else (
- echo [INFO] Installing MinGW 32bit
- cinst mingw --x86 || cinst mingw --x86
- )
)
-echo [INFO] Installing Make 4.1
-curl -fsS --retry 3 -o C:\tools\make-4.1.zip https://epics.anl.gov/download/tools/make-4.1-win64.zip
+echo [INFO] Installing Make 4.2.1 from ANL web site
+curl -fsS --retry 3 -o C:\tools\make-4.2.1.zip https://epics.anl.gov/download/tools/make-4.2.1-win64.zip
cd \tools
-"C:\Program Files\7-Zip\7z" e make-4.1.zip
+"C:\Program Files\7-Zip\7z" e make-4.2.1.zip
diff --git a/.tools/make-tar.sh b/.tools/make-tar.sh
index 4c9c33a7b..e6bf59315 100755
--- a/.tools/make-tar.sh
+++ b/.tools/make-tar.sh
@@ -16,23 +16,46 @@ PREFIX="$3"
if ! [ "$TOPREV" ]
then
cat <&2
-usage: $0 [rev] [outfile.tar.gz] [prefix/]
+usage: $0 [ []]
may be any git revision spec. (tag, branch, or commit id).
- Output file may be .tar.gz, .tar.bz2, or any extension supported by "tar -a".
- If output file name is omitted, "base-.tar.gz" will be used.
- If is omitted, the default prefix is "base-/".
+ If provided, must end with ".tar", ".tar.gz" or ".tar.bz2".
+ If is omitted, "base-.tar.gz" will be used.
+ If provided, must end with "/". If is omitted,
+ the default is "base-/".
EOF
exit 1
fi
-[ "$FINALTAR" ] || FINALTAR="base-$TOPREV.tar.gz"
-[ "$PREFIX" ] || PREFIX="base-$TOPREV/"
+case "$FINALTAR" in
+"")
+ TAROPT=-z
+ FINALTAR="base-$TOPREV.tar.gz"
+ ;;
+*.tar)
+ TAROPT=""
+ ;;
+*.tar.gz)
+ TAROPT=-z
+ ;;
+*.tar.bz2)
+ TAROPT=-j
+ ;;
+*)
+ die "outfile must end with '.tar.gz' or '.tar.bz2'"
+ ;;
+esac
case "$PREFIX" in
-*/) ;;
-*) die "Prefix must end with '/'";;
+"")
+ PREFIX="base-$TOPREV/"
+ ;;
+*/)
+ ;;
+*)
+ die "Prefix must end with '/'"
+ ;;
esac
# Check for both and R
@@ -97,13 +120,11 @@ then
fi
# Use the filtered list to build the final tar
-# The -a option chooses compression automatically based on output file name.
-
-tar -C "$TDIR"/tar --files-from="$TDIR"/list.2 -caf "$FINALTAR"
+tar -c $TAROPT -C "$TDIR"/tar -T "$TDIR"/list.2 -f "$FINALTAR"
echo "Wrote $FINALTAR"
-tar -taf "$FINALTAR" > "$TDIR"/list.3
+tar -t $TAROPT -f "$FINALTAR" > "$TDIR"/list.3
# make sure we haven't picked up anything extra
if ! diff -u "$TDIR"/list.2 "$TDIR"/list.3
diff --git a/configure/CONFIG_BASE b/configure/CONFIG_BASE
index 137ce0098..963b83a32 100644
--- a/configure/CONFIG_BASE
+++ b/configure/CONFIG_BASE
@@ -30,10 +30,11 @@ endif # BASE_TOP
#---------------------------------------------------------------
# Where to find the installed build tools
# Windows does not like commands with relative paths starting ../
-# but the Perl scripts in TOP/src/tools are OK
+# so TOOLS must be an absolute path, although Perl scripts are OK.
+# FIND_TOOL is for scripts run before the build reaches src/tools.
TOOLS = $(abspath $(EPICS_BASE_HOST_BIN))
-FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) $(TOP)/src/tools/$(1)))
+FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) $(EPICS_BASE)/src/tools/$(1)))
#---------------------------------------------------------------
# EPICS Base build tools and tool flags
@@ -54,3 +55,8 @@ INSTALL_LIBRARY = $(INSTALL)
# tools for making header dependencies and variable replacement
MKMF = $(PERL) $(TOOLS)/mkmf.pl
REPLACEVAR = $(PERL) $(TOOLS)/replaceVAR.pl
+
+#---------------------------------------------------------------
+# tools for cleaning out unwanted files
+CVSCLEAN = $(call FIND_TOOL,cvsclean.pl)
+DEPCLEAN = $(call FIND_TOOL,depclean.pl)
diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION
index e55ee5d33..53753d7e4 100644
--- a/configure/CONFIG_BASE_VERSION
+++ b/configure/CONFIG_BASE_VERSION
@@ -48,16 +48,16 @@ EPICS_VERSION = 7
EPICS_REVISION = 0
# EPICS_MODIFICATION must be a number >=0 and <256
-EPICS_MODIFICATION = 2
+EPICS_MODIFICATION = 3
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
-# Not included if zero
-EPICS_PATCH_LEVEL = 2
+# Not included in the official EPICS version number if zero
+EPICS_PATCH_LEVEL = 0
# Between official releases, the EPICS_PATCH_LEVEL gets incremented
# and a -DEV suffix is added (similar to the Maven -SNAPSHOT versions)
-#EPICS_DEV_SNAPSHOT=
-EPICS_DEV_SNAPSHOT=-DEV
+EPICS_DEV_SNAPSHOT=
+#EPICS_DEV_SNAPSHOT=-DEV
#EPICS_DEV_SNAPSHOT=-pre1
#EPICS_DEV_SNAPSHOT=-pre1-DEV
#EPICS_DEV_SNAPSHOT=-pre2
diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD
index d2ddfae03..d0ac1bf1f 100644
--- a/configure/RULES_BUILD
+++ b/configure/RULES_BUILD
@@ -564,6 +564,6 @@ include $(CONFIG)/RULES_EXPAND
include $(CONFIG)/RULES_COMMON
else
- $(warning RULES_BUILD included more than once. \
- Use 'make show-makefiles' to work out why.)
+ $(warning Warning: Base configure/RULES_BUILD file included more than once. \
+ Does configure/RELEASE have multiple pointers to $(EPICS_BASE)?)
endif # BASE_RULES_BUILD
diff --git a/configure/RULES_COMMON b/configure/RULES_COMMON
index fb8f211e4..ff428c342 100644
--- a/configure/RULES_COMMON
+++ b/configure/RULES_COMMON
@@ -8,16 +8,18 @@
# These rules show the set of Makefiles, config files and
# rules files loaded by GNUmake.
-# Protect against filenames containing colons (Windows)
-SAFE_MAKEFILES = $(subst :,__colon__,$(MAKEFILE_LIST))
-SHOW_MAKEFILES = $(SAFE_MAKEFILES:%=show-makefile.%)
-show-makefiles: $(SHOW_MAKEFILES)
+show-makefiles::
+ @echo
+ @echo Makefiles read:
+define SHOW_MAKEFILE_template
+show-makefiles::
+ @echo " $(1)"
+endef
+$(foreach file,$(MAKEFILE_LIST), \
+ $(eval $(call SHOW_MAKEFILE_template,$(file))))
-# The sort prevents warnings about duplicate targets:
-$(sort $(SHOW_MAKEFILES)): show-makefile.%:
- @echo " $(subst __colon__,:,$(@:show-makefile.%=%))"
+.PHONY: show-makefiles
-.PHONY: show-makefiles show-makefile.%
# These rules support printing a Makefile variable values.
# Many variables are only set inside an O. build directory.
@@ -30,6 +32,17 @@ PRINT.%:
.PHONY: PRINT PRINT.%
+# Clean rules for recursively deleting editor backup files
+# and dependency (.d) files from CWD and below.
+
+cvsclean:
+ $(PERL) $(CVSCLEAN)
+depclean:
+ $(PERL) $(DEPCLEAN)
+
+.PHONY: cvsclean depclean
+
+
# User specific rules
#
-include $(HOME)/configure/RULES_USER
diff --git a/configure/RULES_DIRS b/configure/RULES_DIRS
index ec156745d..db68a84a5 100644
--- a/configure/RULES_DIRS
+++ b/configure/RULES_DIRS
@@ -86,10 +86,11 @@ $(DIRS) $(dirActionTargets) $(dirArchTargets) $(dirActionArchTargets) :
$(ARCHS) $(ACTIONS) $(actionArchTargets) :%: \
$(foreach dir, $(DIRS), $(dir)$(DIVIDER)%)
-.PHONY : $(DIRS) all host rebuild
-.PHONY : $(ARCHS) $(ACTIONS)
-.PHONY : $(dirActionTargets) $(dirArchTargets)
-.PHONY : $(dirActionArchTargets)
-.PHONY : $(actionArchTargets)
+
+.PHONY: $(DIRS) all host rebuild
+.PHONY: $(ARCHS) $(ACTIONS)
+.PHONY: $(dirActionTargets) $(dirArchTargets)
+.PHONY: $(dirActionArchTargets)
+.PHONY: $(actionArchTargets)
include $(CONFIG)/RULES_COMMON
diff --git a/configure/RULES_MODULES b/configure/RULES_MODULES
new file mode 100644
index 000000000..08ebb3e10
--- /dev/null
+++ b/configure/RULES_MODULES
@@ -0,0 +1,47 @@
+#*************************************************************************
+# EPICS BASE is distributed subject to a Software License Agreement found
+# in file LICENSE that is included with this distribution.
+#*************************************************************************
+
+# Support modules can use these rules to build submodules too.
+#
+# The requirements to do so are:
+# 1. Create a file CONFIG_SITE.local in the same directory as the
+# Makefile, which defines these variables (the last one is empty):
+# PARENT_MODULE - The name submodules call their parent
+# INSTALL_LOCATION := $($(PARENT_MODULE))
+# CONFIG_INSTALLS =
+# 2. The Makefile must set TOP and include $(TOP)/configure/CONFIG and
+# CONFIG_SITE.local
+# 3. Submodules are added to the SUBMODULES variable in the Makefile
+# 4. Dependencies between submodules must be set using
+# _DEPEND_DIRS =
+# 5. The Makefile must end by including $(TOP)/configure/RULES_MODULES
+# 6. Submodules must have a configure/RELEASE file that contains
+# -include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
+# 7. Submodules must have a configure/CONFIG_SITE file that contains
+# -include $(TOP)/../CONFIG_SITE.local
+
+# Add checked-out submodules to DIRS
+DIRS += $(subst /Makefile,,$(wildcard $(addsuffix /Makefile, $(SUBMODULES))))
+
+include $(CONFIG)/RULES_DIRS
+
+INSTALL_LOCATION_ABS := $(abspath $(INSTALL_LOCATION))
+RELEASE_LOCAL := RELEASE.$(EPICS_HOST_ARCH).local
+
+# Ensure that RELEASE..local exists before doing anything else
+all host $(DIRS) $(ARCHS) $(ACTIONS) $(dirActionTargets) $(dirArchTargets) \
+ $(dirActionArchTargets) $(actionArchTargets): | $(RELEASE_LOCAL)
+
+# Convenience target
+RELEASE.host: $(RELEASE_LOCAL)
+
+$(RELEASE_LOCAL): Makefile CONFIG_SITE.local
+ $(ECHO) Creating $@ with
+ $(ECHO) " $(PARENT_MODULE) = $(INSTALL_LOCATION_ABS)"
+ @echo $(PARENT_MODULE) = $(INSTALL_LOCATION_ABS)> $@
+realclean:
+ $(RM) $(wildcard RELEASE.*.local)
+
+.PHONY: RELEASE.host realclean
diff --git a/configure/RULES_TOP b/configure/RULES_TOP
index bd844e76f..c1644bf6f 100644
--- a/configure/RULES_TOP
+++ b/configure/RULES_TOP
@@ -9,26 +9,30 @@
include $(CONFIG)/RULES_DIRS
+# Disable most top rules when installing into a parent's tree, indicated
+# by PARENT_MODULE being set in the modules/CONFIG_SITE.local file and
+# INSTALL_LOCATION pointing to the the same place as in the parent.
+ifeq ($(origin PARENT_MODULE),file)
+ ifeq ($(INSTALL_LOCATION),$($(PARENT_MODULE)))
+ DISABLE_TOP_RULES=YES
+ endif
+endif
+
+ifndef DISABLE_TOP_RULES
+ #
+ # Rules for a regular application top directory
+ #
+
distclean: realclean cvsclean realuninstall
-CVSCLEAN = $(call FIND_TOOL,cvsclean.pl)
-cvsclean:
- $(PERL) $(CVSCLEAN)
-
-DEPCLEAN = $(call FIND_TOOL,depclean.pl)
-depclean:
- $(PERL) $(DEPCLEAN)
-
realuninstall: uninstallDirs
$(RMDIR) $(INSTALL_LOCATION_BIN)
$(RMDIR) $(INSTALL_LOCATION_LIB)
-UNINSTALL_DIRS += $(INSTALL_DBD) $(INSTALL_INCLUDE) $(INSTALL_DOC) \
- $(INSTALL_HTML) $(INSTALL_TEMPLATES) $(INSTALL_DB) $(DIRECTORY_TARGETS)
+UNINSTALL_DIRS += $(INSTALL_DB) $(INSTALL_DBD) $(INSTALL_DOC) $(INSTALL_HTML)
+UNINSTALL_DIRS += $(INSTALL_INCLUDE) $(INSTALL_TEMPLATES) $(DIRECTORY_TARGETS)
ifneq ($(INSTALL_LOCATION),$(TOP))
-ifneq ($(INSTALL_LOCATION),$(EPICS_BASE))
-UNINSTALL_DIRS += $(INSTALL_CONFIG)
-endif
+ UNINSTALL_DIRS += $(INSTALL_CONFIG)
endif
uninstallDirs:
$(RMDIR) $(UNINSTALL_DIRS)
@@ -42,6 +46,8 @@ uninstall$(DIVIDER)%:
$(RMDIR) $(INSTALL_LOCATION_BIN)/$(archPart)
$(RMDIR) $(INSTALL_LOCATION_LIB)/$(archPart)
+# Remove the bin and lib directories if they have no sub-directories
+#
cleandirs:
@$(NOP)
ifeq ($(wildcard $(INSTALL_LOCATION_BIN)/*),)
@@ -51,6 +57,16 @@ ifeq ($(wildcard $(INSTALL_LOCATION_LIB)/*),)
$(RMDIR) $(INSTALL_LOCATION_LIB)
endif
+else
+ #
+ # Using a disabled rule aborts
+ #
+
+ cleandirs distclean uninstall realuninstall archuninstall:
+ $(error Target '$@' not available in a submodule)
+
+endif # DISABLE_TOP_RULES
+
help:
@echo "Usage: gnumake [options] [target] ..."
@@ -66,33 +82,39 @@ help:
@echo " Cannot be used within an O. dir"
@echo " rebuild - Same as clean install"
@echo " archclean - Removes O. dirs but not O.Common dir"
- @echo " runtests - Run self-tests, summarize results"
+ @echo " depclean - Removes .d files from all O. dirs."
+ @echo " cvsclean - Removes backup files etc. from all dirs below"
+ @echo " runtests - Run self-tests, summarize results immediately"
+ @echo " tapfiles - Run self-tests, save to O./*.tap files"
+ @echo " test-results - Summarize all O./*.tap files"
+ @echo " clean-tests - Removes all O./*.tap files"
@echo "\"Partial\" build targets supported by Makefiles:"
@echo " host - Builds and installs $(EPICS_HOST_ARCH) only."
@echo " inc$(DIVIDER) - Installs only header files."
@echo " build$(DIVIDER) - Builds and installs only."
@echo " install$(DIVIDER) - Builds and installs only."
@echo " clean$(DIVIDER) - Cleans binaries in O. dirs only."
- @echo " uninstall$(DIVIDER) - Remove bin & lib directories for only."
@echo "Targets supported by top level Makefile:"
+ifndef DISABLE_TOP_RULES
@echo " archuninstall - Remove bin & lib directories created by this hostarch."
+ @echo " uninstall$(DIVIDER) - Remove bin & lib directories for only."
@echo " uninstall - Remove install directories created by this hostarch."
@echo " realuninstall - Removes ALL install dirs"
@echo " distclean - Same as realclean cvsclean realuninstall."
- @echo " depclean - Removes all .d files from O. directories."
- @echo " cvsclean - Removes cvs .#* files in all dirs of directory tree"
+endif
@echo " help - Prints this list of valid make targets "
- @echo "Indiv. object targets are supported by O. level Makefile .e.g"
+ @echo "Object targets are supported by the O. level Makefile .e.g"
@echo " xxxRecord.o"
-.PHONY: cleandirs distclean cvsclean depclean
+.PHONY: cleandirs distclean uninstall help
.PHONY: realuninstall archuninstall uninstallDirs
-.PHONY: uninstall help
-# Include /cfg/TOP_RULES* files from tops defined in RELEASE* files
-#
-RELEASE_CFG_TOP_RULES = $(foreach top, $(RELEASE_TOPS), \
- $(wildcard $($(top))/cfg/TOP_RULES*))
-ifneq ($(RELEASE_CFG_TOP_RULES),)
- include $(RELEASE_CFG_TOP_RULES)
+ifndef DISABLE_TOP_RULES
+ # Include /cfg/TOP_RULES* files from tops defined in RELEASE* files
+ #
+ RELEASE_CFG_TOP_RULES = $(foreach top, $(RELEASE_TOPS), \
+ $(wildcard $($(top))/cfg/TOP_RULES*))
+ ifneq ($(RELEASE_CFG_TOP_RULES),)
+ include $(RELEASE_CFG_TOP_RULES)
+ endif
endif
diff --git a/documentation/KnownProblems.html b/documentation/KnownProblems.html
index d38ad630c..787d2db4b 100644
--- a/documentation/KnownProblems.html
+++ b/documentation/KnownProblems.html
@@ -4,17 +4,17 @@
- Known Problems in EPICS 7.0.2
+ Known Problems in EPICS 7.0.3
-EPICS 7.0.2: Known Problems
+EPICS 7.0.3: Known Problems
Any patch files linked below should be applied at the root of the
-base-7.0.2 tree. Download them, then use the GNU Patch program as
+base-7.0.3 tree. Download them, then use the GNU Patch program as
follows:
-% cd /path/to/base-7.0.2
+% cd /path/to/base-7.0.3
% patch -p1 < /path/to/file.patch
The following problems were known by the developers at the time of this
diff --git a/documentation/README.1st b/documentation/README.1st
index f0c560fb0..177205805 100644
--- a/documentation/README.1st
+++ b/documentation/README.1st
@@ -1,24 +1,24 @@
Installation Instructions
- EPICS Base Release 7.0.2
+ EPICS Base Release 7.0.3
--------------------------------------------------------------------------
Table of Contents
- * What is EPICS base?
- * What is new in this release?
- * Copyright
- * Supported platforms
- * Supported compilers
- * Software requirements
- * Host system storage requirements
- * Documentation
- * Directory Structure
- * Build related components
- * Building EPICS base (Unix and Win32)
- * Example application and extension
- * Multiple host platforms
+ * What is EPICS base?
+ * What is new in this release?
+ * Copyright
+ * Supported platforms
+ * Supported compilers
+ * Software requirements
+ * Host system storage requirements
+ * Documentation
+ * Directory Structure
+ * Build related components
+ * Building EPICS base (Unix and Win32)
+ * Example application and extension
+ * Multiple host platforms
--------------------------------------------------------------------------
diff --git a/documentation/README.html b/documentation/README.html
index ab9a1f7a4..6e15470fd 100644
--- a/documentation/README.html
+++ b/documentation/README.html
@@ -9,7 +9,7 @@
Installation Instructions
-EPICS Base Release 7.0.2
+EPICS Base Release 7.0.3
Table of Contents
diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 27670a17e..103e5a957 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -20,7 +20,7 @@ release.
which should also be read to understand what has changed since an earlier
release.
-EPICS Release 7.0.2.2
+EPICS Release 7.0.3
+epicsTimeGetCurrent() optimization
+
+Add a fast path to epicsTimeGetCurrent() and related calls
+in the common case where only the default OS current time provider is
+registered. This path does not take the global mutex guarding
+the time providers list, potentially reducing lock contention.
+
+dbEvent tweak Queue size
+
+The size of the queue used by dbEvent to push monitor updates
+has been slightly increased based on DBR_TIME_DOUBLE to better fill
+an ethernet frame. This may result in slightly fewer, but larger
+frames being sent.
+
+mbbo/mbbiDirect number of bits as precision
+
+Report NOBT as "precision" through the dbAccess API.
+This is not accessible through CA, but is planned to be used through
+QSRV.
+
+EPICS Release 7.0.2.2
+
+Build System changes
+
+
+
+- The GNUmake build targets cvsclean and depclean are now
+available from any directory; previously they were only available from
+application top directories.
+
+- The approach that EPICS Base uses for building submodules inside the parent
+module looks useful for support modules too. The rules for building submodules
+have been modified and extracted into a new RULES_MODULES file, so a
+support module will be able to use them too without having to copy them into its
+own modules/Makefile. There are some specific requirements that support
+modules and their submodules must follow, which are described as comments in the
+new base/configure/RULES_MODULES file itself.
+
+
+
+EPICS_BASE_VERSION Update Policy change
+
+In the past, a build of EPICS using sources checked out from the repository
+branch between official releases would have shown the version number of the
+previous release, followed by a -DEV
suffix, for example
+7.0.2.1-DEV
.
+
+The policy that controls when the number gets updated has been changed, and
+now immediately after a release has been tagged the version number will be
+updated to the next patch release version, plus the -DEV
suffix as
+before. Thus following 7.0.2.2
the version number will show as
+7.0.2.3-DEV
. This does not require the next official release to be
+numbered 7.0.2.3
though, it could become 7.0.3
or even
+7.1.0
if the changes incorporated into it are more substantial than bug
+fixes.
+
+Drop CLOCK_MONOTONIC_RAW from posix/osdMonotonic.c
+
+Turns out this is ~10x slower to query than CLOCK_MONOTONIC.
+
EPICS Release 7.0.2.1
@@ -977,6 +1037,15 @@ of its CALLBACK objects.
+Cleaning up with Multiple CA contexts in a Process
+
+Bruno Martins reported a problem with the CA client library at shutdown in a
+process that uses multiple CA client contexts. The first context that triggers
+the CA client exit handler prevents any others from being able to clean up
+because it resets the ID of an internal epicsThreadPrivate variable which is
+shared by all clients. This action has been removed from the client library,
+which makes cleanup of clients like this possible.
+
Perl CA bindings fixed for macOS Mojave
Apple removed some Perl header files from macOS Mojave that were available
diff --git a/documentation/ReleaseChecklist.html b/documentation/ReleaseChecklist.html
index cb95f7f60..81784e8cc 100644
--- a/documentation/ReleaseChecklist.html
+++ b/documentation/ReleaseChecklist.html
@@ -53,9 +53,10 @@ made.
Short Process for Patch Releases
-The Patch Release date and its scope are agreed upon about four weeks
-ahead of time. If no blocking issues are raised, the release is made by the
-release manager.
+The Patch Release date and its scope are agreed upon a few weeks ahead of the
+release. If no blocking issues are raised, the release is made by the Release
+Manager on or as soon as possible after that date, following the steps below
+starting at Release Approval.
Roles
@@ -72,6 +73,7 @@ release manager.
Responsible for the EPICS website
+
diff --git a/modules/CONFIG_SITE.local b/modules/CONFIG_SITE.local
index 760f9d691..db97a5b78 100644
--- a/modules/CONFIG_SITE.local
+++ b/modules/CONFIG_SITE.local
@@ -3,8 +3,11 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
-# When building submodules, this should always be true:
-INSTALL_LOCATION = $(EPICS_BASE)
+# The name our submodules know us by:
+PARENT_MODULE = EPICS_BASE
-# Stop submodules from installing their configuration files:
+# When building submodules, this should always be true:
+INSTALL_LOCATION := $($(PARENT_MODULE))
+
+# Stop submodules installing their configure/ files into our area
CONFIG_INSTALLS =
diff --git a/modules/Makefile b/modules/Makefile
index da9bfe5a3..9a189daf6 100644
--- a/modules/Makefile
+++ b/modules/Makefile
@@ -5,6 +5,7 @@
TOP = ..
include $(TOP)/configure/CONFIG
+include CONFIG_SITE.local
# Submodules for bundle build
SUBMODULES += libcom
@@ -42,20 +43,4 @@ pcas_DEPEND_DIRS = ca
# Allow sites to add extra submodules
-include Makefile.local
-# Add only checked-out submodules to DIRS
-DIRS += $(subst /Makefile,,$(wildcard $(addsuffix /Makefile, $(SUBMODULES))))
-
-include $(TOP)/configure/RULES_DIRS
-
-INSTALL_LOCATION_ABS := $(abspath $(INSTALL_LOCATION))
-RELEASE_LOCAL := RELEASE.$(EPICS_HOST_ARCH).local
-
-# Ensure that RELEASE..local exists before doing anything else
-all host $(DIRS) $(ARCHS) $(ACTIONS) $(dirActionTargets) $(dirArchTargets) \
- $(dirActionArchTargets) $(actionArchTargets): | $(RELEASE_LOCAL)
-
-$(RELEASE_LOCAL):
- $(ECHO) Creating $@, EPICS_BASE = $(INSTALL_LOCATION_ABS)
- @echo EPICS_BASE = $(INSTALL_LOCATION_ABS)> $@
-realclean:
- $(RM) $(wildcard RELEASE.*.local)
+include $(TOP)/configure/RULES_MODULES
diff --git a/modules/ca/configure/CONFIG_CA_VERSION b/modules/ca/configure/CONFIG_CA_VERSION
index fb80868c0..8a6649666 100644
--- a/modules/ca/configure/CONFIG_CA_VERSION
+++ b/modules/ca/configure/CONFIG_CA_VERSION
@@ -1,4 +1,4 @@
EPICS_CA_MAJOR_VERSION = 4
EPICS_CA_MINOR_VERSION = 13
-EPICS_CA_MAINTENANCE_VERSION = 3
+EPICS_CA_MAINTENANCE_VERSION = 4
EPICS_CA_DEVELOPMENT_FLAG = 0
diff --git a/modules/ca/src/client/CAref.html b/modules/ca/src/client/CAref.html
index 741716116..7b6ff143f 100644
--- a/modules/ca/src/client/CAref.html
+++ b/modules/ca/src/client/CAref.html
@@ -3143,7 +3143,8 @@ indicating the current state of the channel.
COUNT
- The element count to be read from the specified channel. A count of
- zero means use the current element count from the server.
+ zero means use the current element count from the server, effectively
+ resulting in a variable size array subscription.
CHID
diff --git a/modules/ca/src/client/ca_client_context.cpp b/modules/ca/src/client/ca_client_context.cpp
index 2bb3e24a8..edd88cd09 100644
--- a/modules/ca/src/client/ca_client_context.cpp
+++ b/modules/ca/src/client/ca_client_context.cpp
@@ -45,20 +45,12 @@ static epicsThreadOnceId cacOnce = EPICS_THREAD_ONCE_INIT;
const unsigned ca_client_context :: flushBlockThreshold = 0x58000;
-extern "C" void cacExitHandler ( void *)
-{
- epicsThreadPrivateDelete ( caClientCallbackThreadId );
- caClientCallbackThreadId = 0;
- delete ca_client_context::pDefaultServiceInstallMutex;
-}
-
// runs once only for each process
extern "C" void cacOnceFunc ( void * )
{
caClientCallbackThreadId = epicsThreadPrivateCreate ();
assert ( caClientCallbackThreadId );
ca_client_context::pDefaultServiceInstallMutex = newEpicsMutex;
- epicsAtExit ( cacExitHandler,0 );
}
extern epicsThreadPrivateId caClientContextId;
@@ -67,6 +59,8 @@ cacService * ca_client_context::pDefaultService = 0;
epicsMutex * ca_client_context::pDefaultServiceInstallMutex;
ca_client_context::ca_client_context ( bool enablePreemptiveCallback ) :
+ mutex(__FILE__, __LINE__),
+ cbMutex(__FILE__, __LINE__),
createdByThread ( epicsThreadGetIdSelf () ),
ca_exception_func ( 0 ), ca_exception_arg ( 0 ),
pVPrintfFunc ( errlogVprintf ), fdRegFunc ( 0 ), fdRegArg ( 0 ),
diff --git a/modules/ca/src/client/oldAccess.h b/modules/ca/src/client/oldAccess.h
index c893eeb68..1337cb31a 100644
--- a/modules/ca/src/client/oldAccess.h
+++ b/modules/ca/src/client/oldAccess.h
@@ -289,7 +289,6 @@ private:
};
extern "C" void cacOnceFunc ( void * );
-extern "C" void cacExitHandler ( void *);
struct ca_client_context : public cacContextNotify
{
@@ -429,7 +428,6 @@ private:
ca_client_context & operator = ( const ca_client_context & );
friend void cacOnceFunc ( void * );
- friend void cacExitHandler ( void *);
static cacService * pDefaultService;
static epicsMutex * pDefaultServiceInstallMutex;
static const unsigned flushBlockThreshold;
diff --git a/modules/ca/src/client/repeaterSubscribeTimer.cpp b/modules/ca/src/client/repeaterSubscribeTimer.cpp
index 948ccebd5..71ba5ad06 100644
--- a/modules/ca/src/client/repeaterSubscribeTimer.cpp
+++ b/modules/ca/src/client/repeaterSubscribeTimer.cpp
@@ -37,6 +37,7 @@ repeaterSubscribeTimer::repeaterSubscribeTimer (
epicsMutex & cbMutexIn, cacContextNotify & ctxNotifyIn ) :
timer ( queueIn.createTimer () ), iiu ( iiuIn ),
cbMutex ( cbMutexIn ),ctxNotify ( ctxNotifyIn ),
+ stateMutex(__FILE__, __LINE__),
attempts ( 0 ), registered ( false ), once ( false )
{
}
diff --git a/modules/database/configure/CONFIG_DATABASE_VERSION b/modules/database/configure/CONFIG_DATABASE_VERSION
index 4b6ef8a6b..ad8d54ab5 100644
--- a/modules/database/configure/CONFIG_DATABASE_VERSION
+++ b/modules/database/configure/CONFIG_DATABASE_VERSION
@@ -1,4 +1,4 @@
EPICS_DATABASE_MAJOR_VERSION = 3
EPICS_DATABASE_MINOR_VERSION = 17
-EPICS_DATABASE_MAINTENANCE_VERSION = 3
+EPICS_DATABASE_MAINTENANCE_VERSION = 4
EPICS_DATABASE_DEVELOPMENT_FLAG = 0
diff --git a/modules/database/src/ioc/db/callback.c b/modules/database/src/ioc/db/callback.c
index fe6db815b..e1f1d45a2 100644
--- a/modules/database/src/ioc/db/callback.c
+++ b/modules/database/src/ioc/db/callback.c
@@ -55,7 +55,7 @@ typedef struct cbQueueSet {
epicsRingPointerId queue;
int queueOverflow;
int queueOverflows;
- int shutdown;
+ int shutdown; // use atomic
int threadsConfigured;
int threadsRunning;
} cbQueueSet;
@@ -71,12 +71,15 @@ epicsExportAddress(int,callbackParallelThreadsDefault);
/* Timer for Delayed Requests */
static epicsTimerQueueId timerQueue;
-/* Shutdown handling */
-enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
-static volatile enum ctl cbCtl;
-static epicsEventId startStopEvent;
+enum cbState_t {
+ cbInit, /* before callbackInit() and after callbackCleanup() */
+ cbRun, /* after callbackInit() and before callbackStop() */
+ cbStop, /* after callbackStop() and before callbackCleanup() */
+};
-static int callbackIsInit;
+static int cbState; // holdscbState_t, use atomic ops
+
+static epicsEventId startStopEvent;
/* Static data */
static char *threadNamePrefix[NUM_CALLBACK_PRIORITIES] = {
@@ -96,7 +99,7 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2};
int callbackSetQueueSize(int size)
{
- if (callbackIsInit) {
+ if (epicsAtomicGetIntT(&cbState)!=cbInit) {
fprintf(stderr, "Callback system already initialized\n");
return -1;
}
@@ -107,7 +110,7 @@ int callbackSetQueueSize(int size)
int callbackQueueStatus(const int reset, callbackQueueStats *result)
{
int ret;
- if (!callbackIsInit) return -1;
+ if (epicsAtomicGetIntT(&cbState)==cbInit) return -1;
if (result) {
int prio;
result->size = callbackQueueSize;
@@ -151,7 +154,7 @@ void callbackQueueShow(const int reset)
int callbackParallelThreads(int count, const char *prio)
{
- if (callbackIsInit) {
+ if (epicsAtomicGetIntT(&cbState)!=cbInit) {
fprintf(stderr, "Callback system already initialized\n");
return -1;
}
@@ -207,7 +210,7 @@ static void callbackTask(void *arg)
taskwdInsert(0, NULL, NULL);
epicsEventSignal(startStopEvent);
- while(!mySet->shutdown) {
+ while(!epicsAtomicGetIntT(&mySet->shutdown)) {
void *ptr;
if (epicsRingPointerIsEmpty(mySet->queue))
epicsEventMustWait(mySet->semWakeUp);
@@ -230,11 +233,10 @@ void callbackStop(void)
{
int i;
- if (cbCtl == ctlExit) return;
- cbCtl = ctlExit;
+ if (epicsAtomicCmpAndSwapIntT(&cbState, cbRun, cbStop)!=cbRun) return;
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
- callbackQueue[i].shutdown = 1;
+ epicsAtomicSetIntT(&callbackQueue[i].shutdown, 1);
epicsEventSignal(callbackQueue[i].semWakeUp);
}
@@ -252,6 +254,10 @@ void callbackCleanup(void)
{
int i;
+ if(epicsAtomicCmpAndSwapIntT(&cbState, cbStop, cbInit)!=cbStop) {
+ fprintf(stderr, "callbackCleanup() but not stopped\n");
+ }
+
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
cbQueueSet *mySet = &callbackQueue[i];
@@ -261,7 +267,6 @@ void callbackCleanup(void)
}
epicsTimerQueueRelease(timerQueue);
- callbackIsInit = 0;
memset(callbackQueue, 0, sizeof(callbackQueue));
}
@@ -271,15 +276,14 @@ void callbackInit(void)
int j;
char threadName[32];
- if (callbackIsInit) {
- errlogMessage("Warning: callbackInit called again before callbackCleanup\n");
+ if (epicsAtomicCmpAndSwapIntT(&cbState, cbInit, cbRun)!=cbInit) {
+ fprintf(stderr, "Warning: callbackInit called again before callbackCleanup\n");
return;
}
- callbackIsInit = 1;
if(!startStopEvent)
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
- cbCtl = ctlRun;
+
timerQueue = epicsTimerQueueAllocate(0, epicsThreadPriorityScanHigh);
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
index 9304f99b2..77b12de8d 100644
--- a/modules/database/src/ioc/db/dbEvent.c
+++ b/modules/database/src/ioc/db/dbEvent.c
@@ -49,7 +49,13 @@
#include "link.h"
#include "special.h"
-#define EVENTSPERQUE 32
+/* Queue size based on Ethernet MTU of 1500 bytes.
+ * Assume <=66 bytes of ethernet+IP+TCP overhead
+ * and 40 byte CA messages (DBF_TIME_DOUBLE).
+ *
+ * (1500-66)/40 -> 35
+ */
+#define EVENTSPERQUE 36
#define EVENTENTRIES 4 /* the number of que entries for each event */
#define EVENTQUESIZE (EVENTENTRIES * EVENTSPERQUE)
#define EVENTQEMPTY ((struct evSubscrip *)NULL)
diff --git a/modules/database/src/ioc/dbtemplate/Makefile b/modules/database/src/ioc/dbtemplate/Makefile
index 2aa5a0c78..13b4f11fb 100644
--- a/modules/database/src/ioc/dbtemplate/Makefile
+++ b/modules/database/src/ioc/dbtemplate/Makefile
@@ -13,7 +13,7 @@ SRC_DIRS += $(IOCDIR)/dbtemplate
PROD_HOST += msi
-msi_SRCS = msi.c
+msi_SRCS = msi.cpp
msi_LIBS += Com
HTMLS += msi.html
diff --git a/modules/database/src/ioc/dbtemplate/msi.c b/modules/database/src/ioc/dbtemplate/msi.cpp
similarity index 70%
rename from modules/database/src/ioc/dbtemplate/msi.c
rename to modules/database/src/ioc/dbtemplate/msi.cpp
index 69680e17e..462869cb3 100644
--- a/modules/database/src/ioc/dbtemplate/msi.c
+++ b/modules/database/src/ioc/dbtemplate/msi.cpp
@@ -9,6 +9,9 @@
/* msi - macro substitutions and include */
+#include
+#include
+
#include
#include
#include
@@ -18,7 +21,6 @@
#include
#include
-#include
#include
#include
#include
@@ -56,32 +58,35 @@ int din = 0;
typedef struct inputData inputData;
static void inputConstruct(inputData **ppvt);
-static void inputDestruct(inputData *pvt);
-static void inputAddPath(inputData *pvt, char *pval);
-static void inputBegin(inputData *pvt, char *fileName);
-static char *inputNextLine(inputData *pvt);
-static void inputNewIncludeFile(inputData *pvt, char *name);
-static void inputErrPrint(inputData *pvt);
+static void inputDestruct(inputData * const pvt);
+static void inputAddPath(inputData * const pvt, const char * const pval);
+static void inputBegin(inputData * const pvt, const char * const fileName);
+static char *inputNextLine(inputData * const pvt);
+static void inputNewIncludeFile(inputData * const pvt, const char * const name);
+static void inputErrPrint(const inputData * const pvt);
/* Module to read the substitution file */
typedef struct subInfo subInfo;
-static void substituteOpen(subInfo **ppvt, char *substitutionName);
-static void substituteDestruct(subInfo *pvt);
-static int substituteGetNextSet(subInfo *pvt, char **filename);
-static int substituteGetGlobalSet(subInfo *pvt);
-static char *substituteGetReplacements(subInfo *pvt);
-static char *substituteGetGlobalReplacements(subInfo *pvt);
+static void substituteOpen(subInfo **ppvt, const std::string& substitutionName);
+static void substituteDestruct(subInfo * const pvt);
+static bool substituteGetNextSet(subInfo * const pvt, char **filename);
+static bool substituteGetGlobalSet(subInfo * const pvt);
+static const char *substituteGetReplacements(subInfo * const pvt);
+static const char *substituteGetGlobalReplacements(subInfo * const pvt);
/* Forward references to local routines */
-static void usageExit(int status);
-static void abortExit(int status);
-static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval);
-static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName);
+static void usageExit(const int status);
+static void abortExit(const int status);
+static void addMacroReplacements(MAC_HANDLE * const macPvt,
+ const char * const pval);
+static void makeSubstitutions(inputData * const inputPvt,
+ MAC_HANDLE * const macPvt,
+ const char * const templateName);
/*Global variables */
static int opt_V = 0;
-static int opt_D = 0;
+static bool opt_D = false;
static char *outFile = 0;
static int numDeps = 0, depHashes[MAX_DEPS];
@@ -92,23 +97,21 @@ int main(int argc,char **argv)
inputData *inputPvt;
MAC_HANDLE *macPvt;
char *pval;
- int narg;
- char *substitutionName = 0;
+ std::string substitutionName;
char *templateName = 0;
- int i;
- int localScope = 1;
+ bool localScope = true;
inputConstruct(&inputPvt);
macCreateHandle(&macPvt, 0);
while ((argc > 1) && (argv[1][0] == '-')) {
- narg = (strlen(argv[1]) == 2) ? 2 : 1;
+ int narg = (strlen(argv[1]) == 2) ? 2 : 1;
pval = (narg == 1) ? (argv[1] + 2) : argv[2];
if (strncmp(argv[1], "-I", 2) == 0) {
inputAddPath(inputPvt, pval);
}
else if (strcmp(argv[1], "-D") == 0) {
- opt_D = 1;
+ opt_D = true;
narg = 1; /* no argument for this option */
}
else if(strncmp(argv[1], "-o", 2) == 0) {
@@ -118,14 +121,14 @@ int main(int argc,char **argv)
addMacroReplacements(macPvt, pval);
}
else if(strncmp(argv[1], "-S", 2) == 0) {
- substitutionName = epicsStrDup(pval);
+ substitutionName = pval;
}
else if (strcmp(argv[1], "-V") == 0) {
opt_V = 1;
narg = 1; /* no argument for this option */
}
else if (strcmp(argv[1], "-g") == 0) {
- localScope = 0;
+ localScope = false;
narg = 1; /* no argument for this option */
}
else if (strcmp(argv[1], "-h") == 0) {
@@ -137,7 +140,7 @@ int main(int argc,char **argv)
}
argc -= narg;
- for (i = 1; i < argc; i++)
+ for (int i = 1; i < argc; i++)
argv[i] = argv[i + narg];
}
@@ -165,24 +168,24 @@ int main(int argc,char **argv)
if (argc == 2)
templateName = epicsStrDup(argv[1]);
- if (!substitutionName) {
+ if (substitutionName.empty()) {
STEP("Single template+substitutions file");
makeSubstitutions(inputPvt, macPvt, templateName);
}
else {
subInfo *substitutePvt;
char *filename = 0;
- int isGlobal, isFile;
+ bool isGlobal, isFile;
- STEPS("Substitutions from file", substitutionName);
+ STEPS("Substitutions from file", substitutionName.c_str());
substituteOpen(&substitutePvt, substitutionName);
do {
isGlobal = substituteGetGlobalSet(substitutePvt);
if (isGlobal) {
STEP("Handling global macros");
- pval = substituteGetGlobalReplacements(substitutePvt);
- if (pval)
- addMacroReplacements(macPvt, pval);
+ const char *macStr = substituteGetGlobalReplacements(substitutePvt);
+ if (macStr)
+ addMacroReplacements(macPvt, macStr);
}
else if ((isFile = substituteGetNextSet(substitutePvt, &filename))) {
if (templateName)
@@ -193,11 +196,12 @@ int main(int argc,char **argv)
}
STEPS("Handling template file", filename);
- while ((pval = substituteGetReplacements(substitutePvt))) {
+ const char *macStr;
+ while ((macStr = substituteGetReplacements(substitutePvt))) {
if (localScope)
macPushScope(macPvt);
- addMacroReplacements(macPvt, pval);
+ addMacroReplacements(macPvt, macStr);
makeSubstitutions(inputPvt, macPvt, filename);
if (localScope)
@@ -207,18 +211,18 @@ int main(int argc,char **argv)
} while (isGlobal || isFile);
substituteDestruct(substitutePvt);
}
- errlogFlush();
macDeleteHandle(macPvt);
+ errlogFlush(); // macLib calls errlogPrintf()
inputDestruct(inputPvt);
if (opt_D) {
printf("\n");
}
+ fflush(stdout);
free(templateName);
- free(substitutionName);
return opt_V & 2;
}
-void usageExit(int status)
+void usageExit(const int status)
{
fprintf(stderr,
"Usage: msi [options] [template]\n"
@@ -236,7 +240,7 @@ void usageExit(int status)
exit(status);
}
-void abortExit(int status)
+void abortExit(const int status)
{
if (outFile) {
fclose(stdout);
@@ -245,7 +249,8 @@ void abortExit(int status)
exit(status);
}
-static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval)
+static void addMacroReplacements(MAC_HANDLE * const macPvt,
+ const char * const pval)
{
char **pairs;
long status;
@@ -268,7 +273,9 @@ static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval)
typedef enum {cmdInclude,cmdSubstitute} cmdType;
static const char *cmdNames[] = {"include","substitute"};
-static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName)
+static void makeSubstitutions(inputData * const inputPvt,
+ MAC_HANDLE * const macPvt,
+ const char * const templateName)
{
char *input;
static char buffer[MAX_BUFFER_SIZE];
@@ -292,7 +299,6 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem
if (command) {
char *pstart;
char *pend;
- char *copy;
int cmdind=-1;
int i;
@@ -325,16 +331,15 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem
/*skip quote and any trailing blanks*/
while (*++p == ' ') ;
if (*p != '\n' && *p != 0) goto endcmd;
- copy = calloc(pend-pstart + 1, sizeof(char));
- strncpy(copy, pstart, pend-pstart);
+ std::string copy = std::string(pstart, pend);
switch(cmdind) {
case cmdInclude:
- inputNewIncludeFile(inputPvt,copy);
+ inputNewIncludeFile(inputPvt, copy.c_str());
break;
case cmdSubstitute:
- addMacroReplacements(macPvt,copy);
+ addMacroReplacements(macPvt, copy.c_str());
break;
default:
@@ -342,7 +347,6 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem
inputErrPrint(inputPvt);
abortExit(1);
}
- free(copy);
expand = 0;
}
@@ -361,94 +365,72 @@ endcmd:
}
typedef struct inputFile {
- ELLNODE node;
- char *filename;
+ std::string filename;
FILE *fp;
int lineNum;
} inputFile;
-typedef struct pathNode {
- ELLNODE node;
- char *directory;
-} pathNode;
-
struct inputData {
- ELLLIST inputFileList;
- ELLLIST pathList;
+ std::list inputFileList;
+ std::list pathList;
char inputBuffer[MAX_BUFFER_SIZE];
+ inputData() { memset(inputBuffer, 0, sizeof(inputBuffer) * sizeof(inputBuffer[0])); };
};
-static void inputOpenFile(inputData *pinputData, char *filename);
+static void inputOpenFile(inputData *pinputData, const char * const filename);
static void inputCloseFile(inputData *pinputData);
static void inputCloseAllFiles(inputData *pinputData);
static void inputConstruct(inputData **ppvt)
{
- inputData *pinputData;
-
- pinputData = calloc(1, sizeof(inputData));
- ellInit(&pinputData->inputFileList);
- ellInit(&pinputData->pathList);
- *ppvt = pinputData;
+ *ppvt = new inputData;
}
-static void inputDestruct(inputData *pinputData)
+static void inputDestruct(inputData * const pinputData)
{
- pathNode *ppathNode;
-
inputCloseAllFiles(pinputData);
- while ((ppathNode = (pathNode *) ellFirst(&pinputData->pathList))) {
- ellDelete(&pinputData->pathList, &ppathNode->node);
- free(ppathNode->directory);
- free(ppathNode);
- }
- free(pinputData);
+ delete(pinputData);
}
-static void inputAddPath(inputData *pinputData, char *path)
+static void inputAddPath(inputData * const pinputData, const char * const path)
{
- ELLLIST *ppathList = &pinputData->pathList;
- pathNode *ppathNode;
const char *pcolon;
const char *pdir;
size_t len;
- int emptyName;
const char sep = *OSI_PATH_LIST_SEPARATOR;
ENTER;
pdir = path;
/*an empty name at beginning, middle, or end means current directory*/
while (pdir && *pdir) {
- emptyName = ((*pdir == sep) ? 1 : 0);
+ bool emptyName = (*pdir == sep);
if (emptyName) ++pdir;
- ppathNode = (pathNode *) calloc(1, sizeof(pathNode));
- ellAdd(ppathList, &ppathNode->node);
-
+ std::string directory;
if (!emptyName) {
pcolon = strchr(pdir, sep);
len = (pcolon ? (pcolon - pdir) : strlen(pdir));
if (len > 0) {
- ppathNode->directory = (char *) calloc(len + 1, sizeof(char));
- strncpy(ppathNode->directory, pdir, len);
+ directory = std::string(pdir, len);
pdir = pcolon;
/*unless at end skip past first colon*/
if (pdir && *(pdir + 1) != 0) ++pdir;
}
else { /*must have been trailing : */
- emptyName = 1;
+ emptyName = true;
}
}
if (emptyName) {
- ppathNode->directory = (char *) calloc(2, sizeof(char));
- strcpy(ppathNode->directory, ".");
+ directory = ".";
}
+
+ pinputData->pathList.push_back(directory);
}
EXIT;
}
-static void inputBegin(inputData *pinputData, char *fileName)
+static void inputBegin(inputData * const pinputData, const char * const fileName)
{
ENTER;
inputCloseAllFiles(pinputData);
@@ -456,16 +438,16 @@ static void inputBegin(inputData *pinputData, char *fileName)
EXIT;
}
-static char *inputNextLine(inputData *pinputData)
+static char *inputNextLine(inputData * const pinputData)
{
- inputFile *pinputFile;
- char *pline;
+ std::list& inFileList = pinputData->inputFileList;
ENTER;
- while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) {
- pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, pinputFile->fp);
+ while (!inFileList.empty()) {
+ inputFile& inFile = inFileList.front();
+ char *pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, inFile.fp);
if (pline) {
- ++pinputFile->lineNum;
+ ++inFile.lineNum;
EXITS(pline);
return pline;
}
@@ -475,32 +457,31 @@ static char *inputNextLine(inputData *pinputData)
return 0;
}
-static void inputNewIncludeFile(inputData *pinputData, char *name)
+static void inputNewIncludeFile(inputData * const pinputData,
+ const char * const name)
{
ENTER;
inputOpenFile(pinputData,name);
EXIT;
}
-static void inputErrPrint(inputData *pinputData)
+static void inputErrPrint(const inputData *const pinputData)
{
- inputFile *pinputFile;
-
ENTER;
fprintf(stderr, "input: '%s' at ", pinputData->inputBuffer);
- pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList);
- while (pinputFile) {
- fprintf(stderr, "line %d of ", pinputFile->lineNum);
+ const std::list& inFileList = pinputData->inputFileList;
+ std::list::const_iterator inFileIt = inFileList.begin();
+ while (inFileIt != inFileList.end()) {
+ fprintf(stderr, "line %d of ", inFileIt->lineNum);
- if (pinputFile->filename) {
- fprintf(stderr, " file %s\n", pinputFile->filename);
+ if (!inFileIt->filename.empty()) {
+ fprintf(stderr, " file %s\n", inFileIt->filename.c_str());
}
else {
fprintf(stderr, "stdin:\n");
}
- pinputFile = (inputFile *) ellNext(&pinputFile->node);
- if (pinputFile) {
+ if (++inFileIt != inFileList.end()) {
fprintf(stderr, " included from ");
}
else {
@@ -511,12 +492,11 @@ static void inputErrPrint(inputData *pinputData)
EXIT;
}
-static void inputOpenFile(inputData *pinputData,char *filename)
+static void inputOpenFile(inputData *pinputData, const char * const filename)
{
- ELLLIST *ppathList = &pinputData->pathList;
- pathNode *ppathNode = 0;
- inputFile *pinputFile;
- char *fullname = 0;
+ std::list& pathList = pinputData->pathList;
+ std::list::iterator pathIt = pathList.end();
+ std::string fullname;
FILE *fp = 0;
ENTER;
@@ -524,24 +504,19 @@ static void inputOpenFile(inputData *pinputData,char *filename)
STEP("Using stdin");
fp = stdin;
}
- else if ((ellCount(ppathList) == 0) || strchr(filename, '/')){
+ else if (pathList.empty() || strchr(filename, '/')){
STEPS("Opening ", filename);
fp = fopen(filename, "r");
}
else {
- ppathNode = (pathNode *) ellFirst(ppathList);
- while (ppathNode) {
- fullname = calloc(strlen(filename) + strlen(ppathNode->directory) + 2,
- sizeof(char));
- strcpy(fullname, ppathNode->directory);
- strcat(fullname, "/");
- strcat(fullname, filename);
+ pathIt = pathList.begin();
+ while(pathIt != pathList.end()) {
+ fullname = *pathIt + "/" + filename;
STEPS("Trying", filename);
- fp = fopen(fullname, "r");
+ fp = fopen(fullname.c_str(), "r");
if (fp)
break;
- free(fullname);
- ppathNode = (pathNode *) ellNext(&ppathNode->node);
+ ++pathIt;
}
}
@@ -552,20 +527,20 @@ static void inputOpenFile(inputData *pinputData,char *filename)
}
STEP("File opened");
- pinputFile = calloc(1, sizeof(inputFile));
+ inputFile inFile = inputFile();
- if (ppathNode) {
- pinputFile->filename = fullname;
+ if (pathIt != pathList.end()) {
+ inFile.filename = fullname;
}
else if (filename) {
- pinputFile->filename = epicsStrDup(filename);
+ inFile.filename = filename;
}
else {
- pinputFile->filename = epicsStrDup("stdin");
+ inFile.filename = "stdin";
}
if (opt_D) {
- int hash = epicsStrHash(pinputFile->filename, 12345);
+ int hash = epicsStrHash(inFile.filename.c_str(), 12345);
int i = 0;
int match = 0;
@@ -578,7 +553,7 @@ static void inputOpenFile(inputData *pinputData,char *filename)
if (!match) {
const char *wrap = numDeps ? " \\\n" : "";
- printf("%s %s", wrap, pinputFile->filename);
+ printf("%s %s", wrap, inFile.filename.c_str());
if (numDeps < MAX_DEPS) {
depHashes[numDeps++] = hash;
}
@@ -589,33 +564,29 @@ static void inputOpenFile(inputData *pinputData,char *filename)
}
}
- pinputFile->fp = fp;
- ellInsert(&pinputData->inputFileList, 0, &pinputFile->node);
+ inFile.fp = fp;
+ pinputData->inputFileList.push_front(inFile);
EXIT;
}
static void inputCloseFile(inputData *pinputData)
{
- inputFile *pinputFile;
-
+ std::list& inFileList = pinputData->inputFileList;
ENTER;
- pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList);
- if (pinputFile) {
- ellDelete(&pinputData->inputFileList, &pinputFile->node);
- if (fclose(pinputFile->fp))
- fprintf(stderr, "msi: Can't close input file '%s'\n", pinputFile->filename);
- free(pinputFile->filename);
- free(pinputFile);
+ if(!inFileList.empty()) {
+ inputFile& inFile = inFileList.front();
+ if (fclose(inFile.fp))
+ fprintf(stderr, "msi: Can't close input file '%s'\n", inFile.filename.c_str());
+ inFileList.erase(inFileList.begin());
}
EXIT;
}
static void inputCloseAllFiles(inputData *pinputData)
{
- inputFile *pinputFile;
-
ENTER;
- while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) {
+ const std::list& inFileList = pinputData->inputFileList;
+ while(!inFileList.empty()) {
inputCloseFile(pinputData);
}
EXIT;
@@ -627,7 +598,7 @@ typedef enum {
} tokenType;
typedef struct subFile {
- char *substitutionName;
+ std::string substitutionName;
FILE *fp;
int lineNum;
char inputBuffer[MAX_BUFFER_SIZE];
@@ -636,25 +607,20 @@ typedef struct subFile {
char string[MAX_BUFFER_SIZE];
} subFile;
-typedef struct patternNode {
- ELLNODE node;
- char *var;
-} patternNode;
-
struct subInfo {
subFile *psubFile;
- int isFile;
+ bool isFile;
char *filename;
- int isPattern;
- ELLLIST patternList;
- size_t size;
- size_t curLength;
- char *macroReplacements;
+ bool isPattern;
+ std::list patternList;
+ std::string macroReplacements;
+ subInfo() : psubFile(NULL), isFile(false), filename(NULL),
+ isPattern(false) {};
};
static char *subGetNextLine(subFile *psubFile);
static tokenType subGetNextToken(subFile *psubFile);
-static void subFileErrPrint(subFile *psubFile,char * message);
+static void subFileErrPrint(subFile *psubFile, const char * message);
static void freeSubFile(subInfo *psubInfo);
static void freePattern(subInfo *psubInfo);
static void catMacroReplacements(subInfo *psubInfo,const char *value);
@@ -668,7 +634,7 @@ void freeSubFile(subInfo *psubInfo)
if (fclose(psubFile->fp))
fprintf(stderr, "msi: Can't close substitution file\n");
}
- free(psubFile);
+ delete(psubFile);
free(psubInfo->filename);
psubInfo->psubFile = 0;
EXIT;
@@ -676,43 +642,36 @@ void freeSubFile(subInfo *psubInfo)
void freePattern(subInfo *psubInfo)
{
- patternNode *ppatternNode;
-
ENTER;
- while ((ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList))) {
- ellDelete(&psubInfo->patternList, &ppatternNode->node);
- free(ppatternNode->var);
- free(ppatternNode);
- }
- psubInfo->isPattern = 0;
+ psubInfo->patternList.clear();
+ psubInfo->isPattern = false;
EXIT;
}
-static void substituteDestruct(subInfo *psubInfo)
+static void substituteDestruct(subInfo * const psubInfo)
{
ENTER;
freeSubFile(psubInfo);
freePattern(psubInfo);
- free(psubInfo);
+ delete(psubInfo);
EXIT;
}
-static void substituteOpen(subInfo **ppvt, char *substitutionName)
+static void substituteOpen(subInfo **ppvt, const std::string& substitutionName)
{
subInfo *psubInfo;
subFile *psubFile;
FILE *fp;
ENTER;
- psubInfo = calloc(1, sizeof(subInfo));
+ psubInfo = new subInfo;
*ppvt = psubInfo;
- psubFile = calloc(1, sizeof(subFile));
+ psubFile = new subFile;
psubInfo->psubFile = psubFile;
- ellInit(&psubInfo->patternList);
- fp = fopen(substitutionName, "r");
+ fp = fopen(substitutionName.c_str(), "r");
if (!fp) {
- fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName);
+ fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName.c_str());
abortExit(1);
}
@@ -725,7 +684,7 @@ static void substituteOpen(subInfo **ppvt, char *substitutionName)
EXIT;
}
-static int substituteGetGlobalSet(subInfo *psubInfo)
+static bool substituteGetGlobalSet(subInfo * const psubInfo)
{
subFile *psubFile = psubInfo->psubFile;
@@ -737,17 +696,16 @@ static int substituteGetGlobalSet(subInfo *psubInfo)
strcmp(psubFile->string, "global") == 0) {
subGetNextToken(psubFile);
EXITD(1);
- return 1;
+ return true;
}
EXITD(0);
- return 0;
+ return false;
}
-static int substituteGetNextSet(subInfo *psubInfo,char **filename)
+static bool substituteGetNextSet(subInfo * const psubInfo,char **filename)
{
subFile *psubFile = psubInfo->psubFile;
- patternNode *ppatternNode;
ENTER;
*filename = 0;
@@ -756,7 +714,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
if (psubFile->token == tokenEOF) {
EXITD(0);
- return 0;
+ return false;
}
if (psubFile->token == tokenString &&
@@ -764,7 +722,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
size_t len;
STEP("Parsed 'file'");
- psubInfo->isFile = 1;
+ psubInfo->isFile = true;
if (subGetNextToken(psubFile) != tokenString) {
subFileErrPrint(psubFile, "Parse error, expecting a filename");
abortExit(1);
@@ -799,7 +757,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
if (psubFile->token == tokenLBrace) {
EXITD(1);
- return 1;
+ return true;
}
if (psubFile->token == tokenRBrace) {
@@ -815,7 +773,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
STEP("Parsed 'pattern'");
freePattern(psubInfo);
- psubInfo->isPattern = 1;
+ psubInfo->isPattern = true;
while (subGetNextToken(psubFile) == tokenSeparator);
@@ -825,15 +783,13 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
}
STEP("Parsed '{'");
- while (1) {
+ while (true) {
while (subGetNextToken(psubFile) == tokenSeparator);
if (psubFile->token != tokenString)
break;
- ppatternNode = calloc(1, sizeof(patternNode));
- ellAdd(&psubInfo->patternList, &ppatternNode->node);
- ppatternNode->var = epicsStrDup(psubFile->string);
+ psubInfo->patternList.push_back(psubFile->string);
}
if (psubFile->token != tokenRBrace) {
@@ -843,23 +799,21 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
subGetNextToken(psubFile);
EXITD(1);
- return 1;
+ return true;
}
-static char *substituteGetGlobalReplacements(subInfo *psubInfo)
+static const char *substituteGetGlobalReplacements(subInfo * const psubInfo)
{
subFile *psubFile = psubInfo->psubFile;
ENTER;
- if (psubInfo->macroReplacements)
- psubInfo->macroReplacements[0] = 0;
- psubInfo->curLength = 0;
+ psubInfo->macroReplacements.clear();
while (psubFile->token == tokenSeparator)
subGetNextToken(psubFile);
if (psubFile->token == tokenRBrace && psubInfo->isFile) {
- psubInfo->isFile = 0;
+ psubInfo->isFile = false;
free(psubInfo->filename);
psubInfo->filename = 0;
freePattern(psubInfo);
@@ -877,12 +831,12 @@ static char *substituteGetGlobalReplacements(subInfo *psubInfo)
return 0;
}
- while (1) {
+ while (true) {
switch(subGetNextToken(psubFile)) {
case tokenRBrace:
subGetNextToken(psubFile);
- EXITS(psubInfo->macroReplacements);
- return psubInfo->macroReplacements;
+ EXITS(psubInfo->macroReplacements.c_str());
+ return psubInfo->macroReplacements.c_str();
case tokenSeparator:
catMacroReplacements(psubInfo, ",");
@@ -902,21 +856,18 @@ static char *substituteGetGlobalReplacements(subInfo *psubInfo)
}
}
-static char *substituteGetReplacements(subInfo *psubInfo)
+static const char *substituteGetReplacements(subInfo * const psubInfo)
{
subFile *psubFile = psubInfo->psubFile;
- patternNode *ppatternNode;
ENTER;
- if (psubInfo->macroReplacements)
- psubInfo->macroReplacements[0] = 0;
- psubInfo->curLength = 0;
+ psubInfo->macroReplacements.clear();
while (psubFile->token == tokenSeparator)
subGetNextToken(psubFile);
if (psubFile->token==tokenRBrace && psubInfo->isFile) {
- psubInfo->isFile = 0;
+ psubInfo->isFile = false;
free(psubInfo->filename);
psubInfo->filename = 0;
freePattern(psubInfo);
@@ -936,15 +887,16 @@ static char *substituteGetReplacements(subInfo *psubInfo)
}
if (psubInfo->isPattern) {
- int gotFirstPattern = 0;
+ bool gotFirstPattern = false;
while (subGetNextToken(psubFile) == tokenSeparator);
- ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList);
- while (1) {
+ std::list& patternList = psubInfo->patternList;
+ std::list::iterator patternIt = patternList.begin();
+ while (true) {
if (psubFile->token == tokenRBrace) {
subGetNextToken(psubFile);
- EXITS(psubInfo->macroReplacements);
- return psubInfo->macroReplacements;
+ EXITS(psubInfo->macroReplacements.c_str());
+ return psubInfo->macroReplacements.c_str();
}
if (psubFile->token != tokenString) {
@@ -954,13 +906,13 @@ static char *substituteGetReplacements(subInfo *psubInfo)
if (gotFirstPattern)
catMacroReplacements(psubInfo, ",");
- gotFirstPattern = 1;
+ gotFirstPattern = true;
- if (ppatternNode) {
- catMacroReplacements(psubInfo, ppatternNode->var);
+ if (patternIt != patternList.end()) {
+ catMacroReplacements(psubInfo, patternIt->c_str());
catMacroReplacements(psubInfo, "=");
catMacroReplacements(psubInfo, psubFile->string);
- ppatternNode = (patternNode *) ellNext(&ppatternNode->node);
+ ++patternIt;
}
else {
subFileErrPrint(psubFile, "Warning, too many values given");
@@ -969,12 +921,12 @@ static char *substituteGetReplacements(subInfo *psubInfo)
while (subGetNextToken(psubFile) == tokenSeparator);
}
}
- else while(1) {
+ else while(true) {
switch(subGetNextToken(psubFile)) {
case tokenRBrace:
subGetNextToken(psubFile);
- EXITS(psubInfo->macroReplacements);
- return psubInfo->macroReplacements;
+ EXITS(psubInfo->macroReplacements.c_str());
+ return psubInfo->macroReplacements.c_str();
case tokenSeparator:
catMacroReplacements(psubInfo, ",");
@@ -1017,11 +969,12 @@ static char *subGetNextLine(subFile *psubFile)
return &psubFile->inputBuffer[0];
}
-static void subFileErrPrint(subFile *psubFile,char * message)
+static void subFileErrPrint(subFile *psubFile, const char * message)
{
fprintf(stderr, "msi: %s\n",message);
fprintf(stderr, " in substitution file '%s' at line %d:\n %s",
- psubFile->substitutionName, psubFile->lineNum, psubFile->inputBuffer);
+ psubFile->substitutionName.c_str(), psubFile->lineNum,
+ psubFile->inputBuffer);
}
@@ -1107,32 +1060,8 @@ done:
static void catMacroReplacements(subInfo *psubInfo, const char *value)
{
- size_t len = strlen(value);
-
ENTER;
- if (psubInfo->size <= (psubInfo->curLength + len)) {
- size_t newsize = psubInfo->size + MAX_BUFFER_SIZE;
- char *newbuf;
-
- STEP("Enlarging buffer");
- if (newsize <= psubInfo->curLength + len)
- newsize = psubInfo->curLength + len + 1;
- newbuf = calloc(1, newsize);
- if (!newbuf) {
- fprintf(stderr, "calloc failed for size %lu\n",
- (unsigned long) newsize);
- abortExit(1);
- }
- if (psubInfo->macroReplacements) {
- memcpy(newbuf, psubInfo->macroReplacements, psubInfo->curLength);
- free(psubInfo->macroReplacements);
- }
- psubInfo->size = newsize;
- psubInfo->macroReplacements = newbuf;
- }
-
STEPS("Appending", value);
- strcat(psubInfo->macroReplacements, value);
- psubInfo->curLength += len;
+ psubInfo->macroReplacements += value;
EXIT;
}
diff --git a/modules/database/src/std/filters/filters.dbd.pod b/modules/database/src/std/filters/filters.dbd.pod
index f1a848469..d7ab785f5 100644
--- a/modules/database/src/std/filters/filters.dbd.pod
+++ b/modules/database/src/std/filters/filters.dbd.pod
@@ -241,7 +241,7 @@ Assuming there is a system state called "blue", that is being controlled by
some other facility such as a timing system, updates could be restricted to
periods only when "blue" is true by using
- Hal$ camonitor 'test:channel' 'test:channel.{"while":"blue"}'
+ Hal$ camonitor 'test:channel' 'test:channel.{"sync":{"while":"blue"}}'
...
=cut
diff --git a/modules/database/src/std/rec/boRecord.c b/modules/database/src/std/rec/boRecord.c
index 5fb881f55..186182311 100644
--- a/modules/database/src/std/rec/boRecord.c
+++ b/modules/database/src/std/rec/boRecord.c
@@ -348,9 +348,9 @@ static long get_enum_strs(const DBADDR *paddr,struct dbr_enumStrs *pes)
/*SETTING no_str=0 breaks channel access clients*/
pes->no_str = 2;
memset(pes->strs,'\0',sizeof(pes->strs));
- strncpy(pes->strs[0],prec->znam,sizeof(prec->znam));
+ strncpy(pes->strs[0],prec->znam,sizeof(pes->strs[0]));
if(*prec->znam!=0) pes->no_str=1;
- strncpy(pes->strs[1],prec->onam,sizeof(prec->onam));
+ strncpy(pes->strs[1],prec->onam,sizeof(pes->strs[1]));
if(*prec->onam!=0) pes->no_str=2;
return(0);
}
diff --git a/modules/database/src/std/rec/mbbiDirectRecord.c b/modules/database/src/std/rec/mbbiDirectRecord.c
index afb59c83f..779b732d4 100644
--- a/modules/database/src/std/rec/mbbiDirectRecord.c
+++ b/modules/database/src/std/rec/mbbiDirectRecord.c
@@ -51,7 +51,7 @@ static long special(DBADDR *, int);
#define get_array_info NULL
#define put_array_info NULL
#define get_units NULL
-#define get_precision NULL
+static long get_precision(const DBADDR *, long *);
#define get_enum_str NULL
#define get_enum_strs NULL
#define put_enum_str NULL
@@ -204,6 +204,16 @@ static long special(DBADDR *paddr, int after)
}
}
+static long get_precision(const DBADDR *paddr,long *precision)
+{
+ mbbiDirectRecord *prec=(mbbiDirectRecord *)paddr->precord;
+ if(dbGetFieldIndex(paddr)==mbbiDirectRecordVAL)
+ *precision = prec->nobt;
+ else
+ recGblGetPrec(paddr,precision);
+ return 0;
+}
+
static void monitor(mbbiDirectRecord *prec)
{
epicsUInt16 events = recGblResetAlarms(prec);
diff --git a/modules/database/src/std/rec/mbboDirectRecord.c b/modules/database/src/std/rec/mbboDirectRecord.c
index 9260fbda1..dcc2b3c33 100644
--- a/modules/database/src/std/rec/mbboDirectRecord.c
+++ b/modules/database/src/std/rec/mbboDirectRecord.c
@@ -51,7 +51,7 @@ static long special(DBADDR *, int);
#define get_array_info NULL
#define put_array_info NULL
#define get_units NULL
-#define get_precision NULL
+static long get_precision(const DBADDR *, long *);
#define get_enum_str NULL
#define get_enum_strs NULL
#define put_enum_str NULL
@@ -313,6 +313,16 @@ static long special(DBADDR *paddr, int after)
return 0;
}
+static long get_precision(const DBADDR *paddr,long *precision)
+{
+ mbboDirectRecord *prec=(mbboDirectRecord *)paddr->precord;
+ if(dbGetFieldIndex(paddr)==mbboDirectRecordVAL)
+ *precision = prec->nobt;
+ else
+ recGblGetPrec(paddr,precision);
+ return 0;
+}
+
static void monitor(mbboDirectRecord *prec)
{
epicsUInt16 events = recGblResetAlarms(prec);
diff --git a/modules/database/src/std/rec/stateRecord.c b/modules/database/src/std/rec/stateRecord.c
index ca5df0295..fd5b9fba5 100644
--- a/modules/database/src/std/rec/stateRecord.c
+++ b/modules/database/src/std/rec/stateRecord.c
@@ -99,7 +99,7 @@ static void monitor(stateRecord *prec)
monitor_mask = recGblResetAlarms(prec);
if(strncmp(prec->oval,prec->val,sizeof(prec->val))) {
db_post_events(prec,&(prec->val[0]),monitor_mask|DBE_VALUE|DBE_LOG);
- strncpy(prec->oval,prec->val,sizeof(prec->val));
+ strncpy(prec->oval,prec->val,sizeof(prec->oval));
}
return;
}
diff --git a/modules/database/src/std/rec/stringinRecord.c b/modules/database/src/std/rec/stringinRecord.c
index ee84f0dc9..0785a819b 100644
--- a/modules/database/src/std/rec/stringinRecord.c
+++ b/modules/database/src/std/rec/stringinRecord.c
@@ -121,8 +121,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
if (status)
return status;
}
-
- strncpy(prec->oval, prec->val, sizeof(prec->val));
+ strncpy(prec->oval, prec->val, sizeof(prec->oval));
return 0;
}
@@ -183,7 +182,7 @@ static void monitor(stringinRecord *prec)
if (strncmp(prec->oval, prec->val, sizeof(prec->val))) {
monitor_mask |= DBE_VALUE | DBE_LOG;
- strncpy(prec->oval, prec->val, sizeof(prec->val));
+ strncpy(prec->oval, prec->val, sizeof(prec->oval));
}
if (prec->mpst == stringinPOST_Always)
diff --git a/modules/database/src/std/rec/stringoutRecord.c b/modules/database/src/std/rec/stringoutRecord.c
index 0c871fc4d..49f5aecc8 100644
--- a/modules/database/src/std/rec/stringoutRecord.c
+++ b/modules/database/src/std/rec/stringoutRecord.c
@@ -126,8 +126,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
if(status)
return status;
}
-
- strncpy(prec->oval, prec->val, sizeof(prec->val));
+ strncpy(prec->oval, prec->val, sizeof(prec->oval));
return 0;
}
@@ -215,7 +214,7 @@ static void monitor(stringoutRecord *prec)
if (strncmp(prec->oval, prec->val, sizeof(prec->val))) {
monitor_mask |= DBE_VALUE | DBE_LOG;
- strncpy(prec->oval, prec->val, sizeof(prec->val));
+ strncpy(prec->oval, prec->val, sizeof(prec->oval));
}
if (prec->mpst == stringoutPOST_Always)
diff --git a/modules/database/test/ioc/dbtemplate/msi.plt b/modules/database/test/ioc/dbtemplate/msi.plt
index 54cc56aa2..25d8b6ad6 100644
--- a/modules/database/test/ioc/dbtemplate/msi.plt
+++ b/modules/database/test/ioc/dbtemplate/msi.plt
@@ -11,7 +11,7 @@
use strict;
use Test;
-BEGIN {plan tests => 9}
+BEGIN {plan tests => 12}
# Check include/substitute command model
ok(msi('-I .. ../t1-template.txt'), slurp('../t1-result.txt'));
@@ -33,16 +33,9 @@ ok(msi('-S../t6-substitute.txt ../t6-template.txt'), slurp('../t6-result.txt'));
# Output option -o and verbose option -V
my $out = 't7-output.txt';
-my $count = 5; # Try up to 5 times...
-my $result;
-do {
- unlink $out;
- msi("-I.. -V -o $out ../t1-template.txt");
- $result = slurp($out);
- print "# msi output file empty, retrying\n"
- if $result eq '';
-} while ($result eq '') && (--$count > 0);
-ok($result, slurp('../t7-result.txt'));
+unlink $out;
+msi("-I.. -V -o $out ../t1-template.txt");
+ok(slurp($out), slurp('../t7-result.txt'));
# Dependency generation, include/substitute model
ok(msi('-I.. -D -o t8.txt ../t1-template.txt'), slurp('../t8-result.txt'));
@@ -50,6 +43,17 @@ ok(msi('-I.. -D -o t8.txt ../t1-template.txt'), slurp('../t8-result.txt'));
# Dependency generation, dbLoadTemplate format
ok(msi('-I.. -D -ot9.txt -S ../t2-substitution.txt'), slurp('../t9-result.txt'));
+# Substitution file, variable format, with 0 variable definitions
+ok(msi('-I. -I.. -S ../t10-substitute.txt'), slurp('../t10-result.txt'));
+
+# Substitution file, pattern format, with 0 pattern definitions
+ok(msi('-I. -I.. -S ../t11-substitute.txt'), slurp('../t11-result.txt'));
+
+# Substitution file, environment variable macros in template filename
+my %envs = (TEST_NO => 12, PREFIX => 't');
+@ENV{ keys %envs } = values %envs;
+ok(msi('-I. -I.. -S ../t12-substitute.txt'), slurp('../t12-result.txt'));
+delete @ENV{ keys %envs }; # Not really needed
# Test support routines
@@ -63,21 +67,8 @@ sub slurp {
sub msi {
my ($args) = @_;
+ my $nul = $^O eq 'MSWin32' ? 'NUL' : '/dev/null';
my $msi = '@TOP@/bin/@ARCH@/msi';
- $msi .= '.exe' if ($^O eq 'MSWin32') || ($^O eq 'cygwin');
- my $result;
- if ($args =~ m/-o / && $args !~ m/-D/) {
- # An empty result is expected
- $result = `$msi $args`;
- }
- else {
- # Try up to 5 times, sometimes msi fails on Windows
- my $count = 5;
- do {
- $result = `$msi $args`;
- print "# result of '$msi $args' empty, retrying\n"
- if $result eq '';
- } while ($result eq '') && (--$count > 0);
- }
- return $result;
+ $msi =~ tr(/)(\\) if $^O eq 'MSWin32';
+ return `$msi $args 2>$nul`;
}
diff --git a/modules/database/test/ioc/dbtemplate/t10-result.txt b/modules/database/test/ioc/dbtemplate/t10-result.txt
new file mode 100644
index 000000000..47b594ecb
--- /dev/null
+++ b/modules/database/test/ioc/dbtemplate/t10-result.txt
@@ -0,0 +1,4 @@
+# comment line
+a=$(a)
+# comment line
+a=gbl
diff --git a/modules/database/test/ioc/dbtemplate/t10-substitute.txt b/modules/database/test/ioc/dbtemplate/t10-substitute.txt
new file mode 100644
index 000000000..aec88bb6c
--- /dev/null
+++ b/modules/database/test/ioc/dbtemplate/t10-substitute.txt
@@ -0,0 +1,8 @@
+file t10-template.txt {
+ {}
+}
+
+global { a=gbl }
+file t10-template.txt {
+ {}
+}
diff --git a/modules/database/test/ioc/dbtemplate/t10-template.txt b/modules/database/test/ioc/dbtemplate/t10-template.txt
new file mode 100644
index 000000000..7958885a7
--- /dev/null
+++ b/modules/database/test/ioc/dbtemplate/t10-template.txt
@@ -0,0 +1,2 @@
+# comment line
+a=$(a)
diff --git a/modules/database/test/ioc/dbtemplate/t11-result.txt b/modules/database/test/ioc/dbtemplate/t11-result.txt
new file mode 100644
index 000000000..47b594ecb
--- /dev/null
+++ b/modules/database/test/ioc/dbtemplate/t11-result.txt
@@ -0,0 +1,4 @@
+# comment line
+a=$(a)
+# comment line
+a=gbl
diff --git a/modules/database/test/ioc/dbtemplate/t11-substitute.txt b/modules/database/test/ioc/dbtemplate/t11-substitute.txt
new file mode 100644
index 000000000..94dcdbc12
--- /dev/null
+++ b/modules/database/test/ioc/dbtemplate/t11-substitute.txt
@@ -0,0 +1,10 @@
+file t11-template.txt {
+ pattern {}
+ {}
+}
+
+global { a=gbl }
+file t11-template.txt {
+ pattern {}
+ {}
+}
diff --git a/modules/database/test/ioc/dbtemplate/t11-template.txt b/modules/database/test/ioc/dbtemplate/t11-template.txt
new file mode 100644
index 000000000..7958885a7
--- /dev/null
+++ b/modules/database/test/ioc/dbtemplate/t11-template.txt
@@ -0,0 +1,2 @@
+# comment line
+a=$(a)
diff --git a/modules/database/test/ioc/dbtemplate/t12-result.txt b/modules/database/test/ioc/dbtemplate/t12-result.txt
new file mode 100644
index 000000000..6cfcc57da
--- /dev/null
+++ b/modules/database/test/ioc/dbtemplate/t12-result.txt
@@ -0,0 +1,2 @@
+# comment line
+a=foo
diff --git a/modules/database/test/ioc/dbtemplate/t12-substitute.txt b/modules/database/test/ioc/dbtemplate/t12-substitute.txt
new file mode 100644
index 000000000..7a26b6fea
--- /dev/null
+++ b/modules/database/test/ioc/dbtemplate/t12-substitute.txt
@@ -0,0 +1,3 @@
+file $(PREFIX)$(TEST_NO)-template.txt {
+ { a=foo }
+}
diff --git a/modules/database/test/ioc/dbtemplate/t12-template.txt b/modules/database/test/ioc/dbtemplate/t12-template.txt
new file mode 100644
index 000000000..7958885a7
--- /dev/null
+++ b/modules/database/test/ioc/dbtemplate/t12-template.txt
@@ -0,0 +1,2 @@
+# comment line
+a=$(a)
diff --git a/modules/libcom/configure/CONFIG_LIBCOM_VERSION b/modules/libcom/configure/CONFIG_LIBCOM_VERSION
index 5a21198ff..f5d91037c 100644
--- a/modules/libcom/configure/CONFIG_LIBCOM_VERSION
+++ b/modules/libcom/configure/CONFIG_LIBCOM_VERSION
@@ -1,4 +1,4 @@
EPICS_LIBCOM_MAJOR_VERSION = 3
EPICS_LIBCOM_MINOR_VERSION = 17
-EPICS_LIBCOM_MAINTENANCE_VERSION = 3
+EPICS_LIBCOM_MAINTENANCE_VERSION = 5
EPICS_LIBCOM_DEVELOPMENT_FLAG = 0
diff --git a/modules/libcom/src/log/iocLogServer.c b/modules/libcom/src/log/iocLogServer.c
index f42aa4563..e707185ae 100644
--- a/modules/libcom/src/log/iocLogServer.c
+++ b/modules/libcom/src/log/iocLogServer.c
@@ -37,7 +37,7 @@
static unsigned short ioc_log_port;
static long ioc_log_file_limit;
-static char ioc_log_file_name[256];
+static char ioc_log_file_name[512];
static char ioc_log_file_command[256];
@@ -866,7 +866,12 @@ static int setupSIGHUP(struct ioc_log_server *pserver)
*/
static void sighupHandler(int signo)
{
- (void) write(sighupPipe[1], "SIGHUP\n", 7);
+ const char msg[] = "SIGHUP\n";
+ const ssize_t bytesWritten = write(sighupPipe[1], msg, sizeof(msg));
+ if (bytesWritten != sizeof(msg)) {
+ fprintf(stderr, "iocLogServer: failed to write to SIGHUP pipe because "
+ "`%s'\n", strerror(errno));
+ }
}
@@ -884,7 +889,10 @@ static void serviceSighupRequest(void *pParam)
/*
* Read and discard message from pipe.
*/
- (void) read(sighupPipe[0], buff, sizeof buff);
+ if (read(sighupPipe[0], buff, sizeof buff) <= 0) {
+ fprintf(stderr, "iocLogServer: failed to read from SIGHUP pipe because "
+ "`%s'\n", strerror(errno));
+ };
/*
* Determine new log file name.
diff --git a/modules/libcom/src/misc/ipAddrToAsciiAsynchronous.cpp b/modules/libcom/src/misc/ipAddrToAsciiAsynchronous.cpp
index 4301612c9..ec2c16ded 100644
--- a/modules/libcom/src/misc/ipAddrToAsciiAsynchronous.cpp
+++ b/modules/libcom/src/misc/ipAddrToAsciiAsynchronous.cpp
@@ -171,6 +171,7 @@ ipAddrToAsciiEngine & ipAddrToAsciiEngine::allocate ()
}
ipAddrToAsciiGlobal::ipAddrToAsciiGlobal () :
+ mutex(__FILE__, __LINE__),
thread ( *this, "ipToAsciiProxy",
epicsThreadGetStackSize(epicsThreadStackBig),
epicsThreadPriorityLow ),
diff --git a/modules/libcom/src/osi/epicsGeneralTime.c b/modules/libcom/src/osi/epicsGeneralTime.c
index d08e9c6a9..e5dd42201 100644
--- a/modules/libcom/src/osi/epicsGeneralTime.c
+++ b/modules/libcom/src/osi/epicsGeneralTime.c
@@ -78,6 +78,11 @@ static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
static const char * const tsfmt = "%Y-%m-%d %H:%M:%S.%09f";
+/* defined in osiClockTime.c or osdTime.cpp */
+int osdTimeGetCurrent ( epicsTimeStamp *pDest );
+/* cleared if/when gtPvt.timeProviders contains more than the default osdTimeGetCurrent() */
+static int useOsdGetCurrent = 1;
+
/* Implementation */
static void generalTime_InitOnce(void *dummy)
@@ -103,6 +108,9 @@ int generalTimeGetExceptPriority(epicsTimeStamp *pDest, int *pPrio, int ignore)
gtProvider *ptp;
int status = S_time_noProvider;
+ if(useOsdGetCurrent)
+ return osdTimeGetCurrent(pDest);
+
generalTime_Init();
IFDEBUG(2)
@@ -148,6 +156,9 @@ int epicsShareAPI epicsTimeGetCurrent(epicsTimeStamp *pDest)
int status = S_time_noProvider;
epicsTimeStamp ts;
+ if(useOsdGetCurrent)
+ return osdTimeGetCurrent(pDest);
+
generalTime_Init();
IFDEBUG(20)
@@ -370,6 +381,11 @@ static void insertProvider(gtProvider *ptp, ELLLIST *plist, epicsMutexId lock)
ellAdd(plist, &ptp->node);
}
+ /* Check to see if we have more than just the OS default time source */
+ if(plist==>Pvt.timeProviders && (ellCount(plist)!=1 || ptp->get.Time!=&osdTimeGetCurrent)) {
+ useOsdGetCurrent = 0;
+ }
+
epicsMutexUnlock(lock);
}
diff --git a/modules/libcom/src/osi/epicsTime.cpp b/modules/libcom/src/osi/epicsTime.cpp
index 6dd1d3f97..6dd3efd4c 100644
--- a/modules/libcom/src/osi/epicsTime.cpp
+++ b/modules/libcom/src/osi/epicsTime.cpp
@@ -205,9 +205,6 @@ epicsTime::epicsTime (const epicsTimeStamp &ts)
epicsTime::epicsTime () :
secPastEpoch(0u), nSec(0u) {}
-epicsTime::epicsTime (const epicsTime &t) :
- secPastEpoch (t.secPastEpoch), nSec (t.nSec) {}
-
epicsTime epicsTime::getCurrent ()
{
epicsTimeStamp current;
diff --git a/modules/libcom/src/osi/epicsTime.h b/modules/libcom/src/osi/epicsTime.h
index 862bc22d2..bc86b737f 100644
--- a/modules/libcom/src/osi/epicsTime.h
+++ b/modules/libcom/src/osi/epicsTime.h
@@ -82,7 +82,6 @@ public:
class formatProblemWithStructTM {};
epicsTime ();
- epicsTime ( const epicsTime & t );
static epicsTime getEvent ( const epicsTimeEvent & );
static epicsTime getCurrent ();
diff --git a/modules/libcom/src/osi/os/Darwin/osdTime.cpp b/modules/libcom/src/osi/os/Darwin/osdTime.cpp
index 315caeb9b..09471a8e8 100644
--- a/modules/libcom/src/osi/os/Darwin/osdTime.cpp
+++ b/modules/libcom/src/osi/os/Darwin/osdTime.cpp
@@ -26,7 +26,7 @@
static clock_serv_t host_clock;
extern "C" {
-static int osdTimeGetCurrent (epicsTimeStamp *pDest)
+int osdTimeGetCurrent (epicsTimeStamp *pDest)
{
mach_timespec_t mts;
struct timespec ts;
diff --git a/modules/libcom/src/osi/os/WIN32/osdTime.cpp b/modules/libcom/src/osi/os/WIN32/osdTime.cpp
index 348fe7c2f..1b1a70548 100644
--- a/modules/libcom/src/osi/os/WIN32/osdTime.cpp
+++ b/modules/libcom/src/osi/os/WIN32/osdTime.cpp
@@ -47,7 +47,8 @@
extern "C" void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName );
-static int osdTimeGetCurrent ( epicsTimeStamp *pDest );
+extern "C"
+int osdTimeGetCurrent ( epicsTimeStamp *pDest );
// for mingw
#if !defined ( MAXLONGLONG )
@@ -116,7 +117,7 @@ static int done = timeRegister();
//
// osdTimeGetCurrent ()
//
-static int osdTimeGetCurrent ( epicsTimeStamp *pDest )
+int osdTimeGetCurrent ( epicsTimeStamp *pDest )
{
assert ( pCurrentTime );
diff --git a/modules/libcom/src/osi/os/posix/osdExecinfoBackTrace.cpp b/modules/libcom/src/osi/os/posix/osdExecinfoBackTrace.cpp
index ae13d28fc..8ed1bb283 100644
--- a/modules/libcom/src/osi/os/posix/osdExecinfoBackTrace.cpp
+++ b/modules/libcom/src/osi/os/posix/osdExecinfoBackTrace.cpp
@@ -7,12 +7,30 @@
* Author: Till Straumann , 2011, 2014
*/
+// pull in libc feature test macros
+#include
+
+// execinfo.h may not be present if uclibc is configured to omit backtrace()
+#if !defined(__UCLIBC_MAJOR__) || defined(__UCLIBC_HAS_EXECINFO__)
+# define HAS_EXECINFO 1
+#else
+# define HAS_EXECINFO 0
+#endif
+
+#if HAS_EXECINFO
+
#include
+#endif
+
#define epicsExportSharedSymbols
#include "epicsStackTracePvt.h"
int epicsBackTrace(void **buf, int buf_sz)
{
+#if HAS_EXECINFO
return backtrace(buf, buf_sz);
+#else
+ return -1;
+#endif
}
diff --git a/modules/libcom/src/osi/os/posix/osdMutex.c b/modules/libcom/src/osi/os/posix/osdMutex.c
index f569916a5..d1411215c 100644
--- a/modules/libcom/src/osi/os/posix/osdMutex.c
+++ b/modules/libcom/src/osi/os/posix/osdMutex.c
@@ -153,6 +153,10 @@ epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * pmutex)
void epicsMutexOsdShow(struct epicsMutexOSD * pmutex, unsigned int level)
{
+ /* GLIBC w/ NTPL is passing the &lock.__data.__lock as the first argument (UADDR)
+ * of the futex() syscall. __lock is at offset 0 of the enclosing structures.
+ */
+ printf(" pthread_mutex_t* uaddr=%p\n", &pmutex->lock);
}
#else /*defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 */
diff --git a/modules/libcom/src/osi/os/posix/osdTime.cpp b/modules/libcom/src/osi/os/posix/osdTime.cpp
index 1952babd9..cc3cb1965 100644
--- a/modules/libcom/src/osi/os/posix/osdTime.cpp
+++ b/modules/libcom/src/osi/os/posix/osdTime.cpp
@@ -32,7 +32,7 @@
LAST_RESORT_PRIORITY, osdTimeGetCurrent)
extern "C" {
- static int osdTimeGetCurrent (epicsTimeStamp *pDest)
+ int osdTimeGetCurrent (epicsTimeStamp *pDest)
{
struct timeval tv;
struct timezone tz;
diff --git a/modules/libcom/src/osi/os/vxWorks/osdSock.h b/modules/libcom/src/osi/os/vxWorks/osdSock.h
index 272371523..4e5e94ea4 100644
--- a/modules/libcom/src/osi/os/vxWorks/osdSock.h
+++ b/modules/libcom/src/osi/os/vxWorks/osdSock.h
@@ -65,7 +65,7 @@ typedef int SOCKET;
#define socket_ioctl(A,B,C) ioctl(A,B,(int)C)
typedef int osiSockIoctl_t;
typedef int osiSocklen_t;
-typedef int osiSockOptMcastLoop_t;
+typedef char osiSockOptMcastLoop_t;
typedef char osiSockOptMcastTTL_t;
#define FD_IN_FDSET(FD) ((FD)=0)
diff --git a/modules/libcom/src/osi/osiClockTime.c b/modules/libcom/src/osi/osiClockTime.c
index b0f4c35eb..091a95715 100644
--- a/modules/libcom/src/osi/osiClockTime.c
+++ b/modules/libcom/src/osi/osiClockTime.c
@@ -40,7 +40,7 @@ static struct {
static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
-#if defined(CLOCK_REALTIME) && !defined(_WIN32)
+#if defined(CLOCK_REALTIME) && !defined(_WIN32) && !defined(__APPLE__)
/* This code is not used on systems without Posix CLOCK_REALTIME,
* but the only way to detect that is from the OS headers, so the
* Makefile can't exclude compiling this file on those systems.
@@ -48,7 +48,7 @@ static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
/* Forward references */
-static int ClockTimeGetCurrent(epicsTimeStamp *pDest);
+int osdTimeGetCurrent(epicsTimeStamp *pDest);
#if defined(vxWorks) || defined(__rtems__)
static void ClockTimeSync(void *dummy);
@@ -89,7 +89,7 @@ static void ClockTime_InitOnce(void *pfirst)
/* Register as a time provider */
generalTimeRegisterCurrentProvider("OS Clock", LAST_RESORT_PRIORITY,
- ClockTimeGetCurrent);
+ osdTimeGetCurrent);
}
void ClockTime_Init(int synchronize)
@@ -125,7 +125,7 @@ void ClockTime_Init(int synchronize)
else {
/* No synchronization thread */
if (firstTime)
- ClockTimeGetCurrent(&ClockTimePvt.startTime);
+ osdTimeGetCurrent(&ClockTimePvt.startTime);
}
}
}
@@ -191,7 +191,7 @@ static void ClockTimeSync(void *dummy)
/* Time Provider Routine */
-static int ClockTimeGetCurrent(epicsTimeStamp *pDest)
+int osdTimeGetCurrent(epicsTimeStamp *pDest)
{
struct timespec clockNow;
diff --git a/modules/libcom/src/timer/timerPrivate.h b/modules/libcom/src/timer/timerPrivate.h
index 55291309c..259afaee6 100644
--- a/modules/libcom/src/timer/timerPrivate.h
+++ b/modules/libcom/src/timer/timerPrivate.h
@@ -147,7 +147,7 @@ private:
epicsThread thread;
const double sleepQuantum;
bool okToShare;
- bool exitFlag;
+ int exitFlag; // use atomic ops
bool terminateFlag;
void run ();
void reschedule ();
diff --git a/modules/libcom/src/timer/timerQueue.cpp b/modules/libcom/src/timer/timerQueue.cpp
index 5a798d4d4..8f7f98e12 100644
--- a/modules/libcom/src/timer/timerQueue.cpp
+++ b/modules/libcom/src/timer/timerQueue.cpp
@@ -25,6 +25,7 @@ const double timerQueue :: exceptMsgMinPeriod = 60.0 * 5.0; // seconds
epicsTimerQueue::~epicsTimerQueue () {}
timerQueue::timerQueue ( epicsTimerQueueNotify & notifyIn ) :
+ mutex(__FILE__, __LINE__),
notify ( notifyIn ),
pExpireTmr ( 0 ),
processThread ( 0 ),
diff --git a/modules/libcom/src/timer/timerQueueActive.cpp b/modules/libcom/src/timer/timerQueueActive.cpp
index 5d8b72951..4db68c00c 100644
--- a/modules/libcom/src/timer/timerQueueActive.cpp
+++ b/modules/libcom/src/timer/timerQueueActive.cpp
@@ -15,6 +15,7 @@
#include
#define epicsExportSharedSymbols
+#include "epicsAtomic.h"
#include "timerPrivate.h"
#include "errlog.h"
@@ -46,7 +47,7 @@ timerQueueActive ::
_refMgr ( refMgr ), queue ( *this ), thread ( *this, "timerQueue",
epicsThreadGetStackSize ( epicsThreadStackMedium ), priority ),
sleepQuantum ( epicsThreadSleepQuantum() ), okToShare ( okToShareIn ),
- exitFlag ( false ), terminateFlag ( false )
+ exitFlag ( 0 ), terminateFlag ( false )
{
}
@@ -59,7 +60,7 @@ timerQueueActive::~timerQueueActive ()
{
this->terminateFlag = true;
this->rescheduleEvent.signal ();
- while ( ! this->exitFlag ) {
+ while ( ! epics::atomic::get(this->exitFlag) ) {
this->exitEvent.wait ( 1.0 );
}
// in case other threads are waiting here also
@@ -87,7 +88,7 @@ void timerQueueActive :: _printLastChanceExceptionMessage (
void timerQueueActive :: run ()
{
- this->exitFlag = false;
+ epics::atomic::set(this->exitFlag, 0);
while ( ! this->terminateFlag ) {
try {
double delay = this->queue.process ( epicsTime::getCurrent() );
@@ -105,7 +106,7 @@ void timerQueueActive :: run ()
epicsThreadSleep ( 10.0 );
}
}
- this->exitFlag = true;
+ epics::atomic::set(this->exitFlag, 1);
this->exitEvent.signal (); // no access to queue after exitEvent signal
}
@@ -143,7 +144,7 @@ void timerQueueActive::show ( unsigned int level ) const
printf ( "exit event\n" );
this->exitEvent.show ( level - 1u );
printf ( "exitFlag = %c, terminateFlag = %c\n",
- this->exitFlag ? 'T' : 'F',
+ epics::atomic::get(this->exitFlag) ? 'T' : 'F',
this->terminateFlag ? 'T' : 'F' );
}
}
diff --git a/modules/libcom/src/timer/timerQueueActiveMgr.cpp b/modules/libcom/src/timer/timerQueueActiveMgr.cpp
index d6349a84c..eff2e0c53 100644
--- a/modules/libcom/src/timer/timerQueueActiveMgr.cpp
+++ b/modules/libcom/src/timer/timerQueueActiveMgr.cpp
@@ -20,6 +20,7 @@
#include "timerPrivate.h"
timerQueueActiveMgr::timerQueueActiveMgr ()
+ :mutex(__FILE__, __LINE__)
{
}
diff --git a/modules/libcom/test/Makefile b/modules/libcom/test/Makefile
index f2e495a90..c8680e3e6 100755
--- a/modules/libcom/test/Makefile
+++ b/modules/libcom/test/Makefile
@@ -255,8 +255,8 @@ TESTSPEC_RTEMS = libComTestHarness.boot; epicsRunLibComTests
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
-TESTPROD_RTEMS = $(TESTPROD_HOST)
-TESTSCRIPTS_RTEMS += $(filter-out epicsUnitTestTest.t, $(TESTS:%=%.t))
+TESTPROD = $(TESTPROD_HOST)
+TESTSCRIPTS += $(filter-out epicsUnitTestTest.t, $(TESTS:%=%.t))
endif
diff --git a/modules/libcom/test/epicsErrlogTest.c b/modules/libcom/test/epicsErrlogTest.c
index 28d88f6af..2f1c42740 100644
--- a/modules/libcom/test/epicsErrlogTest.c
+++ b/modules/libcom/test/epicsErrlogTest.c
@@ -78,6 +78,7 @@ typedef struct {
size_t checkLen;
epicsEventId jammer;
int jam;
+ epicsEventId done;
} clientPvt;
static void testLogPrefix(void);
@@ -102,12 +103,13 @@ static const char prefixexpectedmsg[] = "A message without prefix"
static char prefixmsgbuffer[1024];
-static
-void testEqInt_(int lhs, int rhs, const char *LHS, const char *RHS)
+static void
+testEqInt_(int line, int lhs, int rhs, const char *LHS, const char *RHS)
{
- testOk(lhs==rhs, "%s (%d) == %s (%d)", LHS, lhs, RHS, rhs);
+ testOk(lhs==rhs, "%d: %s (%d) == %s (%d)", line, LHS, lhs, RHS, rhs);
}
-#define testEqInt(L, R) testEqInt_(L, R, #L, #R);
+#define testEqInt(L, R) testEqInt_(__LINE__, L, R, #L, #R);
+
static
void logClient(void* raw, const char* msg)
{
@@ -161,6 +163,7 @@ void logClient(void* raw, const char* msg)
}
pvt->count++;
+ epicsEventSignal(pvt->done);
}
MAIN(epicsErrlogTest)
@@ -169,7 +172,7 @@ MAIN(epicsErrlogTest)
char msg[256];
clientPvt pvt, pvt2;
- testPlan(32);
+ testPlan(40);
strcpy(msg, truncmsg);
@@ -188,7 +191,9 @@ MAIN(epicsErrlogTest)
pvt2.jam = 0;
pvt.jammer = epicsEventMustCreate(epicsEventEmpty);
+ pvt.done = epicsEventMustCreate(epicsEventEmpty);
pvt2.jammer = epicsEventMustCreate(epicsEventEmpty);
+ pvt2.done = epicsEventMustCreate(epicsEventEmpty);
testDiag("Check listener registration");
@@ -200,6 +205,7 @@ MAIN(epicsErrlogTest)
errlogPrintfNoConsole("%s", pvt.expect);
errlogFlush();
+ epicsEventMustWait(pvt.done);
testEqInt(pvt.count, 1);
errlogAddListener(&logClient, &pvt2);
@@ -210,7 +216,10 @@ MAIN(epicsErrlogTest)
errlogPrintfNoConsole("%s", pvt.expect);
errlogFlush();
+ epicsEventMustWait(pvt.done);
testEqInt(pvt.count, 2);
+
+ epicsEventMustWait(pvt2.done);
testEqInt(pvt2.count, 1);
/* Removes the first listener */
@@ -223,6 +232,10 @@ MAIN(epicsErrlogTest)
errlogPrintfNoConsole("%s", pvt2.expect);
errlogFlush();
+ testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
+ "%d: Listener 1 didn't run", __LINE__);
+ testOk(epicsEventTryWait(pvt2.done) == epicsEventOK,
+ "%d: Listener 2 ran", __LINE__);
testEqInt(pvt.count, 2);
testEqInt(pvt2.count, 2);
@@ -234,6 +247,10 @@ MAIN(epicsErrlogTest)
errlogPrintfNoConsole("Something different");
errlogFlush();
+ testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
+ "%d: Listener 1 didn't run", __LINE__);
+ testOk(epicsEventTryWait(pvt2.done) == epicsEventWaitTimeout,
+ "%d: Listener 2 didn't run", __LINE__);
testEqInt(pvt.count, 2);
testEqInt(pvt2.count, 2);
@@ -248,6 +265,7 @@ MAIN(epicsErrlogTest)
errlogPrintfNoConsole("%s", longmsg);
errlogFlush();
+ epicsEventMustWait(pvt.done);
testEqInt(pvt.count, 3);
pvt.expect = NULL;
@@ -259,13 +277,15 @@ MAIN(epicsErrlogTest)
pvt.jam = 1;
errlogPrintfNoConsole("%s", longmsg);
- epicsThreadSleep(0.1);
+ testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
+ "%d: Listener 1 didn't run", __LINE__);
testEqInt(pvt.count, 3);
epicsEventSignal(pvt.jammer);
errlogFlush();
+ epicsEventMustWait(pvt.done);
testEqInt(pvt.count, 4);
testDiag("Find buffer capacity (%u theoretical)",LOGBUFSIZE);
@@ -303,6 +323,9 @@ MAIN(epicsErrlogTest)
errlogFlush();
}
+ testOk(epicsEventTryWait(pvt.done) == epicsEventOK,
+ "%d: Listener 1 ran", __LINE__);
+
testDiag("Checking buffer use after partial flush");
/* Use the numbers from the largest block size above */
@@ -317,16 +340,18 @@ MAIN(epicsErrlogTest)
for (i = 0; i < N; i++) {
errlogPrintfNoConsole("%s", msg);
}
- epicsThreadSleep(0.1); /* should really be a second Event */
+ testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
+ "%d: Listener 1 didn't run", __LINE__);
testEqInt(pvt.count, 0);
/* Extract the first 2 messages, 2*(sizeof(msgNode) + 128) bytes */
pvt.jam = -2;
epicsEventSignal(pvt.jammer);
- epicsThreadSleep(0.1);
+ epicsThreadSleep(0.5);
testDiag("Drained %u messages", pvt.count);
+ epicsEventMustWait(pvt.done);
testEqInt(pvt.count, 2);
/* The buffer has space for 1 more message: sizeof(msgNode) + 256 bytes */
@@ -335,12 +360,15 @@ MAIN(epicsErrlogTest)
testDiag("Overflow the buffer");
errlogPrintfNoConsole("%s", msg);
+ testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
+ "%d: Listener 1 didn't run", __LINE__);
testEqInt(pvt.count, 2);
epicsEventSignal(pvt.jammer); /* Empty */
errlogFlush();
testDiag("Logged %u messages", pvt.count);
+ epicsEventMustWait(pvt.done);
testEqInt(pvt.count, N+1);
/* Clean up */
diff --git a/modules/libcom/test/epicsStdlibTest.c b/modules/libcom/test/epicsStdlibTest.c
index d82f834b8..e460e67d4 100644
--- a/modules/libcom/test/epicsStdlibTest.c
+++ b/modules/libcom/test/epicsStdlibTest.c
@@ -381,8 +381,14 @@ MAIN(epicsStdlibTest)
testOk(epicsParseFloat("1e-40", &f, NULL) == S_stdlib_underflow,
"Float '1e-40' => underflow");
+#ifdef vxWorks
+ testTodoBegin("Not detected on VxWorks");
+#endif
testOk(epicsParseDouble("1e-330", &d, NULL) == S_stdlib_underflow,
"Double '1e-330' => underflow");
+#ifdef vxWorks
+ testTodoEnd();
+#endif
testOk(epicsScanFloat("1e30", &f) && fabs(f - 1e30) < 1e24,
"Float '1e30'");
diff --git a/modules/libcom/test/epicsTimerTest.cpp b/modules/libcom/test/epicsTimerTest.cpp
index f2a510819..aebf3f066 100644
--- a/modules/libcom/test/epicsTimerTest.cpp
+++ b/modules/libcom/test/epicsTimerTest.cpp
@@ -108,11 +108,11 @@ inline double delayVerify::delay () const
double delayVerify::checkError () const
{
- const double messageThresh = 2.0; // percent
+ const double messageThresh = 5.0; // percent
double actualDelay = this->expireStamp - this->beginStamp;
double measuredError = actualDelay - this->expectedDelay;
double percentError = 100.0 * fabs ( measuredError ) / this->expectedDelay;
- if ( ! testOk1 ( percentError < messageThresh ) ) {
+ if ( ! testOk ( percentError < messageThresh, "%f < %f", percentError, messageThresh ) ) {
testDiag ( "delay = %f s, error = %f s (%.1f %%)",
this->expectedDelay, measuredError, percentError );
}
diff --git a/modules/normativeTypes b/modules/normativeTypes
index e803240fb..c6168a747 160000
--- a/modules/normativeTypes
+++ b/modules/normativeTypes
@@ -1 +1 @@
-Subproject commit e803240fbfbb06d96d94fc70e91b4cf832811b15
+Subproject commit c6168a74772a93f18facc83631c13db381ff15bb
diff --git a/modules/pvAccess b/modules/pvAccess
index 78410499f..936f5d35d 160000
--- a/modules/pvAccess
+++ b/modules/pvAccess
@@ -1 +1 @@
-Subproject commit 78410499f0236f5830a0937567bbb794de7b2347
+Subproject commit 936f5d35d845a3c47a55176876a0f607a582cf09
diff --git a/modules/pvData b/modules/pvData
index 8c275cbc1..6ceaa6adb 160000
--- a/modules/pvData
+++ b/modules/pvData
@@ -1 +1 @@
-Subproject commit 8c275cbc1caca1f6175b75b6f0ffc2d4abc234db
+Subproject commit 6ceaa6adb0b39dc3e37f064ca7e7bb5eea459d96
diff --git a/modules/pvDatabase b/modules/pvDatabase
index b3f96f730..70ee85778 160000
--- a/modules/pvDatabase
+++ b/modules/pvDatabase
@@ -1 +1 @@
-Subproject commit b3f96f730fcf1b07a24125fd06b8c413a1e93b5d
+Subproject commit 70ee85778268bb47397d4cf8aa07e019ccdd8392
diff --git a/modules/pva2pva b/modules/pva2pva
index 521154fd5..ce39c9320 160000
--- a/modules/pva2pva
+++ b/modules/pva2pva
@@ -1 +1 @@
-Subproject commit 521154fd52d149ea578a73cabc1f7e4c784cf14f
+Subproject commit ce39c932013e3af1d4750394ee69ba20bd13fa74
diff --git a/modules/pvaClient b/modules/pvaClient
index b1c101578..aba40922e 160000
--- a/modules/pvaClient
+++ b/modules/pvaClient
@@ -1 +1 @@
-Subproject commit b1c101578bea3610f777a1f51229eb2967dc89f4
+Subproject commit aba40922e6a96519c214c7644bfdf49a8bd0d7f6