Compare commits

...

19 Commits

Author SHA1 Message Date
Andrew Johnson
e4ebc01401 Fix EPICS::PodXHtml and generate section links
Some checks failed
Base / Ub-22 clang C++11 (push) Has been cancelled
Base / Ub-22 clang (push) Has been cancelled
Base / Ub-22 gcc + RT-4.9 (push) Has been cancelled
Base / MacOS clang (push) Has been cancelled
Base / Ub-24 gcc-13 c++20 Werror (push) Has been cancelled
Base / Ub-22 gcc + RT-5.1 beatnik (push) Has been cancelled
Base / Ub-22 gcc + RT-4.10 (push) Has been cancelled
Base / Ub-22 gcc + RT-5.1 pc686 (push) Has been cancelled
Base / Ub-22 gcc + RT-5.1 uC5282 (push) Has been cancelled
Base / Ub-22 gcc unsigned char (push) Has been cancelled
Base / Ub-22 gcc + MinGW, static (push) Has been cancelled
Base / Win2019 MSC-19 (push) Has been cancelled
Base / Win2019 MSC-19, debug (push) Has been cancelled
Base / Win2019 MSC-19, static (push) Has been cancelled
Base / Ub-22 gcc + RT-5.1 xilinx_zynq_a9_qemu (push) Has been cancelled
Base / Win2019 mingw (push) Has been cancelled
Base / Ub-22 gcc + MinGW (push) Has been cancelled
Base / Ub-22 gcc C++11, static (push) Has been cancelled
Check EditorConfig / editorconfig (push) Failing after 1s
Base / Cross linux-aarch64 (push) Failing after 2s
Base / Cross linux-arm gnueabi (push) Failing after 1s
Base / Cross linux-arm gnueabihf (push) Failing after 2s
Base / CentOS-8 (push) Failing after 2s
Base / Fedora-33 (push) Failing after 5s
Base / Fedora-latest (push) Failing after 4s
Base / Rocky-9 (push) Failing after 5s
CodeQL / Analyze (cpp) (push) Failing after 1s
Base / Docker CentOS-7 (push) Failing after 14s
Overrides an internal method in Pod::Simple::XHTML that
appends a section sign which links to this section.
2025-03-19 11:26:51 -05:00
Jeremy Lorelli
6ea6ae633b Define NOCRYPT, NOSOUND and NOKANJI before including windows.h 2025-03-19 11:19:45 -05:00
JJL772
9fac52fa58 Add a release note for the windows.h epicsAtomicOSD change 2025-03-19 11:19:45 -05:00
JJL772
fc5d3c9a5c Disable extraneous WIN32 declarations when including windows.h in epicsAtomicOSD.h 2025-03-19 11:19:45 -05:00
c75ad2673e no longer need extern "C" { } around epicsExport macros 2025-03-19 11:17:14 -05:00
b97a35fec8 Don't use __attribute__((noreturn)) on VxWorks
VxWorks does not mark abort() or exit() as noreturn.
Thus, functions declared noreturn which end in a call
to those functions cause a compiler warning on vxWorks.
2025-03-19 11:06:26 -05:00
Andrew Johnson
350570134e Changed dbServerStats() to count the server layers called 2025-03-19 11:03:46 -05:00
Andrew Johnson
72f3e75c8d Add summary stat's calculation 2025-03-19 11:03:46 -05:00
Andrew Johnson
90f97a7037 Release notes; HAS_DBSERVER_STATS in dbServer.h 2025-03-19 11:03:46 -05:00
Andrew Johnson
fd86f0ff04 Unit tests for dbServerStats() 2025-03-19 11:03:46 -05:00
Andrew Johnson
bcc6cb96ae Added dbServerStats() API for iocStats and similar 2025-03-19 11:03:46 -05:00
Michael Davidsaver
a4bc0db6e6 dbCa CP link updates set PUTF/RPRO 2025-03-19 10:59:28 -05:00
Michael Davidsaver
48ebe2c64e Avoid initMainThread() except on vxworks
Move isOkToBlock tracking to osdThread.
Targets except vxworks can store this flag in epicsThreadOSD.
Continue to use TLS w/ vxWorks.

Note that setting of isOkToBlock for "main" thread becomes lazy.
2025-03-19 10:53:42 -05:00
Michael Davidsaver
9f788996dc db: lock record for db_create_read_log() and dbChannelGetField()
since 27fe3e4468 db_create_read_log()
accesses record fields.

