Compare commits
56 Commits
PSI-7.0.8.
...
7.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4ebc01401 | ||
|
|
6ea6ae633b | ||
|
|
9fac52fa58 | ||
|
|
fc5d3c9a5c | ||
| c75ad2673e | |||
| b97a35fec8 | |||
|
|
350570134e | ||
|
|
72f3e75c8d | ||
|
|
90f97a7037 | ||
|
|
fd86f0ff04 | ||
|
|
bcc6cb96ae | ||
|
|
a4bc0db6e6 | ||
|
|
48ebe2c64e | ||
|
|
9f788996dc | ||
|
|
13d6ca598c | ||
| ee1a49045a | |||
|
|
78f263f359 | ||
| 43e75e3901 | |||
|
|
131578124b | ||
|
|
7bd3e7aa2e | ||
|
|
86154953f5 | ||
|
|
07572ab025 | ||
|
|
0733beae50 | ||
|
|
a3d8531008 | ||
|
|
7a6e11cae0 | ||
|
|
7384351181 | ||
|
|
890cbc2c0d | ||
|
|
2612b47c3f | ||
|
|
f4c474eb77 | ||
|
|
1735a821db | ||
|
|
1d19ba4cc2 | ||
|
|
144f9756ea | ||
|
|
48eed22f3b | ||
|
|
8ac2c87156 | ||
|
|
0d2ffcd97f | ||
|
|
c8eccfcb8f | ||
|
|
6fb40b02fe | ||
|
|
dad0ee9c89 | ||
|
|
065fe7cab6 | ||
|
|
e4ad4becde | ||
|
|
1cd141c540 | ||
| 72026a27a0 | |||
|
|
9fb820b46e | ||
|
|
721e9cc3a7 | ||
|
|
0186836449 | ||
|
|
b90ab7de13 | ||
|
|
333be085c0 | ||
|
|
169948967f | ||
|
|
5a11954c51 | ||
|
|
9f8a8b9c1f | ||
|
|
d0cf47cd6f | ||
|
|
f4aee8e6b7 | ||
| dac620a708 | |||
| 4ee766b6b1 | |||
| 5143258011 | |||
|
|
ead8b7e82b |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
162
.github/workflows/ci-scripts-build.yml
vendored
162
.github/workflows/ci-scripts-build.yml
vendored
@@ -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 <<EOF > 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
|
||||
|
||||
@@ -48,11 +48,11 @@ EPICS_VERSION = 7
|
||||
EPICS_REVISION = 0
|
||||
|
||||
# EPICS_MODIFICATION must be a number >=0 and <256
|
||||
EPICS_MODIFICATION = 8
|
||||
EPICS_MODIFICATION = 9
|
||||
|
||||
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
|
||||
# Not included in the official EPICS version number if zero
|
||||
EPICS_PATCH_LEVEL = 2
|
||||
EPICS_PATCH_LEVEL = 1
|
||||
|
||||
# Immediately after an official release the EPICS_PATCH_LEVEL is incremented
|
||||
# and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions)
|
||||
@@ -71,6 +71,3 @@ endif
|
||||
EPICS_SHORT_VERSION=$(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION)$(EPICS_PATCH_VSTRING)
|
||||
EPICS_VERSION_NUMBER=$(EPICS_SHORT_VERSION)$(EPICS_DEV_SNAPSHOT)$(EPICS_SITE_VSTRING)
|
||||
EPICS_VERSION_STRING="EPICS Version $(EPICS_VERSION_NUMBER)"
|
||||
|
||||
# Provide this in case anyone is still using the old name
|
||||
COMMIT_DATE="-no-date-"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
EPICS_CA_MAJOR_VERSION = 4
|
||||
EPICS_CA_MINOR_VERSION = 14
|
||||
EPICS_CA_MAINTENANCE_VERSION = 5
|
||||
EPICS_CA_MAINTENANCE_VERSION = 6
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Version number for the database APIs and shared library
|
||||
|
||||
EPICS_DATABASE_MAJOR_VERSION = 3
|
||||
EPICS_DATABASE_MINOR_VERSION = 23
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 2
|
||||
EPICS_DATABASE_MINOR_VERSION = 24
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 1
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Version number for the libcom APIs and shared library
|
||||
|
||||
EPICS_LIBCOM_MAJOR_VERSION = 3
|
||||
EPICS_LIBCOM_MINOR_VERSION = 23
|
||||
EPICS_LIBCOM_MAINTENANCE_VERSION = 2
|
||||
EPICS_LIBCOM_MINOR_VERSION = 24
|
||||
EPICS_LIBCOM_MAINTENANCE_VERSION = 1
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
|
||||
@@ -92,3 +92,6 @@ EPICS_IOC_LOG_FILE_NAME=
|
||||
EPICS_IOC_LOG_FILE_COMMAND=
|
||||
EPICS_IOC_LOG_FILE_LIMIT=1000000
|
||||
|
||||
# Set to 'YES' to call abort() rather than suspend the current thread
|
||||
# when an assert() fails
|
||||
EPICS_ABORT_ON_ASSERT=NO
|
||||
|
||||
11
configure/os/CONFIG.darwin-aarch64-debug.Common
Normal file
11
configure/os/CONFIG.darwin-aarch64-debug.Common
Normal file
@@ -0,0 +1,11 @@
|
||||
# CONFIG.darwin-aarch64-debug.Common
|
||||
#
|
||||
# Definitions for darwin-aarch64-debug host builds - darwin-aarch64 target build with debug compiler flags
|
||||
# Sites may override these definitions in CONFIG_SITE.darwin-aarch64-debug.Common
|
||||
#-------------------------------------------------------
|
||||
|
||||
include $(CONFIG)/os/CONFIG.darwin-aarch64.Common
|
||||
|
||||
# Removes -O optimization and adds -g compile option
|
||||
HOST_OPT=NO
|
||||
|
||||
14
configure/os/CONFIG.darwin-aarch64.darwin-aarch64-debug
Normal file
14
configure/os/CONFIG.darwin-aarch64.darwin-aarch64-debug
Normal file
@@ -0,0 +1,14 @@
|
||||
# CONFIG.darwin-aarch64.darwin-aarch64-debug
|
||||
#
|
||||
# Definitions for darwin-aarch64 host - darwin-aarch64-debug target build with debug compiler flags
|
||||
# Sites may override these definitions in CONFIG_SITE.darwin-aarch64.darwin-aarch64-debug
|
||||
#-------------------------------------------------------
|
||||
|
||||
-include $(CONFIG)/os/CONFIG.Common.darwin-aarch64
|
||||
-include $(CONFIG)/os/CONFIG.darwin-aarch64.darwin-aarch64
|
||||
-include $(CONFIG)/os/CONFIG_SITE.Common.darwin-aarch64
|
||||
-include $(CONFIG)/os/CONFIG_SITE.darwin-aarch64.darwin-aarch64
|
||||
|
||||
|
||||
BUILD_CLASS=HOST
|
||||
HOST_OPT = NO
|
||||
11
configure/os/CONFIG.darwin-x86-debug.Common
Normal file
11
configure/os/CONFIG.darwin-x86-debug.Common
Normal file
@@ -0,0 +1,11 @@
|
||||
# CONFIG.darwin-x86-debug.Common
|
||||
#
|
||||
# Definitions for darwin-x86-debug host builds - darwin-x86 target build with debug compiler flags
|
||||
# Sites may override these definitions in CONFIG_SITE.darwin-x86-debug.Common
|
||||
#-------------------------------------------------------
|
||||
|
||||
include $(CONFIG)/os/CONFIG.darwin-x86.Common
|
||||
|
||||
# Removes -O optimization and adds -g compile option
|
||||
HOST_OPT=NO
|
||||
|
||||
@@ -64,7 +64,3 @@ COMMANDLINE_LIBRARY ?= EPICS
|
||||
#else
|
||||
COMMANDLINE_LIBRARY ?= $(strip $(if $(wildcard $(if $(GNU_DIR),$(GNU_DIR)/include/readline/readline.h)), READLINE, EPICS))
|
||||
#endif
|
||||
|
||||
#if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE>2
|
||||
OP_SYS_CPPFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2
|
||||
#endif
|
||||
|
||||
@@ -8,41 +8,163 @@ under the 3.15 release to which they were originally committed.** Thus it is
|
||||
important to read more than just the first section to understand everything that
|
||||
has changed in each release.
|
||||
|
||||
The PVA submodules each have their own individual sets of release notes which
|
||||
should also be read to understand what has changed since earlier releases:
|
||||
The external PVA submodules each have their own individual release notes files.
|
||||
However the entries describing changes included in those submodules since EPICS
|
||||
7.0.5 have now been copied into the appropriate place of this file.
|
||||
|
||||
- [normativeTypes](https://github.com/epics-base/normativeTypesCPP/blob/master/documentation/RELEASE_NOTES.md)
|
||||
- [pvAccess](http://epics-base.github.io/pvAccessCPP/pvarelease_notes.html)
|
||||
- [pvData](http://epics-base.github.io/pvDataCPP/release_notes.html)
|
||||
- [pvDatabase](https://github.com/epics-base/pvDatabaseCPP/blob/master/documentation/RELEASE_NOTES.md)
|
||||
- [pva2pva](https://epics-base.github.io/pva2pva/release_notes.html)
|
||||
- [pvaClient](https://github.com/epics-base/pvaClientCPP/blob/master/documentation/RELEASE_NOTES.md)
|
||||
__This version of EPICS has not been released yet.__
|
||||
|
||||
**This version of EPICS has not been released yet.**
|
||||
## Changes made on the 7.0 branch since 7.0.9
|
||||
|
||||
## Changes made on the 7.0 branch since 7.0.8.1
|
||||
__Add new items below here__
|
||||
|
||||
### DBE_PROPERTY event rate changed
|
||||
### Reduce symbol and macro pollution from epicsAtomic.h on WIN32
|
||||
|
||||
Updating property fields now only post DBE_PROPERTY events if the
|
||||
`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()`
|
||||
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
|
||||
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.
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.9
|
||||
|
||||
### Core documentation published at ReadTheDocs
|
||||
|
||||
The `documentation` directory's `Makefile` can now run various publication scripts including Sphinx and Doxygen to generate formatted documentation that is now being published
|
||||
[at docs.epics-controls.org](https://docs.epics-controls.org/projects/base/en/latest/index.html)
|
||||
and integrated into the main [EPICS Documentation website](https://docs.epics-controls.org/en/latest/index.html).
|
||||
The best place to find out more about these mechanisms is the
|
||||
[Contribution Guide](https://docs.epics-controls.org/en/latest/CONTRIBUTING.html)
|
||||
although it doesn't currently cover the new processes added to epics-base.
|
||||
|
||||
Much of the documentation generated from .dbd.pod files at build time is now
|
||||
also being converted into MarkDown (.md) files and installed into the top-level
|
||||
`doc` directory. Some users might find it quicker to look up information about a
|
||||
record type by opening these files in a text editor intead of opening a browser
|
||||
and loading the HTML versions or finding and opening the files from the EPICS
|
||||
Documentation site.
|
||||
|
||||
### Post monitors from compress record when it's reset
|
||||
|
||||
Writing into a compress record's `RES` field now posts a monitor event instead
|
||||
of only changing `VAL`. Monitor clients will therefore receive an empty array.
|
||||
|
||||
### The AMSG error message propagates through MSS links
|
||||
|
||||
A database link with the MSS attribute will now propagate not only SEVR and
|
||||
STAT, but also AMSG. This field contains additional information that complements
|
||||
STAT. Links with MS or MSI attributes do not propagate STAT, and therefore do
|
||||
not propagate AMSG, either.
|
||||
|
||||
Channel Access links do not propagate AMSG, regardless of the MSS attribute,
|
||||
because the message is not available as Channel metadata.
|
||||
|
||||
### Reloading record aliases
|
||||
|
||||
Aliases can now be defined more than once as long as they still refer to the
|
||||
same record, unless the global variable `dbRecordsOnceOnly` is non-zero.
|
||||
This allows database files to be loaded multiple times, even if they contain
|
||||
alias definitions.
|
||||
|
||||
### `DBE_PROPERTY` event rate changed
|
||||
|
||||
Updating property fields now only posts `DBE_PROPERTY` events if the
|
||||
field actually changed.
|
||||
|
||||
### Changes to msi related to include paths
|
||||
|
||||
There are two changes to `msi` included here.
|
||||
|
||||
`msi` now treats files included by .template or .substutiions files in a more
|
||||
consistent way: for relative paths, it will always look relative to the current
|
||||
working directory if no `-I` flags are passed, and if they are passed then it
|
||||
will search for the _relative_ path from each of those flags. That is, the
|
||||
following will now find the file `bar.template` located at
|
||||
`/some/path/rel/path/bar.template`
|
||||
```
|
||||
$ cat foo.substitutions
|
||||
file rel/path/bar.template {
|
||||
# contents
|
||||
}
|
||||
$ msi -I /some/path foo.substitutions
|
||||
```
|
||||
|
||||
Note that this does provide one change from previous behaviour: when opening a
|
||||
file from the command line, `msi` will not use the `-I`-specified paths to
|
||||
search for the file, but will only work relative to the current working
|
||||
directory, consistent with most commandline utilities.
|
||||
|
||||
### Allow users to delete previously created records from the database
|
||||
|
||||
From this release, record instances and aliases that have already been loaded
|
||||
by an IOC can be removed from the database again before the call to iocInit
|
||||
by loading a second instance of the named records but using `"#"` in place of
|
||||
the record type. Values for the fields are not required or advised, just use
|
||||
an empty record body { }. This is useful when a template defines records that
|
||||
an empty record body `{}`. This is useful when a template defines records that
|
||||
are not wanted in some IOCs, without having to split or duplicate the original
|
||||
template.
|
||||
|
||||
For example this will remove the record named "unwanted":
|
||||
|
||||
```
|
||||
record("#", "unwanted") { }
|
||||
record("#", "unwanted") {}
|
||||
```
|
||||
|
||||
### Only keep readline history for interactive sessions
|
||||
|
||||
Previously, all IOCsh commands were persisited in the libreadline history
|
||||
(when readline support is included).
|
||||
Going forward, only interactive commands are saved.
|
||||
|
||||
### Type change to asTrap serverSpecific data
|
||||
|
||||
Change `void*` to `dbChannel*` in `asTrapWriteBeforeWithData()` and
|
||||
`asTrapWriteMessage::serverSpecific` to reflect the reality since
|
||||
the `dbAddr*` to `dbChannel*` migration.
|
||||
External code wishing to support both before and after 3.15 should
|
||||
already be conditionally casting to/from the appropriate type.
|
||||
|
||||
### Fix issues with `_FORTIFY_SOURCE=3`
|
||||
|
||||
This release fixes the false positives failures whhen building with `_FORTIFY_SOURCE` level 3.
|
||||
The override introduced in 7.0.8.1 has been removed.
|
||||
|
||||
### Other
|
||||
|
||||
- genVersionHeader: work with git submodules and worktrees.
|
||||
- avoid UB with self `pthread_join()`
|
||||
- freebsd: Add support for x86 and amd64 builds
|
||||
- Clear AMSG when SEVR becomes zero.
|
||||
- `seqRecord` fix support for link `DLY0`
|
||||
- Add `ABORT_ON_ASSERT` flag to `CONFIG_SITE_ENV`
|
||||
- rationalize osdMutex
|
||||
|
||||
### Submodule updates
|
||||
|
||||
The pvDatabase module was updated to version 4.7.2:
|
||||
|
||||
* Resolved issue with changed field set in the case where the top level (master)
|
||||
field ("_") is not requested by the client, but the master field callback causes
|
||||
all fields to be marked as updated, rather than only those fields that have
|
||||
actually been modified.
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.8.1
|
||||
|
||||
### Limit to `_FORTIFY_SOURCE=2`
|
||||
@@ -280,6 +402,50 @@ The floating point modulo function `FMOD(NUM,DEN)` has been added to the CALC
|
||||
expression engine and is available to all software using that (calc and calcout
|
||||
record types, access security library and some extensions).
|
||||
|
||||
### Submodule updates
|
||||
|
||||
The pvData module was updated to version 8.0.6:
|
||||
|
||||
- Compatible changes
|
||||
- Actually enable JSON-5 output in PVStructure::Formatter::JSON when available.
|
||||
- Fix unaligned access issues for some ARM/Linux targets.
|
||||
|
||||
The pvAccess module was updated to version 7.1.7:
|
||||
|
||||
- Changes
|
||||
- Registering the PVA server with the IOC now sets the `PVAS_SERVER_PORT`
|
||||
variable in the environment.
|
||||
|
||||
The pva2pva module was updated to version 1.4.1:
|
||||
|
||||
- Bug Fixes
|
||||
- `dbLoadGroup` was fixed
|
||||
- Additions
|
||||
- Support for "meta" member at top of array of structs
|
||||
|
||||
The pvDatabase module was updated to version 4.7.1:
|
||||
|
||||
* Added data distributor plugin which can be used for distributing data between
|
||||
a group of clients. The plugin is triggered by the request string of the
|
||||
form:
|
||||
|
||||
`_[distributor=group:<group id>;set:<set_id>;trigger:<field_name>;updates:<n_updates>;mode:<update_mode>]`
|
||||
|
||||
The plugin parameters are optional and are described bellow:
|
||||
|
||||
- group: this parameter indicates a group that client application belongs to (default value: "default"); groups of clients are completely independent of each other
|
||||
|
||||
- set: this parameter designates a client set that application belongs to within its group (default value: "default")
|
||||
|
||||
- trigger: this is the PV structure field that distinguishes different channel updates (default value: "timeStamp"); for example, for area detector images one could use the "uniqueId" field of the NTND structure
|
||||
|
||||
- updates: this parameter configures how many sequential updates a client (or a set of clients) will receive before the data distributor starts updating the next one (default value: "1")
|
||||
|
||||
- mode: this parameter configures how channel updates are to be distributed between clients in a set:
|
||||
- one: update goes to one client per set
|
||||
- all: update goes to all clients in a set
|
||||
- default is "one" if client set id is not specified, and "all" if set id is specified
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.7
|
||||
@@ -358,10 +524,10 @@ changed to `(p)->dtor`.
|
||||
The order over operations when processing a waveformRecord is adjusted
|
||||
so that updates to NORD is posted with the correct timestamp.
|
||||
|
||||
### Automatic COMMANDLINE_LIBRARY w/ newer compilers
|
||||
### Automatic `COMMANDLINE_LIBRARY` with newer compilers
|
||||
|
||||
When built with a compiler supporting `__has_include<>`, the presence
|
||||
of the `<readline/readline.h>` will be used to automatically determine
|
||||
of a `readline/readline.h` header will be used to automatically determine
|
||||
a default value for `COMMANDLINE_LIBRARY`.
|
||||
|
||||
Mingw builds with readline support now link `-ltermcap` instead of `-lcurses`.
|
||||
@@ -530,6 +696,44 @@ or if unsupported (`$TERM` not set, or Windows < 10).
|
||||
The `dbnd` server side filter now passes through alarm and property
|
||||
change events, even when not exceeding the deadband.
|
||||
|
||||
### Submodule updates
|
||||
|
||||
The pvData module was updated to version 8.0.5:
|
||||
|
||||
- Compatible changes
|
||||
- Internal changes to use the YAJL API for generating JSON and JSON-5 output.
|
||||
|
||||
The pvAccess module was updated to version 7.1.6:
|
||||
|
||||
- Changes to caProvider
|
||||
- Bug fix related to enum values.
|
||||
- More internal changes to improve performance when connecting tens of
|
||||
thousands of CA channels.
|
||||
- Several minor internal improvements.
|
||||
|
||||
The pva2pva module was updated to version 1.4.0:
|
||||
|
||||
- Bug Fixes
|
||||
- Apply ACF when writing to atomic group
|
||||
- Additions
|
||||
- Add new "structure" to @ref qsrv_group_map_types
|
||||
- Changes
|
||||
- Add Access Security hooks for single and group writes.
|
||||
- Enable "Async Soft Channel" for output links
|
||||
- When built against Base 7.0.6.1, set timeStamp.userTag from UTAG field.
|
||||
- Add DTYP="QSRV Set UTag" for longin, which sets UTAG=VAL.
|
||||
|
||||
The pvDatabase module was updated to version 4.7.0:
|
||||
|
||||
* Added support for the whole structure (master field) server side plugins.
|
||||
The whole structure is identified as the `_` string, and a pvRequest string
|
||||
that applies a plugin to it takes the form:
|
||||
|
||||
`field(_[XYZ=A:3;B:uniqueId])`
|
||||
|
||||
where `XYZ` is the name of a specific filter plugin that takes parameters
|
||||
`A` and `B` with values `3` and `uniqueId` respectively.
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.6.1
|
||||
@@ -596,6 +800,15 @@ This was done to simplify the code and may have improved performance slightly fo
|
||||
|
||||
Many of the built-in record types have had improvements to their documentation with additional fields added to the tables, rewrites of descriptions and links to other documents added or fixed.
|
||||
|
||||
### Submodule updates
|
||||
|
||||
The pvAccess module was updated to version 7.1.4:
|
||||
|
||||
- Changes to caProvider
|
||||
- Resolve issues with pv structures that don't have a value field
|
||||
- Add NULL checks for handling unusual structures
|
||||
- Speed up channel creation when using large numbers of channels
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.6
|
||||
@@ -606,7 +819,7 @@ These target architectures have been removed:
|
||||
|
||||
+ darwin-ppc, darwin-ppcx86
|
||||
+ linux-386, linux-486, linux-586, linux-686, linux-athlon (cross-build)
|
||||
+ linux-cris, linux-cris_v10, linux-cris_v32 (cross-build)
|
||||
+ linux-cris, linux-cris\_v10, linux-cris\_v32 (cross-build)
|
||||
+ RTEMS-at91rm9200ek, RTEMS-gen68360, RTEMS-mcp750, RTEMS-mvme167,
|
||||
RTEMS-psim (cross-build)
|
||||
|
||||
@@ -624,9 +837,9 @@ running on RTEMS 5:
|
||||
|
||||
- RTEMS-beagleboneblack
|
||||
- RTEMS-pc686
|
||||
- RTEMS-qoriq_e500 (MVME2500)
|
||||
- RTEMS-xilinx_zynq_a9_qemu
|
||||
- RTEMS-xilinx_zynq_zedboard
|
||||
- RTEMS-qoriq\_e500 (MVME2500)
|
||||
- RTEMS-xilinx\_zynq\_a9\_qemu
|
||||
- RTEMS-xilinx\_zynq\_zedboard
|
||||
|
||||
The EPICS support for RTEMS 4 has always relied on RTEMS-specific
|
||||
kernel APIs which cannot be used on an SMP system, so a new port was
|
||||
@@ -638,7 +851,7 @@ to run `make distclean` if switching a single source tree from one
|
||||
to the other (both header files and dependency files are different
|
||||
between the two and must be cleaned out).
|
||||
|
||||
The configuration variable RTEMS_VERSION in the EPICS config file
|
||||
The configuration variable `RTEMS_VERSION` in the EPICS config file
|
||||
`configure/os/CONFIG_SITE.Common.RTEMS` must be set to the full 3-
|
||||
part version number for RTEMS 4 releases, e.g. `4.9.1`, `4.10.2`
|
||||
but for RTEMS 5.1 and later it must only contain the major version
|
||||
@@ -836,6 +1049,39 @@ Test programs written directly in Perl as a `.plt` script should implement a
|
||||
similar timeout for themselves. The "netget" test in Base does this in a way
|
||||
that works on Windows as well as Unix-like hosts.
|
||||
|
||||
### Submodule updates
|
||||
|
||||
The pvAccess module was updated to version 7.1.4:
|
||||
|
||||
- Changes
|
||||
- Adjust argument parsing with pvput (Jesus Vasquez).
|
||||
|
||||
The pva2pva module was updated to version 1.3.1:
|
||||
|
||||
- Bug Fixes
|
||||
- Correct handling for server side filters.
|
||||
- Changes
|
||||
- Syncing softMain.cpp with epics-base
|
||||
|
||||
The pvDatabase module was updated to version 4.6.0:
|
||||
|
||||
* Access Security is now supported.
|
||||
* <b>special</b> has been revised and extended.
|
||||
* addRecord, removeRecord, processRecord, and traceRecord are replaced by pvdbcr versions.
|
||||
* <b>support</b> is DEPRECATED
|
||||
|
||||
The pvaClient module was updated to version 4.8.0:
|
||||
|
||||
* `PvaClientNTMultiData::getChannelChangeFlags` is a new method. It fixes
|
||||
issue #66.
|
||||
* Fix for issue #68. Both `PvaClientArray` and `PvaClientField` are not longer
|
||||
present. Neither was previously implemented.
|
||||
* Several public methods are now protected. They were never meant to be called
|
||||
by clients.
|
||||
* Issue #70 has been fixed.
|
||||
* Changes was made to increase the performance of `pvaMultiChannel`.
|
||||
* doxygen changes were made.
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.5
|
||||
@@ -873,7 +1119,7 @@ compile device supports as loadable modules.
|
||||
### Priority inversion safe Posix mutexes
|
||||
|
||||
On Posix systems, epicsMutex now support priority inheritance if available.
|
||||
The IOC needs to run with SCHED_FIFO engaged to use these.
|
||||
The IOC needs to run with `SCHED_FIFO` engaged to use these.
|
||||
Support for Posix implementations before POSIX.1-2001 (`_XOPEN_SOURCE < 500`,
|
||||
glibc version < 2.3.3) has been dropped.
|
||||
|
||||
@@ -1006,14 +1252,14 @@ properly handle zero-length arrays. The `caget`, `caput` and `camonitor`
|
||||
client programs are known to work with empty arrays as long as they were
|
||||
built with this or a later version of EPICS.
|
||||
|
||||
#### Change to the db_access.h `dbr_size_n(TYPE, COUNT)` macro
|
||||
#### Change to the db\_access.h `dbr_size_n(TYPE, COUNT)` macro
|
||||
|
||||
When called with COUNT=0 this macro no longer returns the number of bytes
|
||||
required for a scalar (1 element) but for an empty array (0 elements).
|
||||
Make sure code that uses this doesn't call it with COUNT=0 when it really
|
||||
means COUNT=1.
|
||||
|
||||
Note that the db_access.h header file is included by cadef.h so the change
|
||||
Note that the db\_access.h header file is included by cadef.h so the change
|
||||
can impact Channel Access client programs that use this macro.
|
||||
|
||||
#### Channel Access support for zero-length arrays
|
||||
@@ -1123,6 +1369,35 @@ GNUmake added the directive `undefine` in version 3.82 to allow variables to
|
||||
be undefined. Support for this has been added to the EPICS Release file parser,
|
||||
so `undefine` can now be used in configure/RELEASE files to unset variables.
|
||||
|
||||
|
||||
### Submodule updates
|
||||
|
||||
The pvData module was updated to version 8.0.4:
|
||||
|
||||
- Incompatible changes
|
||||
- Remove `ByteBuffer::align()`
|
||||
- Compatible changes
|
||||
- Deprecate `SerializableControl::alignBuffer()` and
|
||||
`DeserializableControl::alignData()`
|
||||
- `shared_vector_convert<>()` fix convert of empty, untyped, array
|
||||
|
||||
The pvAccess module was updated to version 7.1.3:
|
||||
|
||||
- Bug fixes
|
||||
- Increase default TCP timeout to 40 seconds.
|
||||
Applies a 4/3 multiplier on `$EPICS_PVA_CONN_TMO` for compatibility.
|
||||
- CA Provider implementation restructured to simplify, reduce duplication
|
||||
and fix issues #163 and #165.
|
||||
- Changes
|
||||
- Enable building of pvtools to all except vxWorks, RTEMS and iOS.
|
||||
|
||||
The pva2pva module was updated to version 1.3.0:
|
||||
|
||||
- Changes
|
||||
- Add `dbLoadGroup()` iocsh function to read group JSON definitions
|
||||
from a file. Mappings in files must refer to full record names
|
||||
instead of fields. eg. 'recname.VAL' instead of 'VAL'.
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.4.1
|
||||
@@ -1188,7 +1463,7 @@ The following launchpad bugs have fixes included in this release:
|
||||
operators on aarch64
|
||||
- [lp: 1853148](https://bugs.launchpad.net/bugs/1853148), mingw compiler
|
||||
problem with printf/scanf formats
|
||||
- [lp: 1852653](https://bugs.launchpad.net/bugs/1852653), USE_TYPED_DSET
|
||||
- [lp: 1852653](https://bugs.launchpad.net/bugs/1852653), `USE_TYPED_DSET`
|
||||
incompatible with C++
|
||||
- [lp: 1862328](https://bugs.launchpad.net/bugs/1862328), Race condition on
|
||||
IOC start leaves rsrv unresponsive
|
||||
@@ -1198,7 +1473,7 @@ The following launchpad bugs have fixes included in this release:
|
||||
- [lp: 1868680](https://bugs.launchpad.net/bugs/1868680), Access Security file
|
||||
reload (asInit) fails
|
||||
|
||||
### \*_API macros in EPICS headers
|
||||
### `*_API` macros in EPICS headers
|
||||
|
||||
Internally, the Com and ca libraries now express dllimport/export (Windows)
|
||||
and symbol visibility (GCC) using library-specific macros (eg. `LIBCOM_API`)
|
||||
@@ -1432,7 +1707,7 @@ The API functions `epicsGetExecDir()` and `epicsGetExecName()` are also
|
||||
added to `osiFileName.h` to provide runtime access to the directory or
|
||||
filename of the executable with which the process was started.
|
||||
|
||||
### Decouple LINKER_USE_RPATH and STATIC_BUILD
|
||||
### Decouple `LINKER_USE_RPATH` and `STATIC_BUILD`
|
||||
|
||||
Previously, setting `STATIC_BUILD=NO` implied `LINKER_USE_RPATH=NO`.
|
||||
This is no longer the case. Setting `LINKER_USE_RPATH=YES` will
|
||||
@@ -2011,7 +2286,7 @@ number instead, like this:
|
||||
Channel Access does not (and probably never will) directly support 64-bit
|
||||
integer types, so the new field types are presented to the CA server as
|
||||
`DBF_DOUBLE` values. This means that field values larger than 2^52
|
||||
(0x10_0000_0000_0000 = 4503599627370496) cannot be transported over Channel
|
||||
(0x10\_0000\_0000\_0000 = 4503599627370496) cannot be transported over Channel
|
||||
Access without their least significant bits being truncated. The EPICS V4
|
||||
pvAccess network protocol _can_ transport 64-bit data types however, and a
|
||||
future release of the pvaSrv module will connect this ability to the fields of
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -285,7 +285,11 @@ Inf (Infinite) value. UDF defaults to TRUE but can be set in a database file.
|
||||
Record and device support routines which write to the VAL field are generally
|
||||
responsible for setting and clearing UDF.
|
||||
|
||||
=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF
|
||||
The B<UDFS> field specifies the alarm severity that the record will be set to
|
||||
whenever its value is undefined (i.e., the UDF field is 1). This includes the
|
||||
initial severity of the record being undefined after the IOC boots.
|
||||
|
||||
=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF, UDFS
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
@@ -226,25 +226,48 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
}
|
||||
|
||||
if (!status && precord != dbChannelRecord(chan))
|
||||
recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode,
|
||||
recGblInheritSevrMsg(plink->value.pv_link.pvlMask & pvlOptMsMode,
|
||||
plink->precord,
|
||||
dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr);
|
||||
dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr,
|
||||
dbChannelRecord(chan)->amsg);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Some records get options (precsision, units, ...) for some fields
|
||||
* from an input link. We need to catch the case that this link
|
||||
* points back to the same field or we will end in an infinite recursion.
|
||||
*/
|
||||
static long dbDbGetOptionLoopSafe(const struct link *plink, short dbrType,
|
||||
void *pbuffer, long option)
|
||||
{
|
||||
/* We need to cast away const to set the flags.
|
||||
That's ok because we know that plink is never actually readonly.
|
||||
And we reset everything to its original state.
|
||||
*/
|
||||
struct link *mutable_plink = (struct link *)plink;
|
||||
long status = S_dbLib_badLink;
|
||||
dbChannel *chan = linkChannel(plink);
|
||||
DBADDR *paddr = &chan->addr;
|
||||
long number_elements = 0;
|
||||
|
||||
dbScanLock(paddr->precord);
|
||||
if (!(mutable_plink->flags & DBLINK_FLAG_VISITED)) {
|
||||
mutable_plink->flags |= DBLINK_FLAG_VISITED;
|
||||
status = dbGet(paddr, dbrType, pbuffer, &option, &number_elements, NULL);
|
||||
mutable_plink->flags &= ~DBLINK_FLAG_VISITED;
|
||||
}
|
||||
dbScanUnlock(paddr->precord);
|
||||
return status;
|
||||
}
|
||||
|
||||
static long dbDbGetControlLimits(const struct link *plink, double *low,
|
||||
double *high)
|
||||
{
|
||||
dbChannel *chan = linkChannel(plink);
|
||||
DBADDR *paddr = &chan->addr;
|
||||
struct buffer {
|
||||
DBRctrlDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_CTRL_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
NULL);
|
||||
long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_CTRL_DOUBLE);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
@@ -257,16 +280,11 @@ static long dbDbGetControlLimits(const struct link *plink, double *low,
|
||||
static long dbDbGetGraphicLimits(const struct link *plink, double *low,
|
||||
double *high)
|
||||
{
|
||||
dbChannel *chan = linkChannel(plink);
|
||||
DBADDR *paddr = &chan->addr;
|
||||
struct buffer {
|
||||
DBRgrDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_GR_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
NULL);
|
||||
long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_GR_DOUBLE);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
@@ -279,16 +297,11 @@ static long dbDbGetGraphicLimits(const struct link *plink, double *low,
|
||||
static long dbDbGetAlarmLimits(const struct link *plink, double *lolo,
|
||||
double *low, double *high, double *hihi)
|
||||
{
|
||||
dbChannel *chan = linkChannel(plink);
|
||||
DBADDR *paddr = &chan->addr;
|
||||
struct buffer {
|
||||
DBRalDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_AL_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_AL_DOUBLE);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
@@ -302,16 +315,11 @@ static long dbDbGetAlarmLimits(const struct link *plink, double *lolo,
|
||||
|
||||
static long dbDbGetPrecision(const struct link *plink, short *precision)
|
||||
{
|
||||
dbChannel *chan = linkChannel(plink);
|
||||
DBADDR *paddr = &chan->addr;
|
||||
struct buffer {
|
||||
DBRprecision
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_PRECISION;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_PRECISION);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
@@ -322,16 +330,11 @@ static long dbDbGetPrecision(const struct link *plink, short *precision)
|
||||
|
||||
static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize)
|
||||
{
|
||||
dbChannel *chan = linkChannel(plink);
|
||||
DBADDR *paddr = &chan->addr;
|
||||
struct buffer {
|
||||
DBRunits
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_UNITS;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_UNITS);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
@@ -376,8 +379,8 @@ static long dbDbPutValue(struct link *plink, short dbrType,
|
||||
dbCommon *pdest = dbChannelRecord(chan);
|
||||
long status = dbPut(paddr, dbrType, pbuffer, nRequest);
|
||||
|
||||
recGblInheritSevr(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta,
|
||||
psrce->nsev);
|
||||
recGblInheritSevrMsg(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta,
|
||||
psrce->nsev, psrce->namsg);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
|
||||
@@ -75,21 +75,21 @@ static const iocshFuncDef dbbFuncDef = {"dbb",1,dbbArgs,
|
||||
"Set Breakpoint on a record\n"
|
||||
"This command spawns one breakpoint continuation task per lockset,"
|
||||
" in which further record execution is run\n"};
|
||||
static void dbbCallFunc(const iocshArgBuf *args) { dbb(args[0].sval);}
|
||||
static void dbbCallFunc(const iocshArgBuf *args) { iocshSetError(dbb(args[0].sval));}
|
||||
|
||||
/* dbd */
|
||||
static const iocshArg dbdArg0 = { "record name",iocshArgStringRecord};
|
||||
static const iocshArg * const dbdArgs[1] = {&dbdArg0};
|
||||
static const iocshFuncDef dbdFuncDef = {"dbd",1,dbdArgs,
|
||||
"Remove breakpoint from a record.\n"};
|
||||
static void dbdCallFunc(const iocshArgBuf *args) { dbd(args[0].sval);}
|
||||
static void dbdCallFunc(const iocshArgBuf *args) { iocshSetError(dbd(args[0].sval));}
|
||||
|
||||
/* dbc */
|
||||
static const iocshArg dbcArg0 = { "record name",iocshArgStringRecord};
|
||||
static const iocshArg * const dbcArgs[1] = {&dbcArg0};
|
||||
static const iocshFuncDef dbcFuncDef = {"dbc",1,dbcArgs,
|
||||
"Continue processing in a lockset until next breakpoint is found.\n"};
|
||||
static void dbcCallFunc(const iocshArgBuf *args) { dbc(args[0].sval);}
|
||||
static void dbcCallFunc(const iocshArgBuf *args) { iocshSetError(dbc(args[0].sval));}
|
||||
|
||||
/* dbs */
|
||||
static const iocshArg dbsArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -97,12 +97,12 @@ static const iocshArg * const dbsArgs[1] = {&dbsArg0};
|
||||
static const iocshFuncDef dbsFuncDef = {"dbs",1,dbsArgs,
|
||||
"Step through record processing within a lockset.\n"
|
||||
"If called without an argument, automatically steps with the last breakpoint.\n"};
|
||||
static void dbsCallFunc(const iocshArgBuf *args) { dbs(args[0].sval);}
|
||||
static void dbsCallFunc(const iocshArgBuf *args) { iocshSetError(dbs(args[0].sval));}
|
||||
|
||||
/* dbstat */
|
||||
static const iocshFuncDef dbstatFuncDef = {"dbstat",0,0,
|
||||
"Print list of suspended records, and breakpoints set in locksets.\n"};
|
||||
static void dbstatCallFunc(const iocshArgBuf *args) { dbstat();}
|
||||
static void dbstatCallFunc(const iocshArgBuf *args) { iocshSetError(dbstat());}
|
||||
|
||||
/* dbp */
|
||||
static const iocshArg dbpArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -119,7 +119,9 @@ static const iocshFuncDef dbpFuncDef = {
|
||||
" 3 - Fields of minor interest to a System developer.\n"
|
||||
" 4 - Internal record fields.\n"};
|
||||
static void dbpCallFunc(const iocshArgBuf *args)
|
||||
{ dbp(args[0].sval,args[1].ival);}
|
||||
{
|
||||
iocshSetError(dbp(args[0].sval,args[1].ival));
|
||||
}
|
||||
|
||||
/* dbap */
|
||||
static const iocshArg dbapArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -127,7 +129,7 @@ static const iocshArg * const dbapArgs[1] = {&dbapArg0};
|
||||
static const iocshFuncDef dbapFuncDef = {"dbap",1,dbapArgs,
|
||||
"Auto Print.\n"
|
||||
"Toggle automatic printing after processing a record that has a breakpoint.\n"};
|
||||
static void dbapCallFunc(const iocshArgBuf *args) { dbap(args[0].sval);}
|
||||
static void dbapCallFunc(const iocshArgBuf *args) { iocshSetError(dbap(args[0].sval));}
|
||||
|
||||
/* dbsr */
|
||||
static const iocshArg dbsrArg0 = { "interest level",iocshArgInt};
|
||||
@@ -150,7 +152,7 @@ static const iocshFuncDef dbcarFuncDef = {"dbcar",2,dbcarArgs,
|
||||
" 2 - Shows info. for all links.\n"};
|
||||
static void dbcarCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbcar(args[0].sval,args[1].ival);
|
||||
iocshSetError(dbcar(args[0].sval,args[1].ival));
|
||||
}
|
||||
|
||||
/* dbjlr */
|
||||
@@ -162,7 +164,7 @@ static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs,
|
||||
"List all JSON links in a record. If no record is specified, print for all\n"};
|
||||
static void dbjlrCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbjlr(args[0].sval,args[1].ival);
|
||||
iocshSetError(dbjlr(args[0].sval,args[1].ival));
|
||||
}
|
||||
|
||||
/* dbel */
|
||||
@@ -176,7 +178,7 @@ static const iocshFuncDef dbelFuncDef = {"dbel",2,dbelArgs,
|
||||
"Example: dbel aitest 2\n"};
|
||||
static void dbelCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbel(args[0].sval, args[1].ival);
|
||||
iocshSetError(dbel(args[0].sval, args[1].ival));
|
||||
}
|
||||
|
||||
/* dba */
|
||||
@@ -187,7 +189,7 @@ static const iocshFuncDef dbaFuncDef = {"dba",1,dbaArgs,
|
||||
"Print information in the dbAddr structure for a specific field.\n"
|
||||
"If no field is specified, VAL is assumed.\n\n"
|
||||
"Example: dba(\"aitest.HIGH\")\n"};
|
||||
static void dbaCallFunc(const iocshArgBuf *args) { dba(args[0].sval);}
|
||||
static void dbaCallFunc(const iocshArgBuf *args) { iocshSetError(dba(args[0].sval));}
|
||||
|
||||
/* dbl */
|
||||
static const iocshArg dblArg0 = { "record type",iocshArgString};
|
||||
@@ -204,7 +206,7 @@ static const iocshFuncDef dblFuncDef = {"dbl",2,dblArgs,
|
||||
" dbl(\"ai\",\"HIGH LOW VAL PREC\")\n"};
|
||||
static void dblCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbl(args[0].sval,args[1].sval);
|
||||
iocshSetError(dbl(args[0].sval,args[1].sval));
|
||||
}
|
||||
|
||||
/* dbnr */
|
||||
@@ -213,7 +215,7 @@ static const iocshArg * const dbnrArgs[1] = {&dbnrArg0};
|
||||
static const iocshFuncDef dbnrFuncDef = {"dbnr",1,dbnrArgs,
|
||||
"List number of records and aliases by type.\n"
|
||||
"If verbose, list all record types regardless of being instanced\n"};
|
||||
static void dbnrCallFunc(const iocshArgBuf *args) { dbnr(args[0].ival);}
|
||||
static void dbnrCallFunc(const iocshArgBuf *args) { iocshSetError(dbnr(args[0].ival));}
|
||||
|
||||
/* dbli */
|
||||
static const iocshArg dbliArg0 = { "pattern",iocshArgString};
|
||||
@@ -221,7 +223,7 @@ static const iocshArg * const dbliArgs[1] = {&dbliArg0};
|
||||
static const iocshFuncDef dbliFuncDef = {"dbli",1,dbliArgs,
|
||||
"List info() tags with names matching pattern.\n\n"
|
||||
"Example: dbli(\"autosave*\")\n"};
|
||||
static void dbliCallFunc(const iocshArgBuf *args) { dbli(args[0].sval);}
|
||||
static void dbliCallFunc(const iocshArgBuf *args) { iocshSetError(dbli(args[0].sval));}
|
||||
|
||||
/* dbla */
|
||||
static const iocshArg dblaArg0 = { "pattern",iocshArgStringRecord};
|
||||
@@ -229,7 +231,7 @@ static const iocshArg * const dblaArgs[1] = {&dblaArg0};
|
||||
static const iocshFuncDef dblaFuncDef = {"dbla",1,dblaArgs,
|
||||
"List record alias()s by alias name pattern.\n\n"
|
||||
"Example: dbla(\"alia*\")\n"};
|
||||
static void dblaCallFunc(const iocshArgBuf *args) { dbla(args[0].sval);}
|
||||
static void dblaCallFunc(const iocshArgBuf *args) { iocshSetError(dbla(args[0].sval));}
|
||||
|
||||
/* dbgrep */
|
||||
static const iocshArg dbgrepArg0 = { "pattern",iocshArgStringRecord};
|
||||
@@ -240,7 +242,7 @@ static const iocshFuncDef dbgrepFuncDef = {"dbgrep",1,dbgrepArgs,
|
||||
" - \"?\", which matches 0 or one characters.\n"
|
||||
" - \"*\", which matches 0 or more characters.\n\n"
|
||||
"Example: dbgrep(\"*gpibAi*\")\n"};
|
||||
static void dbgrepCallFunc(const iocshArgBuf *args) { dbgrep(args[0].sval);}
|
||||
static void dbgrepCallFunc(const iocshArgBuf *args) { iocshSetError(dbgrep(args[0].sval));}
|
||||
|
||||
/* dbgf */
|
||||
static const iocshArg dbgfArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -250,7 +252,7 @@ static const iocshFuncDef dbgfFuncDef = {"dbgf",1,dbgfArgs,
|
||||
"Print current value of record field.\n"
|
||||
"If no field name is specified, VAL is assumed.\n\n"
|
||||
"Example: dbgf(\"aitest.VAL\")\n"};
|
||||
static void dbgfCallFunc(const iocshArgBuf *args) { dbgf(args[0].sval);}
|
||||
static void dbgfCallFunc(const iocshArgBuf *args) { iocshSetError(dbgf(args[0].sval));}
|
||||
|
||||
/* dbpf */
|
||||
static const iocshArg dbpfArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -261,7 +263,7 @@ static const iocshFuncDef dbpfFuncDef = {"dbpf",2,dbpfArgs,
|
||||
"Change value of record field and read it back with dbgf.\n"
|
||||
"If no field is specified, VAL is assumed\n"};
|
||||
static void dbpfCallFunc(const iocshArgBuf *args)
|
||||
{ dbpf(args[0].sval,args[1].sval);}
|
||||
{ iocshSetError(dbpf(args[0].sval,args[1].sval));}
|
||||
|
||||
/* dbpr */
|
||||
static const iocshArg dbprArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -279,14 +281,14 @@ static const iocshFuncDef dbprFuncDef = {
|
||||
"Example: dbpr aitest 3\n"
|
||||
};
|
||||
static void dbprCallFunc(const iocshArgBuf *args)
|
||||
{ dbpr(args[0].sval,args[1].ival);}
|
||||
{ iocshSetError(dbpr(args[0].sval,args[1].ival));}
|
||||
|
||||
/* dbtr */
|
||||
static const iocshArg dbtrArg0 = { "record name",iocshArgStringRecord};
|
||||
static const iocshArg * const dbtrArgs[1] = {&dbtrArg0};
|
||||
static const iocshFuncDef dbtrFuncDef = {"dbtr",1,dbtrArgs,
|
||||
"Process record and then some fields.\n"};
|
||||
static void dbtrCallFunc(const iocshArgBuf *args) { dbtr(args[0].sval);}
|
||||
static void dbtrCallFunc(const iocshArgBuf *args) { iocshSetError(dbtr(args[0].sval));}
|
||||
|
||||
/* dbtgf */
|
||||
static const iocshArg dbtgfArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -296,7 +298,7 @@ static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs,
|
||||
"Get and print the specified field with all possible DBR_* types\n"
|
||||
"Example: dbtgf aitest\n"
|
||||
"Example: dbtgf aitest.VAL\n"};
|
||||
static void dbtgfCallFunc(const iocshArgBuf *args) { dbtgf(args[0].sval);}
|
||||
static void dbtgfCallFunc(const iocshArgBuf *args) { iocshSetError(dbtgf(args[0].sval));}
|
||||
|
||||
/* dbtpf */
|
||||
static const iocshArg dbtpfArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -308,7 +310,7 @@ static const iocshFuncDef dbtpfFuncDef = {"dbtpf",2,dbtpfArgs,
|
||||
"for all possible DBR_* types\n\n"
|
||||
"Example: dbtpf aitest 5.0\n"};
|
||||
static void dbtpfCallFunc(const iocshArgBuf *args)
|
||||
{ dbtpf(args[0].sval,args[1].sval);}
|
||||
{ iocshSetError(dbtpf(args[0].sval,args[1].sval));}
|
||||
|
||||
/* dbior */
|
||||
static const iocshArg dbiorArg0 = { "driver name",iocshArgString};
|
||||
@@ -317,7 +319,7 @@ static const iocshArg * const dbiorArgs[] = {&dbiorArg0,&dbiorArg1};
|
||||
static const iocshFuncDef dbiorFuncDef = {"dbior",2,dbiorArgs,
|
||||
"Driver Report.\n"};
|
||||
static void dbiorCallFunc(const iocshArgBuf *args)
|
||||
{ dbior(args[0].sval,args[1].ival);}
|
||||
{ iocshSetError(dbior(args[0].sval,args[1].ival));}
|
||||
|
||||
/* dbhcr */
|
||||
static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0,
|
||||
@@ -327,7 +329,7 @@ static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0,
|
||||
"Use the UNIX sort command:\n"
|
||||
"dbhcr > report\n"
|
||||
"sort report > report.sorted\n"};
|
||||
static void dbhcrCallFunc(const iocshArgBuf *args) { dbhcr();}
|
||||
static void dbhcrCallFunc(const iocshArgBuf *args) { iocshSetError(dbhcr());}
|
||||
|
||||
/* gft */
|
||||
static const iocshArg gftArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -336,7 +338,7 @@ static const iocshFuncDef gftFuncDef = {"gft",1,gftArgs,
|
||||
"Report dbChannel info and value.\n"
|
||||
"Example: gft aitest\n"
|
||||
"Example: gft aitest.VAL\n"};
|
||||
static void gftCallFunc(const iocshArgBuf *args) { gft(args[0].sval);}
|
||||
static void gftCallFunc(const iocshArgBuf *args) { iocshSetError(gft(args[0].sval));}
|
||||
|
||||
/* pft */
|
||||
static const iocshArg pftArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -346,7 +348,7 @@ static const iocshFuncDef pftFuncDef = {"pft",2,pftArgs,
|
||||
"dbChannel put value.\n"
|
||||
"Example: pft aitest 5.0\n"};
|
||||
static void pftCallFunc(const iocshArgBuf *args)
|
||||
{ pft(args[0].sval,args[1].sval);}
|
||||
{ iocshSetError(pft(args[0].sval,args[1].sval));}
|
||||
|
||||
/* dbtpn */
|
||||
static const iocshArg dbtpnArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -359,12 +361,12 @@ static const iocshFuncDef dbtpnFuncDef = {"dbtpn",2,dbtpnArgs,
|
||||
"Example: dbtpn aitest\n"
|
||||
"Example: dbtpn aitest 5.0\n"};
|
||||
static void dbtpnCallFunc(const iocshArgBuf *args)
|
||||
{ dbtpn(args[0].sval,args[1].sval);}
|
||||
{ iocshSetError(dbtpn(args[0].sval,args[1].sval));}
|
||||
|
||||
/* dbNotifyDump */
|
||||
static const iocshFuncDef dbNotifyDumpFuncDef = {"dbNotifyDump",0,0,
|
||||
"Report status of any active async processing with completion notification.\n"};
|
||||
static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { dbNotifyDump();}
|
||||
static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { iocshSetError(dbNotifyDump());}
|
||||
|
||||
/* dbPutAttribute */
|
||||
static const iocshArg dbPutAttrArg0 = { "record type",iocshArgString};
|
||||
@@ -375,7 +377,7 @@ static const iocshArg * const dbPutAttrArgs[] =
|
||||
static const iocshFuncDef dbPutAttrFuncDef = {"dbPutAttribute",3,dbPutAttrArgs,
|
||||
"Set/Create record attribute.\n"};
|
||||
static void dbPutAttrCallFunc(const iocshArgBuf *args)
|
||||
{ dbPutAttribute(args[0].sval,args[1].sval,args[2].sval);}
|
||||
{ iocshSetError(dbPutAttribute(args[0].sval,args[1].sval,args[2].sval));}
|
||||
|
||||
/* tpn */
|
||||
static const iocshArg tpnArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -385,7 +387,7 @@ static const iocshFuncDef tpnFuncDef = {"tpn",2,tpnArgs,
|
||||
"Test Process Notify.\n\n"
|
||||
"Example: tpn aitest 5.0\n"};
|
||||
static void tpnCallFunc(const iocshArgBuf *args)
|
||||
{ tpn(args[0].sval,args[1].sval);}
|
||||
{ iocshSetError(tpn(args[0].sval,args[1].sval));}
|
||||
|
||||
/* dblsr */
|
||||
static const iocshArg dblsrArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -399,7 +401,7 @@ static const iocshFuncDef dblsrFuncDef = {"dblsr",2,dblsrArgs,
|
||||
" 2 - Show each record and all database links in the lock set.\n\n"
|
||||
"Example: dblsr aitest 2\n"};
|
||||
static void dblsrCallFunc(const iocshArgBuf *args)
|
||||
{ dblsr(args[0].sval,args[1].ival);}
|
||||
{ iocshSetError(dblsr(args[0].sval,args[1].ival));}
|
||||
|
||||
/* dbLockShowLocked */
|
||||
static const iocshArg dbLockShowLockedArg0 = { "interest level",iocshArgInt};
|
||||
@@ -412,7 +414,7 @@ static const iocshFuncDef dbLockShowLockedFuncDef = {
|
||||
"Example: dbLockShowLocked 0\n"
|
||||
};
|
||||
static void dbLockShowLockedCallFunc(const iocshArgBuf *args)
|
||||
{ dbLockShowLocked(args[0].ival);}
|
||||
{ iocshSetError(dbLockShowLocked(args[0].ival));}
|
||||
|
||||
/* scanOnceSetQueueSize */
|
||||
static const iocshArg scanOnceSetQueueSizeArg0 = { "size",iocshArgInt};
|
||||
@@ -423,7 +425,7 @@ static const iocshFuncDef scanOnceSetQueueSizeFuncDef = {"scanOnceSetQueueSize",
|
||||
"Must be called before iocInit().\n"};
|
||||
static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
scanOnceSetQueueSize(args[0].ival);
|
||||
iocshSetError(scanOnceSetQueueSize(args[0].ival));
|
||||
}
|
||||
|
||||
/* scanOnceQueueShow */
|
||||
@@ -444,7 +446,7 @@ static const iocshFuncDef scanpplFuncDef = {"scanppl",1,scanpplArgs,
|
||||
"Print info for records with periodic scan.\n"
|
||||
"If rate == 0.0, all periods are shown.\n"};
|
||||
static void scanpplCallFunc(const iocshArgBuf *args)
|
||||
{ scanppl(args[0].dval);}
|
||||
{ iocshSetError(scanppl(args[0].dval));}
|
||||
|
||||
/* scanpel */
|
||||
static const iocshArg scanpelArg0 = { "event name",iocshArgString};
|
||||
@@ -452,7 +454,7 @@ static const iocshArg * const scanpelArgs[1] = {&scanpelArg0};
|
||||
static const iocshFuncDef scanpelFuncDef = {"scanpel",1,scanpelArgs,
|
||||
"Print info for records with SCAN = \"Event\".\n"};
|
||||
static void scanpelCallFunc(const iocshArgBuf *args)
|
||||
{ scanpel(args[0].sval);}
|
||||
{ iocshSetError(scanpel(args[0].sval));}
|
||||
|
||||
/* postEvent */
|
||||
static const iocshArg postEventArg0 = { "event name",iocshArgString};
|
||||
@@ -468,7 +470,7 @@ static void postEventCallFunc(const iocshArgBuf *args)
|
||||
/* scanpiol */
|
||||
static const iocshFuncDef scanpiolFuncDef = {"scanpiol",0,0,
|
||||
"Print info for records with SCAN = \"I/O Intr\".\n"};
|
||||
static void scanpiolCallFunc(const iocshArgBuf *args) { scanpiol();}
|
||||
static void scanpiolCallFunc(const iocshArgBuf *args) { iocshSetError(scanpiol());}
|
||||
|
||||
/* callbackSetQueueSize */
|
||||
static const iocshArg callbackSetQueueSizeArg0 = { "bufsize",iocshArgInt};
|
||||
@@ -479,7 +481,7 @@ static const iocshFuncDef callbackSetQueueSizeFuncDef = {"callbackSetQueueSize",
|
||||
"Must be called before iocInit().\n"};
|
||||
static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
callbackSetQueueSize(args[0].ival);
|
||||
iocshSetError(callbackSetQueueSize(args[0].ival));
|
||||
}
|
||||
|
||||
/* callbackQueueShow */
|
||||
@@ -504,7 +506,7 @@ static const iocshFuncDef callbackParallelThreadsFuncDef = {"callbackParallelThr
|
||||
"or one of LOW, MEDIUM, or HIGH.\n"};
|
||||
static void callbackParallelThreadsCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
callbackParallelThreads(args[0].ival, args[1].sval);
|
||||
iocshSetError(callbackParallelThreads(args[0].ival, args[1].sval));
|
||||
}
|
||||
|
||||
/* dbStateCreate */
|
||||
@@ -514,7 +516,8 @@ static const iocshFuncDef dbStateCreateFuncDef = {"dbStateCreate", 1, dbStateCre
|
||||
"Allocate new state name for \"state\" filter.\n"};
|
||||
static void dbStateCreateCallFunc (const iocshArgBuf *args)
|
||||
{
|
||||
dbStateCreate(args[0].sval);
|
||||
if (!dbStateCreate(args[0].sval))
|
||||
iocshSetError(-1);
|
||||
}
|
||||
|
||||
/* dbStateSet */
|
||||
@@ -527,6 +530,8 @@ static void dbStateSetCallFunc (const iocshArgBuf *args)
|
||||
|
||||
if (sid)
|
||||
dbStateSet(sid);
|
||||
else
|
||||
iocshSetError(-1);
|
||||
}
|
||||
|
||||
/* dbStateClear */
|
||||
@@ -539,6 +544,8 @@ static void dbStateClearCallFunc (const iocshArgBuf *args)
|
||||
|
||||
if (sid)
|
||||
dbStateClear(sid);
|
||||
else
|
||||
iocshSetError(-1);
|
||||
}
|
||||
|
||||
/* dbStateShow */
|
||||
@@ -552,6 +559,8 @@ static void dbStateShowCallFunc (const iocshArgBuf *args)
|
||||
|
||||
if (sid)
|
||||
dbStateShow(sid, args[1].ival);
|
||||
else
|
||||
iocshSetError(-1);
|
||||
}
|
||||
|
||||
/* dbStateShowAll */
|
||||
|
||||
@@ -391,6 +391,10 @@ typedef struct lset {
|
||||
#define dbGetSevr(link, sevr) \
|
||||
dbGetAlarm(link, NULL, sevr)
|
||||
|
||||
/** @brief Lookup link field name from pointer.
|
||||
* Returns only field name. aka. value of ``dbFldDes::name``
|
||||
* @since 3.16.2
|
||||
*/
|
||||
DBCORE_API const char * dbLinkFieldName(const struct link *plink);
|
||||
|
||||
DBCORE_API void dbInitLink(struct link *plink, short dbfType);
|
||||
|
||||
@@ -127,6 +127,31 @@ 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;
|
||||
|
||||
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;
|
||||
nmatch++;
|
||||
if (name)
|
||||
break; /* No duplicate names in serverList */
|
||||
}
|
||||
}
|
||||
if (channels) *channels = tch;
|
||||
if (clients) *clients = tcl;
|
||||
return nmatch;
|
||||
}
|
||||
|
||||
#define STARTSTOP(routine, method, newState) \
|
||||
void routine(void) \
|
||||
{ \
|
||||
|
||||
@@ -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
|
||||
@@ -59,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);
|
||||
|
||||
@@ -145,6 +142,30 @@ 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 server layers.
|
||||
*
|
||||
* This is an API for iocStats and similar to fetch the number of channels
|
||||
* 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 -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);
|
||||
|
||||
/** @brief Initialize all registered servers.
|
||||
*
|
||||
* Calls all dbServer::init() methods.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1029,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -260,8 +260,8 @@ int recGblSetSevr(void *precord, epicsEnum16 new_stat, epicsEnum16 new_sevr)
|
||||
return recGblSetSevrMsg(precord, new_stat, new_sevr, NULL);
|
||||
}
|
||||
|
||||
void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
|
||||
epicsEnum16 sevr)
|
||||
void recGblInheritSevrMsg(int msMode, void *precord, epicsEnum16 stat,
|
||||
epicsEnum16 sevr, const char *msg)
|
||||
{
|
||||
switch (msMode) {
|
||||
case pvlOptNMS:
|
||||
@@ -274,11 +274,17 @@ void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
|
||||
recGblSetSevr(precord, LINK_ALARM, sevr);
|
||||
break;
|
||||
case pvlOptMSS:
|
||||
recGblSetSevr(precord, stat, sevr);
|
||||
/* Only MSS inherits msg */
|
||||
recGblSetSevrMsg(precord, stat, sevr, "%s", msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
|
||||
epicsEnum16 sevr)
|
||||
{
|
||||
recGblInheritSevrMsg(msMode, precord, stat, sevr, NULL);
|
||||
}
|
||||
|
||||
void recGblFwdLink(void *precord)
|
||||
{
|
||||
|
||||
@@ -73,6 +73,8 @@ DBCORE_API int recGblSetSevr(void *precord, epicsEnum16 new_stat,
|
||||
epicsEnum16 new_sevr);
|
||||
DBCORE_API void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
|
||||
epicsEnum16 sevr);
|
||||
DBCORE_API void recGblInheritSevrMsg(int msMode, void *precord, epicsEnum16 stat,
|
||||
epicsEnum16 sevr, const char *msg);
|
||||
DBCORE_API int recGblSetSevrMsg(void *precord, epicsEnum16 new_stat,
|
||||
epicsEnum16 new_sevr,
|
||||
EPICS_PRINTF_FMT(const char *msg), ...) EPICS_PRINTF_STYLE(4,5);
|
||||
|
||||
@@ -1263,24 +1263,50 @@ static void dbRecordInfo(char *name, char *value)
|
||||
}
|
||||
}
|
||||
|
||||
static long createAlias(DBENTRY *pdbentry, const char *alias)
|
||||
{
|
||||
DBENTRY tempEntry;
|
||||
long status;
|
||||
dbRecordNode *precnode = pdbentry->precnode;
|
||||
|
||||
if (precnode->aliasedRecnode) precnode = precnode->aliasedRecnode;
|
||||
dbInitEntry(pdbentry->pdbbase, &tempEntry);
|
||||
status = dbFindRecord(&tempEntry, alias);
|
||||
if (status == 0) {
|
||||
if (tempEntry.precnode->aliasedRecnode != precnode) {
|
||||
if (tempEntry.precnode->aliasedRecnode)
|
||||
fprintf(stderr, ERL_ERROR ": Alias \"%s\" for \"%s\": name already used by an alias for \"%s\"\n",
|
||||
alias, dbGetRecordName(pdbentry),
|
||||
tempEntry.precnode->aliasedRecnode->recordname);
|
||||
else
|
||||
fprintf(stderr, ERL_ERROR ": Alias \"%s\" for \"%s\": name already used by a record\n",
|
||||
alias, dbGetRecordName(pdbentry));
|
||||
status = S_dbLib_recExists;
|
||||
} else if (dbRecordsOnceOnly) {
|
||||
fprintf(stderr, ERL_ERROR ": Alias \"%s\" already defined and dbRecordsOnceOnly set.\n",
|
||||
alias);
|
||||
status = S_dbLib_recExists;
|
||||
}
|
||||
} else {
|
||||
status = dbCreateAlias(pdbentry, alias);
|
||||
}
|
||||
dbFinishEntry(&tempEntry);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void dbRecordAlias(char *name)
|
||||
{
|
||||
DBENTRY *pdbentry;
|
||||
tempListNode *ptempListNode;
|
||||
long status;
|
||||
|
||||
if(dbRecordNameValidate(name))
|
||||
return;
|
||||
|
||||
if (duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbentry = ptempListNode->item;
|
||||
status = dbCreateAlias(pdbentry, name);
|
||||
if (status) {
|
||||
fprintf(stderr, "Can't create alias \"%s\" for \"%s\"\n",
|
||||
name, dbGetRecordName(pdbentry));
|
||||
|
||||
if (createAlias(pdbentry, name) != 0) {
|
||||
yyerror(NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1298,9 +1324,7 @@ static void dbAlias(char *name, char *alias)
|
||||
alias, name);
|
||||
yyerror(NULL);
|
||||
}
|
||||
else if (dbCreateAlias(pdbEntry, alias)) {
|
||||
fprintf(stderr, "Can't create alias \"%s\" referring to \"%s\"\n",
|
||||
alias, name);
|
||||
else if (createAlias(pdbEntry, alias) != 0) {
|
||||
yyerror(NULL);
|
||||
}
|
||||
dbFinishEntry(pdbEntry);
|
||||
|
||||
@@ -207,7 +207,7 @@ static const iocshFuncDef dbPvdTableSizeFuncDef = {
|
||||
};
|
||||
static void dbPvdTableSizeCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbPvdTableSize(args[0].ival);
|
||||
iocshSetError(dbPvdTableSize(args[0].ival));
|
||||
}
|
||||
|
||||
/* dbReportDeviceConfig */
|
||||
|
||||
@@ -72,6 +72,7 @@ DBCORE_API extern const maplinkType pamaplinkType[LINK_NTYPES];
|
||||
/* DBLINK Flag bits */
|
||||
#define DBLINK_FLAG_INITIALIZED 1 /* dbInitLink() called */
|
||||
#define DBLINK_FLAG_TSELisTIME 2 /* Use TSEL to get timeStamp */
|
||||
#define DBLINK_FLAG_VISITED 4 /* Used in loop detection */
|
||||
|
||||
struct macro_link {
|
||||
char *macroStr;
|
||||
|
||||
@@ -14,6 +14,7 @@ SRC_DIRS += $(IOCDIR)/dbtemplate
|
||||
PROD_HOST += msi
|
||||
|
||||
msi_SRCS = msi.cpp
|
||||
msi_SYS_LIBS_WIN32 = shlwapi
|
||||
DOCS += msi.md
|
||||
|
||||
INC += dbLoadTemplate.h
|
||||
|
||||
@@ -41,6 +41,17 @@ static int var_count, sub_count;
|
||||
int dbTemplateMaxVars = 100;
|
||||
epicsExportAddress(int, dbTemplateMaxVars);
|
||||
|
||||
static
|
||||
int msiLoadRecords(const char *fname, const char *subs)
|
||||
{
|
||||
int ret = dbLoadRecords(fname, subs);
|
||||
if(ret) {
|
||||
fprintf(stderr, "dbLoadRecords(\"%s\", %s)\n", fname, subs);
|
||||
yyerror("Error while reading included file");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%start substitution_file
|
||||
@@ -167,7 +178,7 @@ pattern_definition: global_definitions
|
||||
fprintf(stderr, "pattern_definition: pattern_values empty\n");
|
||||
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
|
||||
#endif
|
||||
dbLoadRecords(db_file_name, sub_collect+1);
|
||||
if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
|
||||
}
|
||||
| O_BRACE pattern_values C_BRACE
|
||||
{
|
||||
@@ -175,7 +186,7 @@ pattern_definition: global_definitions
|
||||
fprintf(stderr, "pattern_definition:\n");
|
||||
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
|
||||
#endif
|
||||
dbLoadRecords(db_file_name, sub_collect+1);
|
||||
if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
|
||||
*sub_locals = '\0';
|
||||
sub_count = 0;
|
||||
}
|
||||
@@ -190,7 +201,7 @@ pattern_definition: global_definitions
|
||||
fprintf(stderr, "pattern_definition:\n");
|
||||
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
|
||||
#endif
|
||||
dbLoadRecords(db_file_name, sub_collect+1);
|
||||
if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
|
||||
dbmfFree($1);
|
||||
*sub_locals = '\0';
|
||||
sub_count = 0;
|
||||
@@ -250,7 +261,7 @@ variable_substitution: global_definitions
|
||||
fprintf(stderr, "variable_substitution: variable_definitions empty\n");
|
||||
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
|
||||
#endif
|
||||
dbLoadRecords(db_file_name, sub_collect+1);
|
||||
if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
|
||||
}
|
||||
| O_BRACE variable_definitions C_BRACE
|
||||
{
|
||||
@@ -258,7 +269,7 @@ variable_substitution: global_definitions
|
||||
fprintf(stderr, "variable_substitution:\n");
|
||||
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
|
||||
#endif
|
||||
dbLoadRecords(db_file_name, sub_collect+1);
|
||||
if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
|
||||
*sub_locals = '\0';
|
||||
}
|
||||
| WORD O_BRACE variable_definitions C_BRACE
|
||||
@@ -272,7 +283,7 @@ variable_substitution: global_definitions
|
||||
fprintf(stderr, "variable_substitution:\n");
|
||||
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
|
||||
#endif
|
||||
dbLoadRecords(db_file_name, sub_collect+1);
|
||||
if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
|
||||
dbmfFree($1);
|
||||
*sub_locals = '\0';
|
||||
}
|
||||
@@ -328,6 +339,7 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect)
|
||||
{
|
||||
FILE *fp;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
line_num = 1;
|
||||
|
||||
@@ -377,7 +389,7 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect)
|
||||
yyrestart(fp);
|
||||
}
|
||||
|
||||
yyparse();
|
||||
err = yyparse();
|
||||
|
||||
for (i = 0; i < var_count; i++) {
|
||||
dbmfFree(vars[i]);
|
||||
@@ -390,5 +402,5 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect)
|
||||
dbmfFree(db_file_name);
|
||||
db_file_name = NULL;
|
||||
}
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
#include <osiFileName.h>
|
||||
#include <osiUnistd.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <shlwapi.h>
|
||||
#endif
|
||||
|
||||
#define MAX_BUFFER_SIZE 4096
|
||||
#define MAX_DEPS 1024
|
||||
|
||||
@@ -61,7 +65,7 @@ typedef struct inputData inputData;
|
||||
static void inputConstruct(inputData **ppvt);
|
||||
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 void inputBegin(inputData * const pvt, const char * const fileName, bool fromCmdLine);
|
||||
static char *inputNextLine(inputData * const pvt);
|
||||
static void inputNewIncludeFile(inputData * const pvt, const char * const name);
|
||||
static void inputErrPrint(const inputData * const pvt);
|
||||
@@ -83,7 +87,8 @@ 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);
|
||||
const char * const templateName,
|
||||
bool fromCmdLine);
|
||||
|
||||
/*Global variables */
|
||||
static int opt_V = 0;
|
||||
@@ -171,7 +176,7 @@ int main(int argc,char **argv)
|
||||
|
||||
if (substitutionName.empty()) {
|
||||
STEP("Single template+substitutions file");
|
||||
makeSubstitutions(inputPvt, macPvt, templateName);
|
||||
makeSubstitutions(inputPvt, macPvt, templateName, true);
|
||||
}
|
||||
else {
|
||||
subInfo *substitutePvt;
|
||||
@@ -203,7 +208,7 @@ int main(int argc,char **argv)
|
||||
macPushScope(macPvt);
|
||||
|
||||
addMacroReplacements(macPvt, macStr);
|
||||
makeSubstitutions(inputPvt, macPvt, filename);
|
||||
makeSubstitutions(inputPvt, macPvt, filename, false);
|
||||
|
||||
if (localScope)
|
||||
macPopScope(macPvt);
|
||||
@@ -276,14 +281,15 @@ static const char *cmdNames[] = {"include","substitute"};
|
||||
|
||||
static void makeSubstitutions(inputData * const inputPvt,
|
||||
MAC_HANDLE * const macPvt,
|
||||
const char * const templateName)
|
||||
const char * const templateName,
|
||||
bool fromCmdLine)
|
||||
{
|
||||
char *input;
|
||||
static char buffer[MAX_BUFFER_SIZE];
|
||||
int n;
|
||||
|
||||
ENTER;
|
||||
inputBegin(inputPvt, templateName);
|
||||
inputBegin(inputPvt, templateName, fromCmdLine);
|
||||
while ((input = inputNextLine(inputPvt))) {
|
||||
int expand=1;
|
||||
char *p;
|
||||
@@ -378,7 +384,7 @@ struct inputData {
|
||||
inputData() { memset(inputBuffer, 0, sizeof(inputBuffer) * sizeof(inputBuffer[0])); };
|
||||
};
|
||||
|
||||
static void inputOpenFile(inputData *pinputData, const char * const filename);
|
||||
static void inputOpenFile(inputData *pinputData, const char * const filename, bool fromCmdLine);
|
||||
static void inputCloseFile(inputData *pinputData);
|
||||
static void inputCloseAllFiles(inputData *pinputData);
|
||||
|
||||
@@ -431,11 +437,11 @@ static void inputAddPath(inputData * const pinputData, const char * const path)
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void inputBegin(inputData * const pinputData, const char * const fileName)
|
||||
static void inputBegin(inputData * const pinputData, const char * const fileName, bool fromCmdLine)
|
||||
{
|
||||
ENTER;
|
||||
inputCloseAllFiles(pinputData);
|
||||
inputOpenFile(pinputData, fileName);
|
||||
inputOpenFile(pinputData, fileName, fromCmdLine);
|
||||
EXIT;
|
||||
}
|
||||
|
||||
@@ -462,7 +468,7 @@ static void inputNewIncludeFile(inputData * const pinputData,
|
||||
const char * const name)
|
||||
{
|
||||
ENTER;
|
||||
inputOpenFile(pinputData,name);
|
||||
inputOpenFile(pinputData, name, false);
|
||||
EXIT;
|
||||
}
|
||||
|
||||
@@ -493,7 +499,15 @@ static void inputErrPrint(const inputData *const pinputData)
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void inputOpenFile(inputData *pinputData, const char * const filename)
|
||||
static int isPathRelative(const char * const path) {
|
||||
#ifdef _WIN32
|
||||
return path && PathIsRelativeA(path);
|
||||
#else
|
||||
return path && path[0] != '/';
|
||||
#endif
|
||||
}
|
||||
|
||||
static void inputOpenFile(inputData *pinputData, const char * const filename, bool fromCmdLine)
|
||||
{
|
||||
std::list<std::string>& pathList = pinputData->pathList;
|
||||
std::list<std::string>::iterator pathIt = pathList.end();
|
||||
@@ -505,7 +519,7 @@ static void inputOpenFile(inputData *pinputData, const char * const filename)
|
||||
STEP("Using stdin");
|
||||
fp = stdin;
|
||||
}
|
||||
else if (pathList.empty() || strchr(filename, '/')){
|
||||
else if (fromCmdLine || pathList.empty() || !isPathRelative(filename)){
|
||||
STEPS("Opening ", filename);
|
||||
fp = fopen(filename, "r");
|
||||
}
|
||||
|
||||
@@ -60,6 +60,18 @@ Switches have the following meanings:
|
||||
2. . (the current directory)
|
||||
3. .. (the parent of the current directory)
|
||||
|
||||
Note that relative path searching is handled as
|
||||
|
||||
$ cat foo.substitutions
|
||||
file rel/path/bar.template {
|
||||
# contents
|
||||
}
|
||||
$ msi -I . -I /some/path foo.substitutions
|
||||
|
||||
which will try to find `bar.template` at the path `./rel/path/` followed by
|
||||
`/some/path/rel/path`.
|
||||
|
||||
|
||||
- **-M _substitutions_**
|
||||
|
||||
This parameter specifies macro values for the template instance.
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
#include "iocsh.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
IOCSH_STATIC_FUNC void dlload(const char* name)
|
||||
IOCSH_STATIC_FUNC int dlload(const char* name)
|
||||
{
|
||||
if (!epicsLoadLibrary(name)) {
|
||||
printf("epicsLoadLibrary failed: %s\n", epicsLoadError());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const iocshArg dlloadArg0 = { "path/library.so", iocshArgStringPath};
|
||||
@@ -28,7 +30,7 @@ static const iocshFuncDef dlloadFuncDef = {
|
||||
};
|
||||
static void dlloadCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dlload(args[0].sval);
|
||||
iocshSetError(dlload(args[0].sval));
|
||||
}
|
||||
|
||||
static void dlloadRegistar(void) {
|
||||
|
||||
@@ -22,6 +22,9 @@ enum iocStateEnum {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Query present IOC run state
|
||||
* @since 3.15.8
|
||||
*/
|
||||
DBCORE_API enum iocStateEnum getIocState(void);
|
||||
DBCORE_API int iocInit(void);
|
||||
DBCORE_API int iocBuild(void);
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -163,9 +163,11 @@ void camsgtask ( void *pParm )
|
||||
|
||||
int casClientInitiatingCurrentThread ( char * pBuf, size_t bufSize )
|
||||
{
|
||||
struct client * pClient = ( struct client * )
|
||||
epicsThreadPrivateGet ( rsrvCurrentClient );
|
||||
struct client * pClient;
|
||||
if ( ! rsrvCurrentClient )
|
||||
return RSRV_ERROR; /* not yet initialized, or disabled via dbServer */
|
||||
|
||||
pClient = ( struct client * ) epicsThreadPrivateGet ( rsrvCurrentClient );
|
||||
if ( ! pClient )
|
||||
return RSRV_ERROR;
|
||||
|
||||
|
||||
@@ -1536,6 +1536,13 @@ struct client *create_tcp_client (SOCKET sock , const osiSockAddr *peerAddr)
|
||||
|
||||
void casStatsFetch ( unsigned *pChanCount, unsigned *pCircuitCount )
|
||||
{
|
||||
if(!clientQlock) { /* not yet initialized, or disabled via dbServer */
|
||||
if(pChanCount)
|
||||
*pChanCount = 0;
|
||||
if(pCircuitCount)
|
||||
*pCircuitCount = 0;
|
||||
return;
|
||||
}
|
||||
LOCK_CLIENTQ;
|
||||
{
|
||||
int circuitCount = ellCount ( &clientQ );
|
||||
|
||||
@@ -33,7 +33,7 @@ These fields control where the record will read data from when it is processed:
|
||||
The DTYP field selects which device support layer should be responsible for
|
||||
providing input data to the record.
|
||||
The ai device support layers provided by EPICS Base are documented in the
|
||||
L<Device Support|devSoft> section.
|
||||
L<Device Support Interface> section.
|
||||
External support modules may provide additional device support for this record
|
||||
type.
|
||||
If not set explicitly, the DTYP value defaults to the first device support that
|
||||
|
||||
@@ -384,6 +384,7 @@ static long special(DBADDR *paddr, int after)
|
||||
|
||||
if (special_type == SPC_RESET) {
|
||||
reset(prec);
|
||||
monitor(prec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -282,10 +282,10 @@ static void processCallback(epicsCallback *arg)
|
||||
static long get_units(DBADDR *paddr, char *units)
|
||||
{
|
||||
seqRecord *prec = (seqRecord *) paddr->precord;
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0);
|
||||
|
||||
if (fieldOffset >= 0)
|
||||
switch (fieldOffset & 2) {
|
||||
switch (fieldOffset & 3) {
|
||||
case 0: /* DLYn */
|
||||
strcpy(units, "s");
|
||||
break;
|
||||
@@ -299,11 +299,11 @@ static long get_units(DBADDR *paddr, char *units)
|
||||
static long get_precision(const DBADDR *paddr, long *pprecision)
|
||||
{
|
||||
seqRecord *prec = (seqRecord *) paddr->precord;
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0);
|
||||
short precision;
|
||||
|
||||
if (fieldOffset >= 0)
|
||||
switch (fieldOffset & 2) {
|
||||
switch (fieldOffset & 3) {
|
||||
case 0: /* DLYn */
|
||||
*pprecision = seqDLYprecision;
|
||||
return 0;
|
||||
@@ -321,10 +321,10 @@ static long get_precision(const DBADDR *paddr, long *pprecision)
|
||||
static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
|
||||
{
|
||||
seqRecord *prec = (seqRecord *) paddr->precord;
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0);
|
||||
|
||||
if (fieldOffset >= 0)
|
||||
switch (fieldOffset & 2) {
|
||||
switch (fieldOffset & 3) {
|
||||
case 0: /* DLYn */
|
||||
pgd->lower_disp_limit = 0.0;
|
||||
pgd->lower_disp_limit = 10.0;
|
||||
@@ -341,9 +341,9 @@ static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
|
||||
|
||||
static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
|
||||
{
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0);
|
||||
|
||||
if (fieldOffset >= 0 && (fieldOffset & 2) == 0) { /* DLYn */
|
||||
if (fieldOffset >= 0 && (fieldOffset & 3) == 0) { /* DLYn */
|
||||
pcd->lower_ctrl_limit = 0.0;
|
||||
pcd->upper_ctrl_limit = seqDLYlimit;
|
||||
}
|
||||
@@ -355,9 +355,9 @@ static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
|
||||
static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
|
||||
{
|
||||
seqRecord *prec = (seqRecord *) paddr->precord;
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0);
|
||||
|
||||
if (fieldOffset >= 0 && (fieldOffset & 2) == 2) /* DOn */
|
||||
if (fieldOffset >= 0 && (fieldOffset & 3) == 2) /* DOn */
|
||||
dbGetAlarmLimits(get_dol(prec, fieldOffset),
|
||||
&pad->lower_alarm_limit, &pad->lower_warning_limit,
|
||||
&pad->upper_warning_limit, &pad->upper_alarm_limit);
|
||||
|
||||
@@ -36,6 +36,7 @@ dbTestIoc_DBD += xLink.dbd
|
||||
dbTestIoc_DBD += devx.dbd
|
||||
dbTestIoc_DBD += jlinkz.dbd
|
||||
dbTestIoc_DBD += dbLinkdset.dbd
|
||||
dbTestIoc_DBD += dbCore.dbd
|
||||
TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db
|
||||
|
||||
testHarness_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
@@ -183,6 +184,11 @@ testHarness_SRCS += dbStaticTest.c
|
||||
TESTFILES += ../dbStaticTest.db
|
||||
TESTFILES += ../dbStaticTestAlias1.db
|
||||
TESTFILES += ../dbStaticTestAlias2.db
|
||||
TESTFILES += ../dbStaticTestAliasAgain1.db
|
||||
TESTFILES += ../dbStaticTestAliasAgain2.db
|
||||
TESTFILES += ../dbStaticTestAliasAgain3.db
|
||||
TESTFILES += ../dbStaticTestAliasAgainError1.db
|
||||
TESTFILES += ../dbStaticTestAliasAgainError2.db
|
||||
TESTFILES += ../dbStaticTestRemove.db
|
||||
TESTS += dbStaticTest
|
||||
|
||||
@@ -202,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
|
||||
|
||||
@@ -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; i<Size; i++) \
|
||||
@@ -174,6 +176,7 @@ static void check(short dbr_type) {
|
||||
off = Offset; req = 15; \
|
||||
memset(buf, 0, sizeof(buf)); \
|
||||
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
|
||||
dbScanLock(dbChannelRecord(pch)); \
|
||||
pfl = db_create_read_log(pch); \
|
||||
pfl->type = 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<Size; i++) \
|
||||
|
||||
@@ -40,6 +40,8 @@ void oneReport(unsigned level)
|
||||
void oneStats(unsigned *channels, unsigned *clients)
|
||||
{
|
||||
oneState = STATS_CALLED;
|
||||
if (channels) *channels = 2;
|
||||
if (clients) *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(35);
|
||||
|
||||
/* Prove that we handle substring names properly */
|
||||
epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones");
|
||||
@@ -151,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");
|
||||
@@ -163,8 +169,26 @@ MAIN(dbServerTest)
|
||||
|
||||
testDiag("Checking server methods called");
|
||||
dbsr(0);
|
||||
testOk(oneState == REPORT_CALLED, "dbsr called report()");
|
||||
testOk(oneState == REPORT_CALLED, "dbsr called one::report()");
|
||||
|
||||
testDiag("Checking stats functionality");
|
||||
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) == 1 && oneState == STATS_CALLED,
|
||||
"dbServerStats(NULL, &cl) called one::stats()");
|
||||
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);
|
||||
|
||||
testDiag("Checking client identification");
|
||||
oneSim = NULL;
|
||||
name[0] = 0;
|
||||
status = dbServerClient(name, sizeof(name));
|
||||
@@ -188,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");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <dbUnitTest.h>
|
||||
#include <testMain.h>
|
||||
#include <epicsString.h>
|
||||
#include <iocsh.h>
|
||||
|
||||
|
||||
static void testEntryRemoved(const char *pv)
|
||||
@@ -321,16 +322,19 @@ static void testDbVerify(const char *record)
|
||||
dbFinishEntry(&entry);
|
||||
}
|
||||
|
||||
static void testWrongAliasRecord(const char *filename)
|
||||
static void testReadDatabase(const char *filename, int expectToFail)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
long status;
|
||||
dbPath(pdbbase,"." OSI_PATH_LIST_SEPARATOR "..");
|
||||
dbOpenFile(pdbbase, filename, &fp);
|
||||
if(!fp) {
|
||||
testAbort("Unable to read %s", filename);
|
||||
testAbort("Unable to open %s", filename);
|
||||
}
|
||||
testOk(dbReadDatabaseFP(&pdbbase, fp, NULL, NULL) != 0,
|
||||
"Wrong alias record in %s is expected to fail", filename);
|
||||
status = dbReadDatabaseFP(&pdbbase, fp, NULL, NULL);
|
||||
testOk(!status == !expectToFail,
|
||||
"Reading %s%s", filename,
|
||||
expectToFail ? " is expected to fail" : "");
|
||||
}
|
||||
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
@@ -341,7 +345,7 @@ MAIN(dbStaticTest)
|
||||
char *ldirDup;
|
||||
FILE *fp = NULL;
|
||||
|
||||
testPlan(340);
|
||||
testPlan(350);
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
@@ -370,8 +374,21 @@ MAIN(dbStaticTest)
|
||||
}
|
||||
free(ldirDup);
|
||||
|
||||
testWrongAliasRecord("dbStaticTestAlias1.db");
|
||||
testWrongAliasRecord("dbStaticTestAlias2.db");
|
||||
testReadDatabase("dbStaticTestAlias1.db", 1);
|
||||
testReadDatabase("dbStaticTestAlias2.db", 1);
|
||||
|
||||
/* Test re-defining aliases */
|
||||
testReadDatabase("dbStaticTestAliasAgain1.db", 0);
|
||||
testReadDatabase("dbStaticTestAliasAgain1.db", 0);
|
||||
testReadDatabase("dbStaticTestAliasAgain2.db", 0);
|
||||
testReadDatabase("dbStaticTestAliasAgain2.db", 0);
|
||||
testReadDatabase("dbStaticTestAliasAgain3.db", 0);
|
||||
testReadDatabase("dbStaticTestAliasAgain3.db", 0);
|
||||
testReadDatabase("dbStaticTestAliasAgainError1.db", 1);
|
||||
testReadDatabase("dbStaticTestAliasAgainError2.db", 1);
|
||||
iocshCmd("var dbRecordsOnceOnly 1");
|
||||
testReadDatabase("dbStaticTestAliasAgain2.db", 1);
|
||||
testReadDatabase("dbStaticTestAliasAgain3.db", 1);
|
||||
|
||||
testEntry("testrec.VAL");
|
||||
testEntry("testalias.VAL");
|
||||
|
||||
4
modules/database/test/ioc/db/dbStaticTestAliasAgain1.db
Normal file
4
modules/database/test/ioc/db/dbStaticTestAliasAgain1.db
Normal file
@@ -0,0 +1,4 @@
|
||||
# Test re-load alias in record
|
||||
record(x, "testrecAgain") {
|
||||
alias("testaliasAgain1")
|
||||
}
|
||||
2
modules/database/test/ioc/db/dbStaticTestAliasAgain2.db
Normal file
2
modules/database/test/ioc/db/dbStaticTestAliasAgain2.db
Normal file
@@ -0,0 +1,2 @@
|
||||
# Test re-load alias for record
|
||||
alias("testrecAgain", "testaliasAgain2")
|
||||
2
modules/database/test/ioc/db/dbStaticTestAliasAgain3.db
Normal file
2
modules/database/test/ioc/db/dbStaticTestAliasAgain3.db
Normal file
@@ -0,0 +1,2 @@
|
||||
# Test re-load alias for alias
|
||||
alias("testaliasAgain2", "testaliasAgain3")
|
||||
@@ -0,0 +1,2 @@
|
||||
# ERROR: alias using name of exising alias for different record
|
||||
alias("testrec", "testaliasAgain1")
|
||||
@@ -0,0 +1,2 @@
|
||||
# ERROR: alias using name of exising record fails
|
||||
alias("testrecAgain", "testrec")
|
||||
@@ -12,7 +12,7 @@
|
||||
use strict;
|
||||
use Test;
|
||||
|
||||
BEGIN {plan tests => 12}
|
||||
BEGIN {plan tests => 14}
|
||||
|
||||
# Check include/substitute command model
|
||||
ok(msi('-I .. ../t1-template.txt'), slurp('../t1-result.txt'));
|
||||
@@ -56,6 +56,12 @@ my %envs = (TEST_NO => 12, PREFIX => 't');
|
||||
ok(msi('-I. -I.. -S ../t12-substitute.txt'), slurp('../t12-result.txt'));
|
||||
delete @ENV{ keys %envs }; # Not really needed
|
||||
|
||||
# Substitution file, relative path includes
|
||||
ok(msi('-I @TOP@/modules -S ../t13-substitute.txt'), slurp('../t13-result.txt'));
|
||||
|
||||
# Template file, relative path includes
|
||||
ok(msi('-I @TOP@/modules ../t14-template.txt'), slurp('../t14-result.txt'));
|
||||
|
||||
# Test support routines
|
||||
|
||||
sub slurp {
|
||||
|
||||
2
modules/database/test/ioc/dbtemplate/t13-result.txt
Normal file
2
modules/database/test/ioc/dbtemplate/t13-result.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
# comment line
|
||||
a=foo
|
||||
3
modules/database/test/ioc/dbtemplate/t13-substitute.txt
Normal file
3
modules/database/test/ioc/dbtemplate/t13-substitute.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
file database/test/ioc/dbtemplate/t13-template.txt {
|
||||
{ a=foo }
|
||||
}
|
||||
2
modules/database/test/ioc/dbtemplate/t13-template.txt
Normal file
2
modules/database/test/ioc/dbtemplate/t13-template.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
# comment line
|
||||
a=$(a)
|
||||
1
modules/database/test/ioc/dbtemplate/t14-include.txt
Normal file
1
modules/database/test/ioc/dbtemplate/t14-include.txt
Normal file
@@ -0,0 +1 @@
|
||||
I'm a file!
|
||||
5
modules/database/test/ioc/dbtemplate/t14-result.txt
Normal file
5
modules/database/test/ioc/dbtemplate/t14-result.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
This is t14-template.txt
|
||||
|
||||
I'm a file!
|
||||
|
||||
End of t14-template.txt
|
||||
5
modules/database/test/ioc/dbtemplate/t14-template.txt
Normal file
5
modules/database/test/ioc/dbtemplate/t14-template.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
This is t14-template.txt
|
||||
|
||||
include "database/test/ioc/dbtemplate/t14-include.txt"
|
||||
|
||||
End of t14-template.txt
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -251,8 +279,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
|
||||
|
||||
146
modules/database/test/std/rec/epicsExportTest.c
Normal file
146
modules/database/test/std/rec/epicsExportTest.c
Normal file
@@ -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 <epicsUnitTest.h>
|
||||
#include <dbUnitTest.h>
|
||||
#include <testMain.h>
|
||||
#include <dbAccess.h>
|
||||
#include <longinRecord.h>
|
||||
#include <aSubRecord.h>
|
||||
#include <iocsh.h>
|
||||
#include <registryFunction.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
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<struct longinRecord*>(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
|
||||
24
modules/database/test/std/rec/epicsExportTest.db
Normal file
24
modules/database/test/std/rec/epicsExportTest.db
Normal file
@@ -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")
|
||||
}
|
||||
16
modules/database/test/std/rec/epicsExportTest.dbd
Normal file
16
modules/database/test/std/rec/epicsExportTest.dbd
Normal file
@@ -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)
|
||||
95
modules/database/test/std/rec/epicsExportTestMain.c
Normal file
95
modules/database/test/std/rec/epicsExportTestMain.c
Normal file
@@ -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 <epicsUnitTest.h>
|
||||
#include <dbUnitTest.h>
|
||||
#include <testMain.h>
|
||||
#include <dbAccess.h>
|
||||
#include <iocsh.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
2
modules/database/test/std/rec/epicsExportTestxx.cpp
Normal file
2
modules/database/test/std/rec/epicsExportTestxx.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
// just compile as c++
|
||||
#include "epicsExportTest.c"
|
||||
@@ -510,6 +510,7 @@ static void rtshellCallFunc(const iocshArgBuf *args)
|
||||
|
||||
if (!cmd) {
|
||||
fprintf(stderr, "ERR: No such command\n");
|
||||
iocshSetError(-1);
|
||||
|
||||
} else {
|
||||
fflush(stdout);
|
||||
@@ -517,6 +518,7 @@ static void rtshellCallFunc(const iocshArgBuf *args)
|
||||
ret = (*cmd->command)(args[1].aval.ac,args[1].aval.av);
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
iocshSetError(ret);
|
||||
if(ret)
|
||||
fprintf(stderr, "ERR: %d\n",ret);
|
||||
}
|
||||
@@ -611,18 +613,27 @@ static void nfsMountCallFunc(const iocshArgBuf *args)
|
||||
}
|
||||
*cp = '/';
|
||||
}
|
||||
nfsMount(args[0].sval, args[1].sval, args[2].sval);
|
||||
iocshSetError(nfsMount(args[0].sval, args[1].sval, args[2].sval));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void zoneset(const char *zone)
|
||||
int zoneset(const char *zone)
|
||||
{
|
||||
if(zone)
|
||||
setenv("TZ", zone, 1);
|
||||
else
|
||||
unsetenv("TZ");
|
||||
int ret;
|
||||
if(zone) {
|
||||
if ((ret = setenv("TZ", zone, 1)) < 0)
|
||||
return ret;
|
||||
}
|
||||
#if defined( __NEWLIB_MINOR__ ) /* Added in newlib 2.2.0 */
|
||||
else if ((ret = unsetenv("TZ")) < 0)
|
||||
return ret;
|
||||
#else
|
||||
else
|
||||
unsetenv("TZ");
|
||||
#endif
|
||||
tzset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const iocshArg zonesetArg0 = {"zone string", iocshArgString};
|
||||
@@ -634,7 +645,7 @@ static const iocshFuncDef zonesetFuncDef = {"zoneset",1,zonesetArgs
|
||||
};
|
||||
static void zonesetCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
zoneset(args[0].sval);
|
||||
iocshSetError(zoneset(args[0].sval));
|
||||
}
|
||||
|
||||
#ifndef RTEMS_LEGACY_STACK
|
||||
@@ -667,6 +678,7 @@ static void setlogmaskCallFunc(const iocshArgBuf *args)
|
||||
return;
|
||||
}
|
||||
printf("Error: unknown log level.\n");
|
||||
iocshSetError(-1);
|
||||
}
|
||||
}
|
||||
static const iocshArg setlogmaskArg0 = {"level name", iocshArgString};
|
||||
|
||||
@@ -483,18 +483,27 @@ static void nfsMountCallFunc(const iocshArgBuf *args)
|
||||
}
|
||||
*cp = '/';
|
||||
}
|
||||
nfsMount(args[0].sval, args[1].sval, args[2].sval);
|
||||
iocshSetError(nfsMount(args[0].sval, args[1].sval, args[2].sval));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void zoneset(const char *zone)
|
||||
int zoneset(const char *zone)
|
||||
{
|
||||
if(zone)
|
||||
setenv("TZ", zone, 1);
|
||||
else
|
||||
unsetenv("TZ");
|
||||
int ret;
|
||||
if(zone) {
|
||||
if ((ret = setenv("TZ", zone, 1)) < 0)
|
||||
return ret;
|
||||
}
|
||||
#if defined( __NEWLIB_MINOR__ ) /* Added in newlib 2.2.0 */
|
||||
else if ((ret = unsetenv("TZ")) < 0)
|
||||
return ret;
|
||||
#else
|
||||
else
|
||||
unsetenv("TZ");
|
||||
#endif
|
||||
tzset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const iocshArg zonesetArg0 = {"zone string", iocshArgString};
|
||||
@@ -502,7 +511,7 @@ static const iocshArg * const zonesetArgs[1] = {&zonesetArg0};
|
||||
static const iocshFuncDef zonesetFuncDef = {"zoneset",1,zonesetArgs};
|
||||
static void zonesetCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
zoneset(args[0].sval);
|
||||
iocshSetError(zoneset(args[0].sval));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -311,13 +311,13 @@ inline bool tsSLIterConst<T>::valid () const
|
||||
template < class T >
|
||||
inline bool tsSLIterConst<T>::operator == ( const tsSLIterConst<T> &rhs ) const
|
||||
{
|
||||
return this->pEntry == rhs.pConstEntry;
|
||||
return this->pEntry == rhs.pEntry;
|
||||
}
|
||||
|
||||
template < class T >
|
||||
inline bool tsSLIterConst<T>::operator != (const tsSLIterConst<T> &rhs) const
|
||||
{
|
||||
return this->pEntry != rhs.pConstEntry;
|
||||
return this->pEntry != rhs.pEntry;
|
||||
}
|
||||
|
||||
template < class T >
|
||||
|
||||
1
modules/libcom/src/env/envDefs.h
vendored
1
modules/libcom/src/env/envDefs.h
vendored
@@ -76,6 +76,7 @@ LIBCOM_API extern const ENV_PARAM EPICS_IOC_LOG_FILE_COMMAND;
|
||||
LIBCOM_API extern const ENV_PARAM IOCSH_PS1;
|
||||
LIBCOM_API extern const ENV_PARAM IOCSH_HISTSIZE;
|
||||
LIBCOM_API extern const ENV_PARAM IOCSH_HISTEDIT_DISABLE;
|
||||
LIBCOM_API extern const ENV_PARAM EPICS_ABORT_ON_ASSERT;
|
||||
LIBCOM_API extern const ENV_PARAM *env_param_list[];
|
||||
|
||||
struct in_addr;
|
||||
|
||||
@@ -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*);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -1325,7 +1325,7 @@ iocshCmd (const char *cmd)
|
||||
int epicsStdCall
|
||||
iocshLoad(const char *pathname, const char *macros)
|
||||
{
|
||||
if (pathname)
|
||||
if (pathname && !getenv("IOCSH_STARTUP_SCRIPT"))
|
||||
epicsEnvSet("IOCSH_STARTUP_SCRIPT", pathname);
|
||||
return iocshBody(pathname, NULL, macros);
|
||||
}
|
||||
|
||||
@@ -139,10 +139,12 @@ static void epicsEnvSetCallFunc(const iocshArgBuf *args)
|
||||
|
||||
if (name == NULL) {
|
||||
fprintf(stderr, "Missing environment variable name argument.\n");
|
||||
iocshSetError(-1);
|
||||
return;
|
||||
}
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "Missing environment variable value argument.\n");
|
||||
iocshSetError(-1);
|
||||
return;
|
||||
}
|
||||
epicsEnvSet (name, value);
|
||||
@@ -159,6 +161,7 @@ static void epicsEnvUnsetCallFunc(const iocshArgBuf *args)
|
||||
|
||||
if (name == NULL) {
|
||||
fprintf(stderr, "Missing environment variable name argument.\n");
|
||||
iocshSetError(-1);
|
||||
return;
|
||||
}
|
||||
epicsEnvUnset (name);
|
||||
@@ -215,7 +218,7 @@ static const iocshFuncDef iocLogInitFuncDef = {"iocLogInit",0,0,
|
||||
" see 'setIocLogDisable' command\n"};
|
||||
static void iocLogInitCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocLogInit ();
|
||||
iocshSetError(iocLogInit ());
|
||||
}
|
||||
|
||||
/* iocLogDisable */
|
||||
@@ -354,6 +357,7 @@ static void threadCallFunc(const iocshArgBuf *args)
|
||||
tid = epicsThreadGetId (cp);
|
||||
if (!tid) {
|
||||
fprintf(stderr, "\t'%s' is not a known thread name\n", cp);
|
||||
iocshSetError(-1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -429,6 +433,7 @@ static void epicsThreadResumeCallFunc(const iocshArgBuf *args)
|
||||
tid = epicsThreadGetId(cp);
|
||||
if (!tid) {
|
||||
fprintf(stderr, "'%s' is not a valid thread name\n", cp);
|
||||
iocshSetError(-1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -437,12 +442,14 @@ static void epicsThreadResumeCallFunc(const iocshArgBuf *args)
|
||||
epicsThreadGetName(tid, nameBuf, sizeof nameBuf);
|
||||
if (nameBuf[0] == '\0') {
|
||||
fprintf(stderr, "'%s' is not a valid thread id\n", cp);
|
||||
iocshSetError(-1);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
if (!epicsThreadIsSuspended(tid)) {
|
||||
fprintf(stderr, "Thread %s is not suspended\n", cp);
|
||||
iocshSetError(-1);
|
||||
continue;
|
||||
}
|
||||
epicsThreadResume(tid);
|
||||
@@ -458,7 +465,7 @@ static const iocshFuncDef generalTimeReportFuncDef = {"generalTimeReport",1,gene
|
||||
" 1 - Additionally show current time obtained from each provider.\n"};
|
||||
static void generalTimeReportCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
generalTimeReport(args[0].ival);
|
||||
iocshSetError(generalTimeReport(args[0].ival));
|
||||
}
|
||||
|
||||
/* installLastResortEventProvider */
|
||||
@@ -467,7 +474,7 @@ static const iocshFuncDef installLastResortEventProviderFuncDef = {"installLastR
|
||||
"which returns the current time for every event number\n"};
|
||||
static void installLastResortEventProviderCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
installLastResortEventProvider();
|
||||
iocshSetError(installLastResortEventProvider());
|
||||
}
|
||||
|
||||
static iocshVarDef comDefs[] = {
|
||||
|
||||
@@ -24,7 +24,7 @@ LIBCOM_API void * callocMustSucceed(size_t count, size_t size, const char *msg)
|
||||
void * mem = NULL;
|
||||
if (count > 0 && size > 0) {
|
||||
while ((mem = calloc(count, size)) == NULL) {
|
||||
errlogPrintf("%s: callocMustSucceed(%lu, %lu) - calloc failed\n",
|
||||
errlogPrintf("%s: callocMustSucceed(%lu, %lu) - " ERL_ERROR " calloc failed\n",
|
||||
msg, (unsigned long)count, (unsigned long)size);
|
||||
errlogPrintf("Thread %s (%p) suspending.\n",
|
||||
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
|
||||
@@ -40,7 +40,7 @@ LIBCOM_API void * mallocMustSucceed(size_t size, const char *msg)
|
||||
void * mem = NULL;
|
||||
if (size > 0) {
|
||||
while ((mem = malloc(size)) == NULL) {
|
||||
errlogPrintf("%s: mallocMustSucceed(%lu) - malloc failed\n",
|
||||
errlogPrintf("%s: mallocMustSucceed(%lu) - " ERL_ERROR " malloc failed\n",
|
||||
msg, (unsigned long)size);
|
||||
errlogPrintf("Thread %s (%p) suspending.\n",
|
||||
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
|
||||
@@ -59,7 +59,7 @@ LIBCOM_API void cantProceed(const char *msg, ...)
|
||||
errlogVprintf(msg, pvar);
|
||||
va_end(pvar);
|
||||
|
||||
errlogPrintf("Thread %s (%p) can't proceed, suspending.\n",
|
||||
errlogPrintf(ANSI_RED("CRITICAL ERROR") " Thread %s (%p) can't proceed, suspending.\n",
|
||||
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
|
||||
|
||||
epicsStackTrace();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -56,18 +57,24 @@ LIBCOM_API void cantProceed(
|
||||
* gracefully when memory runs out.
|
||||
*/
|
||||
/** @{ */
|
||||
/** \brief A calloc() that never returns NULL.
|
||||
/** \brief A calloc() which suspends on error.
|
||||
* \param count Number of objects.
|
||||
* \param size Size of each object.
|
||||
* \param errorMessage What this memory is needed for.
|
||||
* \return Pointer to zeroed allocated memory.
|
||||
* \param errorMessage Context added to logged error message
|
||||
* \return Pointer to zeroed allocated memory. Should later be free() d
|
||||
*
|
||||
* Will always return NULL for a zero length allocation.
|
||||
* Will never return NULL otherwise.
|
||||
*/
|
||||
LIBCOM_API void * callocMustSucceed(size_t count, size_t size,
|
||||
const char *errorMessage);
|
||||
/** \brief A malloc() that never returns NULL.
|
||||
/** \brief A malloc() which suspends on error.
|
||||
* \param size Size of block to allocate.
|
||||
* \param errorMessage What this memory is needed for.
|
||||
* \return Pointer to allocated memory.
|
||||
* \param errorMessage Context added to logged error message
|
||||
* \return Pointer to allocated memory. Should later be free() d
|
||||
*
|
||||
* Will always return NULL for a zero length allocation.
|
||||
* Will never return NULL otherwise.
|
||||
*/
|
||||
LIBCOM_API void * mallocMustSucceed(size_t size, const char *errorMessage);
|
||||
/** @} */
|
||||
|
||||
@@ -35,8 +35,6 @@
|
||||
#include "cantProceed.h"
|
||||
#include "epicsExit.h"
|
||||
|
||||
void epicsMutexCleanup(void);
|
||||
|
||||
typedef struct exitNode {
|
||||
ELLNODE node;
|
||||
epicsExitFunc func;
|
||||
@@ -115,8 +113,6 @@ LIBCOM_API void epicsExitCallAtExits(void)
|
||||
epicsExitCallAtExitsPvt ( pep );
|
||||
destroyExitPvt ( pep );
|
||||
}
|
||||
/* Handle specially to avoid circular reference */
|
||||
epicsMutexCleanup();
|
||||
}
|
||||
|
||||
LIBCOM_API void epicsExitCallAtThreadExits(void)
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#ifndef epicsExith
|
||||
#define epicsExith
|
||||
#include <libComAPI.h>
|
||||
#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.
|
||||
*
|
||||
|
||||
@@ -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<REGISTRAR>(&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<REGISTRYFUNCTION>(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 */
|
||||
|
||||
@@ -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);
|
||||
/** @} */
|
||||
|
||||
|
||||
@@ -58,4 +58,9 @@
|
||||
*/
|
||||
#define EPICS_UNUSED __attribute__((unused))
|
||||
|
||||
/*
|
||||
* No return marker
|
||||
*/
|
||||
#define EPICS_NORETURN __attribute__((noreturn))
|
||||
|
||||
#endif /* ifndef compilerSpecific_h */
|
||||
|
||||
@@ -57,4 +57,12 @@
|
||||
*/
|
||||
#define EPICS_UNUSED __attribute__((unused))
|
||||
|
||||
/*
|
||||
* No return marker
|
||||
*/
|
||||
#ifndef vxWorks
|
||||
// VxWorks does not mark abort() or exit() noreturn!
|
||||
#define EPICS_NORETURN __attribute__((noreturn))
|
||||
#endif
|
||||
|
||||
#endif /* ifndef 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 */
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -26,30 +26,23 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsThread.h"
|
||||
#include "valgrind/valgrind.h"
|
||||
#include "ellLib.h"
|
||||
#include "errlog.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsMutexImpl.h"
|
||||
#include "epicsThread.h"
|
||||
#include "cantProceed.h"
|
||||
|
||||
static epicsThreadOnceId epicsMutexOsiOnce = EPICS_THREAD_ONCE_INIT;
|
||||
static ELLLIST mutexList;
|
||||
static ELLLIST freeList;
|
||||
|
||||
struct epicsMutexParm {
|
||||
ELLNODE node;
|
||||
epicsMutexOSD * id;
|
||||
# ifdef LOG_LAST_OWNER
|
||||
epicsThreadId lastOwner;
|
||||
# endif
|
||||
const char *pFileName;
|
||||
int lineno;
|
||||
};
|
||||
|
||||
static epicsMutexOSD * epicsMutexGlobalLock;
|
||||
static ELLLIST mutexList = ELLLIST_INIT;
|
||||
|
||||
/* Specially initialized to bootstrap initialization.
|
||||
* When supported (posix and !rtems) use statically initiallized mutex.
|
||||
* Otherwise, initialize via epicsMutexOsdSetup().
|
||||
*/
|
||||
struct epicsMutexParm epicsMutexGlobalLock = {ELLNODE_INIT, __FILE__, __LINE__};
|
||||
|
||||
// vxWorks 5.4 gcc fails during compile when I use std::exception
|
||||
using namespace std;
|
||||
@@ -76,176 +69,82 @@ const char * epicsMutex::invalidMutex::what () const throw ()
|
||||
return "epicsMutex::invalidMutex()";
|
||||
}
|
||||
|
||||
static void epicsMutexOsiInit(void *) {
|
||||
ellInit(&mutexList);
|
||||
ellInit(&freeList);
|
||||
VALGRIND_CREATE_MEMPOOL(&freeList, 0, 0);
|
||||
epicsMutexGlobalLock = epicsMutexOsdCreate();
|
||||
}
|
||||
|
||||
epicsMutexId epicsStdCall epicsMutexOsiCreate(
|
||||
const char *pFileName,int lineno)
|
||||
{
|
||||
epicsMutexOSD * id;
|
||||
epicsMutexOsdSetup();
|
||||
|
||||
epicsThreadOnce(&epicsMutexOsiOnce, epicsMutexOsiInit, NULL);
|
||||
epicsMutexId ret = (epicsMutexId)calloc(1, sizeof(*ret));
|
||||
if(ret) {
|
||||
ret->pFileName = pFileName;
|
||||
ret->lineno = lineno;
|
||||
|
||||
if(!epicsMutexOsdPrepare(ret)) {
|
||||
epicsMutexMustLock(&epicsMutexGlobalLock);
|
||||
ellAdd(&mutexList, &ret->node);
|
||||
(void)epicsMutexUnlock(&epicsMutexGlobalLock);
|
||||
|
||||
} else {
|
||||
free(ret);
|
||||
ret = NULL;
|
||||
}
|
||||
|
||||
id = epicsMutexOsdCreate();
|
||||
if(!id) {
|
||||
return 0;
|
||||
}
|
||||
epicsMutexLockStatus lockStat =
|
||||
epicsMutexOsdLock(epicsMutexGlobalLock);
|
||||
assert ( lockStat == epicsMutexLockOK );
|
||||
epicsMutexParm *pmutexNode =
|
||||
reinterpret_cast < epicsMutexParm * > ( ellFirst(&freeList) );
|
||||
if(pmutexNode) {
|
||||
ellDelete(&freeList,&pmutexNode->node);
|
||||
VALGRIND_MEMPOOL_FREE(&freeList, pmutexNode);
|
||||
} else {
|
||||
pmutexNode = static_cast < epicsMutexParm * > ( calloc(1,sizeof(epicsMutexParm)) );
|
||||
}
|
||||
VALGRIND_MEMPOOL_ALLOC(&freeList, pmutexNode, sizeof(epicsMutexParm));
|
||||
pmutexNode->id = id;
|
||||
# ifdef LOG_LAST_OWNER
|
||||
pmutexNode->lastOwner = 0;
|
||||
# endif
|
||||
pmutexNode->pFileName = pFileName;
|
||||
pmutexNode->lineno = lineno;
|
||||
ellAdd(&mutexList,&pmutexNode->node);
|
||||
epicsMutexOsdUnlock(epicsMutexGlobalLock);
|
||||
return(pmutexNode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
epicsMutexId epicsStdCall epicsMutexOsiMustCreate(
|
||||
const char *pFileName,int lineno)
|
||||
{
|
||||
epicsMutexId id = epicsMutexOsiCreate(pFileName,lineno);
|
||||
assert(id);
|
||||
return(id );
|
||||
if(!id) {
|
||||
cantProceed("epicsMutexOsiMustCreate() fails at %s:%d\n",
|
||||
pFileName, lineno);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void epicsStdCall epicsMutexDestroy(epicsMutexId pmutexNode)
|
||||
{
|
||||
epicsMutexLockStatus lockStat =
|
||||
epicsMutexOsdLock(epicsMutexGlobalLock);
|
||||
assert ( lockStat == epicsMutexLockOK );
|
||||
ellDelete(&mutexList,&pmutexNode->node);
|
||||
epicsMutexOsdDestroy(pmutexNode->id);
|
||||
VALGRIND_MEMPOOL_FREE(&freeList, pmutexNode);
|
||||
VALGRIND_MEMPOOL_ALLOC(&freeList, &pmutexNode->node, sizeof(pmutexNode->node));
|
||||
ellAdd(&freeList,&pmutexNode->node);
|
||||
epicsMutexOsdUnlock(epicsMutexGlobalLock);
|
||||
}
|
||||
|
||||
void epicsStdCall epicsMutexUnlock(epicsMutexId pmutexNode)
|
||||
{
|
||||
epicsMutexOsdUnlock(pmutexNode->id);
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsStdCall epicsMutexLock(
|
||||
epicsMutexId pmutexNode)
|
||||
{
|
||||
epicsMutexLockStatus status =
|
||||
epicsMutexOsdLock(pmutexNode->id);
|
||||
# ifdef LOG_LAST_OWNER
|
||||
if ( status == epicsMutexLockOK ) {
|
||||
pmutexNode->lastOwner = epicsThreadGetIdSelf();
|
||||
}
|
||||
# endif
|
||||
return status;
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsStdCall epicsMutexTryLock(
|
||||
epicsMutexId pmutexNode)
|
||||
{
|
||||
epicsMutexLockStatus status =
|
||||
epicsMutexOsdTryLock(pmutexNode->id);
|
||||
# ifdef LOG_LAST_OWNER
|
||||
if ( status == epicsMutexLockOK ) {
|
||||
pmutexNode->lastOwner = epicsThreadGetIdSelf();
|
||||
}
|
||||
# endif
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Empty the freeList.
|
||||
* Called from epicsExit.c, but not via epicsAtExit()
|
||||
* to avoid the possibility of a circular reference.
|
||||
*/
|
||||
extern "C"
|
||||
void epicsMutexCleanup(void)
|
||||
{
|
||||
ELLNODE *cur;
|
||||
epicsMutexLockStatus lockStat =
|
||||
epicsMutexOsdLock(epicsMutexGlobalLock);
|
||||
assert ( lockStat == epicsMutexLockOK );
|
||||
|
||||
while((cur=ellGet(&freeList))!=NULL) {
|
||||
VALGRIND_MEMPOOL_FREE(&freeList, cur);
|
||||
free(cur);
|
||||
if(pmutexNode) {
|
||||
epicsMutexMustLock(&epicsMutexGlobalLock);
|
||||
ellDelete(&mutexList, &pmutexNode->node);
|
||||
(void)epicsMutexUnlock(&epicsMutexGlobalLock);
|
||||
epicsMutexOsdCleanup(pmutexNode);
|
||||
free(pmutexNode);
|
||||
}
|
||||
|
||||
epicsMutexOsdUnlock(epicsMutexGlobalLock);
|
||||
}
|
||||
|
||||
void epicsStdCall epicsMutexShow(
|
||||
epicsMutexId pmutexNode, unsigned int level)
|
||||
{
|
||||
# ifdef LOG_LAST_OWNER
|
||||
char threadName [255];
|
||||
if ( pmutexNode->lastOwner ) {
|
||||
# error currently not safe to fetch name for stale thread
|
||||
epicsThreadGetName ( pmutexNode->lastOwner,
|
||||
threadName, sizeof ( threadName ) );
|
||||
}
|
||||
else {
|
||||
strcpy ( threadName, "<not used>" );
|
||||
}
|
||||
printf("epicsMutexId %p last owner \"%s\" source %s line %d\n",
|
||||
(void *)pmutexNode, threadName,
|
||||
pmutexNode->pFileName, pmutexNode->lineno);
|
||||
# else
|
||||
printf("epicsMutexId %p source %s line %d\n",
|
||||
(void *)pmutexNode, pmutexNode->pFileName,
|
||||
pmutexNode->lineno);
|
||||
# endif
|
||||
printf("epicsMutexId %p source %s line %d\n",
|
||||
(void *)pmutexNode, pmutexNode->pFileName,
|
||||
pmutexNode->lineno);
|
||||
if ( level > 0 ) {
|
||||
epicsMutexOsdShow(pmutexNode->id,level-1);
|
||||
epicsMutexOsdShow(pmutexNode,level-1);
|
||||
}
|
||||
}
|
||||
|
||||
void epicsStdCall epicsMutexShowAll(int onlyLocked,unsigned int level)
|
||||
{
|
||||
epicsMutexParm *pmutexNode;
|
||||
epicsMutexOsdSetup();
|
||||
|
||||
if (epicsMutexOsiOnce == EPICS_THREAD_ONCE_INIT)
|
||||
return;
|
||||
|
||||
printf("ellCount(&mutexList) %d ellCount(&freeList) %d\n",
|
||||
ellCount(&mutexList),ellCount(&freeList));
|
||||
printf("ellCount(&mutexList) %d\n", ellCount(&mutexList));
|
||||
epicsMutexOsdShowAll();
|
||||
epicsMutexLockStatus lockStat =
|
||||
epicsMutexOsdLock(epicsMutexGlobalLock);
|
||||
assert ( lockStat == epicsMutexLockOK );
|
||||
pmutexNode = reinterpret_cast < epicsMutexParm * > ( ellFirst(&mutexList) );
|
||||
while(pmutexNode) {
|
||||
epicsMutexMustLock(&epicsMutexGlobalLock);
|
||||
for(ELLNODE *cur =ellFirst(&mutexList); cur; cur = ellNext(cur)) {
|
||||
epicsMutexParm *lock = CONTAINER(cur, epicsMutexParm, node);
|
||||
if(onlyLocked) {
|
||||
epicsMutexLockStatus status;
|
||||
status = epicsMutexOsdTryLock(pmutexNode->id);
|
||||
if(status==epicsMutexLockOK) {
|
||||
epicsMutexOsdUnlock(pmutexNode->id);
|
||||
pmutexNode =
|
||||
reinterpret_cast < epicsMutexParm * >
|
||||
( ellNext(&pmutexNode->node) );
|
||||
continue;
|
||||
// cycle through to test state
|
||||
if(epicsMutexTryLock(lock)==epicsMutexLockOK) {
|
||||
epicsMutexUnlock(lock);
|
||||
continue; // was not locked, skip
|
||||
}
|
||||
}
|
||||
epicsMutexShow(pmutexNode, level);
|
||||
pmutexNode =
|
||||
reinterpret_cast < epicsMutexParm * > ( ellNext(&pmutexNode->node) );
|
||||
epicsMutexShow(lock, level);
|
||||
}
|
||||
epicsMutexOsdUnlock(epicsMutexGlobalLock);
|
||||
epicsMutexUnlock(&epicsMutexGlobalLock);
|
||||
}
|
||||
|
||||
#if !defined(__GNUC__) || __GNUC__<4 || (__GNUC__==4 && __GNUC_MINOR__<8)
|
||||
|
||||
@@ -247,21 +247,6 @@ LIBCOM_API void epicsStdCall epicsMutexShow(
|
||||
LIBCOM_API void epicsStdCall epicsMutexShowAll(
|
||||
int onlyLocked,unsigned int level);
|
||||
|
||||
/**@privatesection
|
||||
* The following are interfaces to the OS dependent
|
||||
* implementation and should NOT be called directly by
|
||||
* user code.
|
||||
*/
|
||||
struct epicsMutexOSD * epicsMutexOsdCreate(void);
|
||||
void epicsMutexOsdDestroy(struct epicsMutexOSD *);
|
||||
void epicsMutexOsdUnlock(struct epicsMutexOSD *);
|
||||
epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD *);
|
||||
epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD *);
|
||||
void epicsMutexOsdShow(struct epicsMutexOSD *,unsigned int level);
|
||||
#ifdef EPICS_PRIVATE_API
|
||||
void epicsMutexOsdShowAll(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
66
modules/libcom/src/osi/epicsMutexImpl.h
Normal file
66
modules/libcom/src/osi/epicsMutexImpl.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* Copyright (c) 2023 Michael Davidsaver
|
||||
* SPDX-License-Identifier: EPICS
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* Only include from osdMutex.c */
|
||||
|
||||
#ifndef epicsMutexImpl_H
|
||||
#define epicsMutexImpl_H
|
||||
|
||||
#if defined(vxWorks)
|
||||
# include <vxWorks.h>
|
||||
# include <semLib.h>
|
||||
#elif defined(_WIN32)
|
||||
# define VC_EXTRALEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
#elif defined(__rtems__)
|
||||
# include <rtems.h>
|
||||
# include <rtems/score/cpuopts.h>
|
||||
#else
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include "ellLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct epicsMutexParm {
|
||||
/* global list of mutex */
|
||||
ELLNODE node;
|
||||
/* location where mutex was allocated */
|
||||
const char *pFileName;
|
||||
int lineno;
|
||||
#if defined(vxWorks)
|
||||
SEM_ID osd;
|
||||
#elif defined(_WIN32)
|
||||
CRITICAL_SECTION osd;
|
||||
#elif defined(__RTEMS_MAJOR__) && __RTEMS_MAJOR__<5
|
||||
Semaphore_Control *osd;
|
||||
#else
|
||||
pthread_mutex_t osd;
|
||||
#endif
|
||||
};
|
||||
|
||||
void epicsMutexOsdSetup(void);
|
||||
long epicsMutexOsdPrepare(struct epicsMutexParm *);
|
||||
void epicsMutexOsdCleanup(struct epicsMutexParm *);
|
||||
void epicsMutexOsdShow(struct epicsMutexParm *,unsigned int level);
|
||||
void epicsMutexOsdShowAll(void);
|
||||
|
||||
extern struct epicsMutexParm epicsMutexGlobalLock;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C
|
||||
#endif
|
||||
|
||||
#endif // epicsMutexImpl_H
|
||||
@@ -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();
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsThread.h"
|
||||
#include "rtemsNamePvt.h"
|
||||
#include "errlog.h"
|
||||
|
||||
/* #define EPICS_RTEMS_SEMAPHORE_STATS */
|
||||
@@ -47,12 +48,9 @@ epicsEventCreate(epicsEventInitialState initialState)
|
||||
{
|
||||
rtems_status_code sc;
|
||||
rtems_id sid;
|
||||
rtems_interrupt_level level;
|
||||
static char c1 = 'a';
|
||||
static char c2 = 'a';
|
||||
static char c3 = 'a';
|
||||
static uint32_t name;
|
||||
|
||||
sc = rtems_semaphore_create (rtems_build_name ('B', c3, c2, c1),
|
||||
sc = rtems_semaphore_create (next_rtems_name ('B', &name),
|
||||
initialState,
|
||||
RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE |
|
||||
RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL,
|
||||
@@ -62,26 +60,6 @@ epicsEventCreate(epicsEventInitialState initialState)
|
||||
errlogPrintf ("Can't create binary semaphore: %s\n", rtems_status_text (sc));
|
||||
return NULL;
|
||||
}
|
||||
rtems_interrupt_disable (level);
|
||||
if (c1 == 'z') {
|
||||
if (c2 == 'z') {
|
||||
if (c3 == 'z') {
|
||||
c3 = 'a';
|
||||
}
|
||||
else {
|
||||
c3++;
|
||||
}
|
||||
c2 = 'a';
|
||||
}
|
||||
else {
|
||||
c2++;
|
||||
}
|
||||
c1 = 'a';
|
||||
}
|
||||
else {
|
||||
c1++;
|
||||
}
|
||||
rtems_interrupt_enable (level);
|
||||
return (epicsEventId)sid;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <rtems.h>
|
||||
#include <rtems/error.h>
|
||||
|
||||
@@ -26,97 +27,84 @@
|
||||
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsMutexImpl.h"
|
||||
#include "rtemsNamePvt.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#define RTEMS_FAST_MUTEX
|
||||
/* #define EPICS_RTEMS_SEMAPHORE_STATS */
|
||||
/*
|
||||
* Some performance tuning instrumentation
|
||||
*/
|
||||
#ifdef EPICS_RTEMS_SEMAPHORE_STATS
|
||||
unsigned long semMstat[4];
|
||||
#define SEMSTAT(i) semMstat[i]++;
|
||||
#else
|
||||
#define SEMSTAT(i)
|
||||
#endif
|
||||
uint32_t next_rtems_name(char prefix, uint32_t* counter)
|
||||
{
|
||||
uint32_t next;
|
||||
rtems_interrupt_level level;
|
||||
char a, b, c;
|
||||
|
||||
struct epicsMutexOSD *
|
||||
epicsMutexOsdCreate(void)
|
||||
rtems_interrupt_disable (level);
|
||||
next = *counter;
|
||||
*counter = (next+1)%(26u*26u*26u);
|
||||
rtems_interrupt_enable (level);
|
||||
|
||||
a = 'a' + (next % 26u);
|
||||
next /= 26u;
|
||||
b = 'a' + (next % 26u);
|
||||
next /= 26u;
|
||||
c = 'a' + (next % 26u); // modulo should be redundant, but ... paranoia
|
||||
|
||||
return rtems_build_name(prefix, a, b, c);
|
||||
}
|
||||
|
||||
void epicsMutexOsdSetup(void)
|
||||
{
|
||||
// TODO: use RTEMS_SYSINIT_ITEM() ?
|
||||
if(!epicsMutexGlobalLock.osd) {
|
||||
epicsMutexOsdPrepare(&epicsMutexGlobalLock);
|
||||
}
|
||||
}
|
||||
|
||||
long epicsMutexOsdPrepare(struct epicsMutexParm *mutex)
|
||||
{
|
||||
rtems_status_code sc;
|
||||
rtems_id sid;
|
||||
rtems_interrupt_level level;
|
||||
static char c1 = 'a';
|
||||
static char c2 = 'a';
|
||||
static char c3 = 'a';
|
||||
static uint32_t name;
|
||||
|
||||
sc = rtems_semaphore_create (rtems_build_name ('M', c3, c2, c1),
|
||||
sc = rtems_semaphore_create (next_rtems_name ('M', &name),
|
||||
1,
|
||||
RTEMS_PRIORITY|RTEMS_BINARY_SEMAPHORE|RTEMS_INHERIT_PRIORITY|RTEMS_NO_PRIORITY_CEILING|RTEMS_LOCAL,
|
||||
0,
|
||||
&sid);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
errlogPrintf ("Can't create mutex semaphore: %s\n", rtems_status_text (sc));
|
||||
return NULL;
|
||||
return ENOMEM;
|
||||
}
|
||||
rtems_interrupt_disable (level);
|
||||
if (c1 == 'z') {
|
||||
if (c2 == 'z') {
|
||||
if (c3 == 'z') {
|
||||
c3 = 'a';
|
||||
}
|
||||
else {
|
||||
c3++;
|
||||
}
|
||||
c2 = 'a';
|
||||
}
|
||||
else {
|
||||
c2++;
|
||||
}
|
||||
c1 = 'a';
|
||||
}
|
||||
else {
|
||||
c1++;
|
||||
}
|
||||
rtems_interrupt_enable (level);
|
||||
#ifdef RTEMS_FAST_MUTEX
|
||||
{
|
||||
Semaphore_Control *the_semaphore;
|
||||
Objects_Locations location;
|
||||
Objects_Locations location;
|
||||
|
||||
the_semaphore = _Semaphore_Get( sid, &location );
|
||||
_Thread_Enable_dispatch();
|
||||
mutex->osd = _Semaphore_Get( sid, &location );
|
||||
_Thread_Enable_dispatch(); /* _Semaphore_Get() disables */
|
||||
|
||||
return (struct epicsMutexOSD *)the_semaphore;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return (struct epicsMutexOSD *)sid;
|
||||
}
|
||||
|
||||
void epicsMutexOsdDestroy(struct epicsMutexOSD * id)
|
||||
void epicsMutexOsdCleanup(struct epicsMutexParm *mutex)
|
||||
{
|
||||
rtems_status_code sc;
|
||||
rtems_id sid;
|
||||
#ifdef RTEMS_FAST_MUTEX
|
||||
Semaphore_Control *the_semaphore = (Semaphore_Control *)id;
|
||||
Semaphore_Control *the_semaphore = mutex->osd;
|
||||
sid = the_semaphore->Object.id;
|
||||
#else
|
||||
sid = (rtems_id)id;
|
||||
#endif
|
||||
sc = rtems_semaphore_delete (sid);
|
||||
if (sc == RTEMS_RESOURCE_IN_USE) {
|
||||
rtems_semaphore_release (sid);
|
||||
sc = rtems_semaphore_delete (sid);
|
||||
}
|
||||
if (sc != RTEMS_SUCCESSFUL)
|
||||
errlogPrintf ("Can't destroy semaphore %p (%lx): %s\n", id, (unsigned long)sid, rtems_status_text (sc));
|
||||
errlogPrintf ("Can't destroy semaphore %p (%lx): %s\n",
|
||||
mutex, (unsigned long)sid, rtems_status_text (sc));
|
||||
}
|
||||
|
||||
void epicsMutexOsdUnlock(struct epicsMutexOSD * id)
|
||||
void epicsMutexUnlock(struct epicsMutexParm *mutex)
|
||||
{
|
||||
#ifdef RTEMS_FAST_MUTEX
|
||||
Semaphore_Control *the_semaphore = (Semaphore_Control *)id;
|
||||
Semaphore_Control *the_semaphore = mutex->osd;
|
||||
_Thread_Disable_dispatch();
|
||||
_CORE_mutex_Surrender (
|
||||
&the_semaphore->Core_control.mutex,
|
||||
@@ -124,18 +112,13 @@ void epicsMutexOsdUnlock(struct epicsMutexOSD * id)
|
||||
NULL
|
||||
);
|
||||
_Thread_Enable_dispatch();
|
||||
#else
|
||||
epicsEventSignal (id);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * id)
|
||||
epicsMutexLockStatus epicsMutexLock(struct epicsMutexParm *mutex)
|
||||
{
|
||||
#ifdef RTEMS_FAST_MUTEX
|
||||
Semaphore_Control *the_semaphore = (Semaphore_Control *)id;
|
||||
Semaphore_Control *the_semaphore = mutex->osd;
|
||||
ISR_Level level;
|
||||
SEMSTAT(0)
|
||||
_ISR_Disable( level );
|
||||
_CORE_mutex_Seize(
|
||||
&the_semaphore->Core_control.mutex,
|
||||
@@ -148,19 +131,12 @@ epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * id)
|
||||
return epicsMutexLockOK;
|
||||
else
|
||||
return epicsMutexLockError;
|
||||
#else
|
||||
SEMSTAT(0)
|
||||
return((epicsEventWait (id) == epicsEventWaitOK)
|
||||
?epicsMutexLockOK : epicsMutexLockError);
|
||||
#endif
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * id)
|
||||
epicsMutexLockStatus epicsMutexTryLock(struct epicsMutexParm *mutex)
|
||||
{
|
||||
#ifdef RTEMS_FAST_MUTEX
|
||||
Semaphore_Control *the_semaphore = (Semaphore_Control *)id;
|
||||
Semaphore_Control *the_semaphore = mutex->osd;
|
||||
ISR_Level level;
|
||||
SEMSTAT(2)
|
||||
_ISR_Disable( level );
|
||||
_CORE_mutex_Seize(
|
||||
&the_semaphore->Core_control.mutex,
|
||||
@@ -175,25 +151,12 @@ epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * id)
|
||||
return epicsMutexLockTimeout;
|
||||
else
|
||||
return epicsMutexLockError;
|
||||
#else
|
||||
epicsEventWaitStatus status;
|
||||
SEMSTAT(2)
|
||||
status = epicsEventTryWait(id);
|
||||
return((status==epicsEventWaitOK
|
||||
? epicsMutexLockOK
|
||||
: (status==epicsEventWaitTimeout)
|
||||
? epicsMutexLockTimeout
|
||||
: epicsMutexLockError));
|
||||
#endif
|
||||
}
|
||||
|
||||
LIBCOM_API void epicsMutexOsdShow(struct epicsMutexOSD * id,unsigned int level)
|
||||
LIBCOM_API void epicsMutexOsdShow(struct epicsMutexParm *mutex,unsigned int level)
|
||||
{
|
||||
#ifdef RTEMS_FAST_MUTEX
|
||||
Semaphore_Control *the_semaphore = (Semaphore_Control *)id;
|
||||
id = (struct epicsMutexOSD *)the_semaphore->Object.id;
|
||||
#endif
|
||||
epicsEventShow ((epicsEventId)id,level);
|
||||
Semaphore_Control *the_semaphore = mutex->osd;
|
||||
epicsEventShow ((epicsEventId)the_semaphore->Object.id,level);
|
||||
}
|
||||
|
||||
void epicsMutexOsdShowAll(void) {}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "epicsStdio.h"
|
||||
#include "errlog.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsMutexImpl.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsThread.h"
|
||||
#include "cantProceed.h"
|
||||
@@ -54,12 +55,13 @@ struct taskVar {
|
||||
int refcnt;
|
||||
int joinable;
|
||||
int isRunning;
|
||||
int isOkToBlock;
|
||||
EPICSTHREADFUNC funptr;
|
||||
void *parm;
|
||||
unsigned int threadVariableCapacity;
|
||||
void **threadVariables;
|
||||
};
|
||||
static struct epicsMutexOSD *taskVarMutex;
|
||||
static struct epicsMutexParm taskVarMutex = {ELLNODE_INIT, __FILE__, __LINE__};
|
||||
static struct taskVar *taskVarHead;
|
||||
#define RTEMS_NOTEPAD_TASKVAR 11
|
||||
|
||||
@@ -67,15 +69,7 @@ static struct taskVar *taskVarHead;
|
||||
* Support for `once-only' execution
|
||||
*/
|
||||
static volatile int initialized = 0; /* strictly speaking 'volatile' is not enough here, but it shouldn't hurt */
|
||||
static struct epicsMutexOSD *onceMutex;
|
||||
|
||||
static
|
||||
void epicsMutexOsdMustLock(struct epicsMutexOSD * L)
|
||||
{
|
||||
while(epicsMutexOsdLock(L)!=epicsMutexLockOK) {
|
||||
cantProceed("epicsThreadOnce() mutex error");
|
||||
}
|
||||
}
|
||||
static struct epicsMutexParm onceMutex = {ELLNODE_INIT, __FILE__, __LINE__};
|
||||
|
||||
/*
|
||||
* Just map osi 0 to 99 into RTEMS 199 to 100
|
||||
@@ -161,13 +155,13 @@ epicsThreadGetStackSize (epicsThreadStackSizeClass size)
|
||||
static void
|
||||
taskVarLock (void)
|
||||
{
|
||||
epicsMutexOsdMustLock (taskVarMutex);
|
||||
epicsMutexMustLock (&taskVarMutex);
|
||||
}
|
||||
|
||||
static void
|
||||
taskVarUnlock (void)
|
||||
{
|
||||
epicsMutexOsdUnlock (taskVarMutex);
|
||||
epicsMutexUnlock (&taskVarMutex);
|
||||
}
|
||||
|
||||
static
|
||||
@@ -226,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;
|
||||
@@ -242,8 +236,9 @@ 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];
|
||||
char c[3] = {0,0,0};
|
||||
strncpy(c, v->name, 3);
|
||||
sc = rtems_barrier_create(rtems_build_name('~', c[0], c[1], c[2]),
|
||||
RTEMS_BARRIER_AUTOMATIC_RELEASE | RTEMS_LOCAL,
|
||||
@@ -288,12 +283,10 @@ epicsThreadInit (void)
|
||||
rtems_task_priority old;
|
||||
|
||||
rtems_task_set_priority (RTEMS_SELF, epicsThreadGetOssPriorityValue(99), &old);
|
||||
onceMutex = epicsMutexOsdCreate();
|
||||
taskVarMutex = epicsMutexOsdCreate();
|
||||
if (!onceMutex || !taskVarMutex)
|
||||
cantProceed("epicsThreadInit() can't create global mutexes\n");
|
||||
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;
|
||||
@@ -317,7 +310,7 @@ epicsThreadCreateOpt (
|
||||
unsigned int stackSize;
|
||||
rtems_id tid;
|
||||
rtems_status_code sc;
|
||||
char c[4];
|
||||
char c[4] = {0,0,0,0};
|
||||
|
||||
if (!initialized)
|
||||
epicsThreadInit();
|
||||
@@ -347,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));
|
||||
@@ -612,26 +605,26 @@ void epicsThreadOnce(epicsThreadOnceId *id, void(*func)(void *), void *arg)
|
||||
#define EPICS_THREAD_ONCE_DONE (epicsThreadId) 1
|
||||
|
||||
if (!initialized) epicsThreadInit();
|
||||
epicsMutexOsdMustLock(onceMutex);
|
||||
epicsMutexMustLock(&onceMutex);
|
||||
if (*id != EPICS_THREAD_ONCE_DONE) {
|
||||
if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */
|
||||
*id = epicsThreadGetIdSelf(); /* mark active */
|
||||
epicsMutexOsdUnlock(onceMutex);
|
||||
epicsMutexUnlock(&onceMutex);
|
||||
func(arg);
|
||||
epicsMutexOsdMustLock(onceMutex);
|
||||
epicsMutexMustLock(&onceMutex);
|
||||
*id = EPICS_THREAD_ONCE_DONE; /* mark done */
|
||||
} else if (*id == epicsThreadGetIdSelf()) {
|
||||
epicsMutexOsdUnlock(onceMutex);
|
||||
epicsMutexUnlock(&onceMutex);
|
||||
cantProceed("Recursive epicsThreadOnce() initialization\n");
|
||||
} else
|
||||
while (*id != EPICS_THREAD_ONCE_DONE) {
|
||||
/* Another thread is in the above func(arg) call. */
|
||||
epicsMutexOsdUnlock(onceMutex);
|
||||
epicsMutexUnlock(&onceMutex);
|
||||
epicsThreadSleep(epicsThreadSleepQuantum());
|
||||
epicsMutexOsdMustLock(onceMutex);
|
||||
epicsMutexMustLock(&onceMutex);
|
||||
}
|
||||
}
|
||||
epicsMutexOsdUnlock(onceMutex);
|
||||
epicsMutexUnlock(&onceMutex);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -879,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;
|
||||
}
|
||||
|
||||
20
modules/libcom/src/osi/os/RTEMS-score/rtemsNamePvt.h
Normal file
20
modules/libcom/src/osi/os/RTEMS-score/rtemsNamePvt.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2023 Michael Davidsaver
|
||||
* SPDX-License-Identifier: EPICS
|
||||
* EPICS Base is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef RTEMSNAMEPVT_H
|
||||
#define RTEMSNAMEPVT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Compute rtems_build_name(prefix, A, B, C) where A, B, C are the letters a-z.
|
||||
* eg. "Qaaa"
|
||||
*
|
||||
* 'counter' is incremented atomically during each call.
|
||||
*/
|
||||
uint32_t next_rtems_name(char prefix, uint32_t* counter);
|
||||
|
||||
#endif // RTEMSNAMEPVT_H
|
||||
@@ -19,27 +19,68 @@
|
||||
|
||||
#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 <algorithm> 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
|
||||
|
||||
/* 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"
|
||||
|
||||
#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")
|
||||
#pragma pop_macro("NOCRYPT")
|
||||
#pragma pop_macro("NOSOUND")
|
||||
#pragma pop_macro("NOKANJI")
|
||||
|
||||
#if defined ( _WIN64 )
|
||||
# define MS_ATOMIC_64
|
||||
|
||||
@@ -20,147 +20,59 @@
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define VC_EXTRALEAN
|
||||
#define STRICT
|
||||
#include <windows.h>
|
||||
#if _WIN32_WINNT < 0x0501
|
||||
# error Minimum supported is Windows XP
|
||||
#endif
|
||||
|
||||
#define EPICS_PRIVATE_API
|
||||
|
||||
#include "libComAPI.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsMutexImpl.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsStdio.h"
|
||||
|
||||
typedef struct epicsMutexOSD {
|
||||
union {
|
||||
HANDLE mutex;
|
||||
CRITICAL_SECTION criticalSection;
|
||||
} os;
|
||||
} epicsMutexOSD;
|
||||
|
||||
static BOOL thisIsNT = FALSE;
|
||||
static LONG weHaveInitialized = 0;
|
||||
|
||||
/*
|
||||
* epicsMutexCreate ()
|
||||
*/
|
||||
epicsMutexOSD * epicsMutexOsdCreate ( void )
|
||||
static epicsThreadOnceId epicsMutexOsdOnce = EPICS_THREAD_ONCE_INIT;
|
||||
static void epicsMutexOsdInit(void* unused)
|
||||
{
|
||||
epicsMutexOSD * pSem;
|
||||
|
||||
if ( ! weHaveInitialized ) {
|
||||
BOOL status;
|
||||
OSVERSIONINFO osInfo;
|
||||
osInfo.dwOSVersionInfoSize = sizeof ( OSVERSIONINFO );
|
||||
status = GetVersionEx ( & osInfo );
|
||||
thisIsNT = status && ( osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT );
|
||||
weHaveInitialized = 1;
|
||||
}
|
||||
|
||||
pSem = malloc ( sizeof (*pSem) );
|
||||
if ( pSem ) {
|
||||
if ( thisIsNT ) {
|
||||
InitializeCriticalSection ( &pSem->os.criticalSection );
|
||||
}
|
||||
else {
|
||||
pSem->os.mutex = CreateMutex ( NULL, FALSE, NULL );
|
||||
if ( pSem->os.mutex == 0 ) {
|
||||
free ( pSem );
|
||||
pSem = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pSem;
|
||||
(void)unused;
|
||||
InitializeCriticalSection(&epicsMutexGlobalLock.osd);
|
||||
}
|
||||
|
||||
/*
|
||||
* epicsMutexOsdDestroy ()
|
||||
*/
|
||||
void epicsMutexOsdDestroy ( epicsMutexOSD * pSem )
|
||||
void epicsMutexOsdSetup()
|
||||
{
|
||||
if ( thisIsNT ) {
|
||||
DeleteCriticalSection ( &pSem->os.criticalSection );
|
||||
}
|
||||
else {
|
||||
CloseHandle ( pSem->os.mutex );
|
||||
}
|
||||
free ( pSem );
|
||||
epicsThreadOnce(&epicsMutexOsdOnce, &epicsMutexOsdInit, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* epicsMutexOsdUnlock ()
|
||||
*/
|
||||
void epicsMutexOsdUnlock ( epicsMutexOSD * pSem )
|
||||
long epicsMutexOsdPrepare(struct epicsMutexParm *mutex)
|
||||
{
|
||||
if ( thisIsNT ) {
|
||||
LeaveCriticalSection ( &pSem->os.criticalSection );
|
||||
}
|
||||
else {
|
||||
BOOL success = ReleaseMutex ( pSem->os.mutex );
|
||||
assert ( success );
|
||||
}
|
||||
InitializeCriticalSection(&mutex->osd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* epicsMutexOsdLock ()
|
||||
*/
|
||||
epicsMutexLockStatus epicsMutexOsdLock ( epicsMutexOSD * pSem )
|
||||
void epicsMutexOsdCleanup(struct epicsMutexParm *mutex)
|
||||
{
|
||||
if ( thisIsNT ) {
|
||||
EnterCriticalSection ( &pSem->os.criticalSection );
|
||||
}
|
||||
else {
|
||||
DWORD status = WaitForSingleObject ( pSem->os.mutex, INFINITE );
|
||||
if ( status != WAIT_OBJECT_0 ) {
|
||||
return epicsMutexLockError;
|
||||
}
|
||||
}
|
||||
DeleteCriticalSection(&mutex->osd);
|
||||
}
|
||||
|
||||
void epicsStdCall epicsMutexUnlock ( struct epicsMutexParm *mutex )
|
||||
{
|
||||
LeaveCriticalSection ( &mutex->osd );
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsStdCall epicsMutexLock ( struct epicsMutexParm *mutex )
|
||||
{
|
||||
EnterCriticalSection ( &mutex->osd );
|
||||
return epicsMutexLockOK;
|
||||
}
|
||||
|
||||
/*
|
||||
* epicsMutexOsdTryLock ()
|
||||
*/
|
||||
epicsMutexLockStatus epicsMutexOsdTryLock ( epicsMutexOSD * pSem )
|
||||
epicsMutexLockStatus epicsStdCall epicsMutexTryLock ( struct epicsMutexParm *mutex )
|
||||
{
|
||||
if ( thisIsNT ) {
|
||||
if ( TryEnterCriticalSection ( &pSem->os.criticalSection ) ) {
|
||||
return epicsMutexLockOK;
|
||||
}
|
||||
else {
|
||||
return epicsMutexLockTimeout;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DWORD status = WaitForSingleObject ( pSem->os.mutex, 0 );
|
||||
if ( status != WAIT_OBJECT_0 ) {
|
||||
if (status == WAIT_TIMEOUT) {
|
||||
return epicsMutexLockTimeout;
|
||||
}
|
||||
else {
|
||||
return epicsMutexLockError;
|
||||
}
|
||||
}
|
||||
}
|
||||
return epicsMutexLockOK;
|
||||
return TryEnterCriticalSection ( &mutex->osd ) ? epicsMutexLockOK : epicsMutexLockTimeout;
|
||||
}
|
||||
|
||||
/*
|
||||
* epicsMutexOsdShow ()
|
||||
*/
|
||||
void epicsMutexOsdShow ( epicsMutexOSD * pSem, unsigned level )
|
||||
void epicsMutexOsdShow ( struct epicsMutexParm *mutex, unsigned level )
|
||||
{
|
||||
if ( thisIsNT ) {
|
||||
printf ("epicsMutex: win32 critical section at %p\n",
|
||||
(void * ) & pSem->os.criticalSection );
|
||||
}
|
||||
else {
|
||||
printf ( "epicsMutex: win32 mutex at %p\n",
|
||||
( void * ) pSem->os.mutex );
|
||||
}
|
||||
(void)level;
|
||||
printf ("epicsMutex: win32 critical section at %p\n",
|
||||
(void * ) & mutex->osd );
|
||||
}
|
||||
|
||||
void epicsMutexOsdShowAll(void) {}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user