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
+
@@ -124,80 +126,81 @@ release manager.

- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + - + + + + - - - - - - - - - - - - - - + - + - + - + - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  Website ManagerCreate a release milestone on Launchpad if necessary and set the - expected release date. Note that pre-release and release-candidate - versions do not appear on Launchpad, only the final release.Create a release milestone on Launchpad. If a target release date is + known set "Date Targeted" to the expected release date. Note that + pre-release and release-candidate versions should not get Launchpad + milestones, only the final release.
Creating pre-release and release-candidate versions
  Release Manager Edit and commit changes to the EPICS version number file configure/CONFIG_BASE_VERSION.
  Release Manager Tag the module in Git, using these tag conventions:
  • - R7.0.2-pren + R7.0.3-pren — pre-release tag
  • - R7.0.2-rcn + R7.0.3-rcn — release candidate tag
cd base-7.0
- git tag -m 'ANJ: Tagged for 7.0.2-rc1' R7.0.2-rc1 + git tag -m 'ANJ: Tagged for 7.0.3-rc1' R7.0.3-rc1
Note that submodules must not be tagged with the version used for the top-level, they each have their own separate version numbers that are only tagged at the final release.
  Release Manager Export the tagged version into a tarfile. The make-tar.sh script generates a gzipped tarfile directly from the tag, excluding the files and directories that are only used for continuous integration:
cd base-7.0
- ./.tools/make-tar.sh R7.0.2-rc1 base-7.0.2-rc1.tar.gz base-7.0.2-rc1/ + ./.tools/make-tar.sh R7.0.3-rc1 base-7.0.3-rc1.tar.gz base-7.0.3-rc1/
Create a GPG signature file of the tarfile as follows:
- gpg --armor --sign --detach-sig base-7.0.2-rc1.tar.gz + gpg --armor --sign --detach-sig base-7.0.3-rc1.tar.gz
  Release Manager Test the tarfile by extracting its contents and building it on at least one supported platform.
  Website Manager Copy the tarfile and its signature to the Base download area of the website and add the new files to the website Base download index page.
  Website Manager Create or update a website subdirectory to hold the release documentation, and copy in selected files from the base/documentation and base/html directories of the tarfile.
  Website Manager Create or modify the webpage for the new release with links to the release documents and tar file. Pre-release and release-candidate @@ -257,10 +260,11 @@ release manager.

Release Approval + Release Approval
  Release Manager Obtain a positive Ok to release from all platform developers once a release candidate version has gone for 2 weeks without any major @@ -270,7 +274,7 @@ release manager.

Creating the final release version
  Release Manager

For each external submodule, check if the module's release version @@ -282,7 +286,7 @@ release manager.

commit.

  Release Manager Edit and commit changes to the EPICS Base version number file and the embedded module version files: @@ -294,33 +298,33 @@ release manager.

  Release Manager Tag the epics-base module in Git:
cd base-7.0
- git tag -m 'ANJ: Tagged for 7.0.2' R7.0.2 + git tag -m 'ANJ: Tagged for 7.0.3' R7.0.3
Don't push the new tag to the Launchpad repository yet.
  Release Manager Export the tagged version into a tarfile. The make-tar.sh script generates a gzipped tarfile directly from the tag, excluding the files and directories that are only used for continuous integration:
cd base-7.0
- ./.tools/make-tar.sh R7.0.2 base-7.0.2.tar.gz base-7.0.2/ + ./.tools/make-tar.sh R7.0.3 base-7.0.3.tar.gz base-7.0.3/
Create a GPG signature file of the tarfile as follows:
- gpg --armor --sign --detach-sig base-7.0.2.tar.gz + gpg --armor --sign --detach-sig base-7.0.3.tar.gz
  Release Manager Test the tar file by extracting its contents and building it on at least one supported platform. When this succeeds the new git tag can be @@ -331,7 +335,7 @@ release manager.

  Release Manager Edit and commit changes to the EPICS Base version number file and the embedded module version files: @@ -345,64 +349,95 @@ release manager.

with a "-DEV" tag added (where applicable).
 Publish to epics.anl.gov
Release Manager Copy the tarfile and its signature to the Base download area of the website.
 Release ManagerFind all Launchpad bug reports with the status Fix Committed which - have been fixed in this release and mark them Fix Released.
Publish and Announce it
 Website ManagerUpload the tar file and its .asc signature file to the - Launchpad milestone for this release version.
  Website Manager Update the website subdirectory that holds the release documentation, and copy in the files from the base/documentation directory of the tarfile.
  Website Manager Update the webpage for the new release with links to the release documents and tar file.
  Website Manager Add the new release tar file to the website Base download index page.
  Website Manager Link to the release webpage from other relevent areas of the website - update front page and sidebars.
  Website Manager Add an entry to the website News page, linking to the new version webpage.
 Publish to epics-controls
Website ManagerUpload the tar file and its .asc signature file to the + epics-controls web-server [ToDo: ssh-key, location]
Website ManagerFollow instructions on + + Add a page for a new release to create a new release webpage (not + required for a patch release though, just edit the existing page).
Publish to Launchpad
Website ManagerGo to the Launchpad milestone for this release. Click the Create + release button and add the release date. Put a URL for the release page + in the Release notes box, and click the Create release button. Upload + the tar file and its .asc signature file to the new Launchpad + release page.
Release ManagerFind all Launchpad bug reports with the status Fix Committed which + have been fixed in this release and mark them Fix Released.
Make Announcement
Release Manager Announce the release on the tech-talk mailing list.
+
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