From 131578124b2e5baf7cb1d6b8fd3e2a7b75001fe7 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 21 Feb 2025 09:58:43 +0100 Subject: [PATCH 01/19] ci: drop AppVeyor builds on VS2010/VS2012 --- .appveyor.yml | 7 ------- .appveyor/epics-base-7.yml | 7 ------- 2 files changed, 14 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index cf6534906..7632b32c4 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -52,8 +52,6 @@ environment: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - CMP: vs2015 - CMP: vs2013 - - CMP: vs2012 - - CMP: vs2010 - CMP: gcc APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 # TODO: static linking w/ readline isn't working. Bypass auto-detect @@ -67,11 +65,6 @@ platform: # Matrix configuration: exclude sets of jobs matrix: exclude: - # VS2012 and older installs don't have the 64 bit compiler - - platform: x64 - CMP: vs2012 - - platform: x64 - CMP: vs2010 # Exclude more jobs to reduce build time # Skip 32-bit for "middle-aged" compilers - platform: x86 diff --git a/.appveyor/epics-base-7.yml b/.appveyor/epics-base-7.yml index 218577c28..8616544e9 100644 --- a/.appveyor/epics-base-7.yml +++ b/.appveyor/epics-base-7.yml @@ -59,8 +59,6 @@ environment: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - CMP: vs2015 - CMP: vs2013 - - CMP: vs2012 - - CMP: vs2010 - CMP: gcc APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 # TODO: static linking w/ readline isn't working. Bypass auto-detect @@ -74,11 +72,6 @@ platform: # Matrix configuration: exclude sets of jobs matrix: exclude: - # VS2012 and older installs don't have the 64 bit compiler - - platform: x64 - CMP: vs2012 - - platform: x64 - CMP: vs2010 # Exclude more jobs to reduce build time # Skip 32-bit for "middle-aged" compilers - platform: x86 From 43e75e3901d82f2c985ecff364dec70b6cee6af0 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 27 Aug 2024 11:52:25 +0200 Subject: [PATCH 02/19] decorate functions that do not return --- modules/ca/src/client/acctst.c | 4 +--- modules/ca/src/client/caDiagnostics.h | 3 ++- modules/libcom/src/flex/flexdef.h | 6 +++--- modules/libcom/src/misc/cantProceed.h | 3 ++- modules/libcom/src/misc/epicsExit.h | 4 +++- modules/libcom/src/misc/epicsUnitTest.h | 3 ++- modules/libcom/src/osi/compiler/clang/compilerSpecific.h | 5 +++++ modules/libcom/src/osi/compiler/gcc/compilerSpecific.h | 5 +++++ modules/libcom/src/osi/compiler/msvc/compilerSpecific.h | 5 +++++ modules/libcom/src/osi/compilerDependencies.h | 4 ++++ modules/libcom/src/osi/epicsThread.h | 1 + modules/libcom/src/yacc/defs.h | 2 +- 12 files changed, 34 insertions(+), 11 deletions(-) diff --git a/modules/ca/src/client/acctst.c b/modules/ca/src/client/acctst.c index 44cc0673a..101da24c1 100644 --- a/modules/ca/src/client/acctst.c +++ b/modules/ca/src/client/acctst.c @@ -3396,7 +3396,7 @@ void verifyContextRundownChanStillExist ( showProgressEnd ( interestLevel ); } -int acctst ( const char * pName, unsigned interestLevel, unsigned channelCount, +void acctst ( const char * pName, unsigned interestLevel, unsigned channelCount, unsigned repetitionCount, enum ca_preemptive_callback_select select ) { chid chan; @@ -3549,8 +3549,6 @@ int acctst ( const char * pName, unsigned interestLevel, unsigned channelCount, printf ( "\nTest Complete\n" ); epicsExit ( EXIT_SUCCESS ); - - return 0; } diff --git a/modules/ca/src/client/caDiagnostics.h b/modules/ca/src/client/caDiagnostics.h index 5bf1755bc..019913e1c 100644 --- a/modules/ca/src/client/caDiagnostics.h +++ b/modules/ca/src/client/caDiagnostics.h @@ -20,7 +20,8 @@ extern "C" { enum appendNumberFlag {appendNumber, dontAppendNumber}; int catime ( const char *channelName, unsigned channelCount, enum appendNumberFlag appNF ); -int acctst ( const char *pname, unsigned logggingInterestLevel, +EPICS_NORETURN +void acctst ( const char *pname, unsigned logggingInterestLevel, unsigned channelCount, unsigned repetitionCount, enum ca_preemptive_callback_select select ); diff --git a/modules/libcom/src/flex/flexdef.h b/modules/libcom/src/flex/flexdef.h index dd6123da2..e0822f367 100644 --- a/modules/libcom/src/flex/flexdef.h +++ b/modules/libcom/src/flex/flexdef.h @@ -696,16 +696,16 @@ extern void dataend (void); extern void flexerror (char[]) NORETURN; /* report a fatal error message and terminate */ -extern void flexfatal (char[]); +extern void flexfatal (char[]) NORETURN; /* return current time */ extern char *flex_gettime(); /* report an error message formatted with one integer argument */ -extern void lerrif (char[], int); +extern void lerrif (char[], int) NORETURN; /* report an error message formatted with one string argument */ -extern void lerrsf (char[], char[]); +extern void lerrsf (char[], char[]) NORETURN; /* spit out a "# line" statement */ extern void line_directive_out (FILE*); diff --git a/modules/libcom/src/misc/cantProceed.h b/modules/libcom/src/misc/cantProceed.h index f9559b14c..2150ead3f 100644 --- a/modules/libcom/src/misc/cantProceed.h +++ b/modules/libcom/src/misc/cantProceed.h @@ -43,7 +43,8 @@ extern "C" { * \param errorMessage A printf-style error message describing the error. * \param ... Any parameters required for the error message. */ -LIBCOM_API void cantProceed( +LIBCOM_API EPICS_NORETURN +void cantProceed( EPICS_PRINTF_FMT(const char *errorMessage), ... ) EPICS_PRINTF_STYLE(1,2); diff --git a/modules/libcom/src/misc/epicsExit.h b/modules/libcom/src/misc/epicsExit.h index 304094e8d..4a837e977 100644 --- a/modules/libcom/src/misc/epicsExit.h +++ b/modules/libcom/src/misc/epicsExit.h @@ -23,6 +23,7 @@ #ifndef epicsExith #define epicsExith #include +#include "compilerDependencies.h" #ifdef __cplusplus extern "C" { @@ -38,7 +39,8 @@ typedef void (*epicsExitFunc)(void *arg); * \brief Calls epicsExitCallAtExits(), then the OS exit() routine. * \param status Passed to exit() */ -LIBCOM_API void epicsExit(int status); +LIBCOM_API EPICS_NORETURN +void epicsExit(int status); /** * \brief Arrange to call epicsExit() later from a low priority thread. * diff --git a/modules/libcom/src/misc/epicsUnitTest.h b/modules/libcom/src/misc/epicsUnitTest.h index eea53335f..1f51eeeb2 100644 --- a/modules/libcom/src/misc/epicsUnitTest.h +++ b/modules/libcom/src/misc/epicsUnitTest.h @@ -223,7 +223,8 @@ LIBCOM_API void testTodoEnd(void); * \param fmt A printf-style format string giving the reason for stopping. * \param ... Any parameters required for the format string. */ -LIBCOM_API void testAbort(EPICS_PRINTF_FMT(const char *fmt), ...) +LIBCOM_API EPICS_NORETURN +void testAbort(EPICS_PRINTF_FMT(const char *fmt), ...) EPICS_PRINTF_STYLE(1, 2); /** @} */ diff --git a/modules/libcom/src/osi/compiler/clang/compilerSpecific.h b/modules/libcom/src/osi/compiler/clang/compilerSpecific.h index 6bba842ee..bf2474a55 100644 --- a/modules/libcom/src/osi/compiler/clang/compilerSpecific.h +++ b/modules/libcom/src/osi/compiler/clang/compilerSpecific.h @@ -58,4 +58,9 @@ */ #define EPICS_UNUSED __attribute__((unused)) +/* + * No return marker + */ +#define EPICS_NORETURN __attribute__((noreturn)) + #endif /* ifndef compilerSpecific_h */ diff --git a/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h b/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h index d56d32212..47d06780f 100644 --- a/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h +++ b/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h @@ -57,4 +57,9 @@ */ #define EPICS_UNUSED __attribute__((unused)) +/* + * No return marker + */ +#define EPICS_NORETURN __attribute__((noreturn)) + #endif /* ifndef compilerSpecific_h */ diff --git a/modules/libcom/src/osi/compiler/msvc/compilerSpecific.h b/modules/libcom/src/osi/compiler/msvc/compilerSpecific.h index 631da4f38..8e274fcae 100644 --- a/modules/libcom/src/osi/compiler/msvc/compilerSpecific.h +++ b/modules/libcom/src/osi/compiler/msvc/compilerSpecific.h @@ -51,4 +51,9 @@ # define EPICS_PRINTF_FMT(a) _Printf_format_string_ a #endif +/* + * No return marker + */ +#define EPICS_NORETURN __declspec(noreturn) + #endif /* ifndef compilerSpecific_h */ diff --git a/modules/libcom/src/osi/compilerDependencies.h b/modules/libcom/src/osi/compilerDependencies.h index dc574c44a..55762ee20 100644 --- a/modules/libcom/src/osi/compilerDependencies.h +++ b/modules/libcom/src/osi/compilerDependencies.h @@ -52,6 +52,10 @@ # define EPICS_UNUSED #endif +#ifndef EPICS_NORETURN +# define EPICS_NORETURN +#endif + #ifndef EPICS_FUNCTION #if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)) || (defined(__cplusplus) && __cplusplus>=201103L) # define EPICS_FUNCTION __func__ diff --git a/modules/libcom/src/osi/epicsThread.h b/modules/libcom/src/osi/epicsThread.h index c8035685b..fcd96e574 100644 --- a/modules/libcom/src/osi/epicsThread.h +++ b/modules/libcom/src/osi/epicsThread.h @@ -437,6 +437,7 @@ private: epicsThread ( const epicsThread & ); epicsThread & operator = ( const epicsThread & ); friend void epicsThreadCallEntryPoint ( void * ); + EPICS_NORETURN void printLastChanceExceptionMessage ( const char * pExceptionTypeName, const char * pExceptionContext ); diff --git a/modules/libcom/src/yacc/defs.h b/modules/libcom/src/yacc/defs.h index 516eea680..8906150ce 100644 --- a/modules/libcom/src/yacc/defs.h +++ b/modules/libcom/src/yacc/defs.h @@ -297,7 +297,7 @@ extern void tokenized_start(char *s) NORETURN; extern void retyped_warning(char *s); extern void reprec_warning(char *s); extern void revalued_warning(char *s); -extern void terminal_start(char *s); +extern void terminal_start(char *s) NORETURN; extern void restarted_warning(void); extern void no_grammar(void) NORETURN; extern void terminal_lhs(int s_lineno) NORETURN; From 78f263f3590184ae23e5a21609889f53012a60b2 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Wed, 5 Mar 2025 16:55:32 +0100 Subject: [PATCH 03/19] Update GitHub Actions build configuration (#609) * ci: bump GHA ubuntu jobs (22->24, 20->22) * ci: add GHA job for CentOS-7 on self-managed Docker * ci: add jobs for CentOS-8 and Rocky-9 on GHA-managed Docker --- .github/workflows/ci-scripts-build.yml | 162 +++++++++++++++---------- 1 file changed, 100 insertions(+), 62 deletions(-) diff --git a/.github/workflows/ci-scripts-build.yml b/.github/workflows/ci-scripts-build.yml index f7c53909c..d26291526 100644 --- a/.github/workflows/ci-scripts-build.yml +++ b/.github/workflows/ci-scripts-build.yml @@ -50,9 +50,9 @@ jobs: matrix: # Job names also name artifacts, character limitations apply include: - - os: ubuntu-22.04 - cmp: gcc-12 - name: "Ub-22 gcc-12 c++20 Werror" + - os: ubuntu-24.04 + cmp: gcc + name: "Ub-24 gcc-13 c++20 Werror" # Turn all warnings into errors, # except for those we could not fix (yet). # Remove respective -Wno-error=... flag once it is fixed. @@ -73,79 +73,79 @@ jobs: -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3' CMD_LDFLAGS=-Wl,-z,relro" - - os: ubuntu-20.04 + - os: ubuntu-22.04 cmp: gcc configuration: default cross: "windows-x64-mingw" - name: "Ub-20 gcc + MinGW" + name: "Ub-22 gcc + MinGW" - - os: ubuntu-20.04 + - os: ubuntu-22.04 cmp: gcc configuration: static cross: "windows-x64-mingw" - name: "Ub-20 gcc + MinGW, static" + name: "Ub-22 gcc + MinGW, static" - - os: ubuntu-20.04 + - os: ubuntu-22.04 cmp: gcc configuration: static extra: "CMD_CXXFLAGS=-std=c++11" - name: "Ub-20 gcc C++11, static" + name: "Ub-22 gcc C++11, static" - - os: ubuntu-20.04 + - os: ubuntu-22.04 cmp: gcc configuration: static extra: "CMD_CFLAGS=-funsigned-char CMD_CXXFLAGS=-funsigned-char" - name: "Ub-20 gcc unsigned char" + name: "Ub-22 gcc unsigned char" - - os: ubuntu-20.04 + - os: ubuntu-22.04 cmp: clang configuration: default - name: "Ub-20 clang" + name: "Ub-22 clang" - - os: ubuntu-20.04 + - os: ubuntu-22.04 cmp: clang configuration: default extra: "CMD_CXXFLAGS=-std=c++11" - name: "Ub-20 clang C++11" + name: "Ub-22 clang C++11" - - os: ubuntu-20.04 + - os: ubuntu-22.04 cmp: gcc configuration: default cross: "RTEMS-pc686-qemu@5" - name: "Ub-20 gcc + RT-5.1 pc686" + name: "Ub-22 gcc + RT-5.1 pc686" - - os: ubuntu-20.04 + - os: ubuntu-22.04 cmp: gcc configuration: default cross: "RTEMS-beatnik@5" test: NO - name: "Ub-20 gcc + RT-5.1 beatnik" + name: "Ub-22 gcc + RT-5.1 beatnik" - - os: ubuntu-20.04 + - os: ubuntu-22.04 cmp: gcc configuration: default cross: "RTEMS-xilinx_zynq_a9_qemu@5" test: NO - name: "Ub-20 gcc + RT-5.1 xilinx_zynq_a9_qemu" + name: "Ub-22 gcc + RT-5.1 xilinx_zynq_a9_qemu" - - os: ubuntu-20.04 + - os: ubuntu-22.04 cmp: gcc configuration: default cross: "RTEMS-uC5282@5" test: NO - name: "Ub-20 gcc + RT-5.1 uC5282" + name: "Ub-22 gcc + RT-5.1 uC5282" - - os: ubuntu-20.04 + - os: ubuntu-22.04 cmp: gcc configuration: default - name: "Ub-20 gcc + RT-4.10" + name: "Ub-22 gcc + RT-4.10" cross: "RTEMS-pc386-qemu@4.10" test: NO - - os: ubuntu-20.04 + - os: ubuntu-22.04 cmp: gcc configuration: default - name: "Ub-20 gcc + RT-4.9" + name: "Ub-22 gcc + RT-4.9" cross: "RTEMS-pc386-qemu@4.9" - os: macos-latest @@ -239,10 +239,15 @@ jobs: matrix: # Job names also name artifacts, character limitations apply include: - #- name: "CentOS-7" - # image: centos:7 - # cmp: gcc - # configuration: default + - name: "CentOS-8" + image: centos:8 + cmp: gcc + configuration: default + + - name: "Rocky-9" + image: rockylinux:9 + cmp: gcc + configuration: default - name: "Fedora-33" image: fedora:33 @@ -255,47 +260,29 @@ jobs: configuration: default steps: - - name: "Build newer Git" - # actions/checkout@v2 wants git >=2.18 - # centos:7 has 1.8 - if: matrix.image=='centos:7' + - name: "Fix repo URLs on CentOS-8" + # centos:8 is frozen, repos are in the vault + if: matrix.image=='centos:8' run: | - yum -y install curl make gcc curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker - curl https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.29.0.tar.gz | tar -xz - cd git-* - make -j2 prefix=/usr/local all - make prefix=/usr/local install - cd .. - rm -rf git-* - type -a git - git --version + sed -i -e "s|mirrorlist=|#mirrorlist=|" \ + -e "s|#baseurl=http://mirror|baseurl=http://vault|" \ + /etc/yum.repos.d/CentOS-Linux-{BaseOS,AppStream,Extras,Plus}.repo - name: "Redhat setup" run: | - dnfyum() { - dnf -y "$@" || yum -y "$@" - return $? - } - dnfyum install python3 gdb make perl gcc-c++ glibc-devel readline-devel ncurses-devel perl-devel perl-Test-Simple - git --version || dnfyum install git - # rather than just bite the bullet and link python3 -> python, - # people would rather just break all existing scripts... - [ -e /usr/bin/python ] || ln -sf python3 /usr/bin/python - python --version + dnf -y install python3 gdb make perl gcc-c++ glibc-devel readline-devel ncurses-devel perl-devel perl-Test-Simple + git --version || dnf -y install git + python3 --version - uses: actions/checkout@v4 with: submodules: true - name: Automatic core dumper analysis uses: mdavidsaver/ci-core-dumper@master - if: matrix.image!='centos:7' - - name: Automatic core dumper analysis - uses: mdavidsaver/ci-core-dumper@node16 - if: matrix.image=='centos:7' - name: Prepare and compile dependencies - run: python .ci/cue.py prepare + run: python3 .ci/cue.py prepare - name: Build main module - run: python .ci/cue.py build + run: python3 .ci/cue.py build - name: Run main module tests - run: python .ci/cue.py -T 20M test + run: python3 .ci/cue.py -T 20M test - name: Upload tapfiles Artifact if: ${{ always() }} uses: actions/upload-artifact@v4 @@ -305,4 +292,55 @@ jobs: if-no-files-found: ignore - name: Collect and show test results if: ${{ always() }} - run: python .ci/cue.py -T 5M test-results + run: python3 .ci/cue.py -T 5M test-results + + build-docker: + name: Docker CentOS-7 + runs-on: ubuntu-latest + env: + CMP: gcc + BCFG: default + + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Run... + run: | + env > env.list + cat < runit.sh + #!/bin/sh + set -e -x + cd /io + id + + sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo + sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo + sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo + yum -y install epel-release + yum -y install \ + curl make gcc curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker \ + python3 gdb make perl gcc-c++ glibc-devel readline-devel ncurses-devel perl-devel perl-Test-Simple \ + libevent-devel sudo re2c + [ -e /usr/bin/python ] || ln -sf /usr/bin/python3 /usr/bin/python + + # fake out cue.py + ln -s /bin/true /usr/bin/apt-get + + # quiet warnings spam from perl + export LANG=C + + python --version + python .ci/cue.py prepare + python .ci/cue.py build + python .ci/cue.py -T 15M test + python .ci/cue.py test-results + EOF + chmod +x runit.sh + docker run --rm --quiet \ + --pull=always \ + --env-file env.list \ + -v `pwd`:/io \ + centos:7 \ + /io/runit.sh From ee1a49045a96eb780a8a7744b0951f67ee87e158 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 26 Feb 2025 16:39:02 +0100 Subject: [PATCH 04/19] support CROSS_COMPILER_RUNTESTS_ARCHS other than RTEMS --- modules/database/test/ioc/db/Makefile | 4 ++-- modules/database/test/std/filters/Makefile | 4 ++-- modules/database/test/std/link/Makefile | 4 ++-- modules/database/test/std/rec/Makefile | 4 ++-- modules/libcom/test/Makefile | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/database/test/ioc/db/Makefile b/modules/database/test/ioc/db/Makefile index 4395c291e..369bac9d7 100644 --- a/modules/database/test/ioc/db/Makefile +++ b/modules/database/test/ioc/db/Makefile @@ -208,8 +208,8 @@ TESTSPEC_RTEMS = dbTestHarness.boot; epicsRunDbTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) -TESTPROD_RTEMS = $(TESTPROD_HOST) -TESTSCRIPTS_RTEMS += $(TESTS:%=%.t) +TESTPROD += $(TESTPROD_HOST) +TESTSCRIPTS += $(TESTS:%=%.t) endif include $(TOP)/configure/RULES diff --git a/modules/database/test/std/filters/Makefile b/modules/database/test/std/filters/Makefile index 2576ce46d..e078b3727 100644 --- a/modules/database/test/std/filters/Makefile +++ b/modules/database/test/std/filters/Makefile @@ -81,8 +81,8 @@ TESTSPEC_RTEMS = filterTestHarness.boot; epicsRunFilterTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) -TESTPROD_RTEMS = $(TESTPROD_HOST) -TESTSCRIPTS_RTEMS += $(TESTS:%=%.t) +TESTPROD += $(TESTPROD_HOST) +TESTSCRIPTS += $(TESTS:%=%.t) endif include $(TOP)/configure/RULES diff --git a/modules/database/test/std/link/Makefile b/modules/database/test/std/link/Makefile index fca089966..e4410d76e 100644 --- a/modules/database/test/std/link/Makefile +++ b/modules/database/test/std/link/Makefile @@ -57,8 +57,8 @@ TESTSPEC_RTEMS = linkTestHarness.boot; epicsRunLinkTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) - TESTPROD = $(TESTPROD_HOST) - TESTSCRIPTS_RTEMS += $(TESTS:%=%.t) + TESTPROD += $(TESTPROD_HOST) + TESTSCRIPTS += $(TESTS:%=%.t) endif include $(TOP)/configure/RULES diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile index ebc4ac3d0..377e747e0 100644 --- a/modules/database/test/std/rec/Makefile +++ b/modules/database/test/std/rec/Makefile @@ -251,8 +251,8 @@ TESTSPEC_RTEMS = recordTestHarness.boot; epicsRunRecordTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) -TESTPROD_RTEMS = $(TESTPROD_HOST) -TESTSCRIPTS_RTEMS += $(TESTS:%=%.t) +TESTPROD += $(TESTPROD_HOST) +TESTSCRIPTS += $(TESTS:%=%.t) endif include $(TOP)/configure/RULES diff --git a/modules/libcom/test/Makefile b/modules/libcom/test/Makefile index a3893661f..195f97dc1 100644 --- a/modules/libcom/test/Makefile +++ b/modules/libcom/test/Makefile @@ -295,7 +295,7 @@ TESTSPEC_RTEMS = libComTestHarness.boot; epicsRunLibComTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) -TESTPROD = $(TESTPROD_HOST) +TESTPROD += $(TESTPROD_HOST) TESTSCRIPTS += $(filter-out epicsUnitTestTest.t, $(TESTS:%=%.t)) endif From 13d6ca598cca495b2e559e808392ed22265c57e3 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 5 Feb 2025 09:26:46 -0800 Subject: [PATCH 05/19] initHookRegister() make idempotent and MustSucceed --- modules/libcom/src/iocsh/initHooks.c | 18 +++++++++++++----- modules/libcom/src/iocsh/initHooks.h | 6 +++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/modules/libcom/src/iocsh/initHooks.c b/modules/libcom/src/iocsh/initHooks.c index 90b1de2c5..2def0e2d4 100644 --- a/modules/libcom/src/iocsh/initHooks.c +++ b/modules/libcom/src/iocsh/initHooks.c @@ -21,6 +21,7 @@ #include "ellLib.h" #include "epicsMutex.h" #include "epicsThread.h" +#include "cantProceed.h" #include "initHooks.h" @@ -52,19 +53,26 @@ static void initHookInit(void) int initHookRegister(initHookFunction func) { initHookLink *newHook; + ELLNODE *cur; if (!func) return 0; initHookInit(); - newHook = (initHookLink *)malloc(sizeof(initHookLink)); - if (!newHook) { - printf("Cannot malloc a new initHookLink\n"); - return -1; + epicsMutexMustLock(listLock); + + for(cur = ellFirst(&functionList); cur; cur = ellNext(cur)) { + const initHookLink *fn = CONTAINER(cur, initHookLink, node); + if(fn->func==func) { + /* silently ignore duplicate */ + epicsMutexUnlock(listLock); + return 0; + } } + + newHook = (initHookLink *)mallocMustSucceed(sizeof(initHookLink), "initHookRegister"); newHook->func = func; - epicsMutexMustLock(listLock); ellAdd(&functionList, &newHook->node); epicsMutexUnlock(listLock); return 0; diff --git a/modules/libcom/src/iocsh/initHooks.h b/modules/libcom/src/iocsh/initHooks.h index 573817f6b..57a1cc1fc 100644 --- a/modules/libcom/src/iocsh/initHooks.h +++ b/modules/libcom/src/iocsh/initHooks.h @@ -163,7 +163,11 @@ typedef void (*initHookFunction)(initHookState state); * * Registers \p func for initHook notifications * \param func Pointer to application's notification function. - * \return 0 if Ok, -1 on error (memory allocation failure). + * \return Always zero. (before UNRELEASED could return -1 on allocation failure) + * + * \since UNRELEASED initHookRegister is idempotent. + * Previously, repeated registrations would result + * in duplicate calls to the hook function. */ LIBCOM_API int initHookRegister(initHookFunction func); From 9f788996dcb8eb4eea6d36831a79e8d2edf29638 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 23 Feb 2023 11:06:55 -0800 Subject: [PATCH 06/19] db: lock record for db_create_read_log() and dbChannelGetField() since 27fe3e4468ec62d4d17f775c4aced939a666ba36 db_create_read_log() accesses record fields. remove now redundant db_create_read_log() calls. --- modules/database/src/ioc/db/dbChannel.c | 12 ++++++++ modules/database/src/ioc/db/dbChannel.h | 4 +++ modules/database/src/ioc/db/dbUnitTest.c | 29 ++------------------ modules/database/src/ioc/db/db_access.c | 13 +++++++++ modules/database/src/ioc/rsrv/camessage.c | 26 ------------------ modules/database/test/ioc/db/dbChArrTest.cpp | 8 ++++-- 6 files changed, 37 insertions(+), 55 deletions(-) diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c index fd682c93e..ad0531580 100644 --- a/modules/database/src/ioc/db/dbChannel.c +++ b/modules/database/src/ioc/db/dbChannel.c @@ -635,9 +635,21 @@ long dbChannelGetField(dbChannel *chan, short dbrType, void *pbuffer, { dbCommon *precord = chan->addr.precord; long status = 0; + unsigned char local_fl = 0; dbScanLock(precord); + if (!pfl && (ellCount(&chan->pre_chain) || ellCount(&chan->post_chain))) { + pfl = db_create_read_log(chan); + if (pfl) { + local_fl = 1; + pfl = dbChannelRunPreChain(chan, pfl); + pfl = dbChannelRunPostChain(chan, pfl); + } + } status = dbChannelGet(chan, dbrType, pbuffer, options, nRequest, pfl); + if (local_fl) { + db_delete_field_log(pfl); + } dbScanUnlock(precord); return status; } diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h index d4cb9e9bc..4bfdd6bc3 100644 --- a/modules/database/src/ioc/db/dbChannel.h +++ b/modules/database/src/ioc/db/dbChannel.h @@ -511,6 +511,10 @@ DBCORE_API long dbChannelGet(dbChannel *chan, short type, * \param[in,out] nRequest Pointer to the element count. * \param[in] pfl Pointer to a db_field_log or NULL. * \returns 0, or an error status value. + * + * \since UNRELEASED If pfl is NULL and chan has filters, db_create_read_log() will be called + * internally to create a temporary db_field_log which is passed to dbChannelGet() + * then deallocated. */ DBCORE_API long dbChannelGetField(dbChannel *chan, short type, void *pbuffer, long *options, long *nRequest, void *pfl); diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c index 5ac710f48..671763636 100644 --- a/modules/database/src/ioc/db/dbUnitTest.c +++ b/modules/database/src/ioc/db/dbUnitTest.c @@ -202,7 +202,6 @@ void testdbGetFieldEqual(const char* pv, int dbrType, ...) void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) { dbChannel *chan = dbChannelCreate(pv); - db_field_log *pfl = NULL; long nReq = 1; union anybuf pod; long status = S_dbLib_recNotFound; @@ -212,18 +211,7 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) goto done; } - if(ellCount(&chan->filters)) { - pfl = db_create_read_log(chan); - if (!pfl) { - testFail("can't db_create_read_log w/ %s", pv); - goto done; - } - - pfl = dbChannelRunPreChain(chan, pfl); - pfl = dbChannelRunPostChain(chan, pfl); - } - - status = dbChannelGetField(chan, dbrType, pod.bytes, NULL, &nReq, pfl); + status = dbChannelGetField(chan, dbrType, pod.bytes, NULL, &nReq, NULL); if (status) { testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status)); goto done; @@ -261,7 +249,6 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) } done: - db_delete_field_log(pfl); if(chan) dbChannelDelete(chan); } @@ -288,7 +275,6 @@ done: void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsigned long cnt, const void *pbufraw) { dbChannel *chan = dbChannelCreate(pv); - db_field_log *pfl = NULL; const long vSize = dbValueSize(dbfType); const long nStore = vSize * nRequest; long status = S_dbLib_recNotFound; @@ -300,24 +286,13 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign goto done; } - if(ellCount(&chan->filters)) { - pfl = db_create_read_log(chan); - if (!pfl) { - testFail("can't db_create_read_log w/ %s", pv); - goto done; - } - - pfl = dbChannelRunPreChain(chan, pfl); - pfl = dbChannelRunPostChain(chan, pfl); - } - gbuf = gstore = malloc(nStore); if(!gbuf && nStore!=0) { /* note that malloc(0) is allowed to return NULL on success */ testFail("Allocation failed esize=%ld total=%ld", vSize, nStore); return; } - status = dbChannelGetField(chan, dbfType, gbuf, NULL, &nRequest, pfl); + status = dbChannelGetField(chan, dbfType, gbuf, NULL, &nRequest, NULL); if (status) { testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status); diff --git a/modules/database/src/ioc/db/db_access.c b/modules/database/src/ioc/db/db_access.c index 0943315cf..cf83ec8a0 100644 --- a/modules/database/src/ioc/db/db_access.c +++ b/modules/database/src/ioc/db/db_access.c @@ -148,6 +148,7 @@ int dbChannel_get_count( long options; long i; long zero = 0; + unsigned char local_fl = 0; /* The order of the DBR* elements in the "newSt" structures below is * very important and must correspond to the order of processing @@ -156,6 +157,16 @@ int dbChannel_get_count( dbScanLock(dbChannelRecord(chan)); + /* If filters are involved in a read, create field log and run filters */ + if (!pfl && (ellCount(&chan->pre_chain) || ellCount(&chan->post_chain))) { + pfl = db_create_read_log(chan); + if (pfl) { + local_fl = 1; + pfl = dbChannelRunPreChain(chan, pfl); + pfl = dbChannelRunPostChain(chan, pfl); + } + } + switch(buffer_type) { case(oldDBR_STRING): status = dbChannelGet(chan, DBR_STRING, pbuffer, &zero, nRequest, pfl); @@ -800,6 +811,8 @@ int dbChannel_get_count( dbScanUnlock(dbChannelRecord(chan)); + if (local_fl) db_delete_field_log(pfl); + if (status) return -1; return 0; } diff --git a/modules/database/src/ioc/rsrv/camessage.c b/modules/database/src/ioc/rsrv/camessage.c index 81cb36a87..36844d162 100644 --- a/modules/database/src/ioc/rsrv/camessage.c +++ b/modules/database/src/ioc/rsrv/camessage.c @@ -493,7 +493,6 @@ static void read_reply ( void *pArg, struct dbChannel *dbch, const int readAccess = asCheckGet ( pciu->asClientPVT ); int status; int autosize; - int local_fl = 0; long item_count; ca_uint32_t payload_size; dbAddr *paddr=&dbch->addr; @@ -535,21 +534,9 @@ static void read_reply ( void *pArg, struct dbChannel *dbch, return; } - /* If filters are involved in a read, create field log and run filters */ - if (!pfl && (ellCount(&dbch->pre_chain) || ellCount(&dbch->post_chain))) { - pfl = db_create_read_log(dbch); - if (pfl) { - local_fl = 1; - pfl = dbChannelRunPreChain(dbch, pfl); - pfl = dbChannelRunPostChain(dbch, pfl); - } - } - status = dbChannel_get_count ( dbch, pevext->msg.m_dataType, pPayload, &item_count, pfl); - if (local_fl) db_delete_field_log(pfl); - if ( status < 0 ) { /* Clients recv the status of the operation directly to the * event/put/get callback. (from CA_V41()) @@ -616,7 +603,6 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p ca_uint32_t payloadSize; void *pPayload; int status; - int local_fl = 0; db_field_log *pfl = NULL; if ( ! pciu ) { @@ -655,21 +641,9 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p return RSRV_OK; } - /* If filters are involved in a read, create field log and run filters */ - if (ellCount(&pciu->dbch->pre_chain) || ellCount(&pciu->dbch->post_chain)) { - pfl = db_create_read_log(pciu->dbch); - if (pfl) { - local_fl = 1; - pfl = dbChannelRunPreChain(pciu->dbch, pfl); - pfl = dbChannelRunPostChain(pciu->dbch, pfl); - } - } - status = dbChannel_get ( pciu->dbch, mp->m_dataType, pPayload, mp->m_count, pfl ); - if (local_fl) db_delete_field_log(pfl); - if ( status < 0 ) { send_err ( mp, ECA_GETFAIL, pClient, RECORD_NAME ( pciu->dbch ) ); SEND_UNLOCK ( pClient ); diff --git a/modules/database/test/ioc/db/dbChArrTest.cpp b/modules/database/test/ioc/db/dbChArrTest.cpp index 96870d212..1560f72a6 100644 --- a/modules/database/test/ioc/db/dbChArrTest.cpp +++ b/modules/database/test/ioc/db/dbChArrTest.cpp @@ -130,9 +130,11 @@ static void check(short dbr_type) { off = Offset; req = 10; \ memset(buf, 0, sizeof(buf)); \ (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \ + dbScanLock(dbChannelRecord(pch)); \ pfl = db_create_read_log(pch); \ testOk(pfl && pfl->type == dbfl_type_ref, "Valid pfl, type = ref"); \ - testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \ + testOk(!dbChannelGet(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \ + dbScanUnlock(dbChannelRecord(pch)); \ testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \ if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \ for (i=0; itype = dbfl_type_ref; \ pfl->field_type = DBF_CHAR; \ @@ -181,7 +184,8 @@ static void check(short dbr_type) { pfl->no_elements = 26; \ pfl->dtor = freeArray; \ pfl->u.r.field = epicsStrDup("abcdefghijklmnopqrsstuvwxyz"); \ - testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \ + testOk(!dbChannelGet(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \ + dbScanUnlock(dbChannelRecord(pch)); \ testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \ if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \ for (i=0; i Date: Mon, 9 Jan 2023 11:46:17 -0800 Subject: [PATCH 07/19] Avoid initMainThread() except on vxworks Move isOkToBlock tracking to osdThread. Targets except vxworks can store this flag in epicsThreadOSD. Continue to use TLS w/ vxWorks. Note that setting of isOkToBlock for "main" thread becomes lazy. --- modules/libcom/src/osi/epicsThread.cpp | 35 ------------------- modules/libcom/src/osi/os/Linux/osdThread.h | 1 + .../libcom/src/osi/os/RTEMS-score/osdThread.c | 32 +++++++++++++++-- modules/libcom/src/osi/os/WIN32/osdThread.c | 16 +++++++++ modules/libcom/src/osi/os/posix/osdThread.c | 15 ++++++++ modules/libcom/src/osi/os/posix/osdThread.h | 1 + modules/libcom/src/osi/os/vxWorks/osdThread.c | 30 +++++++++++++++- 7 files changed, 91 insertions(+), 39 deletions(-) diff --git a/modules/libcom/src/osi/epicsThread.cpp b/modules/libcom/src/osi/epicsThread.cpp index b1273fd55..5de8fd99b 100644 --- a/modules/libcom/src/osi/epicsThread.cpp +++ b/modules/libcom/src/osi/epicsThread.cpp @@ -339,32 +339,6 @@ void epicsThread :: show ( unsigned level ) const throw () } extern "C" { - static epicsThreadOnceId okToBlockOnce = EPICS_THREAD_ONCE_INIT; - epicsThreadPrivateId okToBlockPrivate; - static const int okToBlockNo = 0; - static const int okToBlockYes = 1; - - static void epicsThreadOnceIdInit(void *) - { - okToBlockPrivate = epicsThreadPrivateCreate(); - } - - int epicsStdCall epicsThreadIsOkToBlock(void) - { - const int *pokToBlock; - epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL); - pokToBlock = (int *) epicsThreadPrivateGet(okToBlockPrivate); - return (pokToBlock ? *pokToBlock : 0); - } - - void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock) - { - const int *pokToBlock; - epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL); - pokToBlock = (isOkToBlock) ? &okToBlockYes : &okToBlockNo; - epicsThreadPrivateSet(okToBlockPrivate, (void *)pokToBlock); - } - epicsThreadId epicsStdCall epicsThreadMustCreate ( const char *name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr,void *parm) @@ -375,12 +349,3 @@ extern "C" { return id; } } // extern "C" - -static epicsThreadId initMainThread(void) { - epicsThreadId main = epicsThreadGetIdSelf(); - epicsThreadSetOkToBlock(1); - return main; -} - -// Ensure the main thread gets a unique ID and allows blocking I/O -epicsThreadId epicsThreadMainId = initMainThread(); diff --git a/modules/libcom/src/osi/os/Linux/osdThread.h b/modules/libcom/src/osi/os/Linux/osdThread.h index 4bf7f29a6..a691b7ad6 100644 --- a/modules/libcom/src/osi/os/Linux/osdThread.h +++ b/modules/libcom/src/osi/os/Linux/osdThread.h @@ -37,6 +37,7 @@ typedef struct epicsThreadOSD { int isRealTimeScheduled; int isOnThreadList; int isRunning; + int isOkToBlock; unsigned int osiPriority; int joinable; char name[1]; /* actually larger */ diff --git a/modules/libcom/src/osi/os/RTEMS-score/osdThread.c b/modules/libcom/src/osi/os/RTEMS-score/osdThread.c index e34717a36..abae0d20a 100644 --- a/modules/libcom/src/osi/os/RTEMS-score/osdThread.c +++ b/modules/libcom/src/osi/os/RTEMS-score/osdThread.c @@ -55,6 +55,7 @@ struct taskVar { int refcnt; int joinable; int isRunning; + int isOkToBlock; EPICSTHREADFUNC funptr; void *parm; unsigned int threadVariableCapacity; @@ -219,7 +220,7 @@ void epicsThreadExitMain (void) static rtems_status_code setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr, - void *parm, int joinable) + void *parm, int joinable, int isOkToBlock) { struct taskVar *v; uint32_t note; @@ -235,6 +236,7 @@ setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr, v->threadVariableCapacity = 0; v->threadVariables = NULL; v->isRunning = 1; + v->isOkToBlock = isOkToBlock; if (joinable) { char c[3] = {0,0,0}; strncpy(c, v->name, 3); @@ -284,7 +286,7 @@ epicsThreadInit (void) epicsMutexOsdPrepare(&taskVarMutex); epicsMutexOsdPrepare(&onceMutex); rtems_task_ident (RTEMS_SELF, 0, &tid); - if(setThreadInfo (tid, "_main_", NULL, NULL, 0) != RTEMS_SUCCESSFUL) + if(setThreadInfo (tid, "_main_", NULL, NULL, 0, 1) != RTEMS_SUCCESSFUL) cantProceed("epicsThreadInit() unable to setup _main_"); osdThreadHooksRunMain((epicsThreadId)tid); initialized = 1; @@ -338,7 +340,7 @@ epicsThreadCreateOpt ( name, rtems_status_text(sc)); return 0; } - sc = setThreadInfo (tid, name, funptr, parm, opts->joinable); + sc = setThreadInfo (tid, name, funptr, parm, opts->joinable, 0); if (sc != RTEMS_SUCCESSFUL) { errlogPrintf ("epicsThreadCreate create failure during setup for %s: %s\n", name, rtems_status_text(sc)); @@ -870,3 +872,27 @@ LIBCOM_API int epicsThreadGetCPUs(void) return 1; #endif } + + +int epicsStdCall epicsThreadIsOkToBlock(void) +{ + uint32_t note = 0; + struct taskVar *v; + + rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, ¬e); + v = (void *)note; + + return v && v->isOkToBlock; +} + +void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock) +{ + uint32_t note = 0; + struct taskVar *v; + + rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, ¬e); + v = (void *)note; + + if(v) + v->isOkToBlock = !!isOkToBlock; +} diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.c b/modules/libcom/src/osi/os/WIN32/osdThread.c index d055b9bd9..d95c9971b 100644 --- a/modules/libcom/src/osi/os/WIN32/osdThread.c +++ b/modules/libcom/src/osi/os/WIN32/osdThread.c @@ -103,6 +103,7 @@ typedef struct epicsThreadOSD { char isSuspended; int joinable; int isRunning; + int isOkToBlock; HANDLE timer; /* waitable timer */ } win32ThreadParam; @@ -586,6 +587,7 @@ static win32ThreadParam * epicsThreadImplicitCreate ( void ) pParm->handle = handle; pParm->id = id; + pParm->isOkToBlock = 1; win32ThreadPriority = GetThreadPriority ( pParm->handle ); assert ( win32ThreadPriority != THREAD_PRIORITY_ERROR_RETURN ); pParm->epicsPriority = epicsThreadGetOsiPriorityValue ( win32ThreadPriority ); @@ -1224,3 +1226,17 @@ void testPriorityMapping () return 0; } #endif + +int epicsStdCall epicsThreadIsOkToBlock(void) +{ + struct epicsThreadOSD *pthreadInfo = epicsThreadGetIdSelf(); + + return(pthreadInfo->isOkToBlock); +} + +void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock) +{ + struct epicsThreadOSD *pthreadInfo = epicsThreadGetIdSelf(); + + pthreadInfo->isOkToBlock = !!isOkToBlock; +} diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c index e9ea2dfbc..db043dff6 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.c +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -665,6 +665,7 @@ static epicsThreadOSD *createImplicit(void) assert(pthreadInfo); pthreadInfo->tid = tid; pthreadInfo->osiPriority = 0; + pthreadInfo->isOkToBlock = 1; #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 if(pthread_getschedparam(tid,&pthreadInfo->schedPolicy,&pthreadInfo->schedParam) == 0) { @@ -1085,3 +1086,17 @@ LIBCOM_API int epicsThreadGetCPUs(void) #endif return 1; } + +int epicsStdCall epicsThreadIsOkToBlock(void) +{ + epicsThreadOSD *pthreadInfo = epicsThreadGetIdSelf(); + + return(pthreadInfo->isOkToBlock); +} + +void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock) +{ + epicsThreadOSD *pthreadInfo = epicsThreadGetIdSelf(); + + pthreadInfo->isOkToBlock = !!isOkToBlock; +} diff --git a/modules/libcom/src/osi/os/posix/osdThread.h b/modules/libcom/src/osi/os/posix/osdThread.h index ea49030a3..ceb72fb1f 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.h +++ b/modules/libcom/src/osi/os/posix/osdThread.h @@ -35,6 +35,7 @@ typedef struct epicsThreadOSD { int isRealTimeScheduled; int isOnThreadList; int isRunning; + int isOkToBlock; unsigned int osiPriority; int joinable; char name[1]; /* actually larger */ diff --git a/modules/libcom/src/osi/os/vxWorks/osdThread.c b/modules/libcom/src/osi/os/vxWorks/osdThread.c index c7cc2f5eb..59e576ceb 100644 --- a/modules/libcom/src/osi/os/vxWorks/osdThread.c +++ b/modules/libcom/src/osi/os/vxWorks/osdThread.c @@ -127,7 +127,8 @@ static void epicsThreadInit(void) taskIdListSize = ID_LIST_CHUNK; atRebootRegister(); ALLOT_JOIN(0); - done = 1; + done = 1; /* avoids recursive call */ + epicsThreadSetOkToBlock(1); } lock = 0; } @@ -577,3 +578,30 @@ LIBCOM_API int epicsThreadGetCPUs(void) { return 1; } + + +static epicsThreadOnceId okToBlockOnce = EPICS_THREAD_ONCE_INIT; +static epicsThreadPrivateId okToBlockPrivate; +static const int okToBlockNo = 0; +static const int okToBlockYes = 1; + +static void epicsThreadOnceIdInit(void *not_used) +{ + okToBlockPrivate = epicsThreadPrivateCreate(); +} + +int epicsStdCall epicsThreadIsOkToBlock(void) +{ + const int *pokToBlock; + epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL); + pokToBlock = (int *) epicsThreadPrivateGet(okToBlockPrivate); + return (pokToBlock ? *pokToBlock : 0); +} + +void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock) +{ + const int *pokToBlock; + epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL); + pokToBlock = (isOkToBlock) ? &okToBlockYes : &okToBlockNo; + epicsThreadPrivateSet(okToBlockPrivate, (void *)pokToBlock); +} From a4bc0db6e61162d929b9f4c9f6137c95171ca8ae Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Dec 2024 18:18:37 -0800 Subject: [PATCH 08/19] dbCa CP link updates set PUTF/RPRO --- modules/database/src/ioc/db/dbCa.c | 56 ++++++------------- modules/database/src/ioc/db/dbCaPvt.h | 1 + modules/database/src/ioc/db/db_access.c | 14 +++++ .../database/src/ioc/db/db_access_routines.h | 6 +- 4 files changed, 38 insertions(+), 39 deletions(-) diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c index 42fe374a8..98a38d427 100644 --- a/modules/database/src/ioc/db/dbCa.c +++ b/modules/database/src/ioc/db/dbCa.c @@ -115,10 +115,9 @@ static int dbca_chan_count; * During link modification or IOC shutdown the pca->plink pointer (guarded by caLink.lock) * is used as a flag to indicate that a link is no longer active. * - * References to the struct caLink are owned by the dbCaTask, and any scanOnceCallback() - * which is in progress. + * References to the struct caLink are owned by the dbCaTask. * - * The libca and scanOnceCallback callbacks take no action if pca->plink==NULL. + * The libca callbacks take no action if pca->plink==NULL. * * dbCaPutLinkCallback causes an additional complication because * when dbCaRemoveLink is called the callback may not have occured. @@ -788,38 +787,6 @@ static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) return status; } -static void scanComplete(void *raw, dbCommon *prec) -{ - caLink *pca = raw; - epicsMutexMustLock(pca->lock); - if(!pca->plink) { - /* IOC shutdown or link re-targeted. Do nothing. */ - } else if(pca->scanningOnce==0) { - errlogPrintf("dbCa.c complete callback w/ scanningOnce==0\n"); - } else if(--pca->scanningOnce){ - /* another scan is queued */ - if(scanOnceCallback(prec, scanComplete, raw)) { - errlogPrintf("dbCa.c failed to re-queue scanOnce\n"); - } else - caLinkInc(pca); - } - epicsMutexUnlock(pca->lock); - caLinkDec(pca); -} - -/* must be called with pca->lock held */ -static void scanLinkOnce(dbCommon *prec, caLink *pca) { - if(pca->scanningOnce==0) { - if(scanOnceCallback(prec, scanComplete, pca)) { - errlogPrintf("dbCa.c failed to queue scanOnce\n"); - } else - caLinkInc(pca); - } - if(pca->scanningOnce<5) - pca->scanningOnce++; - /* else too many scans queued */ -} - static lset dbCa_lset = { 0, 1, /* not Constant, Volatile */ NULL, dbCaRemoveLink, @@ -856,7 +823,9 @@ static void connectionCallback(struct connection_handler_args arg) if (precord && ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))) - scanLinkOnce(precord, pca); + { + link_action |= CA_DBPROCESS; + } goto done; } pca->hasReadAccess = ca_read_access(arg.chid); @@ -988,7 +957,9 @@ static void eventCallback(struct event_handler_args arg) if ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)) - scanLinkOnce(precord, pca); + { + addAction(pca, CA_DBPROCESS); + } } done: epicsMutexUnlock(pca->lock); @@ -1061,7 +1032,9 @@ static void accessRightsCallback(struct access_rights_handler_args arg) if (precord && ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))) - scanLinkOnce(precord, pca); + { + addAction(pca, CA_DBPROCESS); + } done: epicsMutexUnlock(pca->lock); } @@ -1273,6 +1246,13 @@ static void dbCaTask(void *arg) printLinks(pca); } } + if (link_action & CA_DBPROCESS) { + dbCommon *prec; + epicsMutexMustLock(pca->lock); + prec = pca->plink->precord; + epicsMutexUnlock(pca->lock); + db_process(prec); + } } SEVCHK(ca_flush_io(), "dbCaTask"); } diff --git a/modules/database/src/ioc/db/dbCaPvt.h b/modules/database/src/ioc/db/dbCaPvt.h index 6c1d902ca..1ee0745e0 100644 --- a/modules/database/src/ioc/db/dbCaPvt.h +++ b/modules/database/src/ioc/db/dbCaPvt.h @@ -31,6 +31,7 @@ #define CA_MONITOR_STRING 0x20 #define CA_GET_ATTRIBUTES 0x40 #define CA_SYNC 0x1000 +#define CA_DBPROCESS 0x2000 /* write type */ #define CA_PUT 0x1 #define CA_PUT_CALLBACK 0x2 diff --git a/modules/database/src/ioc/db/db_access.c b/modules/database/src/ioc/db/db_access.c index cf83ec8a0..62e8ffe3c 100644 --- a/modules/database/src/ioc/db/db_access.c +++ b/modules/database/src/ioc/db/db_access.c @@ -1042,3 +1042,17 @@ int db_put_process(processNotify *ppn, notifyPutType type, ppn->status = notifyError; return 1; } + +void db_process(struct dbCommon *prec) +{ + if (prec->pact) { + if (dbAccessDebugPUTF && prec->tpro) + printf("%s: dbPutField to Active '%s', setting RPRO=1\n", + epicsThreadGetNameSelf(), prec->name); + prec->rpro = TRUE; + } else { + /* indicate that dbPutField called dbProcess */ + prec->putf = TRUE; + (void)dbProcess(prec); + } +} diff --git a/modules/database/src/ioc/db/db_access_routines.h b/modules/database/src/ioc/db/db_access_routines.h index ee2f07940..1871b9343 100644 --- a/modules/database/src/ioc/db/db_access_routines.h +++ b/modules/database/src/ioc/db/db_access_routines.h @@ -22,6 +22,8 @@ extern "C" { #include "dbCoreAPI.h" +struct dbCommon; + DBCORE_API extern struct dbBase *pdbbase; DBCORE_API extern volatile int interruptAccept; @@ -36,7 +38,9 @@ DBCORE_API int dbChannel_put(struct dbChannel *chan, int src_type, const void *psrc, long no_elements); DBCORE_API int dbChannel_get_count(struct dbChannel *chan, int buffer_type, void *pbuffer, long *nRequest, void *pfl); - +#ifdef EPICS_DBCA_PRIVATE_API +DBCORE_API void db_process(struct dbCommon *prec); +#endif #ifdef __cplusplus } From bcc6cb96ae461623b16ae699b83704e5dc5349e2 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 3 Feb 2025 12:00:08 -0600 Subject: [PATCH 09/19] Added dbServerStats() API for iocStats and similar --- modules/database/src/ioc/db/dbServer.c | 20 ++++++++++++++++++++ modules/database/src/ioc/db/dbServer.h | 15 ++++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/modules/database/src/ioc/db/dbServer.c b/modules/database/src/ioc/db/dbServer.c index d39b345e0..58a643573 100644 --- a/modules/database/src/ioc/db/dbServer.c +++ b/modules/database/src/ioc/db/dbServer.c @@ -127,6 +127,26 @@ int dbServerClient(char *pBuf, size_t bufSize) return -1; } +int dbServerStats(const char *name, unsigned *channels, unsigned *clients) +{ + dbServer *psrv = (dbServer *)ellFirst(&serverList); + + if (state != running || !psrv) + return -1; + + while (psrv) { + if (strcmp(name, psrv->name) == 0) { + if (!psrv->stats) + return -1; + + psrv->stats(channels, clients); + return 0; + } + psrv = (dbServer *)ellNext(&psrv->node); + } + return -1; +} + #define STARTSTOP(routine, method, newState) \ void routine(void) \ { \ diff --git a/modules/database/src/ioc/db/dbServer.h b/modules/database/src/ioc/db/dbServer.h index 326166060..d816311dd 100644 --- a/modules/database/src/ioc/db/dbServer.h +++ b/modules/database/src/ioc/db/dbServer.h @@ -17,9 +17,6 @@ * the dbServer interface provides allow the IOC to start, pause and stop * the servers together, and to provide status and debugging information * to the IOC user/developer through a common set of commands. - * - * @todo No API is provided yet for calling stats() methods. - * Nothing in the IOC calls dbStopServers(), not sure where it should go. */ #ifndef INC_dbServer_H @@ -145,6 +142,18 @@ DBCORE_API void dbsr(unsigned level); */ DBCORE_API int dbServerClient(char *pBuf, size_t bufSize); +/** @brief Fetch statistics from named server. + * + * This is an API for iocStats and similar to fetch the number of channels + * and clients connected to the named server layer. + * @param name Server name + * @param channels Where to return the channel count + * @param clients Where to return the client count + * @returns 0 on success; -1 if IOC isn't running, no such named server, + * or that server doesn't implement the stats method. + */ +DBCORE_API int dbServerStats(const char *name, unsigned *channels, unsigned *clients); + /** @brief Initialize all registered servers. * * Calls all dbServer::init() methods. From fd86f0ff04360900e7b90cafce09085448d8e6ea Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 3 Feb 2025 12:54:26 -0600 Subject: [PATCH 10/19] Unit tests for dbServerStats() --- modules/database/src/ioc/db/dbServer.c | 2 +- modules/database/test/ioc/db/dbServerTest.c | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/modules/database/src/ioc/db/dbServer.c b/modules/database/src/ioc/db/dbServer.c index 58a643573..f08eec828 100644 --- a/modules/database/src/ioc/db/dbServer.c +++ b/modules/database/src/ioc/db/dbServer.c @@ -131,7 +131,7 @@ int dbServerStats(const char *name, unsigned *channels, unsigned *clients) { dbServer *psrv = (dbServer *)ellFirst(&serverList); - if (state != running || !psrv) + if (!name || state != running || !psrv) return -1; while (psrv) { diff --git a/modules/database/test/ioc/db/dbServerTest.c b/modules/database/test/ioc/db/dbServerTest.c index 99640a619..9379d9fb1 100644 --- a/modules/database/test/ioc/db/dbServerTest.c +++ b/modules/database/test/ioc/db/dbServerTest.c @@ -40,6 +40,8 @@ void oneReport(unsigned level) void oneStats(unsigned *channels, unsigned *clients) { oneState = STATS_CALLED; + *channels = 2; + *clients = 1; } int oneClient(char *pbuf, size_t len) @@ -128,8 +130,9 @@ MAIN(dbServerTest) char name[16]; char *theName = "The One"; int status; + unsigned ch=0, cl=0; - testPlan(25); + testPlan(29); /* Prove that we handle substring names properly */ epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); @@ -163,7 +166,12 @@ MAIN(dbServerTest) testDiag("Checking server methods called"); dbsr(0); - testOk(oneState == REPORT_CALLED, "dbsr called report()"); + testOk(oneState == REPORT_CALLED, "dbsr called one::report()"); + testOk(dbServerStats("none", &ch, &cl) != 0, "Stats: unknown name rejected"); + testOk(dbServerStats("no-routines", &ch, &cl) != 0, "Stats: no-routine rejected"); + testOk(dbServerStats("one", &ch, &cl) == 0 && oneState == STATS_CALLED, + "dbServerStats('one') called one::stats()"); + testOk(ch == 2 && cl == 1, "Stats: counts returned as expected"); oneSim = NULL; name[0] = 0; From 90f97a7037ecffb32544755355dacbf7239301ce Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 3 Feb 2025 13:35:49 -0600 Subject: [PATCH 11/19] Release notes; HAS_DBSERVER_STATS in dbServer.h --- documentation/RELEASE_NOTES.md | 7 +++++++ modules/database/src/ioc/db/dbServer.h | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 732c98654..1b0f0e6b7 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -18,6 +18,13 @@ __This version of EPICS has not been released yet.__ __Add new items below here__ +### New `dbServerStats()` API for iocStats + +A new routine provides the ability to request channel and client counts from +name server layers that implement the `stats()` method. A preprocessor macro +`HAS_DBSERVER_STATS` macro is defined in the `dbServer.h` header file to +simplify code that needs to support older versions of Base as well. + ----- ## EPICS Release 7.0.9 diff --git a/modules/database/src/ioc/db/dbServer.h b/modules/database/src/ioc/db/dbServer.h index d816311dd..86c3f7d82 100644 --- a/modules/database/src/ioc/db/dbServer.h +++ b/modules/database/src/ioc/db/dbServer.h @@ -142,6 +142,11 @@ DBCORE_API void dbsr(unsigned level); */ DBCORE_API int dbServerClient(char *pBuf, size_t bufSize); +/** @brief CPP Macro indicating the dbServerStats() routine exists. + * @since UNRELEASED + */ +#define HAS_DBSERVER_STATS + /** @brief Fetch statistics from named server. * * This is an API for iocStats and similar to fetch the number of channels @@ -151,6 +156,8 @@ DBCORE_API int dbServerClient(char *pBuf, size_t bufSize); * @param clients Where to return the client count * @returns 0 on success; -1 if IOC isn't running, no such named server, * or that server doesn't implement the stats method. + * + * @since UNRELEASED */ DBCORE_API int dbServerStats(const char *name, unsigned *channels, unsigned *clients); From 72f3e75c8de5eaf44ee6e13ea37040024cc476cd Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 18 Feb 2025 14:20:14 -0600 Subject: [PATCH 12/19] Add summary stat's calculation --- documentation/RELEASE_NOTES.md | 3 ++- modules/database/src/ioc/db/dbServer.c | 22 ++++++++++++++++++--- modules/database/src/ioc/db/dbServer.h | 6 ++++-- modules/database/test/ioc/db/dbServerTest.c | 18 +++++++++++++---- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 1b0f0e6b7..387c4191b 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -21,7 +21,8 @@ __Add new items below here__ ### New `dbServerStats()` API for iocStats A new routine provides the ability to request channel and client counts from -name server layers that implement the `stats()` method. A preprocessor macro +named server layers that implement the `stats()` method, or to get a summary +of the counts from all registered server layers. A preprocessor macro `HAS_DBSERVER_STATS` macro is defined in the `dbServer.h` header file to simplify code that needs to support older versions of Base as well. diff --git a/modules/database/src/ioc/db/dbServer.c b/modules/database/src/ioc/db/dbServer.c index f08eec828..c4e5f55c3 100644 --- a/modules/database/src/ioc/db/dbServer.c +++ b/modules/database/src/ioc/db/dbServer.c @@ -130,11 +130,23 @@ int dbServerClient(char *pBuf, size_t bufSize) int dbServerStats(const char *name, unsigned *channels, unsigned *clients) { dbServer *psrv = (dbServer *)ellFirst(&serverList); + unsigned tch, tcl; - if (!name || state != running || !psrv) + if (state != running || !psrv) return -1; - while (psrv) { + for (tch = 0, tcl = 0; psrv; + psrv = (dbServer *)ellNext(&psrv->node)) { + if (!name) { + if (psrv->stats) { + unsigned lch, lcl; + + psrv->stats(&lch, &lcl); + tch += lch; + tcl += lcl; + } + continue; + } if (strcmp(name, psrv->name) == 0) { if (!psrv->stats) return -1; @@ -142,7 +154,11 @@ int dbServerStats(const char *name, unsigned *channels, unsigned *clients) psrv->stats(channels, clients); return 0; } - psrv = (dbServer *)ellNext(&psrv->node); + } + if (!name) { + if (channels) *channels = tch; + if (clients) *clients = tcl; + return 0; } return -1; } diff --git a/modules/database/src/ioc/db/dbServer.h b/modules/database/src/ioc/db/dbServer.h index 86c3f7d82..9b16c3b3d 100644 --- a/modules/database/src/ioc/db/dbServer.h +++ b/modules/database/src/ioc/db/dbServer.h @@ -151,9 +151,11 @@ DBCORE_API int dbServerClient(char *pBuf, size_t bufSize); * * This is an API for iocStats and similar to fetch the number of channels * and clients connected to the named server layer. + * If the name given is NULL the statistics returned are the totals for + * all the registered server layers. * @param name Server name - * @param channels Where to return the channel count - * @param clients Where to return the client count + * @param channels NULL, or where to return the channel count + * @param clients NULL or where to return the client count * @returns 0 on success; -1 if IOC isn't running, no such named server, * or that server doesn't implement the stats method. * diff --git a/modules/database/test/ioc/db/dbServerTest.c b/modules/database/test/ioc/db/dbServerTest.c index 9379d9fb1..97e547deb 100644 --- a/modules/database/test/ioc/db/dbServerTest.c +++ b/modules/database/test/ioc/db/dbServerTest.c @@ -40,8 +40,8 @@ void oneReport(unsigned level) void oneStats(unsigned *channels, unsigned *clients) { oneState = STATS_CALLED; - *channels = 2; - *clients = 1; + if (channels) *channels = 2; + if (clients) *clients = 1; } int oneClient(char *pbuf, size_t len) @@ -132,7 +132,7 @@ MAIN(dbServerTest) int status; unsigned ch=0, cl=0; - testPlan(29); + testPlan(32); /* Prove that we handle substring names properly */ epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); @@ -167,12 +167,22 @@ MAIN(dbServerTest) testDiag("Checking server methods called"); dbsr(0); testOk(oneState == REPORT_CALLED, "dbsr called one::report()"); + + testDiag("Checking stats functionality"); testOk(dbServerStats("none", &ch, &cl) != 0, "Stats: unknown name rejected"); testOk(dbServerStats("no-routines", &ch, &cl) != 0, "Stats: no-routine rejected"); testOk(dbServerStats("one", &ch, &cl) == 0 && oneState == STATS_CALLED, "dbServerStats('one') called one::stats()"); - testOk(ch == 2 && cl == 1, "Stats: counts returned as expected"); + testOk(ch == 2 && cl == 1, "Stats: ch==%d, cl==%d (expected 2, 1)", ch, cl); + ch = 10; cl = 10; oneState = NOTHING_CALLED; + testOk(dbServerStats(NULL, NULL, &cl) == 0 && oneState == STATS_CALLED, + "dbServerStats(NULL, &cl) called one::stats()"); + testOk(dbServerStats(NULL, &ch, NULL) == 0 && oneState == STATS_CALLED, + "dbServerStats(NULL, &ch) called one::stats()"); + testOk(ch == 2 && cl == 1, "Stats: ch==%d, cl==%d (expected 2, 1)", ch, cl); + + testDiag("Checking client identification"); oneSim = NULL; name[0] = 0; status = dbServerClient(name, sizeof(name)); From 350570134e9b3e0964efe10e4afe2525acdb007f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 26 Feb 2025 13:29:08 -0600 Subject: [PATCH 13/19] Changed dbServerStats() to count the server layers called --- modules/database/src/ioc/db/dbServer.c | 39 ++++++++------------- modules/database/src/ioc/db/dbServer.h | 21 ++++++----- modules/database/test/ioc/db/dbServerTest.c | 21 +++++++---- 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/modules/database/src/ioc/db/dbServer.c b/modules/database/src/ioc/db/dbServer.c index c4e5f55c3..527fb97ca 100644 --- a/modules/database/src/ioc/db/dbServer.c +++ b/modules/database/src/ioc/db/dbServer.c @@ -130,37 +130,26 @@ int dbServerClient(char *pBuf, size_t bufSize) int dbServerStats(const char *name, unsigned *channels, unsigned *clients) { dbServer *psrv = (dbServer *)ellFirst(&serverList); - unsigned tch, tcl; - if (state != running || !psrv) return -1; - for (tch = 0, tcl = 0; psrv; - psrv = (dbServer *)ellNext(&psrv->node)) { - if (!name) { - if (psrv->stats) { - unsigned lch, lcl; + unsigned tch = 0, tcl = 0, nmatch = 0; + for (; psrv; psrv = (dbServer *)ellNext(&psrv->node)) { + if (psrv->stats && + (!name || strcmp(name, psrv->name) == 0)) { + unsigned lch = 0, lcl = 0; - psrv->stats(&lch, &lcl); - tch += lch; - tcl += lcl; - } - continue; - } - if (strcmp(name, psrv->name) == 0) { - if (!psrv->stats) - return -1; - - psrv->stats(channels, clients); - return 0; + psrv->stats(&lch, &lcl); + tch += lch; + tcl += lcl; + nmatch++; + if (name) + break; /* No duplicate names in serverList */ } } - if (!name) { - if (channels) *channels = tch; - if (clients) *clients = tcl; - return 0; - } - return -1; + if (channels) *channels = tch; + if (clients) *clients = tcl; + return nmatch; } #define STARTSTOP(routine, method, newState) \ diff --git a/modules/database/src/ioc/db/dbServer.h b/modules/database/src/ioc/db/dbServer.h index 9b16c3b3d..2083a705a 100644 --- a/modules/database/src/ioc/db/dbServer.h +++ b/modules/database/src/ioc/db/dbServer.h @@ -56,8 +56,8 @@ typedef struct dbServer { /** @brief Get number of channels and clients currently connected. * - * @param channels NULL or pointer for returning channel count. - * @param clients NULL or pointer for returning client count. + * @param channels @c NULL or pointer for returning channel count. + * @param clients @c NULL or pointer for returning client count. */ void (* stats) (unsigned *channels, unsigned *clients); @@ -147,21 +147,24 @@ DBCORE_API int dbServerClient(char *pBuf, size_t bufSize); */ #define HAS_DBSERVER_STATS -/** @brief Fetch statistics from named server. +/** @brief Fetch statistics from server layers. * * This is an API for iocStats and similar to fetch the number of channels - * and clients connected to the named server layer. - * If the name given is NULL the statistics returned are the totals for - * all the registered server layers. + * and clients connected to the registered server layers. + * If the name given is NULL the statistics returned are the totals from + * all registered server layers, otherwise just from the named server. * @param name Server name * @param channels NULL, or where to return the channel count * @param clients NULL or where to return the client count - * @returns 0 on success; -1 if IOC isn't running, no such named server, - * or that server doesn't implement the stats method. + * @returns -1 if the IOC isn't running or no servers are registered, without + * writing to the statistics variables. Otherwise it writes to the statistics + * variables and returns the number of dbServer::stats() methods called, + * 0 if a named server wasn't found or doesn't have a stats() method. * * @since UNRELEASED */ -DBCORE_API int dbServerStats(const char *name, unsigned *channels, unsigned *clients); +DBCORE_API int dbServerStats(const char *name, unsigned *channels, + unsigned *clients); /** @brief Initialize all registered servers. * diff --git a/modules/database/test/ioc/db/dbServerTest.c b/modules/database/test/ioc/db/dbServerTest.c index 97e547deb..db4ba59d1 100644 --- a/modules/database/test/ioc/db/dbServerTest.c +++ b/modules/database/test/ioc/db/dbServerTest.c @@ -132,7 +132,7 @@ MAIN(dbServerTest) int status; unsigned ch=0, cl=0; - testPlan(32); + testPlan(35); /* Prove that we handle substring names properly */ epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); @@ -154,6 +154,9 @@ MAIN(dbServerTest) testDiag("Registering dbServer 'disabled'"); testOk(dbRegisterServer(&disabled) == 0, "Registration accepted"); + testOk(dbServerStats("one", &ch, &cl) == -1 && oneState == NOTHING_CALLED, + "dbServerStats returns error before IOC running"); + testDiag("Changing server state"); dbInitServers(); testOk(oneState == INIT_CALLED, "dbInitServers"); @@ -169,16 +172,19 @@ MAIN(dbServerTest) testOk(oneState == REPORT_CALLED, "dbsr called one::report()"); testDiag("Checking stats functionality"); - testOk(dbServerStats("none", &ch, &cl) != 0, "Stats: unknown name rejected"); - testOk(dbServerStats("no-routines", &ch, &cl) != 0, "Stats: no-routine rejected"); - testOk(dbServerStats("one", &ch, &cl) == 0 && oneState == STATS_CALLED, + testOk(dbServerStats("none", &ch, &cl) == 0, "Stats: unknown name ignored"); + testOk(dbServerStats("one", &ch, &cl) == 1 && oneState == STATS_CALLED, "dbServerStats('one') called one::stats()"); testOk(ch == 2 && cl == 1, "Stats: ch==%d, cl==%d (expected 2, 1)", ch, cl); + testOk(dbServerStats("no-routines", &ch, &cl) == 0, + "dbServerStats('no-routines') layer not counted"); + testOk(ch == 0 && cl == 0, "Stats: ch==%d, cl==%d (expected 0, 0)", ch, cl); + ch = 10; cl = 10; oneState = NOTHING_CALLED; - testOk(dbServerStats(NULL, NULL, &cl) == 0 && oneState == STATS_CALLED, + testOk(dbServerStats(NULL, NULL, &cl) == 1 && oneState == STATS_CALLED, "dbServerStats(NULL, &cl) called one::stats()"); - testOk(dbServerStats(NULL, &ch, NULL) == 0 && oneState == STATS_CALLED, + testOk(dbServerStats(NULL, &ch, NULL) == 1 && oneState == STATS_CALLED, "dbServerStats(NULL, &ch) called one::stats()"); testOk(ch == 2 && cl == 1, "Stats: ch==%d, cl==%d (expected 2, 1)", ch, cl); @@ -206,6 +212,9 @@ MAIN(dbServerTest) status = dbServerClient(name, sizeof(name)); testOk(oneState != CLIENT_CALLED_KNOWN, "No call to client() when paused"); + testOk(dbServerStats("one", &ch, &cl) == -1 && oneState != STATS_CALLED, + "No call to stats() when paused"); + dbStopServers(); testOk(oneState == STOP_CALLED, "dbStopServers"); testOk(dbUnregisterServer(&toolate) != 0, "No unreg' if not reg'ed"); From b97a35fec87ae9186026d8df118f990e7d24f814 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 12 Mar 2025 12:07:34 +0100 Subject: [PATCH 14/19] Don't use __attribute__((noreturn)) on VxWorks VxWorks does not mark abort() or exit() as noreturn. Thus, functions declared noreturn which end in a call to those functions cause a compiler warning on vxWorks. --- modules/libcom/src/osi/compiler/gcc/compilerSpecific.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h b/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h index 47d06780f..42723b97c 100644 --- a/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h +++ b/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h @@ -60,6 +60,9 @@ /* * No return marker */ +#ifndef vxWorks +// VxWorks does not mark abort() or exit() noreturn! #define EPICS_NORETURN __attribute__((noreturn)) +#endif #endif /* ifndef compilerSpecific_h */ From c75ad2673e55d6e5db2b4d138a8ae65c865ba36a Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Sat, 8 Feb 2025 23:08:19 +0100 Subject: [PATCH 15/19] no longer need extern "C" { } around epicsExport macros --- documentation/RELEASE_NOTES.md | 5 + modules/database/test/std/rec/Makefile | 28 ++++ .../database/test/std/rec/epicsExportTest.c | 146 ++++++++++++++++++ .../database/test/std/rec/epicsExportTest.db | 24 +++ .../database/test/std/rec/epicsExportTest.dbd | 16 ++ .../test/std/rec/epicsExportTestMain.c | 95 ++++++++++++ .../test/std/rec/epicsExportTestxx.cpp | 2 + modules/libcom/src/misc/epicsExport.h | 33 ++-- 8 files changed, 340 insertions(+), 9 deletions(-) create mode 100644 modules/database/test/std/rec/epicsExportTest.c create mode 100644 modules/database/test/std/rec/epicsExportTest.db create mode 100644 modules/database/test/std/rec/epicsExportTest.dbd create mode 100644 modules/database/test/std/rec/epicsExportTestMain.c create mode 100644 modules/database/test/std/rec/epicsExportTestxx.cpp diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 387c4191b..0435a1cc6 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -18,6 +18,11 @@ __This version of EPICS has not been released yet.__ __Add new items below here__ +### epicsExport simplifications + +`epicsExportAddress()`, `epicsExportRegistrar()` and `epicsRegisterFunction()` +no longer require to be wrapped in `extern "C" { }` in C++ code. + ### New `dbServerStats()` API for iocStats A new routine provides the ability to request channel and client counts from diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile index 377e747e0..d6aaf44d3 100644 --- a/modules/database/test/std/rec/Makefile +++ b/modules/database/test/std/rec/Makefile @@ -221,6 +221,34 @@ testHarness_SRCS += linkFilterTest.c TESTFILES += ../linkFilterTest.db TESTS += linkFilterTest +TARGETS += $(COMMON_DIR)/epicsExportTestIoc.dbd +DBDDEPENDS_FILES += epicsExportTestIoc.dbd$(DEP) +epicsExportTestIoc_DBD += base.dbd +epicsExportTestIoc_DBD += epicsExportTest.dbd +TESTFILES += $(COMMON_DIR)/epicsExportTestIoc.dbd ../epicsExportTest.db + +TESTLIBRARY += epicsExportTestLib +epicsExportTestLib_SRCS += epicsExportTest.c +epicsExportTestLib_LIBS += dbCore Com + +TESTPROD_HOST += epicsExportTest +TESTS += epicsExportTest +TARGETS += epicsExportTest$(OBJ) +epicsExportTest_SRCS += epicsExportTestMain.c +epicsExportTest_SRCS += epicsExportTestIoc_registerRecordDeviceDriver.cpp +epicsExportTest_LIBS = epicsExportTestLib + +TESTLIBRARY += epicsExportTestxxLib +epicsExportTestxxLib_SRCS += epicsExportTestxx.cpp +epicsExportTestxxLib_LIBS += dbCore Com + +TESTPROD_HOST += epicsExportTestxx +TESTS += epicsExportTestxx +TARGETS += epicsExportTestxx$(OBJ) +epicsExportTestxx_SRCS += epicsExportTestMain.c +epicsExportTestxx_SRCS += epicsExportTestIoc_registerRecordDeviceDriver.cpp +epicsExportTestxx_LIBS = epicsExportTestxxLib + # These are compile-time tests, no need to link or run TARGETS += dbHeaderTest$(OBJ) TARGET_SRCS += dbHeaderTest.cpp diff --git a/modules/database/test/std/rec/epicsExportTest.c b/modules/database/test/std/rec/epicsExportTest.c new file mode 100644 index 000000000..aaa3f2215 --- /dev/null +++ b/modules/database/test/std/rec/epicsExportTest.c @@ -0,0 +1,146 @@ +/*************************************************************************\ +* Copyright (c) 2025 Dirk Zimoch +* SPDX-License-Identifier: EPICS +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Compile and link test for epicsExport.h +* +* Test if macros eppicsExportAddr, epicsExportRegistrar and epicsRegisterFunction +* expand to valid C and C++ code, in the latter case regardless if +* wrapped with extern "C" {} or not. +* Also test that those macros have the intended effect in both cases. +* +* This file is compiled directly as C +* and included from epicsExportTestxx.cpp as C++ +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int i1, i2; +volatile int v1=10, v2=20; +const int c1=100, c2=200; + +static long myReadLongin(struct dbCommon* prec) +{ + struct longinRecord* pli = +#ifdef __cplusplus + reinterpret_cast(prec); +#else + (struct longinRecord*)prec; +#endif + pli->val=5; + return 0; +} + +/* Also test cast from user-specific const mydset to dset */ +const struct mydset { + long number; + long (*report)(int); + long (*init)(int); + long (*init_record)(struct dbCommon*); + long (*get_ioint_info)(int, struct dbCommon*, IOSCANPVT*); + long (*process)(struct dbCommon*); + long (*something_else)(struct dbCommon*); +} dset1 = { + 6, + NULL, + NULL, + NULL, + NULL, + myReadLongin, + NULL +}, dset2 = { + 6, + NULL, + NULL, + NULL, + NULL, + myReadLongin, + NULL +}; + +static void registrar1() { + i1++; + testPass("registrar1 executed"); +} + +static void registrar2() { + i2++; + testPass("registrar2 executed"); +} + +/* Also test cast from int(*)() to REGISTRAR */ +static int registrar3() { + i1++; + testPass("registrar3 executed"); + return 0; +} + +static int registrar4() { + i2++; + testPass("registrar4 executed"); + return 0; +} + +/* Test both, native (potentially C++) and extern "C" functions */ +static long aSubInit1(aSubRecord* prec) { + *(epicsInt32*)prec->a = 1; + return 0; +} + +static long aSubProc1(aSubRecord* prec) { + *(epicsInt32*)prec->b = 2; + return 0; +} + +#ifdef __cplusplus +extern "C" { +#endif +static long aSubInit2(aSubRecord* prec) { + *(epicsInt32*)prec->a = 3; + return 0; +} +static long aSubProc2(aSubRecord* prec) { + *(epicsInt32*)prec->b = 4; + return 0; +} +#ifdef __cplusplus +} +#endif + +/* Test without wrapping */ +epicsExportAddress(int, i1); +epicsExportAddress(int, v1); +epicsExportAddress(int, c1); +epicsExportAddress(dset, dset1); +epicsExportRegistrar(registrar1); +epicsExportRegistrar(registrar3); +epicsRegisterFunction(aSubInit1); +epicsRegisterFunction(aSubProc1); + +/* In C++ test wrapped in extern "C" {} */ +#ifdef __cplusplus +extern "C" { +#endif +epicsExportAddress(int, i2); +epicsExportAddress(int, v2); +epicsExportAddress(int, c2); +epicsExportAddress(dset, dset2); +epicsExportRegistrar(registrar2); +epicsExportRegistrar(registrar4); +epicsRegisterFunction(aSubInit2); +epicsRegisterFunction(aSubProc2); + +#ifdef __cplusplus +} +#endif diff --git a/modules/database/test/std/rec/epicsExportTest.db b/modules/database/test/std/rec/epicsExportTest.db new file mode 100644 index 000000000..8aa297b55 --- /dev/null +++ b/modules/database/test/std/rec/epicsExportTest.db @@ -0,0 +1,24 @@ +record(longin, "li1") { + field(DTYP, "dset1") + field(VAL, "-1") +} +record(longin, "li2") { + field(DTYP, "dset2") + field(VAL, "-2") +} +record(aSub, "asub1") { + field(FTA, "LONG") + field(FTB, "LONG") + field(INPA, "-1") + field(INPB, "-2") + field(INAM, "aSubInit1") + field(SNAM, "aSubProc1") +} +record(aSub, "asub2") { + field(FTA, "LONG") + field(FTB, "LONG") + field(INPA, "-3") + field(INPB, "-4") + field(INAM, "aSubInit2") + field(SNAM, "aSubProc2") +} diff --git a/modules/database/test/std/rec/epicsExportTest.dbd b/modules/database/test/std/rec/epicsExportTest.dbd new file mode 100644 index 000000000..fc9ee2f6d --- /dev/null +++ b/modules/database/test/std/rec/epicsExportTest.dbd @@ -0,0 +1,16 @@ +variable(i1,int) +variable(i2,int) +variable(v1,int) +variable(v2,int) +variable(c1,int) +variable(c2,int) +device(longin, CONSTANT, dset1, "dset1") +device(longin, CONSTANT, dset2, "dset2") +registrar(registrar1) +registrar(registrar2) +registrar(registrar3) +registrar(registrar4) +function(aSubInit1) +function(aSubProc1) +function(aSubInit2) +function(aSubProc2) diff --git a/modules/database/test/std/rec/epicsExportTestMain.c b/modules/database/test/std/rec/epicsExportTestMain.c new file mode 100644 index 000000000..2f6d11f1e --- /dev/null +++ b/modules/database/test/std/rec/epicsExportTestMain.c @@ -0,0 +1,95 @@ +/*************************************************************************\ +* Copyright (c) 2025 Dirk Zimoch +* SPDX-License-Identifier: EPICS +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Compile and link test for epicsExport.h +* +*/ + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +#endif +int epicsExportTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static int* testVarEquals(const char* name, int expected) +{ + const iocshVarDef *var; + int *p; + + var = iocshFindVariable(name); + if (!var) { + testFail("Cannot access variable %s", name); + return NULL; + } + if (var->type != iocshArgInt) { + testFail("Variable %s has wrong type", name); + return NULL; + } + p = (int*)(var->pval); + testOk(*p == expected, "Variable %s == %d", name, expected); + return p; +} + +MAIN(epicsExportTest) +{ + int *p1, *p2; + + testPlan(31); + testdbPrepare(); + testdbReadDatabase("epicsExportTestIoc.dbd", 0, 0); + testOk(epicsExportTestIoc_registerRecordDeviceDriver(pdbbase)==0, "registerRecordDeviceDriver"); + + testDiag("Testing if dsets and functions are found"); + testdbReadDatabase("epicsExportTest.db", 0, 0); + testIocInitOk(); + + testDiag("Testing if dsets work correctly"); + testdbGetFieldEqual("li1", DBF_LONG, -1); + testdbGetFieldEqual("li2", DBF_LONG, -2); + testdbPutFieldOk("li1.PROC", DBF_LONG, 1); + testdbPutFieldOk("li2.PROC", DBF_LONG, 1); + testdbGetFieldEqual("li1", DBF_LONG, 5); + testdbGetFieldEqual("li2", DBF_LONG, 5); + + testDiag("Testing if functions work correctly"); + testdbGetFieldEqual("asub1.A", DBF_LONG, 1); + testdbGetFieldEqual("asub1.B", DBF_LONG, -2); + testdbGetFieldEqual("asub2.A", DBF_LONG, 3); + testdbGetFieldEqual("asub2.B", DBF_LONG, -4); + testdbPutFieldOk("asub1.PROC", DBF_LONG, 1); + testdbPutFieldOk("asub2.PROC", DBF_LONG, 1); + testdbGetFieldEqual("asub1.A", DBF_LONG, 1); + testdbGetFieldEqual("asub1.B", DBF_LONG, 2); + testdbGetFieldEqual("asub2.A", DBF_LONG, 3); + testdbGetFieldEqual("asub2.B", DBF_LONG, 4); + + testDiag("Testing if variable access works"); + p1 = testVarEquals("i1", 2); + p2 = testVarEquals("i2", 2); + testVarEquals("v1", 10); + testVarEquals("v2", 20); + testVarEquals("c1", 100); + testVarEquals("c2", 200); + + if (p1 && p2) { + testDiag("Testing if variables are accessible from iocsh"); + testOk(iocshCmd("var i1,4") == 0, "Setting i1 = 4 in iocsh"); + testOk(iocshCmd("var i2,5") == 0, "Setting i2 = 5 in iocsh"); + testOk(*p1==4, "Variable i1 == 4"); + testOk(*p2==5, "Variable i2 == 5"); + } + + testIocShutdownOk(); + testdbCleanup(); + return testDone(); +} diff --git a/modules/database/test/std/rec/epicsExportTestxx.cpp b/modules/database/test/std/rec/epicsExportTestxx.cpp new file mode 100644 index 000000000..3bb1628ce --- /dev/null +++ b/modules/database/test/std/rec/epicsExportTestxx.cpp @@ -0,0 +1,2 @@ +// just compile as c++ +#include "epicsExportTest.c" diff --git a/modules/libcom/src/misc/epicsExport.h b/modules/libcom/src/misc/epicsExport.h index 7c084a602..356d62b39 100644 --- a/modules/libcom/src/misc/epicsExport.h +++ b/modules/libcom/src/misc/epicsExport.h @@ -41,6 +41,10 @@ extern "C" { typedef void (*REGISTRAR)(void); +#ifdef __cplusplus +} +#endif + #define EPICS_EXPORT_POBJ(typ, obj) pvar_ ## typ ## _ ## obj #define EPICS_EXPORT_PFUNC(fun) EPICS_EXPORT_POBJ(func, fun) @@ -75,12 +79,16 @@ typedef void (*REGISTRAR)(void); * * \param typ Object's data type. * \param obj Object's name. - * - * \note C++ code needs to wrap with @code extern "C" { } @endcode */ +#ifdef __cplusplus +#define epicsExportAddress(typ, obj) \ + extern "C" { epicsShareExtern typ *EPICS_EXPORT_POBJ(typ,obj); } \ + epicsShareDef typ *EPICS_EXPORT_POBJ(typ, obj) = (typ *) (char *) &obj +#else #define epicsExportAddress(typ, obj) \ epicsShareExtern typ *EPICS_EXPORT_POBJ(typ,obj); \ epicsShareDef typ *EPICS_EXPORT_POBJ(typ, obj) = (typ *) (char *) &obj +#endif /** \brief Declare a registrar function for exporting. * @@ -94,11 +102,15 @@ typedef void (*REGISTRAR)(void); \endcode * * \param fun Registrar function's name. - * - * \note C++ code needs to wrap with @code extern "C" { } @endcode */ +#ifdef __cplusplus +#define epicsExportRegistrar(fun) \ + extern "C" { extern epicsShareFunc REGISTRAR EPICS_EXPORT_PFUNC(fun); } \ + REGISTRAR EPICS_EXPORT_PFUNC(fun) = reinterpret_cast(&fun) +#else #define epicsExportRegistrar(fun) \ epicsShareFunc REGISTRAR EPICS_EXPORT_PFUNC(fun) = (REGISTRAR) &fun +#endif /** \brief Declare and register a function for exporting. * @@ -111,18 +123,21 @@ typedef void (*REGISTRAR)(void); \endcode * * \param fun Function's name - * - * \note C++ code needs to wrap with @code extern "C" { } @endcode */ +#ifdef __cplusplus +#define epicsRegisterFunction(fun) \ + static void register_func_ ## fun(void) \ + { \ + registryFunctionAdd(#fun, reinterpret_cast(fun)); \ + } \ + epicsExportRegistrar(register_func_ ## fun) +#else #define epicsRegisterFunction(fun) \ static void register_func_ ## fun(void) \ { \ registryFunctionAdd(#fun, (REGISTRYFUNCTION) fun); \ } \ epicsExportRegistrar(register_func_ ## fun) - -#ifdef __cplusplus -} #endif #endif /* INC_epicsExport_H */ From fc5d3c9a5c41bccc454a0227fbcf76bdc84e4055 Mon Sep 17 00:00:00 2001 From: JJL772 Date: Thu, 13 Jun 2024 16:37:43 -0400 Subject: [PATCH 16/19] Disable extraneous WIN32 declarations when including windows.h in epicsAtomicOSD.h --- .../libcom/src/osi/os/WIN32/epicsAtomicOSD.h | 57 +++++++++++++------ 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/modules/libcom/src/osi/os/WIN32/epicsAtomicOSD.h b/modules/libcom/src/osi/os/WIN32/epicsAtomicOSD.h index 6a9f4484e..46a4a85aa 100644 --- a/modules/libcom/src/osi/os/WIN32/epicsAtomicOSD.h +++ b/modules/libcom/src/osi/os/WIN32/epicsAtomicOSD.h @@ -19,27 +19,50 @@ #define EPICS_ATOMIC_OS_NAME "WIN32" -#ifdef VC_EXTRALEAN -# define VC_EXTRALEAN_DETECTED_epicsAtomicOSD_h -#else -# define VC_EXTRALEAN -#endif +/* Disable extra declarations that we don't need here (i.e. winsock1, rpc, etc.) */ +#pragma push_macro("WIN32_LEAN_AND_MEAN") +#undef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN -#ifdef STRICT -# define STRICT_DETECTED_epicsAtomicOSD_h -#else -# define STRICT -#endif +#pragma push_macro("STRICT") +#undef STRICT +#define STRICT + +/* Disable min/max macros from windows.h. These macros can cause issues with headers such as that declare or use std::min/max */ +#pragma push_macro("NOMINMAX") +#undef NOMINMAX +#define NOMINMAX + +/* Disable 'service controller' includes */ +#pragma push_macro("NOSERVICE") +#undef NOSERVICE +#define NOSERVICE + +/* Disable 'input management engine' includes */ +#pragma push_macro("NOIME") +#undef NOIME +#define NOIME + +/* Disable 'modem configuration extensions' includes */ +#pragma push_macro("NOMCX") +#undef NOMCX +#define NOMCX + +/* Disable GDI includes */ +#pragma push_macro("NOGDI") +#undef NOGDI +#define NOGDI #include "windows.h" -#ifndef VC_EXTRALEAN_DETECTED_epicsAtomicOSD_h -# undef VC_EXTRALEAN -#endif - -#ifndef STRICT_DETECTED_epicsAtomicOSD_h -# undef STRICT -#endif +/* Restore previous macro values */ +#pragma pop_macro("WIN32_LEAN_AND_MEAN") +#pragma pop_macro("STRICT") +#pragma pop_macro("NOMINMAX") +#pragma pop_macro("NOSERVICE") +#pragma pop_macro("NOIME") +#pragma pop_macro("NOMCX") +#pragma pop_macro("NOGDI") #if defined ( _WIN64 ) # define MS_ATOMIC_64 From 9fac52fa58075d5220749466c3794710b66d0426 Mon Sep 17 00:00:00 2001 From: JJL772 Date: Fri, 21 Jun 2024 17:28:23 -0700 Subject: [PATCH 17/19] Add a release note for the windows.h epicsAtomicOSD change --- documentation/RELEASE_NOTES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 0435a1cc6..82b9a921e 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -18,6 +18,14 @@ __This version of EPICS has not been released yet.__ __Add new items below here__ +### Reduce symbol and macro pollution from epicsAtomic.h on WIN32 + +`epicsAtomic.h` no longer pulls in as many unneeded declarations and macros from +`windows.h`. Prior to this change, including `epicsAtomic.h` at the wrong time +could result in unexpected compiler errors. Due to the nature of `windows.h`, +some unneeded declarations are still pulled in, however the number is greatly reduced. +Code that needs these declarations should explicitly include `windows.h` before `epicsAtomic.h`. + ### epicsExport simplifications `epicsExportAddress()`, `epicsExportRegistrar()` and `epicsRegisterFunction()` From 6ea6ae633b9ea1d97fe5a2c7eabcc2de2c6be4e0 Mon Sep 17 00:00:00 2001 From: Jeremy Lorelli Date: Sat, 15 Feb 2025 13:05:02 -0800 Subject: [PATCH 18/19] Define NOCRYPT, NOSOUND and NOKANJI before including windows.h --- .../libcom/src/osi/os/WIN32/epicsAtomicOSD.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/modules/libcom/src/osi/os/WIN32/epicsAtomicOSD.h b/modules/libcom/src/osi/os/WIN32/epicsAtomicOSD.h index 46a4a85aa..a1221deb7 100644 --- a/modules/libcom/src/osi/os/WIN32/epicsAtomicOSD.h +++ b/modules/libcom/src/osi/os/WIN32/epicsAtomicOSD.h @@ -53,6 +53,21 @@ #undef NOGDI #define NOGDI +/* Disable crypto stuff */ +#pragma push_macro("NOCRYPT") +#undef NOCRYPT +#define NOCRYPT + +/* Disable sound driver routines */ +#pragma push_macro("NOSOUND") +#undef NOSOUND +#define NOSOUND + +/* Disable Kanji writing system support */ +#pragma push_macro("NOKANJI") +#undef NOKANJI +#define NOKANJI + #include "windows.h" /* Restore previous macro values */ @@ -63,6 +78,9 @@ #pragma pop_macro("NOIME") #pragma pop_macro("NOMCX") #pragma pop_macro("NOGDI") +#pragma pop_macro("NOCRYPT") +#pragma pop_macro("NOSOUND") +#pragma pop_macro("NOKANJI") #if defined ( _WIN64 ) # define MS_ATOMIC_64 From e4ebc014011c8c1ae98a5de6d15c2aed76885e70 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 26 Feb 2025 20:15:06 -0600 Subject: [PATCH 19/19] Fix EPICS::PodXHtml and generate section links Overrides an internal method in Pod::Simple::XHTML that appends a section sign which links to this section. --- src/tools/EPICS/PodXHtml.pm | 23 +++++++++++++++++++++-- src/tools/style.css | 5 +++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/tools/EPICS/PodXHtml.pm b/src/tools/EPICS/PodXHtml.pm index d39212988..3a5e64502 100644 --- a/src/tools/EPICS/PodXHtml.pm +++ b/src/tools/EPICS/PodXHtml.pm @@ -7,8 +7,8 @@ use base 'Pod::Simple::XHTML'; BEGIN { if ($Pod::Simple::XHTML::VERSION < '3.16') { - # encode_entities() wasn't a method, add it - our *encode_entities = sub { + # Add encode_entities() as a method + sub encode_entities { my ($self, $str) = @_; my %entities = ( q{>} => 'gt', @@ -37,4 +37,23 @@ sub resolve_pod_page_link { return $ret; } +sub _end_head { + my $h = delete $_[0]{in_head}; + + my $add = $_[0]->html_h_level; + $add = 1 unless defined $add; + $h += $add - 1; + + my $id = $_[0]->idify($_[0]{htext}); + my $text = $_[0]{scratch}; + my $hid = qq{}; + my $link = qq{ §}; + $_[0]{'scratch'} = $_[0]->backlink && ($h - $add == 0) + # backlinks enabled && =head1 + ? qq{$hid$text $link} + : qq{$hid$text $link}; + $_[0]->emit; + push @{ $_[0]{'to_index'} }, [$h, $id, delete $_[0]{'htext'}]; +} + 1; diff --git a/src/tools/style.css b/src/tools/style.css index 09647403b..06df90945 100644 --- a/src/tools/style.css +++ b/src/tools/style.css @@ -69,6 +69,11 @@ A[href="#POD_ERRORS"] { color: #FF0000; } +A.sect { + color: #99ccff; + vertical-align: super; +} + TD { margin: 0; padding: 0;