Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4ebc01401 | ||
|
|
6ea6ae633b | ||
|
|
9fac52fa58 | ||
|
|
fc5d3c9a5c | ||
| c75ad2673e | |||
| b97a35fec8 | |||
|
|
350570134e | ||
|
|
72f3e75c8d | ||
|
|
90f97a7037 | ||
|
|
fd86f0ff04 | ||
|
|
bcc6cb96ae | ||
|
|
a4bc0db6e6 | ||
|
|
48ebe2c64e | ||
|
|
9f788996dc | ||
|
|
13d6ca598c | ||
| ee1a49045a | |||
|
|
78f263f359 | ||
| 43e75e3901 | |||
|
|
131578124b | ||
|
|
7bd3e7aa2e |
@@ -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
|
||||
|
||||
@@ -52,11 +52,11 @@ 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 = 0
|
||||
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)
|
||||
EPICS_DEV_SNAPSHOT=
|
||||
EPICS_DEV_SNAPSHOT=-DEV
|
||||
|
||||
# No changes should be needed below here
|
||||
|
||||
@@ -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,11 +2,11 @@
|
||||
|
||||
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
|
||||
|
||||
EPICS_CA_DEVELOPMENT_FLAG = 0
|
||||
EPICS_CA_DEVELOPMENT_FLAG = 1
|
||||
|
||||
# Immediately after a release the MAINTENANCE_VERSION
|
||||
# will be incremented and the DEVELOPMENT_FLAG set to 1
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
EPICS_DATABASE_MAJOR_VERSION = 3
|
||||
EPICS_DATABASE_MINOR_VERSION = 24
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 0
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 1
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
EPICS_DATABASE_DEVELOPMENT_FLAG = 0
|
||||
EPICS_DATABASE_DEVELOPMENT_FLAG = 1
|
||||
|
||||
# Immediately after a release the MAINTENANCE_VERSION
|
||||
# will be incremented and the DEVELOPMENT_FLAG set to 1
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
EPICS_LIBCOM_MAJOR_VERSION = 3
|
||||
EPICS_LIBCOM_MINOR_VERSION = 24
|
||||
EPICS_LIBCOM_MAINTENANCE_VERSION = 0
|
||||
EPICS_LIBCOM_MAINTENANCE_VERSION = 1
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
EPICS_LIBCOM_DEVELOPMENT_FLAG = 0
|
||||
EPICS_LIBCOM_DEVELOPMENT_FLAG = 1
|
||||
|
||||
# Immediately after a release the MAINTENANCE_VERSION
|
||||
# will be incremented and the DEVELOPMENT_FLAG set to 1
|
||||
|
||||
@@ -12,6 +12,35 @@ 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.
|
||||
|
||||
__This version of EPICS has not been released yet.__
|
||||
|
||||
## Changes made on the 7.0 branch since 7.0.9
|
||||
|
||||
__Add new items below here__
|
||||
|
||||
### Reduce symbol and macro pollution from epicsAtomic.h on WIN32
|
||||
|
||||
`epicsAtomic.h` no longer pulls in as many unneeded declarations and macros from
|
||||
`windows.h`. Prior to this change, including `epicsAtomic.h` at the wrong time
|
||||
could result in unexpected compiler errors. Due to the nature of `windows.h`,
|
||||
some unneeded declarations are still pulled in, however the number is greatly reduced.
|
||||
Code that needs these declarations should explicitly include `windows.h` before `epicsAtomic.h`.
|
||||
|
||||
### epicsExport simplifications
|
||||
|
||||
`epicsExportAddress()`, `epicsExportRegistrar()` and `epicsRegisterFunction()`
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -208,8 +208,8 @@ TESTSPEC_RTEMS = dbTestHarness.boot; epicsRunDbTests
|
||||
|
||||
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
|
||||
TESTPROD_RTEMS = $(TESTPROD_HOST)
|
||||
TESTSCRIPTS_RTEMS += $(TESTS:%=%.t)
|
||||
TESTPROD += $(TESTPROD_HOST)
|
||||
TESTSCRIPTS += $(TESTS:%=%.t)
|
||||
endif
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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"
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -55,6 +55,7 @@ struct taskVar {
|
||||
int refcnt;
|
||||
int joinable;
|
||||
int isRunning;
|
||||
int isOkToBlock;
|
||||
EPICSTHREADFUNC funptr;
|
||||
void *parm;
|
||||
unsigned int threadVariableCapacity;
|
||||
@@ -219,7 +220,7 @@ void epicsThreadExitMain (void)
|
||||
|
||||
static rtems_status_code
|
||||
setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr,
|
||||
void *parm, int joinable)
|
||||
void *parm, int joinable, int isOkToBlock)
|
||||
{
|
||||
struct taskVar *v;
|
||||
uint32_t note;
|
||||
@@ -235,6 +236,7 @@ setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr,
|
||||
v->threadVariableCapacity = 0;
|
||||
v->threadVariables = NULL;
|
||||
v->isRunning = 1;
|
||||
v->isOkToBlock = isOkToBlock;
|
||||
if (joinable) {
|
||||
char c[3] = {0,0,0};
|
||||
strncpy(c, v->name, 3);
|
||||
@@ -284,7 +286,7 @@ epicsThreadInit (void)
|
||||
epicsMutexOsdPrepare(&taskVarMutex);
|
||||
epicsMutexOsdPrepare(&onceMutex);
|
||||
rtems_task_ident (RTEMS_SELF, 0, &tid);
|
||||
if(setThreadInfo (tid, "_main_", NULL, NULL, 0) != RTEMS_SUCCESSFUL)
|
||||
if(setThreadInfo (tid, "_main_", NULL, NULL, 0, 1) != RTEMS_SUCCESSFUL)
|
||||
cantProceed("epicsThreadInit() unable to setup _main_");
|
||||
osdThreadHooksRunMain((epicsThreadId)tid);
|
||||
initialized = 1;
|
||||
@@ -338,7 +340,7 @@ epicsThreadCreateOpt (
|
||||
name, rtems_status_text(sc));
|
||||
return 0;
|
||||
}
|
||||
sc = setThreadInfo (tid, name, funptr, parm, opts->joinable);
|
||||
sc = setThreadInfo (tid, name, funptr, parm, opts->joinable, 0);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
errlogPrintf ("epicsThreadCreate create failure during setup for %s: %s\n",
|
||||
name, rtems_status_text(sc));
|
||||
@@ -870,3 +872,27 @@ LIBCOM_API int epicsThreadGetCPUs(void)
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int epicsStdCall epicsThreadIsOkToBlock(void)
|
||||
{
|
||||
uint32_t note = 0;
|
||||
struct taskVar *v;
|
||||
|
||||
rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, ¬e);
|
||||
v = (void *)note;
|
||||
|
||||
return v && v->isOkToBlock;
|
||||
}
|
||||
|
||||
void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock)
|
||||
{
|
||||
uint32_t note = 0;
|
||||
struct taskVar *v;
|
||||
|
||||
rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, ¬e);
|
||||
v = (void *)note;
|
||||
|
||||
if(v)
|
||||
v->isOkToBlock = !!isOkToBlock;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -103,6 +103,7 @@ typedef struct epicsThreadOSD {
|
||||
char isSuspended;
|
||||
int joinable;
|
||||
int isRunning;
|
||||
int isOkToBlock;
|
||||
HANDLE timer; /* waitable timer */
|
||||
} win32ThreadParam;
|
||||
|
||||
@@ -586,6 +587,7 @@ static win32ThreadParam * epicsThreadImplicitCreate ( void )
|
||||
|
||||
pParm->handle = handle;
|
||||
pParm->id = id;
|
||||
pParm->isOkToBlock = 1;
|
||||
win32ThreadPriority = GetThreadPriority ( pParm->handle );
|
||||
assert ( win32ThreadPriority != THREAD_PRIORITY_ERROR_RETURN );
|
||||
pParm->epicsPriority = epicsThreadGetOsiPriorityValue ( win32ThreadPriority );
|
||||
@@ -1224,3 +1226,17 @@ void testPriorityMapping ()
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int epicsStdCall epicsThreadIsOkToBlock(void)
|
||||
{
|
||||
struct epicsThreadOSD *pthreadInfo = epicsThreadGetIdSelf();
|
||||
|
||||
return(pthreadInfo->isOkToBlock);
|
||||
}
|
||||
|
||||
void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock)
|
||||
{
|
||||
struct epicsThreadOSD *pthreadInfo = epicsThreadGetIdSelf();
|
||||
|
||||
pthreadInfo->isOkToBlock = !!isOkToBlock;
|
||||
}
|
||||
|
||||
@@ -665,6 +665,7 @@ static epicsThreadOSD *createImplicit(void)
|
||||
assert(pthreadInfo);
|
||||
pthreadInfo->tid = tid;
|
||||
pthreadInfo->osiPriority = 0;
|
||||
pthreadInfo->isOkToBlock = 1;
|
||||
|
||||
#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0
|
||||
if(pthread_getschedparam(tid,&pthreadInfo->schedPolicy,&pthreadInfo->schedParam) == 0) {
|
||||
@@ -1085,3 +1086,17 @@ LIBCOM_API int epicsThreadGetCPUs(void)
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
int epicsStdCall epicsThreadIsOkToBlock(void)
|
||||
{
|
||||
epicsThreadOSD *pthreadInfo = epicsThreadGetIdSelf();
|
||||
|
||||
return(pthreadInfo->isOkToBlock);
|
||||
}
|
||||
|
||||
void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock)
|
||||
{
|
||||
epicsThreadOSD *pthreadInfo = epicsThreadGetIdSelf();
|
||||
|
||||
pthreadInfo->isOkToBlock = !!isOkToBlock;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ typedef struct epicsThreadOSD {
|
||||
int isRealTimeScheduled;
|
||||
int isOnThreadList;
|
||||
int isRunning;
|
||||
int isOkToBlock;
|
||||
unsigned int osiPriority;
|
||||
int joinable;
|
||||
char name[1]; /* actually larger */
|
||||
|
||||
@@ -127,7 +127,8 @@ static void epicsThreadInit(void)
|
||||
taskIdListSize = ID_LIST_CHUNK;
|
||||
atRebootRegister();
|
||||
ALLOT_JOIN(0);
|
||||
done = 1;
|
||||
done = 1; /* avoids recursive call */
|
||||
epicsThreadSetOkToBlock(1);
|
||||
}
|
||||
lock = 0;
|
||||
}
|
||||
@@ -577,3 +578,30 @@ LIBCOM_API int epicsThreadGetCPUs(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static epicsThreadOnceId okToBlockOnce = EPICS_THREAD_ONCE_INIT;
|
||||
static epicsThreadPrivateId okToBlockPrivate;
|
||||
static const int okToBlockNo = 0;
|
||||
static const int okToBlockYes = 1;
|
||||
|
||||
static void epicsThreadOnceIdInit(void *not_used)
|
||||
{
|
||||
okToBlockPrivate = epicsThreadPrivateCreate();
|
||||
}
|
||||
|
||||
int epicsStdCall epicsThreadIsOkToBlock(void)
|
||||
{
|
||||
const int *pokToBlock;
|
||||
epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL);
|
||||
pokToBlock = (int *) epicsThreadPrivateGet(okToBlockPrivate);
|
||||
return (pokToBlock ? *pokToBlock : 0);
|
||||
}
|
||||
|
||||
void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock)
|
||||
{
|
||||
const int *pokToBlock;
|
||||
epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL);
|
||||
pokToBlock = (isOkToBlock) ? &okToBlockYes : &okToBlockNo;
|
||||
epicsThreadPrivateSet(okToBlockPrivate, (void *)pokToBlock);
|
||||
}
|
||||
|
||||
@@ -297,7 +297,7 @@ extern void tokenized_start(char *s) NORETURN;
|
||||
extern void retyped_warning(char *s);
|
||||
extern void reprec_warning(char *s);
|
||||
extern void revalued_warning(char *s);
|
||||
extern void terminal_start(char *s);
|
||||
extern void terminal_start(char *s) NORETURN;
|
||||
extern void restarted_warning(void);
|
||||
extern void no_grammar(void) NORETURN;
|
||||
extern void terminal_lhs(int s_lineno) NORETURN;
|
||||
|
||||
@@ -295,7 +295,7 @@ TESTSPEC_RTEMS = libComTestHarness.boot; epicsRunLibComTests
|
||||
|
||||
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
|
||||
TESTPROD = $(TESTPROD_HOST)
|
||||
TESTPROD += $(TESTPROD_HOST)
|
||||
TESTSCRIPTS += $(filter-out epicsUnitTestTest.t, $(TESTS:%=%.t))
|
||||
endif
|
||||
|
||||
|
||||
Submodule modules/normativeTypes updated: 1250a3c236...7a2d264f2c
Submodule modules/pvAccess updated: 96061ca1cc...dafb6aad31
Submodule modules/pvData updated: 0ef8a36172...1c5f75bcd6
Submodule modules/pvDatabase updated: 8cf550ff57...073d2acafc
Submodule modules/pva2pva updated: 3a08da445b...8fa231352d
Submodule modules/pvaClient updated: a34876e36a...09cf317521
@@ -7,8 +7,8 @@ use base 'Pod::Simple::XHTML';
|
||||
|
||||
BEGIN {
|
||||
if ($Pod::Simple::XHTML::VERSION < '3.16') {
|
||||
# encode_entities() wasn't a method, add it
|
||||
our *encode_entities = sub {
|
||||
# Add encode_entities() as a method
|
||||
sub encode_entities {
|
||||
my ($self, $str) = @_;
|
||||
my %entities = (
|
||||
q{>} => 'gt',
|
||||
@@ -37,4 +37,23 @@ sub resolve_pod_page_link {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub _end_head {
|
||||
my $h = delete $_[0]{in_head};
|
||||
|
||||
my $add = $_[0]->html_h_level;
|
||||
$add = 1 unless defined $add;
|
||||
$h += $add - 1;
|
||||
|
||||
my $id = $_[0]->idify($_[0]{htext});
|
||||
my $text = $_[0]{scratch};
|
||||
my $hid = qq{<h$h id="$id">};
|
||||
my $link = qq{ <a class='sect' href="#$id">§</a>};
|
||||
$_[0]{'scratch'} = $_[0]->backlink && ($h - $add == 0)
|
||||
# backlinks enabled && =head1
|
||||
? qq{$hid<a href="#_podtop_">$text</a> $link</h$h>}
|
||||
: qq{$hid$text $link</h$h>};
|
||||
$_[0]->emit;
|
||||
push @{ $_[0]{'to_index'} }, [$h, $id, delete $_[0]{'htext'}];
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
@@ -69,6 +69,11 @@ A[href="#POD_ERRORS"] {
|
||||
color: #FF0000;
|
||||
}
|
||||
|
||||
A.sect {
|
||||
color: #99ccff;
|
||||
vertical-align: super;
|
||||
}
|
||||
|
||||
TD {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
Reference in New Issue
Block a user