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
|
||||
- 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
@ -51,9 +51,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.
|
||||
@ -74,79 +74,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
|
||||
@ -246,10 +246,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
|
||||
@ -262,47 +267,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
|
||||
@ -312,4 +299,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
|
||||
|
@ -18,13 +18,28 @@ __This version of EPICS has not been released yet.__
|
||||
|
||||
__Add new items below here__
|
||||
|
||||
=======
|
||||
### Reduce symbol and macro pollution from epicsAtomic.h on WIN32
|
||||
|
||||
`epicsAtomic.h` no longer pulls in as many unneeded declarations and macros from
|
||||
`windows.h`. Prior to this change, including `epicsAtomic.h` at the wrong time
|
||||
could result in unexpected compiler errors. Due to the nature of `windows.h`,
|
||||
some unneeded declarations are still pulled in, however the number is greatly reduced.
|
||||
Code that needs these declarations should explicitly include `windows.h` before `epicsAtomic.h`.
|
||||
|
||||
### epicsExport simplifications
|
||||
|
||||
`epicsExportAddress()`, `epicsExportRegistrar()` and `epicsRegisterFunction()`
|
||||
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
|
||||
|
||||
@ -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
|
||||
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
|
||||
|
||||
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)
|
||||
* 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
|
||||
}
|
||||
|
@ -540,7 +540,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;
|
||||
@ -582,21 +581,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())
|
||||
@ -663,7 +650,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 ) {
|
||||
@ -702,21 +688,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 );
|
||||
|
@ -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");
|
||||
|
@ -221,29 +221,27 @@ testHarness_SRCS += linkFilterTest.c
|
||||
TESTFILES += ../linkFilterTest.db
|
||||
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
|
||||
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)
|
||||
@ -251,6 +249,12 @@ 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
|
||||
TARGETS += dbHeaderTestxx$(OBJ)
|
||||
TARGET_SRCS += dbHeaderTestxx.cpp
|
||||
|
||||
ifeq ($(T_A),$(EPICS_HOST_ARCH))
|
||||
# Host-only tests of softIoc/softIocPVA, caget and pvget (if present)
|
||||
# Unfortunately hangs too often on CI systems:
|
||||
|
@ -16,7 +16,10 @@
|
||||
#include <iocsh.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)
|
||||
{
|
||||
@ -41,10 +44,10 @@ MAIN(epicsExportTest)
|
||||
{
|
||||
int *p1, *p2;
|
||||
|
||||
testPlan(30);
|
||||
testPlan(31);
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("epicsExportTestIoc.dbd", 0, 0);
|
||||
epicsExportTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testOk(epicsExportTestIoc_registerRecordDeviceDriver(pdbbase)==0, "registerRecordDeviceDriver");
|
||||
|
||||
testDiag("Testing if dsets and functions are found");
|
||||
testdbReadDatabase("epicsExportTest.db", 0, 0);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -21,11 +21,7 @@
|
||||
# error compiler/gcc/compilerSpecific.h is only for use with the gnu compiler
|
||||
#endif
|
||||
|
||||
#if __GNUC__ > 2
|
||||
# define EPICS_ALWAYS_INLINE __inline__ __attribute__((always_inline))
|
||||
#else
|
||||
# define EPICS_ALWAYS_INLINE __inline__
|
||||
#endif
|
||||
#define EPICS_ALWAYS_INLINE __inline__ __attribute__((always_inline))
|
||||
|
||||
/* Expands to a 'const char*' which describes the name of the current function scope */
|
||||
#define EPICS_FUNCTION __PRETTY_FUNCTION__
|
||||
@ -42,9 +38,7 @@
|
||||
/*
|
||||
* CXX_PLACEMENT_DELETE - defined if compiler supports placement delete
|
||||
*/
|
||||
#if __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 96 )
|
||||
# define CXX_PLACEMENT_DELETE
|
||||
#endif
|
||||
#define CXX_PLACEMENT_DELETE
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
@ -56,21 +50,19 @@
|
||||
/*
|
||||
* Deprecation marker
|
||||
*/
|
||||
#if (__GNUC__ > 2)
|
||||
# define EPICS_DEPRECATED __attribute__((deprecated))
|
||||
#endif
|
||||
#define EPICS_DEPRECATED __attribute__((deprecated))
|
||||
|
||||
/*
|
||||
* Unused marker
|
||||
*/
|
||||
#ifndef vxWorks
|
||||
// VxWorks does not mark abort() or exit() noreturn!
|
||||
#define EPICS_UNUSED __attribute__((unused))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* No return marker
|
||||
*/
|
||||
#ifndef vxWorks
|
||||
// VxWorks does not mark abort() or exit() noreturn!
|
||||
#define EPICS_NORETURN __attribute__((noreturn))
|
||||
#endif
|
||||
|
||||
#endif /* ifndef compilerSpecific_h */
|
||||
|
@ -339,32 +339,6 @@ void epicsThread :: show ( unsigned level ) const throw ()
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
static epicsThreadOnceId okToBlockOnce = EPICS_THREAD_ONCE_INIT;
|
||||
epicsThreadPrivateId okToBlockPrivate;
|
||||
static const int okToBlockNo = 0;
|
||||
static const int okToBlockYes = 1;
|
||||
|
||||
static void epicsThreadOnceIdInit(void *)
|
||||
{
|
||||
okToBlockPrivate = epicsThreadPrivateCreate();
|
||||
}
|
||||
|
||||
int epicsStdCall epicsThreadIsOkToBlock(void)
|
||||
{
|
||||
const int *pokToBlock;
|
||||
epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL);
|
||||
pokToBlock = (int *) epicsThreadPrivateGet(okToBlockPrivate);
|
||||
return (pokToBlock ? *pokToBlock : 0);
|
||||
}
|
||||
|
||||
void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock)
|
||||
{
|
||||
const int *pokToBlock;
|
||||
epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL);
|
||||
pokToBlock = (isOkToBlock) ? &okToBlockYes : &okToBlockNo;
|
||||
epicsThreadPrivateSet(okToBlockPrivate, (void *)pokToBlock);
|
||||
}
|
||||
|
||||
epicsThreadId epicsStdCall epicsThreadMustCreate (
|
||||
const char *name, unsigned int priority, unsigned int stackSize,
|
||||
EPICSTHREADFUNC funptr,void *parm)
|
||||
@ -375,12 +349,3 @@ extern "C" {
|
||||
return id;
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
static epicsThreadId initMainThread(void) {
|
||||
epicsThreadId main = epicsThreadGetIdSelf();
|
||||
epicsThreadSetOkToBlock(1);
|
||||
return main;
|
||||
}
|
||||
|
||||
// Ensure the main thread gets a unique ID and allows blocking I/O
|
||||
epicsThreadId epicsThreadMainId = initMainThread();
|
||||
|
@ -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,37 +19,68 @@
|
||||
|
||||
#define EPICS_ATOMIC_OS_NAME "WIN32"
|
||||
|
||||
#ifdef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN_DETECTED_epicsAtomicOSD_h
|
||||
#else
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
#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 VC_EXTRALEAN
|
||||
# define VC_EXTRALEAN_DETECTED_epicsAtomicOSD_h
|
||||
#else
|
||||
# define VC_EXTRALEAN
|
||||
#endif
|
||||
#pragma push_macro("STRICT")
|
||||
#undef STRICT
|
||||
#define STRICT
|
||||
|
||||
#ifdef STRICT
|
||||
# define STRICT_DETECTED_epicsAtomicOSD_h
|
||||
#else
|
||||
# define STRICT
|
||||
#endif
|
||||
/* 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 WIN32_LEAN_AND_MEAN_DETECTED_epicsAtomicOSD_h
|
||||
# undef WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#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) {
|
||||
@ -1095,3 +1096,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 */
|
||||
|
@ -132,7 +132,8 @@ static void epicsThreadInit(void)
|
||||
taskIdListSize = ID_LIST_CHUNK;
|
||||
atRebootRegister();
|
||||
ALLOT_JOIN(0);
|
||||
done = 1;
|
||||
done = 1; /* avoids recursive call */
|
||||
epicsThreadSetOkToBlock(1);
|
||||
}
|
||||
lock = 0;
|
||||
}
|
||||
@ -582,3 +583,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);
|
||||
}
|
||||
|
@ -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