remove now redundant db_create_read_log() calls.
2025-03-19 10:51:08 -05:00
Michael Davidsaver
13d6ca598c initHookRegister() make idempotent and MustSucceed 2025-03-05 10:02:38 -06:00
ee1a49045a support CROSS_COMPILER_RUNTESTS_ARCHS other than RTEMS 2025-03-05 10:00:44 -06:00
Ralph Lange
78f263f359 Update GitHub Actions build configuration (#609)
* ci: bump GHA ubuntu jobs (22->24, 20->22)

* ci: add GHA job for CentOS-7 on self-managed Docker

* ci: add jobs for CentOS-8 and Rocky-9 on GHA-managed Docker
2025-03-05 09:55:32 -06:00
43e75e3901 decorate functions that do not return 2025-03-05 09:51:57 -06:00
Ralph Lange
131578124b ci: drop AppVeyor builds on VS2010/VS2012 2025-02-28 09:59:53 -06:00
51 changed files with 850 additions and 270 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -50,9 +50,9 @@ jobs:
matrix:
# Job names also name artifacts, character limitations apply
include:
- os: ubuntu-22.04
cmp: gcc-12
name: "Ub-22 gcc-12 c++20 Werror"
- os: ubuntu-24.04
cmp: gcc
name: "Ub-24 gcc-13 c++20 Werror"
# Turn all warnings into errors,
# except for those we could not fix (yet).
# Remove respective -Wno-error=... flag once it is fixed.
@@ -73,79 +73,79 @@ jobs:
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3'
CMD_LDFLAGS=-Wl,-z,relro"
- os: ubuntu-20.04
- os: ubuntu-22.04
cmp: gcc
configuration: default
cross: "windows-x64-mingw"
name: "Ub-20 gcc + MinGW"
name: "Ub-22 gcc + MinGW"
- os: ubuntu-20.04
- os: ubuntu-22.04
cmp: gcc
configuration: static
cross: "windows-x64-mingw"
name: "Ub-20 gcc + MinGW, static"
name: "Ub-22 gcc + MinGW, static"
- os: ubuntu-20.04
- os: ubuntu-22.04
cmp: gcc
configuration: static
extra: "CMD_CXXFLAGS=-std=c++11"
name: "Ub-20 gcc C++11, static"
name: "Ub-22 gcc C++11, static"
- os: ubuntu-20.04
- os: ubuntu-22.04
cmp: gcc
configuration: static
extra: "CMD_CFLAGS=-funsigned-char CMD_CXXFLAGS=-funsigned-char"
name: "Ub-20 gcc unsigned char"
name: "Ub-22 gcc unsigned char"
- os: ubuntu-20.04
- os: ubuntu-22.04
cmp: clang
configuration: default
name: "Ub-20 clang"
name: "Ub-22 clang"
- os: ubuntu-20.04
- os: ubuntu-22.04
cmp: clang
configuration: default
extra: "CMD_CXXFLAGS=-std=c++11"
name: "Ub-20 clang C++11"
name: "Ub-22 clang C++11"
- os: ubuntu-20.04
- os: ubuntu-22.04
cmp: gcc
configuration: default
cross: "RTEMS-pc686-qemu@5"
name: "Ub-20 gcc + RT-5.1 pc686"
name: "Ub-22 gcc + RT-5.1 pc686"
- os: ubuntu-20.04
- os: ubuntu-22.04
cmp: gcc
configuration: default
cross: "RTEMS-beatnik@5"
test: NO
name: "Ub-20 gcc + RT-5.1 beatnik"
name: "Ub-22 gcc + RT-5.1 beatnik"
- os: ubuntu-20.04
- os: ubuntu-22.04
cmp: gcc
configuration: default
cross: "RTEMS-xilinx_zynq_a9_qemu@5"
test: NO
name: "Ub-20 gcc + RT-5.1 xilinx_zynq_a9_qemu"
name: "Ub-22 gcc + RT-5.1 xilinx_zynq_a9_qemu"
- os: ubuntu-20.04
- os: ubuntu-22.04
cmp: gcc
configuration: default
cross: "RTEMS-uC5282@5"
test: NO
name: "Ub-20 gcc + RT-5.1 uC5282"
name: "Ub-22 gcc + RT-5.1 uC5282"
- os: ubuntu-20.04
- os: ubuntu-22.04
cmp: gcc
configuration: default
name: "Ub-20 gcc + RT-4.10"
name: "Ub-22 gcc + RT-4.10"
cross: "RTEMS-pc386-qemu@4.10"
test: NO
- os: ubuntu-20.04
- os: ubuntu-22.04
cmp: gcc
configuration: default
name: "Ub-20 gcc + RT-4.9"
name: "Ub-22 gcc + RT-4.9"
cross: "RTEMS-pc386-qemu@4.9"
- os: macos-latest
@@ -239,10 +239,15 @@ jobs:
matrix:
# Job names also name artifacts, character limitations apply
include:
#- name: "CentOS-7"
# image: centos:7
# cmp: gcc
# configuration: default
- name: "CentOS-8"
image: centos:8
cmp: gcc
configuration: default
- name: "Rocky-9"
image: rockylinux:9
cmp: gcc
configuration: default
- name: "Fedora-33"
image: fedora:33
@@ -255,47 +260,29 @@ jobs:
configuration: default
steps:
- name: "Build newer Git"
# actions/checkout@v2 wants git >=2.18
# centos:7 has 1.8
if: matrix.image=='centos:7'
- name: "Fix repo URLs on CentOS-8"
# centos:8 is frozen, repos are in the vault
if: matrix.image=='centos:8'
run: |
yum -y install curl make gcc curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker
curl https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.29.0.tar.gz | tar -xz
cd git-*
make -j2 prefix=/usr/local all
make prefix=/usr/local install
cd ..
rm -rf git-*
type -a git
git --version
sed -i -e "s|mirrorlist=|#mirrorlist=|" \
-e "s|#baseurl=http://mirror|baseurl=http://vault|" \
/etc/yum.repos.d/CentOS-Linux-{BaseOS,AppStream,Extras,Plus}.repo
- name: "Redhat setup"
run: |
dnfyum() {
dnf -y "$@" || yum -y "$@"
return $?
}
dnfyum install python3 gdb make perl gcc-c++ glibc-devel readline-devel ncurses-devel perl-devel perl-Test-Simple
git --version || dnfyum install git
# rather than just bite the bullet and link python3 -> python,
# people would rather just break all existing scripts...
[ -e /usr/bin/python ] || ln -sf python3 /usr/bin/python
python --version
dnf -y install python3 gdb make perl gcc-c++ glibc-devel readline-devel ncurses-devel perl-devel perl-Test-Simple
git --version || dnf -y install git
python3 --version
- uses: actions/checkout@v4
with:
submodules: true
- name: Automatic core dumper analysis
uses: mdavidsaver/ci-core-dumper@master
if: matrix.image!='centos:7'
- name: Automatic core dumper analysis
uses: mdavidsaver/ci-core-dumper@node16
if: matrix.image=='centos:7'
- name: Prepare and compile dependencies
run: python .ci/cue.py prepare
run: python3 .ci/cue.py prepare
- name: Build main module
run: python .ci/cue.py build
run: python3 .ci/cue.py build
- name: Run main module tests
run: python .ci/cue.py -T 20M test
run: python3 .ci/cue.py -T 20M test
- name: Upload tapfiles Artifact
if: ${{ always() }}
uses: actions/upload-artifact@v4
@@ -305,4 +292,55 @@ jobs:
if-no-files-found: ignore
- name: Collect and show test results
if: ${{ always() }}
run: python .ci/cue.py -T 5M test-results
run: python3 .ci/cue.py -T 5M test-results
build-docker:
name: Docker CentOS-7
runs-on: ubuntu-latest
env:
CMP: gcc
BCFG: default
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Run...
run: |
env > env.list
cat <<EOF > runit.sh
#!/bin/sh
set -e -x
cd /io
id
sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo
sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo
sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo
yum -y install epel-release
yum -y install \
curl make gcc curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker \
python3 gdb make perl gcc-c++ glibc-devel readline-devel ncurses-devel perl-devel perl-Test-Simple \
libevent-devel sudo re2c
[ -e /usr/bin/python ] || ln -sf /usr/bin/python3 /usr/bin/python
# fake out cue.py
ln -s /bin/true /usr/bin/apt-get
# quiet warnings spam from perl
export LANG=C
python --version
python .ci/cue.py prepare
python .ci/cue.py build
python .ci/cue.py -T 15M test
python .ci/cue.py test-results
EOF
chmod +x runit.sh
docker run --rm --quiet \
--pull=always \
--env-file env.list \
-v `pwd`:/io \
centos:7 \
/io/runit.sh

View File

@@ -18,6 +18,27 @@ __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.
### 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

View File

@@ -3396,7 +3396,7 @@ void verifyContextRundownChanStillExist (
showProgressEnd ( interestLevel );
}
int acctst ( const char * pName, unsigned interestLevel, unsigned channelCount,
void acctst ( const char * pName, unsigned interestLevel, unsigned channelCount,
unsigned repetitionCount, enum ca_preemptive_callback_select select )
{
chid chan;
@@ -3549,8 +3549,6 @@ int acctst ( const char * pName, unsigned interestLevel, unsigned channelCount,
printf ( "\nTest Complete\n" );
epicsExit ( EXIT_SUCCESS );
return 0;
}

View File

@@ -20,7 +20,8 @@ extern "C" {
enum appendNumberFlag {appendNumber, dontAppendNumber};
int catime ( const char *channelName, unsigned channelCount, enum appendNumberFlag appNF );
int acctst ( const char *pname, unsigned logggingInterestLevel,
EPICS_NORETURN
void acctst ( const char *pname, unsigned logggingInterestLevel,
unsigned channelCount, unsigned repetitionCount,
enum ca_preemptive_callback_select select );

View File

@@ -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");
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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) \
{ \

View File

@@ -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.

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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
}

View File

@@ -493,7 +493,6 @@ static void read_reply ( void *pArg, struct dbChannel *dbch,
const int readAccess = asCheckGet ( pciu->asClientPVT );
int status;
int autosize;
int local_fl = 0;
long item_count;
ca_uint32_t payload_size;
dbAddr *paddr=&dbch->addr;
@@ -535,21 +534,9 @@ static void read_reply ( void *pArg, struct dbChannel *dbch,
return;
}
/* If filters are involved in a read, create field log and run filters */
if (!pfl && (ellCount(&dbch->pre_chain) || ellCount(&dbch->post_chain))) {
pfl = db_create_read_log(dbch);
if (pfl) {
local_fl = 1;
pfl = dbChannelRunPreChain(dbch, pfl);
pfl = dbChannelRunPostChain(dbch, pfl);
}
}
status = dbChannel_get_count ( dbch, pevext->msg.m_dataType,
pPayload, &item_count, pfl);
if (local_fl) db_delete_field_log(pfl);
if ( status < 0 ) {
/* Clients recv the status of the operation directly to the
* event/put/get callback. (from CA_V41())
@@ -616,7 +603,6 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p
ca_uint32_t payloadSize;
void *pPayload;
int status;
int local_fl = 0;
db_field_log *pfl = NULL;
if ( ! pciu ) {
@@ -655,21 +641,9 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p
return RSRV_OK;
}
/* If filters are involved in a read, create field log and run filters */
if (ellCount(&pciu->dbch->pre_chain) || ellCount(&pciu->dbch->post_chain)) {
pfl = db_create_read_log(pciu->dbch);
if (pfl) {
local_fl = 1;
pfl = dbChannelRunPreChain(pciu->dbch, pfl);
pfl = dbChannelRunPostChain(pciu->dbch, pfl);
}
}
status = dbChannel_get ( pciu->dbch, mp->m_dataType,
pPayload, mp->m_count, pfl );
if (local_fl) db_delete_field_log(pfl);
if ( status < 0 ) {
send_err ( mp, ECA_GETFAIL, pClient, RECORD_NAME ( pciu->dbch ) );
SEND_UNLOCK ( pClient );

View File

@@ -208,8 +208,8 @@ TESTSPEC_RTEMS = dbTestHarness.boot; epicsRunDbTests
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
TESTPROD_RTEMS = $(TESTPROD_HOST)
TESTSCRIPTS_RTEMS += $(TESTS:%=%.t)
TESTPROD += $(TESTPROD_HOST)
TESTSCRIPTS += $(TESTS:%=%.t)
endif
include $(TOP)/configure/RULES

View File

@@ -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++) \

View File

@@ -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");

View File

@@ -81,8 +81,8 @@ TESTSPEC_RTEMS = filterTestHarness.boot; epicsRunFilterTests
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
TESTPROD_RTEMS = $(TESTPROD_HOST)
TESTSCRIPTS_RTEMS += $(TESTS:%=%.t)
TESTPROD += $(TESTPROD_HOST)
TESTSCRIPTS += $(TESTS:%=%.t)
endif
include $(TOP)/configure/RULES

View File

@@ -57,8 +57,8 @@ TESTSPEC_RTEMS = linkTestHarness.boot; epicsRunLinkTests
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
TESTPROD = $(TESTPROD_HOST)
TESTSCRIPTS_RTEMS += $(TESTS:%=%.t)
TESTPROD += $(TESTPROD_HOST)
TESTSCRIPTS += $(TESTS:%=%.t)
endif
include $(TOP)/configure/RULES

View File

@@ -221,6 +221,34 @@ testHarness_SRCS += linkFilterTest.c
TESTFILES += ../linkFilterTest.db
TESTS += linkFilterTest
TARGETS += $(COMMON_DIR)/epicsExportTestIoc.dbd
DBDDEPENDS_FILES += epicsExportTestIoc.dbd$(DEP)
epicsExportTestIoc_DBD += base.dbd
epicsExportTestIoc_DBD += epicsExportTest.dbd
TESTFILES += $(COMMON_DIR)/epicsExportTestIoc.dbd ../epicsExportTest.db
TESTLIBRARY += epicsExportTestLib
epicsExportTestLib_SRCS += epicsExportTest.c
epicsExportTestLib_LIBS += dbCore Com
TESTPROD_HOST += epicsExportTest
TESTS += epicsExportTest
TARGETS += epicsExportTest$(OBJ)
epicsExportTest_SRCS += epicsExportTestMain.c
epicsExportTest_SRCS += epicsExportTestIoc_registerRecordDeviceDriver.cpp
epicsExportTest_LIBS = epicsExportTestLib
TESTLIBRARY += epicsExportTestxxLib
epicsExportTestxxLib_SRCS += epicsExportTestxx.cpp
epicsExportTestxxLib_LIBS += dbCore Com
TESTPROD_HOST += epicsExportTestxx
TESTS += epicsExportTestxx
TARGETS += epicsExportTestxx$(OBJ)
epicsExportTestxx_SRCS += epicsExportTestMain.c
epicsExportTestxx_SRCS += epicsExportTestIoc_registerRecordDeviceDriver.cpp
epicsExportTestxx_LIBS = epicsExportTestxxLib
# These are compile-time tests, no need to link or run
TARGETS += dbHeaderTest$(OBJ)
TARGET_SRCS += dbHeaderTest.cpp
@@ -251,8 +279,8 @@ TESTSPEC_RTEMS = recordTestHarness.boot; epicsRunRecordTests
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
TESTPROD_RTEMS = $(TESTPROD_HOST)
TESTSCRIPTS_RTEMS += $(TESTS:%=%.t)
TESTPROD += $(TESTPROD_HOST)
TESTSCRIPTS += $(TESTS:%=%.t)
endif
include $(TOP)/configure/RULES

View File

@@ -0,0 +1,146 @@
/*************************************************************************\
* Copyright (c) 2025 Dirk Zimoch
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* Compile and link test for epicsExport.h
*
* Test if macros eppicsExportAddr, epicsExportRegistrar and epicsRegisterFunction
* expand to valid C and C++ code, in the latter case regardless if
* wrapped with extern "C" {} or not.
* Also test that those macros have the intended effect in both cases.
*
* This file is compiled directly as C
* and included from epicsExportTestxx.cpp as C++
*/
#include <epicsUnitTest.h>
#include <dbUnitTest.h>
#include <testMain.h>
#include <dbAccess.h>
#include <longinRecord.h>
#include <aSubRecord.h>
#include <iocsh.h>
#include <registryFunction.h>
#include <epicsExport.h>
int i1, i2;
volatile int v1=10, v2=20;
const int c1=100, c2=200;
static long myReadLongin(struct dbCommon* prec)
{
struct longinRecord* pli =
#ifdef __cplusplus
reinterpret_cast<struct longinRecord*>(prec);
#else
(struct longinRecord*)prec;
#endif
pli->val=5;
return 0;
}
/* Also test cast from user-specific const mydset to dset */
const struct mydset {
long number;
long (*report)(int);
long (*init)(int);
long (*init_record)(struct dbCommon*);
long (*get_ioint_info)(int, struct dbCommon*, IOSCANPVT*);
long (*process)(struct dbCommon*);
long (*something_else)(struct dbCommon*);
} dset1 = {
6,
NULL,
NULL,
NULL,
NULL,
myReadLongin,
NULL
}, dset2 = {
6,
NULL,
NULL,
NULL,
NULL,
myReadLongin,
NULL
};
static void registrar1() {
i1++;
testPass("registrar1 executed");
}
static void registrar2() {
i2++;
testPass("registrar2 executed");
}
/* Also test cast from int(*)() to REGISTRAR */
static int registrar3() {
i1++;
testPass("registrar3 executed");
return 0;
}
static int registrar4() {
i2++;
testPass("registrar4 executed");
return 0;
}
/* Test both, native (potentially C++) and extern "C" functions */
static long aSubInit1(aSubRecord* prec) {
*(epicsInt32*)prec->a = 1;
return 0;
}
static long aSubProc1(aSubRecord* prec) {
*(epicsInt32*)prec->b = 2;
return 0;
}
#ifdef __cplusplus
extern "C" {
#endif
static long aSubInit2(aSubRecord* prec) {
*(epicsInt32*)prec->a = 3;
return 0;
}
static long aSubProc2(aSubRecord* prec) {
*(epicsInt32*)prec->b = 4;
return 0;
}
#ifdef __cplusplus
}
#endif
/* Test without wrapping */
epicsExportAddress(int, i1);
epicsExportAddress(int, v1);
epicsExportAddress(int, c1);
epicsExportAddress(dset, dset1);
epicsExportRegistrar(registrar1);
epicsExportRegistrar(registrar3);
epicsRegisterFunction(aSubInit1);
epicsRegisterFunction(aSubProc1);
/* In C++ test wrapped in extern "C" {} */
#ifdef __cplusplus
extern "C" {
#endif
epicsExportAddress(int, i2);
epicsExportAddress(int, v2);
epicsExportAddress(int, c2);
epicsExportAddress(dset, dset2);
epicsExportRegistrar(registrar2);
epicsExportRegistrar(registrar4);
epicsRegisterFunction(aSubInit2);
epicsRegisterFunction(aSubProc2);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,24 @@
record(longin, "li1") {
field(DTYP, "dset1")
field(VAL, "-1")
}
record(longin, "li2") {
field(DTYP, "dset2")
field(VAL, "-2")
}
record(aSub, "asub1") {
field(FTA, "LONG")
field(FTB, "LONG")
field(INPA, "-1")
field(INPB, "-2")
field(INAM, "aSubInit1")
field(SNAM, "aSubProc1")
}
record(aSub, "asub2") {
field(FTA, "LONG")
field(FTB, "LONG")
field(INPA, "-3")
field(INPB, "-4")
field(INAM, "aSubInit2")
field(SNAM, "aSubProc2")
}

View File

@@ -0,0 +1,16 @@
variable(i1,int)
variable(i2,int)
variable(v1,int)
variable(v2,int)
variable(c1,int)
variable(c2,int)
device(longin, CONSTANT, dset1, "dset1")
device(longin, CONSTANT, dset2, "dset2")
registrar(registrar1)
registrar(registrar2)
registrar(registrar3)
registrar(registrar4)
function(aSubInit1)
function(aSubProc1)
function(aSubInit2)
function(aSubProc2)

View File

@@ -0,0 +1,95 @@
/*************************************************************************\
* Copyright (c) 2025 Dirk Zimoch
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* Compile and link test for epicsExport.h
*
*/
#include <epicsUnitTest.h>
#include <dbUnitTest.h>
#include <testMain.h>
#include <dbAccess.h>
#include <iocsh.h>
#include <epicsExport.h>
#ifdef __cplusplus
extern "C"
#endif
int epicsExportTestIoc_registerRecordDeviceDriver(struct dbBase *);
static int* testVarEquals(const char* name, int expected)
{
const iocshVarDef *var;
int *p;
var = iocshFindVariable(name);
if (!var) {
testFail("Cannot access variable %s", name);
return NULL;
}
if (var->type != iocshArgInt) {
testFail("Variable %s has wrong type", name);
return NULL;
}
p = (int*)(var->pval);
testOk(*p == expected, "Variable %s == %d", name, expected);
return p;
}
MAIN(epicsExportTest)
{
int *p1, *p2;
testPlan(31);
testdbPrepare();
testdbReadDatabase("epicsExportTestIoc.dbd", 0, 0);
testOk(epicsExportTestIoc_registerRecordDeviceDriver(pdbbase)==0, "registerRecordDeviceDriver");
testDiag("Testing if dsets and functions are found");
testdbReadDatabase("epicsExportTest.db", 0, 0);
testIocInitOk();
testDiag("Testing if dsets work correctly");
testdbGetFieldEqual("li1", DBF_LONG, -1);
testdbGetFieldEqual("li2", DBF_LONG, -2);
testdbPutFieldOk("li1.PROC", DBF_LONG, 1);
testdbPutFieldOk("li2.PROC", DBF_LONG, 1);
testdbGetFieldEqual("li1", DBF_LONG, 5);
testdbGetFieldEqual("li2", DBF_LONG, 5);
testDiag("Testing if functions work correctly");
testdbGetFieldEqual("asub1.A", DBF_LONG, 1);
testdbGetFieldEqual("asub1.B", DBF_LONG, -2);
testdbGetFieldEqual("asub2.A", DBF_LONG, 3);
testdbGetFieldEqual("asub2.B", DBF_LONG, -4);
testdbPutFieldOk("asub1.PROC", DBF_LONG, 1);
testdbPutFieldOk("asub2.PROC", DBF_LONG, 1);
testdbGetFieldEqual("asub1.A", DBF_LONG, 1);
testdbGetFieldEqual("asub1.B", DBF_LONG, 2);
testdbGetFieldEqual("asub2.A", DBF_LONG, 3);
testdbGetFieldEqual("asub2.B", DBF_LONG, 4);
testDiag("Testing if variable access works");
p1 = testVarEquals("i1", 2);
p2 = testVarEquals("i2", 2);
testVarEquals("v1", 10);
testVarEquals("v2", 20);
testVarEquals("c1", 100);
testVarEquals("c2", 200);
if (p1 && p2) {
testDiag("Testing if variables are accessible from iocsh");
testOk(iocshCmd("var i1,4") == 0, "Setting i1 = 4 in iocsh");
testOk(iocshCmd("var i2,5") == 0, "Setting i2 = 5 in iocsh");
testOk(*p1==4, "Variable i1 == 4");
testOk(*p2==5, "Variable i2 == 5");
}
testIocShutdownOk();
testdbCleanup();
return testDone();
}

View File

@@ -0,0 +1,2 @@
// just compile as c++
#include "epicsExportTest.c"

View File

@@ -696,16 +696,16 @@ extern void dataend (void);
extern void flexerror (char[]) NORETURN;
/* report a fatal error message and terminate */
extern void flexfatal (char[]);
extern void flexfatal (char[]) NORETURN;
/* return current time */
extern char *flex_gettime();
/* report an error message formatted with one integer argument */
extern void lerrif (char[], int);
extern void lerrif (char[], int) NORETURN;
/* report an error message formatted with one string argument */
extern void lerrsf (char[], char[]);
extern void lerrsf (char[], char[]) NORETURN;
/* spit out a "# line" statement */
extern void line_directive_out (FILE*);

View File

@@ -21,6 +21,7 @@
#include "ellLib.h"
#include "epicsMutex.h"
#include "epicsThread.h"
#include "cantProceed.h"
#include "initHooks.h"
@@ -52,19 +53,26 @@ static void initHookInit(void)
int initHookRegister(initHookFunction func)
{
initHookLink *newHook;
ELLNODE *cur;
if (!func) return 0;
initHookInit();
newHook = (initHookLink *)malloc(sizeof(initHookLink));
if (!newHook) {
printf("Cannot malloc a new initHookLink\n");
return -1;
epicsMutexMustLock(listLock);
for(cur = ellFirst(&functionList); cur; cur = ellNext(cur)) {
const initHookLink *fn = CONTAINER(cur, initHookLink, node);
if(fn->func==func) {
/* silently ignore duplicate */
epicsMutexUnlock(listLock);
return 0;
}
}
newHook = (initHookLink *)mallocMustSucceed(sizeof(initHookLink), "initHookRegister");
newHook->func = func;
epicsMutexMustLock(listLock);
ellAdd(&functionList, &newHook->node);
epicsMutexUnlock(listLock);
return 0;

View File

@@ -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);

View File

@@ -43,7 +43,8 @@ extern "C" {
* \param errorMessage A printf-style error message describing the error.
* \param ... Any parameters required for the error message.
*/
LIBCOM_API void cantProceed(
LIBCOM_API EPICS_NORETURN
void cantProceed(
EPICS_PRINTF_FMT(const char *errorMessage), ...
) EPICS_PRINTF_STYLE(1,2);

View File

@@ -23,6 +23,7 @@
#ifndef epicsExith
#define epicsExith
#include <libComAPI.h>
#include "compilerDependencies.h"
#ifdef __cplusplus
extern "C" {
@@ -38,7 +39,8 @@ typedef void (*epicsExitFunc)(void *arg);
* \brief Calls epicsExitCallAtExits(), then the OS exit() routine.
* \param status Passed to exit()
*/
LIBCOM_API void epicsExit(int status);
LIBCOM_API EPICS_NORETURN
void epicsExit(int status);
/**
* \brief Arrange to call epicsExit() later from a low priority thread.
*

View File

@@ -41,6 +41,10 @@ extern "C" {
typedef void (*REGISTRAR)(void);
#ifdef __cplusplus
}
#endif
#define EPICS_EXPORT_POBJ(typ, obj) pvar_ ## typ ## _ ## obj
#define EPICS_EXPORT_PFUNC(fun) EPICS_EXPORT_POBJ(func, fun)
@@ -75,12 +79,16 @@ typedef void (*REGISTRAR)(void);
*
* \param typ Object's data type.
* \param obj Object's name.
*
* \note C++ code needs to wrap with @code extern "C" { } @endcode
*/
#ifdef __cplusplus
#define epicsExportAddress(typ, obj) \
extern "C" { epicsShareExtern typ *EPICS_EXPORT_POBJ(typ,obj); } \
epicsShareDef typ *EPICS_EXPORT_POBJ(typ, obj) = (typ *) (char *) &obj
#else
#define epicsExportAddress(typ, obj) \
epicsShareExtern typ *EPICS_EXPORT_POBJ(typ,obj); \
epicsShareDef typ *EPICS_EXPORT_POBJ(typ, obj) = (typ *) (char *) &obj
#endif
/** \brief Declare a registrar function for exporting.
*
@@ -94,11 +102,15 @@ typedef void (*REGISTRAR)(void);
\endcode
*
* \param fun Registrar function's name.
*
* \note C++ code needs to wrap with @code extern "C" { } @endcode
*/
#ifdef __cplusplus
#define epicsExportRegistrar(fun) \
extern "C" { extern epicsShareFunc REGISTRAR EPICS_EXPORT_PFUNC(fun); } \
REGISTRAR EPICS_EXPORT_PFUNC(fun) = reinterpret_cast<REGISTRAR>(&fun)
#else
#define epicsExportRegistrar(fun) \
epicsShareFunc REGISTRAR EPICS_EXPORT_PFUNC(fun) = (REGISTRAR) &fun
#endif
/** \brief Declare and register a function for exporting.
*
@@ -111,18 +123,21 @@ typedef void (*REGISTRAR)(void);
\endcode
*
* \param fun Function's name
*
* \note C++ code needs to wrap with @code extern "C" { } @endcode
*/
#ifdef __cplusplus
#define epicsRegisterFunction(fun) \
static void register_func_ ## fun(void) \
{ \
registryFunctionAdd(#fun, reinterpret_cast<REGISTRYFUNCTION>(fun)); \
} \
epicsExportRegistrar(register_func_ ## fun)
#else
#define epicsRegisterFunction(fun) \
static void register_func_ ## fun(void) \
{ \
registryFunctionAdd(#fun, (REGISTRYFUNCTION) fun); \
} \
epicsExportRegistrar(register_func_ ## fun)
#ifdef __cplusplus
}
#endif
#endif /* INC_epicsExport_H */

View File

@@ -223,7 +223,8 @@ LIBCOM_API void testTodoEnd(void);
* \param fmt A printf-style format string giving the reason for stopping.
* \param ... Any parameters required for the format string.
*/
LIBCOM_API void testAbort(EPICS_PRINTF_FMT(const char *fmt), ...)
LIBCOM_API EPICS_NORETURN
void testAbort(EPICS_PRINTF_FMT(const char *fmt), ...)
EPICS_PRINTF_STYLE(1, 2);
/** @} */

View File

@@ -58,4 +58,9 @@
*/
#define EPICS_UNUSED __attribute__((unused))
/*
* No return marker
*/
#define EPICS_NORETURN __attribute__((noreturn))
#endif /* ifndef compilerSpecific_h */

View File

@@ -57,4 +57,12 @@
*/
#define EPICS_UNUSED __attribute__((unused))
/*
* No return marker
*/
#ifndef vxWorks
// VxWorks does not mark abort() or exit() noreturn!
#define EPICS_NORETURN __attribute__((noreturn))
#endif
#endif /* ifndef compilerSpecific_h */

View File

@@ -51,4 +51,9 @@
# define EPICS_PRINTF_FMT(a) _Printf_format_string_ a
#endif
/*
* No return marker
*/
#define EPICS_NORETURN __declspec(noreturn)
#endif /* ifndef compilerSpecific_h */

View File

@@ -52,6 +52,10 @@
# define EPICS_UNUSED
#endif
#ifndef EPICS_NORETURN
# define EPICS_NORETURN
#endif
#ifndef EPICS_FUNCTION
#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)) || (defined(__cplusplus) && __cplusplus>=201103L)
# define EPICS_FUNCTION __func__

View File

@@ -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();

View File

@@ -437,6 +437,7 @@ private:
epicsThread ( const epicsThread & );
epicsThread & operator = ( const epicsThread & );
friend void epicsThreadCallEntryPoint ( void * );
EPICS_NORETURN
void printLastChanceExceptionMessage (
const char * pExceptionTypeName,
const char * pExceptionContext );

View File

@@ -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 */

View File

@@ -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, &note);
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, &note);
v = (void *)note;
if(v)
v->isOkToBlock = !!isOkToBlock;
}

View File

@@ -19,27 +19,68 @@
#define EPICS_ATOMIC_OS_NAME "WIN32"
#ifdef VC_EXTRALEAN
# define VC_EXTRALEAN_DETECTED_epicsAtomicOSD_h
#else
# define VC_EXTRALEAN
#endif
/* Disable extra declarations that we don't need here (i.e. winsock1, rpc, etc.) */
#pragma push_macro("WIN32_LEAN_AND_MEAN")
#undef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#ifdef STRICT
# define STRICT_DETECTED_epicsAtomicOSD_h
#else
# define STRICT
#endif
#pragma push_macro("STRICT")
#undef STRICT
#define STRICT
/* Disable min/max macros from windows.h. These macros can cause issues with headers such as <algorithm> that declare or use std::min/max */
#pragma push_macro("NOMINMAX")
#undef NOMINMAX
#define NOMINMAX
/* Disable 'service controller' includes */
#pragma push_macro("NOSERVICE")
#undef NOSERVICE
#define NOSERVICE
/* Disable 'input management engine' includes */
#pragma push_macro("NOIME")
#undef NOIME
#define NOIME
/* Disable 'modem configuration extensions' includes */
#pragma push_macro("NOMCX")
#undef NOMCX
#define NOMCX
/* Disable GDI includes */
#pragma push_macro("NOGDI")
#undef NOGDI
#define NOGDI
/* Disable crypto stuff */
#pragma push_macro("NOCRYPT")
#undef NOCRYPT
#define NOCRYPT
/* Disable sound driver routines */
#pragma push_macro("NOSOUND")
#undef NOSOUND
#define NOSOUND
/* Disable Kanji writing system support */
#pragma push_macro("NOKANJI")
#undef NOKANJI
#define NOKANJI
#include "windows.h"
#ifndef VC_EXTRALEAN_DETECTED_epicsAtomicOSD_h
# undef VC_EXTRALEAN
#endif
#ifndef STRICT_DETECTED_epicsAtomicOSD_h
# undef STRICT
#endif
/* Restore previous macro values */
#pragma pop_macro("WIN32_LEAN_AND_MEAN")
#pragma pop_macro("STRICT")
#pragma pop_macro("NOMINMAX")
#pragma pop_macro("NOSERVICE")
#pragma pop_macro("NOIME")
#pragma pop_macro("NOMCX")
#pragma pop_macro("NOGDI")
#pragma pop_macro("NOCRYPT")
#pragma pop_macro("NOSOUND")
#pragma pop_macro("NOKANJI")
#if defined ( _WIN64 )
# define MS_ATOMIC_64

View File

@@ -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;
}

View File

@@ -665,6 +665,7 @@ static epicsThreadOSD *createImplicit(void)
assert(pthreadInfo);
pthreadInfo->tid = tid;
pthreadInfo->osiPriority = 0;
pthreadInfo->isOkToBlock = 1;
#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0
if(pthread_getschedparam(tid,&pthreadInfo->schedPolicy,&pthreadInfo->schedParam) == 0) {
@@ -1085,3 +1086,17 @@ LIBCOM_API int epicsThreadGetCPUs(void)
#endif
return 1;
}
int epicsStdCall epicsThreadIsOkToBlock(void)
{
epicsThreadOSD *pthreadInfo = epicsThreadGetIdSelf();
return(pthreadInfo->isOkToBlock);
}
void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock)
{
epicsThreadOSD *pthreadInfo = epicsThreadGetIdSelf();
pthreadInfo->isOkToBlock = !!isOkToBlock;
}

View File

@@ -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 */

View File

@@ -127,7 +127,8 @@ static void epicsThreadInit(void)
taskIdListSize = ID_LIST_CHUNK;
atRebootRegister();
ALLOT_JOIN(0);
done = 1;
done = 1; /* avoids recursive call */
epicsThreadSetOkToBlock(1);
}
lock = 0;
}
@@ -577,3 +578,30 @@ LIBCOM_API int epicsThreadGetCPUs(void)
{
return 1;
}
static epicsThreadOnceId okToBlockOnce = EPICS_THREAD_ONCE_INIT;
static epicsThreadPrivateId okToBlockPrivate;
static const int okToBlockNo = 0;
static const int okToBlockYes = 1;
static void epicsThreadOnceIdInit(void *not_used)
{
okToBlockPrivate = epicsThreadPrivateCreate();
}
int epicsStdCall epicsThreadIsOkToBlock(void)
{
const int *pokToBlock;
epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL);
pokToBlock = (int *) epicsThreadPrivateGet(okToBlockPrivate);
return (pokToBlock ? *pokToBlock : 0);
}
void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock)
{
const int *pokToBlock;
epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL);
pokToBlock = (isOkToBlock) ? &okToBlockYes : &okToBlockNo;
epicsThreadPrivateSet(okToBlockPrivate, (void *)pokToBlock);
}

View File

@@ -297,7 +297,7 @@ extern void tokenized_start(char *s) NORETURN;
extern void retyped_warning(char *s);
extern void reprec_warning(char *s);
extern void revalued_warning(char *s);
extern void terminal_start(char *s);
extern void terminal_start(char *s) NORETURN;
extern void restarted_warning(void);
extern void no_grammar(void) NORETURN;
extern void terminal_lhs(int s_lineno) NORETURN;

View File

@@ -295,7 +295,7 @@ TESTSPEC_RTEMS = libComTestHarness.boot; epicsRunLibComTests
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
TESTPROD = $(TESTPROD_HOST)
TESTPROD += $(TESTPROD_HOST)
TESTSCRIPTS += $(filter-out epicsUnitTestTest.t, $(TESTS:%=%.t))
endif

View File

@@ -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">&sect;</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;

View File

@@ -69,6 +69,11 @@ A[href="#POD_ERRORS"] {
color: #FF0000;
}
A.sect {
color: #99ccff;
vertical-align: super;
}
TD {
margin: 0;
padding: 0;