Merge branch '7.0' into PSI-7.0
This commit is contained in:
@ -52,8 +52,6 @@ environment:
|
|||||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||||
- CMP: vs2015
|
- CMP: vs2015
|
||||||
- CMP: vs2013
|
- CMP: vs2013
|
||||||
- CMP: vs2012
|
|
||||||
- CMP: vs2010
|
|
||||||
- CMP: gcc
|
- CMP: gcc
|
||||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||||
# TODO: static linking w/ readline isn't working. Bypass auto-detect
|
# TODO: static linking w/ readline isn't working. Bypass auto-detect
|
||||||
@ -67,11 +65,6 @@ platform:
|
|||||||
# Matrix configuration: exclude sets of jobs
|
# Matrix configuration: exclude sets of jobs
|
||||||
matrix:
|
matrix:
|
||||||
exclude:
|
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
|
# Exclude more jobs to reduce build time
|
||||||
# Skip 32-bit for "middle-aged" compilers
|
# Skip 32-bit for "middle-aged" compilers
|
||||||
- platform: x86
|
- platform: x86
|
||||||
|
@ -59,8 +59,6 @@ environment:
|
|||||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||||
- CMP: vs2015
|
- CMP: vs2015
|
||||||
- CMP: vs2013
|
- CMP: vs2013
|
||||||
- CMP: vs2012
|
|
||||||
- CMP: vs2010
|
|
||||||
- CMP: gcc
|
- CMP: gcc
|
||||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||||
# TODO: static linking w/ readline isn't working. Bypass auto-detect
|
# TODO: static linking w/ readline isn't working. Bypass auto-detect
|
||||||
@ -74,11 +72,6 @@ platform:
|
|||||||
# Matrix configuration: exclude sets of jobs
|
# Matrix configuration: exclude sets of jobs
|
||||||
matrix:
|
matrix:
|
||||||
exclude:
|
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
|
# Exclude more jobs to reduce build time
|
||||||
# Skip 32-bit for "middle-aged" compilers
|
# Skip 32-bit for "middle-aged" compilers
|
||||||
- platform: x86
|
- platform: x86
|
||||||
|
162
.github/workflows/ci-scripts-build.yml
vendored
162
.github/workflows/ci-scripts-build.yml
vendored
@ -51,9 +51,9 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
# Job names also name artifacts, character limitations apply
|
# Job names also name artifacts, character limitations apply
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-22.04
|
- os: ubuntu-24.04
|
||||||
cmp: gcc-12
|
cmp: gcc
|
||||||
name: "Ub-22 gcc-12 c++20 Werror"
|
name: "Ub-24 gcc-13 c++20 Werror"
|
||||||
# Turn all warnings into errors,
|
# Turn all warnings into errors,
|
||||||
# except for those we could not fix (yet).
|
# except for those we could not fix (yet).
|
||||||
# Remove respective -Wno-error=... flag once it is fixed.
|
# Remove respective -Wno-error=... flag once it is fixed.
|
||||||
@ -74,79 +74,79 @@ jobs:
|
|||||||
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3'
|
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3'
|
||||||
CMD_LDFLAGS=-Wl,-z,relro"
|
CMD_LDFLAGS=-Wl,-z,relro"
|
||||||
|
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-22.04
|
||||||
cmp: gcc
|
cmp: gcc
|
||||||
configuration: default
|
configuration: default
|
||||||
cross: "windows-x64-mingw"
|
cross: "windows-x64-mingw"
|
||||||
name: "Ub-20 gcc + MinGW"
|
name: "Ub-22 gcc + MinGW"
|
||||||
|
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-22.04
|
||||||
cmp: gcc
|
cmp: gcc
|
||||||
configuration: static
|
configuration: static
|
||||||
cross: "windows-x64-mingw"
|
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
|
cmp: gcc
|
||||||
configuration: static
|
configuration: static
|
||||||
extra: "CMD_CXXFLAGS=-std=c++11"
|
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
|
cmp: gcc
|
||||||
configuration: static
|
configuration: static
|
||||||
extra: "CMD_CFLAGS=-funsigned-char CMD_CXXFLAGS=-funsigned-char"
|
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
|
cmp: clang
|
||||||
configuration: default
|
configuration: default
|
||||||
name: "Ub-20 clang"
|
name: "Ub-22 clang"
|
||||||
|
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-22.04
|
||||||
cmp: clang
|
cmp: clang
|
||||||
configuration: default
|
configuration: default
|
||||||
extra: "CMD_CXXFLAGS=-std=c++11"
|
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
|
cmp: gcc
|
||||||
configuration: default
|
configuration: default
|
||||||
cross: "RTEMS-pc686-qemu@5"
|
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
|
cmp: gcc
|
||||||
configuration: default
|
configuration: default
|
||||||
cross: "RTEMS-beatnik@5"
|
cross: "RTEMS-beatnik@5"
|
||||||
test: NO
|
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
|
cmp: gcc
|
||||||
configuration: default
|
configuration: default
|
||||||
cross: "RTEMS-xilinx_zynq_a9_qemu@5"
|
cross: "RTEMS-xilinx_zynq_a9_qemu@5"
|
||||||
test: NO
|
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
|
cmp: gcc
|
||||||
configuration: default
|
configuration: default
|
||||||
cross: "RTEMS-uC5282@5"
|
cross: "RTEMS-uC5282@5"
|
||||||
test: NO
|
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
|
cmp: gcc
|
||||||
configuration: default
|
configuration: default
|
||||||
name: "Ub-20 gcc + RT-4.10"
|
name: "Ub-22 gcc + RT-4.10"
|
||||||
cross: "RTEMS-pc386-qemu@4.10"
|
cross: "RTEMS-pc386-qemu@4.10"
|
||||||
test: NO
|
test: NO
|
||||||
|
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-22.04
|
||||||
cmp: gcc
|
cmp: gcc
|
||||||
configuration: default
|
configuration: default
|
||||||
name: "Ub-20 gcc + RT-4.9"
|
name: "Ub-22 gcc + RT-4.9"
|
||||||
cross: "RTEMS-pc386-qemu@4.9"
|
cross: "RTEMS-pc386-qemu@4.9"
|
||||||
|
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
@ -246,10 +246,15 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
# Job names also name artifacts, character limitations apply
|
# Job names also name artifacts, character limitations apply
|
||||||
include:
|
include:
|
||||||
#- name: "CentOS-7"
|
- name: "CentOS-8"
|
||||||
# image: centos:7
|
image: centos:8
|
||||||
# cmp: gcc
|
cmp: gcc
|
||||||
# configuration: default
|
configuration: default
|
||||||
|
|
||||||
|
- name: "Rocky-9"
|
||||||
|
image: rockylinux:9
|
||||||
|
cmp: gcc
|
||||||
|
configuration: default
|
||||||
|
|
||||||
- name: "Fedora-33"
|
- name: "Fedora-33"
|
||||||
image: fedora:33
|
image: fedora:33
|
||||||
@ -262,47 +267,29 @@ jobs:
|
|||||||
configuration: default
|
configuration: default
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Build newer Git"
|
- name: "Fix repo URLs on CentOS-8"
|
||||||
# actions/checkout@v2 wants git >=2.18
|
# centos:8 is frozen, repos are in the vault
|
||||||
# centos:7 has 1.8
|
if: matrix.image=='centos:8'
|
||||||
if: matrix.image=='centos:7'
|
|
||||||
run: |
|
run: |
|
||||||
yum -y install curl make gcc curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker
|
sed -i -e "s|mirrorlist=|#mirrorlist=|" \
|
||||||
curl https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.29.0.tar.gz | tar -xz
|
-e "s|#baseurl=http://mirror|baseurl=http://vault|" \
|
||||||
cd git-*
|
/etc/yum.repos.d/CentOS-Linux-{BaseOS,AppStream,Extras,Plus}.repo
|
||||||
make -j2 prefix=/usr/local all
|
|
||||||
make prefix=/usr/local install
|
|
||||||
cd ..
|
|
||||||
rm -rf git-*
|
|
||||||
type -a git
|
|
||||||
git --version
|
|
||||||
- name: "Redhat setup"
|
- name: "Redhat setup"
|
||||||
run: |
|
run: |
|
||||||
dnfyum() {
|
dnf -y install python3 gdb make perl gcc-c++ glibc-devel readline-devel ncurses-devel perl-devel perl-Test-Simple
|
||||||
dnf -y "$@" || yum -y "$@"
|
git --version || dnf -y install git
|
||||||
return $?
|
python3 --version
|
||||||
}
|
|
||||||
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
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Automatic core dumper analysis
|
- name: Automatic core dumper analysis
|
||||||
uses: mdavidsaver/ci-core-dumper@master
|
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
|
- name: Prepare and compile dependencies
|
||||||
run: python .ci/cue.py prepare
|
run: python3 .ci/cue.py prepare
|
||||||
- name: Build main module
|
- name: Build main module
|
||||||
run: python .ci/cue.py build
|
run: python3 .ci/cue.py build
|
||||||
- name: Run main module tests
|
- 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
|
- name: Upload tapfiles Artifact
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@ -312,4 +299,55 @@ jobs:
|
|||||||
if-no-files-found: ignore
|
if-no-files-found: ignore
|
||||||
- name: Collect and show test results
|
- name: Collect and show test results
|
||||||
if: ${{ always() }}
|
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
|
||||||
|
@ -18,13 +18,28 @@ __This version of EPICS has not been released yet.__
|
|||||||
|
|
||||||
__Add new items below here__
|
__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
|
### epicsExport simplifications
|
||||||
|
|
||||||
`epicsExportAddress()`, `epicsExportRegistrar()` and `epicsRegisterFunction()`
|
`epicsExportAddress()`, `epicsExportRegistrar()` and `epicsRegisterFunction()`
|
||||||
no longer require to be wrapped in `extern "C" { }` in C++ code.
|
no longer require to be wrapped in `extern "C" { }` in C++ code.
|
||||||
|
|
||||||
### Build system `$(PYTHON)` default changed
|
### 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
|
## EPICS Release 7.0.9
|
||||||
|
|
||||||
@ -44,12 +59,6 @@ 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
|
and loading the HTML versions or finding and opening the files from the EPICS
|
||||||
Documentation site.
|
Documentation site.
|
||||||
|
|
||||||
### fdManager file descriptor limit removed
|
|
||||||
|
|
||||||
In order to support file descriptors above 1023, fdManager now uses
|
|
||||||
poll() instead of select() on all architectures that support it
|
|
||||||
(Linux, MacOS, Windows, newer RTEMS).
|
|
||||||
|
|
||||||
### Post monitors from compress record when it's reset
|
### Post monitors from compress record when it's reset
|
||||||
|
|
||||||
Writing into a compress record's `RES` field now posts a monitor event instead
|
Writing into a compress record's `RES` field now posts a monitor event instead
|
||||||
|
@ -115,10 +115,9 @@ static int dbca_chan_count;
|
|||||||
* During link modification or IOC shutdown the pca->plink pointer (guarded by caLink.lock)
|
* 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.
|
* 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()
|
* References to the struct caLink are owned by the dbCaTask.
|
||||||
* which is in progress.
|
|
||||||
*
|
*
|
||||||
* 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
|
* dbCaPutLinkCallback causes an additional complication because
|
||||||
* when dbCaRemoveLink is called the callback may not have occured.
|
* 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;
|
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 = {
|
static lset dbCa_lset = {
|
||||||
0, 1, /* not Constant, Volatile */
|
0, 1, /* not Constant, Volatile */
|
||||||
NULL, dbCaRemoveLink,
|
NULL, dbCaRemoveLink,
|
||||||
@ -856,7 +823,9 @@ static void connectionCallback(struct connection_handler_args arg)
|
|||||||
if (precord &&
|
if (precord &&
|
||||||
((ppv_link->pvlMask & pvlOptCP) ||
|
((ppv_link->pvlMask & pvlOptCP) ||
|
||||||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)))
|
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)))
|
||||||
scanLinkOnce(precord, pca);
|
{
|
||||||
|
link_action |= CA_DBPROCESS;
|
||||||
|
}
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
pca->hasReadAccess = ca_read_access(arg.chid);
|
pca->hasReadAccess = ca_read_access(arg.chid);
|
||||||
@ -988,7 +957,9 @@ static void eventCallback(struct event_handler_args arg)
|
|||||||
|
|
||||||
if ((ppv_link->pvlMask & pvlOptCP) ||
|
if ((ppv_link->pvlMask & pvlOptCP) ||
|
||||||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))
|
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))
|
||||||
scanLinkOnce(precord, pca);
|
{
|
||||||
|
addAction(pca, CA_DBPROCESS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
epicsMutexUnlock(pca->lock);
|
epicsMutexUnlock(pca->lock);
|
||||||
@ -1061,7 +1032,9 @@ static void accessRightsCallback(struct access_rights_handler_args arg)
|
|||||||
if (precord &&
|
if (precord &&
|
||||||
((ppv_link->pvlMask & pvlOptCP) ||
|
((ppv_link->pvlMask & pvlOptCP) ||
|
||||||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)))
|
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)))
|
||||||
scanLinkOnce(precord, pca);
|
{
|
||||||
|
addAction(pca, CA_DBPROCESS);
|
||||||
|
}
|
||||||
done:
|
done:
|
||||||
epicsMutexUnlock(pca->lock);
|
epicsMutexUnlock(pca->lock);
|
||||||
}
|
}
|
||||||
@ -1273,6 +1246,13 @@ static void dbCaTask(void *arg)
|
|||||||
printLinks(pca);
|
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");
|
SEVCHK(ca_flush_io(), "dbCaTask");
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#define CA_MONITOR_STRING 0x20
|
#define CA_MONITOR_STRING 0x20
|
||||||
#define CA_GET_ATTRIBUTES 0x40
|
#define CA_GET_ATTRIBUTES 0x40
|
||||||
#define CA_SYNC 0x1000
|
#define CA_SYNC 0x1000
|
||||||
|
#define CA_DBPROCESS 0x2000
|
||||||
/* write type */
|
/* write type */
|
||||||
#define CA_PUT 0x1
|
#define CA_PUT 0x1
|
||||||
#define CA_PUT_CALLBACK 0x2
|
#define CA_PUT_CALLBACK 0x2
|
||||||
|
@ -635,9 +635,21 @@ long dbChannelGetField(dbChannel *chan, short dbrType, void *pbuffer,
|
|||||||
{
|
{
|
||||||
dbCommon *precord = chan->addr.precord;
|
dbCommon *precord = chan->addr.precord;
|
||||||
long status = 0;
|
long status = 0;
|
||||||
|
unsigned char local_fl = 0;
|
||||||
|
|
||||||
dbScanLock(precord);
|
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);
|
status = dbChannelGet(chan, dbrType, pbuffer, options, nRequest, pfl);
|
||||||
|
if (local_fl) {
|
||||||
|
db_delete_field_log(pfl);
|
||||||
|
}
|
||||||
dbScanUnlock(precord);
|
dbScanUnlock(precord);
|
||||||
return status;
|
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,out] nRequest Pointer to the element count.
|
||||||
* \param[in] pfl Pointer to a db_field_log or NULL.
|
* \param[in] pfl Pointer to a db_field_log or NULL.
|
||||||
* \returns 0, or an error status value.
|
* \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,
|
DBCORE_API long dbChannelGetField(dbChannel *chan, short type,
|
||||||
void *pbuffer, long *options, long *nRequest, void *pfl);
|
void *pbuffer, long *options, long *nRequest, void *pfl);
|
||||||
|
@ -127,6 +127,31 @@ int dbServerClient(char *pBuf, size_t bufSize)
|
|||||||
return -1;
|
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) \
|
#define STARTSTOP(routine, method, newState) \
|
||||||
void routine(void) \
|
void routine(void) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -17,9 +17,6 @@
|
|||||||
* the dbServer interface provides allow the IOC to start, pause and stop
|
* the dbServer interface provides allow the IOC to start, pause and stop
|
||||||
* the servers together, and to provide status and debugging information
|
* the servers together, and to provide status and debugging information
|
||||||
* to the IOC user/developer through a common set of commands.
|
* 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
|
#ifndef INC_dbServer_H
|
||||||
@ -59,8 +56,8 @@ typedef struct dbServer {
|
|||||||
|
|
||||||
/** @brief Get number of channels and clients currently connected.
|
/** @brief Get number of channels and clients currently connected.
|
||||||
*
|
*
|
||||||
* @param channels NULL or pointer for returning channel count.
|
* @param channels @c NULL or pointer for returning channel count.
|
||||||
* @param clients NULL or pointer for returning client count.
|
* @param clients @c NULL or pointer for returning client count.
|
||||||
*/
|
*/
|
||||||
void (* stats) (unsigned *channels, unsigned *clients);
|
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);
|
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.
|
/** @brief Initialize all registered servers.
|
||||||
*
|
*
|
||||||
* Calls all dbServer::init() methods.
|
* 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)
|
void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
|
||||||
{
|
{
|
||||||
dbChannel *chan = dbChannelCreate(pv);
|
dbChannel *chan = dbChannelCreate(pv);
|
||||||
db_field_log *pfl = NULL;
|
|
||||||
long nReq = 1;
|
long nReq = 1;
|
||||||
union anybuf pod;
|
union anybuf pod;
|
||||||
long status = S_dbLib_recNotFound;
|
long status = S_dbLib_recNotFound;
|
||||||
@ -212,18 +211,7 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ellCount(&chan->filters)) {
|
status = dbChannelGetField(chan, dbrType, pod.bytes, NULL, &nReq, NULL);
|
||||||
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);
|
|
||||||
if (status) {
|
if (status) {
|
||||||
testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status));
|
testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status));
|
||||||
goto done;
|
goto done;
|
||||||
@ -261,7 +249,6 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
|
|||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
db_delete_field_log(pfl);
|
|
||||||
if(chan)
|
if(chan)
|
||||||
dbChannelDelete(chan);
|
dbChannelDelete(chan);
|
||||||
}
|
}
|
||||||
@ -288,7 +275,6 @@ done:
|
|||||||
void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsigned long cnt, const void *pbufraw)
|
void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsigned long cnt, const void *pbufraw)
|
||||||
{
|
{
|
||||||
dbChannel *chan = dbChannelCreate(pv);
|
dbChannel *chan = dbChannelCreate(pv);
|
||||||
db_field_log *pfl = NULL;
|
|
||||||
const long vSize = dbValueSize(dbfType);
|
const long vSize = dbValueSize(dbfType);
|
||||||
const long nStore = vSize * nRequest;
|
const long nStore = vSize * nRequest;
|
||||||
long status = S_dbLib_recNotFound;
|
long status = S_dbLib_recNotFound;
|
||||||
@ -300,24 +286,13 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign
|
|||||||
goto done;
|
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);
|
gbuf = gstore = malloc(nStore);
|
||||||
if(!gbuf && nStore!=0) { /* note that malloc(0) is allowed to return NULL on success */
|
if(!gbuf && nStore!=0) { /* note that malloc(0) is allowed to return NULL on success */
|
||||||
testFail("Allocation failed esize=%ld total=%ld", vSize, nStore);
|
testFail("Allocation failed esize=%ld total=%ld", vSize, nStore);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = dbChannelGetField(chan, dbfType, gbuf, NULL, &nRequest, pfl);
|
status = dbChannelGetField(chan, dbfType, gbuf, NULL, &nRequest, NULL);
|
||||||
if (status) {
|
if (status) {
|
||||||
testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status);
|
testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status);
|
||||||
|
|
||||||
|
@ -148,6 +148,7 @@ int dbChannel_get_count(
|
|||||||
long options;
|
long options;
|
||||||
long i;
|
long i;
|
||||||
long zero = 0;
|
long zero = 0;
|
||||||
|
unsigned char local_fl = 0;
|
||||||
|
|
||||||
/* The order of the DBR* elements in the "newSt" structures below is
|
/* The order of the DBR* elements in the "newSt" structures below is
|
||||||
* very important and must correspond to the order of processing
|
* very important and must correspond to the order of processing
|
||||||
@ -156,6 +157,16 @@ int dbChannel_get_count(
|
|||||||
|
|
||||||
dbScanLock(dbChannelRecord(chan));
|
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) {
|
switch(buffer_type) {
|
||||||
case(oldDBR_STRING):
|
case(oldDBR_STRING):
|
||||||
status = dbChannelGet(chan, DBR_STRING, pbuffer, &zero, nRequest, pfl);
|
status = dbChannelGet(chan, DBR_STRING, pbuffer, &zero, nRequest, pfl);
|
||||||
@ -800,6 +811,8 @@ int dbChannel_get_count(
|
|||||||
|
|
||||||
dbScanUnlock(dbChannelRecord(chan));
|
dbScanUnlock(dbChannelRecord(chan));
|
||||||
|
|
||||||
|
if (local_fl) db_delete_field_log(pfl);
|
||||||
|
|
||||||
if (status) return -1;
|
if (status) return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1029,3 +1042,17 @@ int db_put_process(processNotify *ppn, notifyPutType type,
|
|||||||
ppn->status = notifyError;
|
ppn->status = notifyError;
|
||||||
return 1;
|
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"
|
#include "dbCoreAPI.h"
|
||||||
|
|
||||||
|
struct dbCommon;
|
||||||
|
|
||||||
DBCORE_API extern struct dbBase *pdbbase;
|
DBCORE_API extern struct dbBase *pdbbase;
|
||||||
DBCORE_API extern volatile int interruptAccept;
|
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);
|
const void *psrc, long no_elements);
|
||||||
DBCORE_API int dbChannel_get_count(struct dbChannel *chan,
|
DBCORE_API int dbChannel_get_count(struct dbChannel *chan,
|
||||||
int buffer_type, void *pbuffer, long *nRequest, void *pfl);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -540,7 +540,6 @@ static void read_reply ( void *pArg, struct dbChannel *dbch,
|
|||||||
const int readAccess = asCheckGet ( pciu->asClientPVT );
|
const int readAccess = asCheckGet ( pciu->asClientPVT );
|
||||||
int status;
|
int status;
|
||||||
int autosize;
|
int autosize;
|
||||||
int local_fl = 0;
|
|
||||||
long item_count;
|
long item_count;
|
||||||
ca_uint32_t payload_size;
|
ca_uint32_t payload_size;
|
||||||
dbAddr *paddr=&dbch->addr;
|
dbAddr *paddr=&dbch->addr;
|
||||||
@ -582,21 +581,9 @@ static void read_reply ( void *pArg, struct dbChannel *dbch,
|
|||||||
return;
|
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,
|
status = dbChannel_get_count ( dbch, pevext->msg.m_dataType,
|
||||||
pPayload, &item_count, pfl);
|
pPayload, &item_count, pfl);
|
||||||
|
|
||||||
if (local_fl) db_delete_field_log(pfl);
|
|
||||||
|
|
||||||
if ( status < 0 ) {
|
if ( status < 0 ) {
|
||||||
/* Clients recv the status of the operation directly to the
|
/* Clients recv the status of the operation directly to the
|
||||||
* event/put/get callback. (from CA_V41())
|
* event/put/get callback. (from CA_V41())
|
||||||
@ -663,7 +650,6 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p
|
|||||||
ca_uint32_t payloadSize;
|
ca_uint32_t payloadSize;
|
||||||
void *pPayload;
|
void *pPayload;
|
||||||
int status;
|
int status;
|
||||||
int local_fl = 0;
|
|
||||||
db_field_log *pfl = NULL;
|
db_field_log *pfl = NULL;
|
||||||
|
|
||||||
if ( ! pciu ) {
|
if ( ! pciu ) {
|
||||||
@ -702,21 +688,9 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p
|
|||||||
return RSRV_OK;
|
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,
|
status = dbChannel_get ( pciu->dbch, mp->m_dataType,
|
||||||
pPayload, mp->m_count, pfl );
|
pPayload, mp->m_count, pfl );
|
||||||
|
|
||||||
if (local_fl) db_delete_field_log(pfl);
|
|
||||||
|
|
||||||
if ( status < 0 ) {
|
if ( status < 0 ) {
|
||||||
send_err ( mp, ECA_GETFAIL, pClient, RECORD_NAME ( pciu->dbch ) );
|
send_err ( mp, ECA_GETFAIL, pClient, RECORD_NAME ( pciu->dbch ) );
|
||||||
SEND_UNLOCK ( pClient );
|
SEND_UNLOCK ( pClient );
|
||||||
|
@ -130,9 +130,11 @@ static void check(short dbr_type) {
|
|||||||
off = Offset; req = 10; \
|
off = Offset; req = 10; \
|
||||||
memset(buf, 0, sizeof(buf)); \
|
memset(buf, 0, sizeof(buf)); \
|
||||||
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
|
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
|
||||||
|
dbScanLock(dbChannelRecord(pch)); \
|
||||||
pfl = db_create_read_log(pch); \
|
pfl = db_create_read_log(pch); \
|
||||||
testOk(pfl && pfl->type == dbfl_type_ref, "Valid pfl, type = ref"); \
|
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); \
|
testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
|
||||||
if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
|
if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
|
||||||
for (i=0; i<Size; i++) \
|
for (i=0; i<Size; i++) \
|
||||||
@ -174,6 +176,7 @@ static void check(short dbr_type) {
|
|||||||
off = Offset; req = 15; \
|
off = Offset; req = 15; \
|
||||||
memset(buf, 0, sizeof(buf)); \
|
memset(buf, 0, sizeof(buf)); \
|
||||||
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
|
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
|
||||||
|
dbScanLock(dbChannelRecord(pch)); \
|
||||||
pfl = db_create_read_log(pch); \
|
pfl = db_create_read_log(pch); \
|
||||||
pfl->type = dbfl_type_ref; \
|
pfl->type = dbfl_type_ref; \
|
||||||
pfl->field_type = DBF_CHAR; \
|
pfl->field_type = DBF_CHAR; \
|
||||||
@ -181,7 +184,8 @@ static void check(short dbr_type) {
|
|||||||
pfl->no_elements = 26; \
|
pfl->no_elements = 26; \
|
||||||
pfl->dtor = freeArray; \
|
pfl->dtor = freeArray; \
|
||||||
pfl->u.r.field = epicsStrDup("abcdefghijklmnopqrsstuvwxyz"); \
|
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); \
|
testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
|
||||||
if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
|
if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
|
||||||
for (i=0; i<Size; i++) \
|
for (i=0; i<Size; i++) \
|
||||||
|
@ -40,6 +40,8 @@ void oneReport(unsigned level)
|
|||||||
void oneStats(unsigned *channels, unsigned *clients)
|
void oneStats(unsigned *channels, unsigned *clients)
|
||||||
{
|
{
|
||||||
oneState = STATS_CALLED;
|
oneState = STATS_CALLED;
|
||||||
|
if (channels) *channels = 2;
|
||||||
|
if (clients) *clients = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int oneClient(char *pbuf, size_t len)
|
int oneClient(char *pbuf, size_t len)
|
||||||
@ -128,8 +130,9 @@ MAIN(dbServerTest)
|
|||||||
char name[16];
|
char name[16];
|
||||||
char *theName = "The One";
|
char *theName = "The One";
|
||||||
int status;
|
int status;
|
||||||
|
unsigned ch=0, cl=0;
|
||||||
|
|
||||||
testPlan(25);
|
testPlan(35);
|
||||||
|
|
||||||
/* Prove that we handle substring names properly */
|
/* Prove that we handle substring names properly */
|
||||||
epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones");
|
epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones");
|
||||||
@ -151,6 +154,9 @@ MAIN(dbServerTest)
|
|||||||
testDiag("Registering dbServer 'disabled'");
|
testDiag("Registering dbServer 'disabled'");
|
||||||
testOk(dbRegisterServer(&disabled) == 0, "Registration accepted");
|
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");
|
testDiag("Changing server state");
|
||||||
dbInitServers();
|
dbInitServers();
|
||||||
testOk(oneState == INIT_CALLED, "dbInitServers");
|
testOk(oneState == INIT_CALLED, "dbInitServers");
|
||||||
@ -163,8 +169,26 @@ MAIN(dbServerTest)
|
|||||||
|
|
||||||
testDiag("Checking server methods called");
|
testDiag("Checking server methods called");
|
||||||
dbsr(0);
|
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;
|
oneSim = NULL;
|
||||||
name[0] = 0;
|
name[0] = 0;
|
||||||
status = dbServerClient(name, sizeof(name));
|
status = dbServerClient(name, sizeof(name));
|
||||||
@ -188,6 +212,9 @@ MAIN(dbServerTest)
|
|||||||
status = dbServerClient(name, sizeof(name));
|
status = dbServerClient(name, sizeof(name));
|
||||||
testOk(oneState != CLIENT_CALLED_KNOWN, "No call to client() when paused");
|
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();
|
dbStopServers();
|
||||||
testOk(oneState == STOP_CALLED, "dbStopServers");
|
testOk(oneState == STOP_CALLED, "dbStopServers");
|
||||||
testOk(dbUnregisterServer(&toolate) != 0, "No unreg' if not reg'ed");
|
testOk(dbUnregisterServer(&toolate) != 0, "No unreg' if not reg'ed");
|
||||||
|
@ -221,29 +221,27 @@ testHarness_SRCS += linkFilterTest.c
|
|||||||
TESTFILES += ../linkFilterTest.db
|
TESTFILES += ../linkFilterTest.db
|
||||||
TESTS += linkFilterTest
|
TESTS += linkFilterTest
|
||||||
|
|
||||||
# These are compile-time tests, no need to link or run
|
|
||||||
TARGETS += dbHeaderTest$(OBJ)
|
|
||||||
TARGET_SRCS += dbHeaderTest.cpp
|
|
||||||
TARGETS += dbHeaderTestxx$(OBJ)
|
|
||||||
TARGET_SRCS += dbHeaderTestxx.cpp
|
|
||||||
|
|
||||||
TARGETS += $(COMMON_DIR)/epicsExportTestIoc.dbd
|
TARGETS += $(COMMON_DIR)/epicsExportTestIoc.dbd
|
||||||
DBDDEPENDS_FILES += epicsExportTestIoc.dbd$(DEP)
|
DBDDEPENDS_FILES += epicsExportTestIoc.dbd$(DEP)
|
||||||
epicsExportTestIoc_DBD += base.dbd
|
epicsExportTestIoc_DBD += base.dbd
|
||||||
epicsExportTestIoc_DBD += epicsExportTest.dbd
|
epicsExportTestIoc_DBD += epicsExportTest.dbd
|
||||||
TESTFILES += $(COMMON_DIR)/epicsExportTestIoc.dbd ../epicsExportTest.db
|
TESTFILES += $(COMMON_DIR)/epicsExportTestIoc.dbd ../epicsExportTest.db
|
||||||
|
|
||||||
TESTLIBRARY += epicsExportTestLib
|
TESTLIBRARY += epicsExportTestLib
|
||||||
epicsExportTestLib_SRCS += epicsExportTest.c
|
epicsExportTestLib_SRCS += epicsExportTest.c
|
||||||
epicsExportTestLib_LIBS += dbCore Com
|
epicsExportTestLib_LIBS += dbCore Com
|
||||||
|
|
||||||
TESTPROD_HOST += epicsExportTest
|
TESTPROD_HOST += epicsExportTest
|
||||||
TESTS += epicsExportTest
|
TESTS += epicsExportTest
|
||||||
TARGETS += epicsExportTest$(OBJ)
|
TARGETS += epicsExportTest$(OBJ)
|
||||||
epicsExportTest_SRCS += epicsExportTestMain.c
|
epicsExportTest_SRCS += epicsExportTestMain.c
|
||||||
epicsExportTest_SRCS += epicsExportTestIoc_registerRecordDeviceDriver.cpp
|
epicsExportTest_SRCS += epicsExportTestIoc_registerRecordDeviceDriver.cpp
|
||||||
epicsExportTest_LIBS = epicsExportTestLib
|
epicsExportTest_LIBS = epicsExportTestLib
|
||||||
|
|
||||||
TESTLIBRARY += epicsExportTestxxLib
|
TESTLIBRARY += epicsExportTestxxLib
|
||||||
epicsExportTestxxLib_SRCS += epicsExportTestxx.cpp
|
epicsExportTestxxLib_SRCS += epicsExportTestxx.cpp
|
||||||
epicsExportTestxxLib_LIBS += dbCore Com
|
epicsExportTestxxLib_LIBS += dbCore Com
|
||||||
|
|
||||||
TESTPROD_HOST += epicsExportTestxx
|
TESTPROD_HOST += epicsExportTestxx
|
||||||
TESTS += epicsExportTestxx
|
TESTS += epicsExportTestxx
|
||||||
TARGETS += epicsExportTestxx$(OBJ)
|
TARGETS += epicsExportTestxx$(OBJ)
|
||||||
@ -251,6 +249,12 @@ epicsExportTestxx_SRCS += epicsExportTestMain.c
|
|||||||
epicsExportTestxx_SRCS += epicsExportTestIoc_registerRecordDeviceDriver.cpp
|
epicsExportTestxx_SRCS += epicsExportTestIoc_registerRecordDeviceDriver.cpp
|
||||||
epicsExportTestxx_LIBS = epicsExportTestxxLib
|
epicsExportTestxx_LIBS = epicsExportTestxxLib
|
||||||
|
|
||||||
|
# These are compile-time tests, no need to link or run
|
||||||
|
TARGETS += dbHeaderTest$(OBJ)
|
||||||
|
TARGET_SRCS += dbHeaderTest.cpp
|
||||||
|
TARGETS += dbHeaderTestxx$(OBJ)
|
||||||
|
TARGET_SRCS += dbHeaderTestxx.cpp
|
||||||
|
|
||||||
ifeq ($(T_A),$(EPICS_HOST_ARCH))
|
ifeq ($(T_A),$(EPICS_HOST_ARCH))
|
||||||
# Host-only tests of softIoc/softIocPVA, caget and pvget (if present)
|
# Host-only tests of softIoc/softIocPVA, caget and pvget (if present)
|
||||||
# Unfortunately hangs too often on CI systems:
|
# Unfortunately hangs too often on CI systems:
|
||||||
|
@ -16,7 +16,10 @@
|
|||||||
#include <iocsh.h>
|
#include <iocsh.h>
|
||||||
#include <epicsExport.h>
|
#include <epicsExport.h>
|
||||||
|
|
||||||
void epicsExportTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
#endif
|
||||||
|
int epicsExportTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||||
|
|
||||||
static int* testVarEquals(const char* name, int expected)
|
static int* testVarEquals(const char* name, int expected)
|
||||||
{
|
{
|
||||||
@ -41,10 +44,10 @@ MAIN(epicsExportTest)
|
|||||||
{
|
{
|
||||||
int *p1, *p2;
|
int *p1, *p2;
|
||||||
|
|
||||||
testPlan(30);
|
testPlan(31);
|
||||||
testdbPrepare();
|
testdbPrepare();
|
||||||
testdbReadDatabase("epicsExportTestIoc.dbd", 0, 0);
|
testdbReadDatabase("epicsExportTestIoc.dbd", 0, 0);
|
||||||
epicsExportTestIoc_registerRecordDeviceDriver(pdbbase);
|
testOk(epicsExportTestIoc_registerRecordDeviceDriver(pdbbase)==0, "registerRecordDeviceDriver");
|
||||||
|
|
||||||
testDiag("Testing if dsets and functions are found");
|
testDiag("Testing if dsets and functions are found");
|
||||||
testdbReadDatabase("epicsExportTest.db", 0, 0);
|
testdbReadDatabase("epicsExportTest.db", 0, 0);
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "ellLib.h"
|
#include "ellLib.h"
|
||||||
#include "epicsMutex.h"
|
#include "epicsMutex.h"
|
||||||
#include "epicsThread.h"
|
#include "epicsThread.h"
|
||||||
|
#include "cantProceed.h"
|
||||||
|
|
||||||
#include "initHooks.h"
|
#include "initHooks.h"
|
||||||
|
|
||||||
@ -52,19 +53,26 @@ static void initHookInit(void)
|
|||||||
int initHookRegister(initHookFunction func)
|
int initHookRegister(initHookFunction func)
|
||||||
{
|
{
|
||||||
initHookLink *newHook;
|
initHookLink *newHook;
|
||||||
|
ELLNODE *cur;
|
||||||
|
|
||||||
if (!func) return 0;
|
if (!func) return 0;
|
||||||
|
|
||||||
initHookInit();
|
initHookInit();
|
||||||
|
|
||||||
newHook = (initHookLink *)malloc(sizeof(initHookLink));
|
epicsMutexMustLock(listLock);
|
||||||
if (!newHook) {
|
|
||||||
printf("Cannot malloc a new initHookLink\n");
|
for(cur = ellFirst(&functionList); cur; cur = ellNext(cur)) {
|
||||||
return -1;
|
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;
|
newHook->func = func;
|
||||||
|
|
||||||
epicsMutexMustLock(listLock);
|
|
||||||
ellAdd(&functionList, &newHook->node);
|
ellAdd(&functionList, &newHook->node);
|
||||||
epicsMutexUnlock(listLock);
|
epicsMutexUnlock(listLock);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -163,7 +163,11 @@ typedef void (*initHookFunction)(initHookState state);
|
|||||||
*
|
*
|
||||||
* Registers \p func for initHook notifications
|
* Registers \p func for initHook notifications
|
||||||
* \param func Pointer to application's notification function.
|
* \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);
|
LIBCOM_API int initHookRegister(initHookFunction func);
|
||||||
|
|
||||||
|
@ -21,11 +21,7 @@
|
|||||||
# error compiler/gcc/compilerSpecific.h is only for use with the gnu compiler
|
# error compiler/gcc/compilerSpecific.h is only for use with the gnu compiler
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if __GNUC__ > 2
|
#define EPICS_ALWAYS_INLINE __inline__ __attribute__((always_inline))
|
||||||
# define EPICS_ALWAYS_INLINE __inline__ __attribute__((always_inline))
|
|
||||||
#else
|
|
||||||
# define EPICS_ALWAYS_INLINE __inline__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Expands to a 'const char*' which describes the name of the current function scope */
|
/* Expands to a 'const char*' which describes the name of the current function scope */
|
||||||
#define EPICS_FUNCTION __PRETTY_FUNCTION__
|
#define EPICS_FUNCTION __PRETTY_FUNCTION__
|
||||||
@ -42,9 +38,7 @@
|
|||||||
/*
|
/*
|
||||||
* CXX_PLACEMENT_DELETE - defined if compiler supports placement delete
|
* CXX_PLACEMENT_DELETE - defined if compiler supports placement delete
|
||||||
*/
|
*/
|
||||||
#if __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 96 )
|
#define CXX_PLACEMENT_DELETE
|
||||||
# define CXX_PLACEMENT_DELETE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
@ -56,21 +50,19 @@
|
|||||||
/*
|
/*
|
||||||
* Deprecation marker
|
* Deprecation marker
|
||||||
*/
|
*/
|
||||||
#if (__GNUC__ > 2)
|
#define EPICS_DEPRECATED __attribute__((deprecated))
|
||||||
# define EPICS_DEPRECATED __attribute__((deprecated))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unused marker
|
* Unused marker
|
||||||
*/
|
*/
|
||||||
#ifndef vxWorks
|
|
||||||
// VxWorks does not mark abort() or exit() noreturn!
|
|
||||||
#define EPICS_UNUSED __attribute__((unused))
|
#define EPICS_UNUSED __attribute__((unused))
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* No return marker
|
* No return marker
|
||||||
*/
|
*/
|
||||||
|
#ifndef vxWorks
|
||||||
|
// VxWorks does not mark abort() or exit() noreturn!
|
||||||
#define EPICS_NORETURN __attribute__((noreturn))
|
#define EPICS_NORETURN __attribute__((noreturn))
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* ifndef compilerSpecific_h */
|
#endif /* ifndef compilerSpecific_h */
|
||||||
|
@ -339,32 +339,6 @@ void epicsThread :: show ( unsigned level ) const throw ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
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 (
|
epicsThreadId epicsStdCall epicsThreadMustCreate (
|
||||||
const char *name, unsigned int priority, unsigned int stackSize,
|
const char *name, unsigned int priority, unsigned int stackSize,
|
||||||
EPICSTHREADFUNC funptr,void *parm)
|
EPICSTHREADFUNC funptr,void *parm)
|
||||||
@ -375,12 +349,3 @@ extern "C" {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
} // extern "C"
|
} // 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();
|
|
||||||
|
@ -37,6 +37,7 @@ typedef struct epicsThreadOSD {
|
|||||||
int isRealTimeScheduled;
|
int isRealTimeScheduled;
|
||||||
int isOnThreadList;
|
int isOnThreadList;
|
||||||
int isRunning;
|
int isRunning;
|
||||||
|
int isOkToBlock;
|
||||||
unsigned int osiPriority;
|
unsigned int osiPriority;
|
||||||
int joinable;
|
int joinable;
|
||||||
char name[1]; /* actually larger */
|
char name[1]; /* actually larger */
|
||||||
|
@ -55,6 +55,7 @@ struct taskVar {
|
|||||||
int refcnt;
|
int refcnt;
|
||||||
int joinable;
|
int joinable;
|
||||||
int isRunning;
|
int isRunning;
|
||||||
|
int isOkToBlock;
|
||||||
EPICSTHREADFUNC funptr;
|
EPICSTHREADFUNC funptr;
|
||||||
void *parm;
|
void *parm;
|
||||||
unsigned int threadVariableCapacity;
|
unsigned int threadVariableCapacity;
|
||||||
@ -219,7 +220,7 @@ void epicsThreadExitMain (void)
|
|||||||
|
|
||||||
static rtems_status_code
|
static rtems_status_code
|
||||||
setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr,
|
setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr,
|
||||||
void *parm, int joinable)
|
void *parm, int joinable, int isOkToBlock)
|
||||||
{
|
{
|
||||||
struct taskVar *v;
|
struct taskVar *v;
|
||||||
uint32_t note;
|
uint32_t note;
|
||||||
@ -235,6 +236,7 @@ setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr,
|
|||||||
v->threadVariableCapacity = 0;
|
v->threadVariableCapacity = 0;
|
||||||
v->threadVariables = NULL;
|
v->threadVariables = NULL;
|
||||||
v->isRunning = 1;
|
v->isRunning = 1;
|
||||||
|
v->isOkToBlock = isOkToBlock;
|
||||||
if (joinable) {
|
if (joinable) {
|
||||||
char c[3] = {0,0,0};
|
char c[3] = {0,0,0};
|
||||||
strncpy(c, v->name, 3);
|
strncpy(c, v->name, 3);
|
||||||
@ -284,7 +286,7 @@ epicsThreadInit (void)
|
|||||||
epicsMutexOsdPrepare(&taskVarMutex);
|
epicsMutexOsdPrepare(&taskVarMutex);
|
||||||
epicsMutexOsdPrepare(&onceMutex);
|
epicsMutexOsdPrepare(&onceMutex);
|
||||||
rtems_task_ident (RTEMS_SELF, 0, &tid);
|
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_");
|
cantProceed("epicsThreadInit() unable to setup _main_");
|
||||||
osdThreadHooksRunMain((epicsThreadId)tid);
|
osdThreadHooksRunMain((epicsThreadId)tid);
|
||||||
initialized = 1;
|
initialized = 1;
|
||||||
@ -338,7 +340,7 @@ epicsThreadCreateOpt (
|
|||||||
name, rtems_status_text(sc));
|
name, rtems_status_text(sc));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
sc = setThreadInfo (tid, name, funptr, parm, opts->joinable);
|
sc = setThreadInfo (tid, name, funptr, parm, opts->joinable, 0);
|
||||||
if (sc != RTEMS_SUCCESSFUL) {
|
if (sc != RTEMS_SUCCESSFUL) {
|
||||||
errlogPrintf ("epicsThreadCreate create failure during setup for %s: %s\n",
|
errlogPrintf ("epicsThreadCreate create failure during setup for %s: %s\n",
|
||||||
name, rtems_status_text(sc));
|
name, rtems_status_text(sc));
|
||||||
@ -870,3 +872,27 @@ LIBCOM_API int epicsThreadGetCPUs(void)
|
|||||||
return 1;
|
return 1;
|
||||||
#endif
|
#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,37 +19,68 @@
|
|||||||
|
|
||||||
#define EPICS_ATOMIC_OS_NAME "WIN32"
|
#define EPICS_ATOMIC_OS_NAME "WIN32"
|
||||||
|
|
||||||
#ifdef WIN32_LEAN_AND_MEAN
|
/* Disable extra declarations that we don't need here (i.e. winsock1, rpc, etc.) */
|
||||||
# define WIN32_LEAN_AND_MEAN_DETECTED_epicsAtomicOSD_h
|
#pragma push_macro("WIN32_LEAN_AND_MEAN")
|
||||||
#else
|
#undef WIN32_LEAN_AND_MEAN
|
||||||
# define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef VC_EXTRALEAN
|
#pragma push_macro("STRICT")
|
||||||
# define VC_EXTRALEAN_DETECTED_epicsAtomicOSD_h
|
#undef STRICT
|
||||||
#else
|
#define STRICT
|
||||||
# define VC_EXTRALEAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef 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 */
|
||||||
# define STRICT_DETECTED_epicsAtomicOSD_h
|
#pragma push_macro("NOMINMAX")
|
||||||
#else
|
#undef NOMINMAX
|
||||||
# define STRICT
|
#define NOMINMAX
|
||||||
#endif
|
|
||||||
|
/* 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"
|
#include "windows.h"
|
||||||
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN_DETECTED_epicsAtomicOSD_h
|
/* Restore previous macro values */
|
||||||
# undef WIN32_LEAN_AND_MEAN
|
#pragma pop_macro("WIN32_LEAN_AND_MEAN")
|
||||||
#endif
|
#pragma pop_macro("STRICT")
|
||||||
|
#pragma pop_macro("NOMINMAX")
|
||||||
#ifndef VC_EXTRALEAN_DETECTED_epicsAtomicOSD_h
|
#pragma pop_macro("NOSERVICE")
|
||||||
# undef VC_EXTRALEAN
|
#pragma pop_macro("NOIME")
|
||||||
#endif
|
#pragma pop_macro("NOMCX")
|
||||||
|
#pragma pop_macro("NOGDI")
|
||||||
#ifndef STRICT_DETECTED_epicsAtomicOSD_h
|
#pragma pop_macro("NOCRYPT")
|
||||||
# undef STRICT
|
#pragma pop_macro("NOSOUND")
|
||||||
#endif
|
#pragma pop_macro("NOKANJI")
|
||||||
|
|
||||||
#if defined ( _WIN64 )
|
#if defined ( _WIN64 )
|
||||||
# define MS_ATOMIC_64
|
# define MS_ATOMIC_64
|
||||||
|
@ -103,6 +103,7 @@ typedef struct epicsThreadOSD {
|
|||||||
char isSuspended;
|
char isSuspended;
|
||||||
int joinable;
|
int joinable;
|
||||||
int isRunning;
|
int isRunning;
|
||||||
|
int isOkToBlock;
|
||||||
HANDLE timer; /* waitable timer */
|
HANDLE timer; /* waitable timer */
|
||||||
} win32ThreadParam;
|
} win32ThreadParam;
|
||||||
|
|
||||||
@ -586,6 +587,7 @@ static win32ThreadParam * epicsThreadImplicitCreate ( void )
|
|||||||
|
|
||||||
pParm->handle = handle;
|
pParm->handle = handle;
|
||||||
pParm->id = id;
|
pParm->id = id;
|
||||||
|
pParm->isOkToBlock = 1;
|
||||||
win32ThreadPriority = GetThreadPriority ( pParm->handle );
|
win32ThreadPriority = GetThreadPriority ( pParm->handle );
|
||||||
assert ( win32ThreadPriority != THREAD_PRIORITY_ERROR_RETURN );
|
assert ( win32ThreadPriority != THREAD_PRIORITY_ERROR_RETURN );
|
||||||
pParm->epicsPriority = epicsThreadGetOsiPriorityValue ( win32ThreadPriority );
|
pParm->epicsPriority = epicsThreadGetOsiPriorityValue ( win32ThreadPriority );
|
||||||
@ -1224,3 +1226,17 @@ void testPriorityMapping ()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#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);
|
assert(pthreadInfo);
|
||||||
pthreadInfo->tid = tid;
|
pthreadInfo->tid = tid;
|
||||||
pthreadInfo->osiPriority = 0;
|
pthreadInfo->osiPriority = 0;
|
||||||
|
pthreadInfo->isOkToBlock = 1;
|
||||||
|
|
||||||
#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0
|
#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0
|
||||||
if(pthread_getschedparam(tid,&pthreadInfo->schedPolicy,&pthreadInfo->schedParam) == 0) {
|
if(pthread_getschedparam(tid,&pthreadInfo->schedPolicy,&pthreadInfo->schedParam) == 0) {
|
||||||
@ -1095,3 +1096,17 @@ LIBCOM_API int epicsThreadGetCPUs(void)
|
|||||||
#endif
|
#endif
|
||||||
return 1;
|
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 isRealTimeScheduled;
|
||||||
int isOnThreadList;
|
int isOnThreadList;
|
||||||
int isRunning;
|
int isRunning;
|
||||||
|
int isOkToBlock;
|
||||||
unsigned int osiPriority;
|
unsigned int osiPriority;
|
||||||
int joinable;
|
int joinable;
|
||||||
char name[1]; /* actually larger */
|
char name[1]; /* actually larger */
|
||||||
|
@ -132,7 +132,8 @@ static void epicsThreadInit(void)
|
|||||||
taskIdListSize = ID_LIST_CHUNK;
|
taskIdListSize = ID_LIST_CHUNK;
|
||||||
atRebootRegister();
|
atRebootRegister();
|
||||||
ALLOT_JOIN(0);
|
ALLOT_JOIN(0);
|
||||||
done = 1;
|
done = 1; /* avoids recursive call */
|
||||||
|
epicsThreadSetOkToBlock(1);
|
||||||
}
|
}
|
||||||
lock = 0;
|
lock = 0;
|
||||||
}
|
}
|
||||||
@ -582,3 +583,30 @@ LIBCOM_API int epicsThreadGetCPUs(void)
|
|||||||
{
|
{
|
||||||
return 1;
|
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);
|
||||||
|
}
|
||||||
|
@ -7,8 +7,8 @@ use base 'Pod::Simple::XHTML';
|
|||||||
|
|
||||||
BEGIN {
|
BEGIN {
|
||||||
if ($Pod::Simple::XHTML::VERSION < '3.16') {
|
if ($Pod::Simple::XHTML::VERSION < '3.16') {
|
||||||
# encode_entities() wasn't a method, add it
|
# Add encode_entities() as a method
|
||||||
our *encode_entities = sub {
|
sub encode_entities {
|
||||||
my ($self, $str) = @_;
|
my ($self, $str) = @_;
|
||||||
my %entities = (
|
my %entities = (
|
||||||
q{>} => 'gt',
|
q{>} => 'gt',
|
||||||
@ -37,4 +37,23 @@ sub resolve_pod_page_link {
|
|||||||
return $ret;
|
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;
|
1;
|
||||||
|
@ -69,6 +69,11 @@ A[href="#POD_ERRORS"] {
|
|||||||
color: #FF0000;
|
color: #FF0000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
A.sect {
|
||||||
|
color: #99ccff;
|
||||||
|
vertical-align: super;
|
||||||
|
}
|
||||||
|
|
||||||
TD {
|
TD {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
Reference in New Issue
Block a user