Compare commits

...

53 Commits

Author SHA1 Message Date
Ralph Lange
ba1bf00ce7 Use split() for fetching last tag with darcs
Some checks failed
Check EditorConfig / editorconfig (push) Failing after 2s
Base / Cross linux-aarch64 (push) Failing after 1s
Base / Cross linux-arm gnueabi (push) Failing after 2s
Base / Cross linux-arm gnueabihf (push) Failing after 1s
Base / CentOS-7 (push) Failing after 2s
Base / Fedora-33 (push) Failing after 1s
Base / Fedora-latest (push) Failing after 2s
Base / MacOS clang-12 (push) Has been cancelled
Base / Ub-20 clang-10 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 beatnik (push) Has been cancelled
Base / Ub-20 gcc-9 + MinGW, static (push) Has been cancelled
Base / Ub-22 gcc-12 c++20 Werror (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-20 clang-10 C++11 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-4.10 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-4.9 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 pc686 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 uC5282 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 xilinx_zynq_a9_qemu (push) Has been cancelled
Base / Win2019 mingw (push) Has been cancelled
Base / Ub-20 gcc-9 + MinGW (push) Has been cancelled
Base / Ub-20 gcc-9 unsigned char (push) Has been cancelled
Base / Ub-20 gcc-9 C++11, static (push) Has been cancelled
(setting $/='' breaks reading multi-line into an array)
2023-12-01 16:50:24 +01:00
Érico Nogueira
68fea5c2ca Set ASL0 for mbboDirect Bx fields.
Since the record's VAL field is ASL0, it doesn't make sense to gate
writes into the Bx fields with ASL1.
2023-12-01 16:49:41 +01:00
Michael Davidsaver
3f03efe7fe Add ERROR to error messages 2023-12-01 16:49:30 +01:00
Michael Davidsaver
98dc97022a const-ify dbLink arrays
external code really should never be modifying pamaplinkType[]
2023-12-01 16:49:18 +01:00
Uchenna Ezeobi
5d01b637a7 Config: Fixed Hard coded LDFLAGS in MVME2500 2023-12-01 16:49:03 +01:00
Michael Davidsaver
69a184ee1d update submodules 2023-12-01 16:48:54 +01:00
Michael Davidsaver
aebacff3db release notes 2023-12-01 16:48:45 +01:00
Michael Davidsaver
27ad623d68 bi "Raw Soft Channel" use MASK
If set, apply MASK to value read into RVAL.
2023-12-01 16:48:06 +01:00
Michael Davidsaver
1b23701946 incorrect error check on GetStdHandle()
Likely inconsequential as GetConsoleMode() should
return 0 when given an invalid handle.
2023-12-01 16:47:45 +01:00
Michael Davidsaver
0502fe25e4 print ANSI escapes to stderr
unconditionally print ANSI some escapes (to colorize errors)
to the stderr stream.
2023-12-01 16:47:18 +01:00
Michael Davidsaver
77a37781af colorize more errlog messages 2023-12-01 16:38:54 +01:00
Jeremy Lorelli
ba21e655df Null check callback function in callbackRequest
Previously, calling callbackRequest(pcallback), where pcallback->callback
is NULL, would result in a crash on one of the callback threads.
2023-12-01 16:36:43 +01:00
Michael Davidsaver
2e727a0262 update ci-scripts 2023-12-01 16:36:35 +01:00
Michael Davidsaver
f4cb31d5d5 dbRecordField() add "did you mean..." hint for unknown field 2023-12-01 16:36:24 +01:00
Michael Davidsaver
ab4e687816 const-ify dbCopyEntry() and dbCopyEntryContents() 2023-12-01 16:36:12 +01:00
Michael Davidsaver
1d1926eaf3 avoid hang during concurrent db_cancel_event()
cf. fab8fd7102
2023-12-01 16:36:03 +01:00
Michael Davidsaver
d31ba6ade3 doc 2023-12-01 16:35:55 +01:00
609147dad0 gha: turn most warnings into errors in github build 2023-12-01 16:35:46 +01:00
Michael Davidsaver
5a5535aa5b iocsh: keep history file 2023-12-01 16:35:36 +01:00
JJL772
de61390049 Com: Make STATIC_ASSERT macro typedefs unique 2023-12-01 16:35:29 +01:00
Michael Davidsaver
304d8217d9 dbRecordsOnceOnly allow append only with "*"
with

> record(ai, "myrec") {}

dbRecordsOnceOnly!=0 currently disallows appending fields with either form:

> record("*", "myrec") {} # error
> record(ai, "myrec") {}  # error

Change the meaning such that dbRecordsOnceOnly!=0
allways allows appending when explicitly intended (rtype "*").

> record("*", "myrec") {} # allowed
> record(ai, "myrec") {}  # error

Also clearly label this parse error.
2023-12-01 16:35:18 +01:00
Jack Harper
2c95dc91e9 iocsh: add comment to cvtArg explaining default iocsharg behaviour 2023-12-01 16:34:35 +01:00
Michael Davidsaver
8cc15c6e68 remove unused local 2023-12-01 16:34:24 +01:00
AlexWells
680e1734a4 iocsh: Add underline separator between help outputs
Also tweaks the overall format of the message a bit.
Add tests for new help output format
2023-12-01 16:34:04 +01:00
4aea7de032 update modules/pvData 2023-12-01 16:33:42 +01:00
Michael Davidsaver
bddbc38e0b update ci-scripts 2023-12-01 16:28:58 +01:00
Jeremy Lorelli
67e669c5e5 libCom: Fix buggy pointer dereference in postfix() 2023-12-01 16:02:34 +01:00
Michael Davidsaver
e291ca0a03 Fix compile w/ vs2012 2023-12-01 15:04:24 +01:00
Emilio Perez
5b84a86ccd Allow adding error symbols after early initialization
This was acomplished by making errSymbolAdd add the error symbol directly
into the global hash table and removing errnumlist which is not needed
anymore.

Unit tests were added for checking the following cases:
- Adding a valid symbol and checking that it exists (fixed by this change)
- Getting an existing error symbol
- Getting a non existing error symbol
- Adding an invalid error symbol (fixed by this change)
- Adding an error symbol with a code that already
  exists (fixed by this change)

Therefore, issue #268 was fixed

error: statically allocate error symbol hash table

This will allow calling errSymbolAdd before errSymBld, therefore, a
function adding error symbols can now be run before iocInit or errlogInit

error: add a constant for the minimum module number

Make adding an identical error symbol not fail

A test case was also added which test that adding an error symbol
with same error code and message as one added before will not fail

Add locking to error symbol table

This protects the cases of:
- simultaneously adding and requesting of an error symbol
- simultaneously adding many error symbols

Update release notes regarding adding error symbols
2023-12-01 15:03:00 +01:00
Michael Davidsaver
cb6442da71 epicsThreadShow() zombies
Flag when the thread has returned, but the tracking
struct is still around.  eg. in need of joining.
2023-12-01 15:01:10 +01:00
Érico Nogueira
49b9573f3a libCom: detect support for backtrace() with __has_include.
This is necessary in order to build epics-base with musl libc, for
example, and any other C libraries which don't include this
functionality. In order to not regress builds with older compilers, we
still support the uclibc check. Furthermore, it has been checked that
uclibc-ng (the maintained version of uclibc) doesn't install the
<execinfo.h> header when the functionality is disabled [1] [2].

To avoid repetition, we don't define HAS_EXECINFO to 0 when it is not
available.

[1] https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git/tree/Makefile.in?id=cdb07d2cd52af39feb425e6d36c02b30916b9f0a#n224
[2] https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git/tree/Makefile.in?id=cdb07d2cd52af39feb425e6d36c02b30916b9f0a#n277
2023-12-01 15:01:01 +01:00
Michael Davidsaver
42dfca2b54 dbEvent: handle multiple db_event_cancel()
Allow for multiple db_event_cancel() (concurrent or
self-cancel) prior to event_task wakeup.

In db_event_cancel(), immediate free() only if idle
(not queued or in progress).  Otherwise, defer free()
to event task.  Avoids need to immediately expunge
canceled event from queue.  Allow event task to
process canceled events as normal (except no user_sub)
until npend==0.
2023-12-01 15:00:51 +01:00
Michael Davidsaver
3fd79a21a2 privatize evSubscrip 2023-12-01 15:00:34 +01:00
Michael Davidsaver
12f311b780 dbEvent minor 2023-12-01 15:00:16 +01:00
Michael Davidsaver
8abc9f8d03 minor 2023-12-01 15:00:08 +01:00
Michael Davidsaver
ae6f01b8ee doc dbLock.h 2023-12-01 14:59:56 +01:00
1d3a2defa2 Merge 7.0 commit 'ca9c95' into PSI-7.0 2023-12-01 14:21:07 +01:00
Michael Davidsaver
ca9c957e62 doc add page for initHooks.h 2023-09-05 08:53:40 +02:00
Michael Davidsaver
8488c9e891 initHookName() assert length
basic compile time consistency check.
2023-09-05 08:53:07 +02:00
Michael Davidsaver
1f2edb69d2 silence -Wformat-security for printfRecord
we know what we are doing... right?
2023-09-05 08:53:03 +02:00
Michael Davidsaver
fe3ee85aee doc errSymTbl.h 2023-09-05 08:52:58 +02:00
Michael Davidsaver
a74789d9c0 db: decimate and sync filters don't drop DBE_PROPERTY 2023-08-28 14:17:00 +02:00
Michael Davidsaver
7830345e59 move IocshRegister() to iocshInit()
No need for both global ctor and lazy init
2023-08-28 14:17:00 +02:00
Michael Davidsaver
1595ed8860 quieting clang-tidy, non-functional changes
Changes to syntax which should not effect generated code
2023-08-28 14:17:00 +02:00
Michael Davidsaver
d97943b697 timerPrivate.h: mark override/final
Clarify the class hierarchy by decorating types and
method override and/or final whenever possible.
Does not change the hierarchy.
2023-08-28 14:17:00 +02:00
Michael Davidsaver
f8f4376594 ~fdRegForOldFdmgr() can't throw doubleDelete
Exceptions can't actually be thrown from a
C++ class destructor.
2023-08-28 14:17:00 +02:00
Érico Nogueira
df397f4ade fix typo in boRecord.dbd.pod 2023-08-28 14:17:00 +02:00
Zainab Olalekan
7fd690e53e Fix libcom test failure on RTEMS 2023-08-28 14:17:00 +02:00
Andrew Johnson
bc5d347bb2 Call perror() before close(), add detail to messages 2023-08-28 14:17:00 +02:00
Chris Johns
3ea29f581b rtems: Close NTP socket 2023-08-28 14:17:00 +02:00
AlexWells
6de82bb0fd Additional help messages for iocsh
Henrique Silva <henrique.silva@ess.eu>
2023-08-28 13:24:16 +02:00
e1a51e2839 extra parentheses around second sizeof to silence "does not compute number of elements" warning 2023-08-28 13:24:16 +02:00
Michael Davidsaver
0f59d823d3 appveyor remove skip_commits: 2023-08-28 13:24:16 +02:00
89 changed files with 1074 additions and 528 deletions

2
.ci

Submodule .ci updated: 1e0e326f74...130e88b709

View File

@@ -51,6 +51,25 @@ 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"
# Turn all warnings into errors,
# except for those we could not fix (yet).
# Remove respective -Wno-error=... flag once it is fixed.
extra: "CMD_CXXFLAGS=-std=c++20
CMD_CPPFLAGS='-fdiagnostics-color
-Werror
-Wno-error=deprecated-declarations
-Wno-error=stringop-truncation
-Wno-error=restrict
-Wno-error=sizeof-pointer-memaccess
-Wno-error=nonnull
-Wno-error=dangling-pointer
-Wno-error=format-overflow
-Wno-error=format-security
-Wno-error=stringop-overread'"
- os: ubuntu-20.04
cmp: gcc
configuration: default

1
.gitignore vendored
View File

@@ -17,3 +17,4 @@ O.*/
*.log
.*.swp
.DS_Store
.iocsh_history

View File

@@ -22,7 +22,7 @@ ARCH_DEP_CFLAGS += -DRTEMS_HAS_ALTIVEC
#OP_SYS_LDLIBS += -lbspExt #does not use posix stuff ... want to ignore
OP_SYS_LDLIBS += -Wl,--gc-sections
#ARCH_DEP_LDFLAGS = -mcpu=8540 -meabi -msdata=sysv -mstrict-align -mspe -mabi=spe -mfloat-gprs=double
ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/powerpc-rtems5/qoriq_e500/lib
ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/$(RTEMS_BSP)/lib
MUNCH_SUFFIX = .img
MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX))

View File

@@ -22,6 +22,56 @@ should also be read to understand what has changed since earlier releases:
## Changes made on the 7.0 branch since 7.0.7
### bi "Raw Soft Channel" use MASK
If MASK is non-zero, The raw device support will now apply MASK to the
value read into RVAL.
eg. allows extraction of a bit from an input integer.
```
record(longin, "integer") {
field(VAL, "0xff")
}
record(bi, "bit1") {
field(DESC, "extract bit 1")
field(DTYP, "Raw Soft Channel")
field(INP , "integer")
field(MASK, "0x2")
field(ZNAM, "Clear")
field(ONAM, "Set")
}
```
### ANSI escapes in stderr
ANSI escape charactor sequences may now be printed to the stderr stream.
These escapes will appear in logs captured from that stream.
Tools which parse and/or render these logs may need to be adjusted to
either strip out the escapes, or to translate them into markup.
(see [ansi2html](https://pypi.org/project/ansi2html/) for example)
### Allow explicit append with `dbRecordsOnceOnly!=0`
Previously setting `dbRecordsOnceOnly!=0` prevented any further changes to a record via a .db file. eg.
```
record(ai, "myrec") {}
```
`dbRecordsOnceOnly!=0` previously disallowed appending fields with either form:
```
record("*", "myrec") {} # error
record(ai, "myrec") {} # error
```
Beginning with this release, `dbRecordsOnceOnly!=0` allows appending when explicitly intended (when record type is `"*"`).
```
record("*", "myrec") {} # allowed
record(ai, "myrec") {} # error
```
### Add `$EPICS_CLI_TIMEOUT`
Add support for CA tools timeout from environment variable `$EPICS_CLI_TIMEOUT`
@@ -63,7 +113,7 @@ Previously, if a subRecord has an invalid `INP*` link, it was silently failing
(and not running the proc function). Now the the status code returned by the
subroutine is returned from `dbProcess()`.
### COMMANDLINE_LIBRARY fallback to GNU_DIR
### COMMANDLINE\_LIBRARY fallback to GNU\_DIR
Fall back to the previous behavior when searching for `readline.h` with older compilers.
@@ -118,7 +168,9 @@ This functionality was suggested in
be added to other output record types if the community finds it useful,
please send feedback about the feature to tech-talk.
### Tab completion for IOC shell
### IOC Shell
#### Tab completion
When built with optional GNU libreadline support, the interactive IOC shell
will perform tab completion for command names as well as for some arguments
@@ -130,14 +182,25 @@ using the new `iocshArgStringRecord` and `iocshArgStringPath` argument types.
Both function identically to `iocshArgString` but indicate how to suggest
completion strings.
Builds on macOS (darwin-x86 or darwin-aarch64 targets) normally use Apple's
Builds on macOS (`darwin-x86` or `darwin-aarch64` targets) normally use Apple's
libedit library in readline compatibility mode, which doesn't support the tab
completion API that GNU readline provides. You can use Homebrew or some other
third-party package manager to install the GNU readline package, then edit the
configure/os/CONFIG_SITE.darwinCommon.darwinCommon file to have EPICS use the
`configure/os/CONFIG_SITE.darwinCommon.darwinCommon` file to have EPICS use the
real thing to get tab completion in the IOC shell. The default settings in that
file currently look for and use a Homebrew-installed readline if present.
#### Persist history
Attempt to read and write command to a file (`./.iocsh_history` by default).
Name may be overwritten with by setting `$EPICS_IOCSH_HISTFILE` to an
alternate path, or disabled by setting to an empty string.
#### Changes to help output
Rework the `help` command output to improve formatting and readability,
and include a visual marker (a line of underlines) between different help commands.
### Add FMOD as CALC Expression
The floating point modulo function `FMOD(NUM,DEN)` has been added to the CALC

View File

@@ -549,7 +549,7 @@ void epicsStdCall caRepeaterRegistrationMessage (
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
fprintf ( stderr, "error sending registration message to CA repeater daemon was \"%s\"\n",
fprintf ( stderr, ERL_ERROR " sending registration message to CA repeater daemon was \"%s\"\n",
sockErrBuf );
}
}
@@ -813,13 +813,13 @@ bool udpiiu::exceptionRespAction (
if ( msg.m_postsize > sizeof ( caHdr ) ){
errlogPrintf (
"error condition \"%s\" detected by %s with context \"%s\" at %s\n",
ERL_ERROR " condition \"%s\" detected by %s with context \"%s\" at %s\n",
ca_message ( msg.m_available ),
name, reinterpret_cast <const char *> ( &reqMsg + 1 ), date );
}
else{
errlogPrintf (
"error condition \"%s\" detected by %s at %s\n",
ERL_ERROR " condition \"%s\" detected by %s at %s\n",
ca_message ( msg.m_available ), name, date );
}

View File

@@ -35,6 +35,7 @@
#include <epicsStdlib.h>
#include <cadef.h>
#include <errlog.h>
#include <epicsGetopt.h>
#include <epicsEvent.h>
#include <epicsString.h>
@@ -549,7 +550,7 @@ int main (int argc, char *argv[])
result = ca_array_put (dbrType, count, pvs[0].chid, pbuf);
}
if (result != ECA_NORMAL) {
fprintf(stderr, "Error from put operation: %s\n", ca_message(result));
fprintf(stderr, ERL_ERROR " from put operation: %s\n", ca_message(result));
free(sbuf); free(dbuf); free(ebuf);
return 1;
}
@@ -570,7 +571,7 @@ int main (int argc, char *argv[])
}
if (result != ECA_NORMAL) {
fprintf(stderr, "Error occured writing data: %s\n", ca_message(result));
fprintf(stderr, ERL_ERROR " occured writing data: %s\n", ca_message(result));
free(sbuf); free(dbuf); free(ebuf);
return 1;
}

View File

@@ -21,7 +21,8 @@ static const iocshArg * const asSetFilenameArgs[] = {&asSetFilenameArg0};
static const iocshFuncDef asSetFilenameFuncDef =
{"asSetFilename",1,asSetFilenameArgs,
"Set path+file name of ACF file.\n"
"No immediate effect. Run as asInit() to (re)load.\n"};
"No immediate effect. Run asInit to (re)load.\n"
"Example: asSetFilename /full/path/to/accessSecurityFile\n"};
static void asSetFilenameCallFunc(const iocshArgBuf *args)
{
asSetFilename(args[0].sval);
@@ -33,7 +34,8 @@ static const iocshArg * const asSetSubstitutionsArgs[] = {&asSetSubstitutionsArg
static const iocshFuncDef asSetSubstitutionsFuncDef =
{"asSetSubstitutions",1,asSetSubstitutionsArgs,
"Set subtitutions used when reading ACF file.\n"
"No immediate effect. Run as asInit() to (re)load.\n"};
"No immediate effect. Run asInit to (re)load.\n"
"Example: asSetSubstitutions var1=5,var2=hello\n"};
static void asSetSubstitutionsCallFunc(const iocshArgBuf *args)
{
asSetSubstitutions(args[0].sval);
@@ -59,7 +61,10 @@ static void asdbdumpCallFunc(const iocshArgBuf *args)
static const iocshArg aspuagArg0 = { "uagname",iocshArgString};
static const iocshArg * const aspuagArgs[] = {&aspuagArg0};
static const iocshFuncDef aspuagFuncDef = {"aspuag",1,aspuagArgs,
"Show members of User Access Group.\n"};
"Show members of the User Access Group.\n"
"If no Group is specified then the members\n"
"of all user access groups are displayed.\n"
"Example: aspuag mygroup\n"};
static void aspuagCallFunc(const iocshArgBuf *args)
{
aspuag(args[0].sval);
@@ -69,7 +74,10 @@ static void aspuagCallFunc(const iocshArgBuf *args)
static const iocshArg asphagArg0 = { "hagname",iocshArgString};
static const iocshArg * const asphagArgs[] = {&asphagArg0};
static const iocshFuncDef asphagFuncDef = {"asphag",1,asphagArgs,
"Show members of Host Access Group.\n"};
"Show members of the Host Access Group.\n"
"If no Group is specified then the members\n"
"of all host access groups are displayed\n"
"Example: asphag mygroup\n"};
static void asphagCallFunc(const iocshArgBuf *args)
{
asphag(args[0].sval);
@@ -78,8 +86,12 @@ static void asphagCallFunc(const iocshArgBuf *args)
/* asprules */
static const iocshArg asprulesArg0 = { "asgname",iocshArgString};
static const iocshArg * const asprulesArgs[] = {&asprulesArg0};
static const iocshFuncDef asprulesFuncDef = {"asprules",1,asprulesArgs,
"List rules of an Access Security Group.\n"};
static const iocshFuncDef asprulesFuncDef = {
"asprules",1,asprulesArgs,
"List rules of an Access Security Group.\n"
"If no Group is specified then list the rules for all groups\n"
"Example: asprules mygroup"
};
static void asprulesCallFunc(const iocshArgBuf *args)
{
asprules(args[0].sval);
@@ -89,8 +101,14 @@ static void asprulesCallFunc(const iocshArgBuf *args)
static const iocshArg aspmemArg0 = { "asgname",iocshArgString};
static const iocshArg aspmemArg1 = { "clients",iocshArgInt};
static const iocshArg * const aspmemArgs[] = {&aspmemArg0,&aspmemArg1};
static const iocshFuncDef aspmemFuncDef = {"aspmem",2,aspmemArgs,
"List members of Access Security Group.\n"};
static const iocshFuncDef aspmemFuncDef = {
"aspmem",2,aspmemArgs,
"List members of Access Security Group.\n"
"If no Group is specified then print the members for all Groups.\n"
"If clients is (0, 1) then Channel Access clients attached to each member\n"
"(are not, are) shown\n"
"Example: aspmem mygroup 1\n",
};
static void aspmemCallFunc(const iocshArgBuf *args)
{
aspmem(args[0].sval,args[1].ival);
@@ -101,8 +119,10 @@ static const iocshArg astacArg0 = { "recordname",iocshArgStringRecord};
static const iocshArg astacArg1 = { "user",iocshArgString};
static const iocshArg astacArg2 = { "host",iocshArgString};
static const iocshArg * const astacArgs[] = {&astacArg0,&astacArg1,&astacArg2};
static const iocshFuncDef astacFuncDef = {"astac",3,astacArgs,
"Test Access Security privlages granted to user+host.\n"};
static const iocshFuncDef astacFuncDef = {
"astac",3,astacArgs,
"Show what read/write permissions the user:host would have when\n"
"accessing a certain PV.\n"};
static void astacCallFunc(const iocshArgBuf *args)
{
astac(args[0].sval,args[1].sval,args[2].sval);
@@ -111,8 +131,14 @@ static void astacCallFunc(const iocshArgBuf *args)
/* ascar */
static const iocshArg ascarArg0 = { "level",iocshArgInt};
static const iocshArg * const ascarArgs[] = {&ascarArg0};
static const iocshFuncDef ascarFuncDef = {"ascar",1,ascarArgs,
"Report status of PVs used in INP*() Access Security rules.\n"};
static const iocshFuncDef ascarFuncDef = {
"ascar",1,ascarArgs,
"Report status of PVs used in INP*() Access Security rules.\n"
"Level 0 - Summary report\n"
" 1 - Summary report plus details on unconnected channels\n"
" 2 - Summary report plus detail report on each channel\n"
"Example: ascar 1\n"
};
static void ascarCallFunc(const iocshArgBuf *args)
{
ascar(args[0].ival);

View File

@@ -21,6 +21,7 @@
#include <ctype.h>
#include "dbDefs.h"
#include "errlog.h"
#include "ellLib.h"
#include "cvtTable.h"
@@ -125,12 +126,12 @@ int main(int argc, char **argv)
}
inFile = fopen(argv[1],"r");
if(!inFile) {
fprintf(stderr,"Error opening %s\n",argv[1]);
fprintf(stderr,ERL_ERROR " opening %s\n",argv[1]);
exit(-1);
}
outFile = fopen(outFilename,"w");
if(!outFile) {
fprintf(stderr,"Error opening %s\n",outFilename);
fprintf(stderr,ERL_ERROR " opening %s\n",outFilename);
exit(-1);
}
while(fgets(inbuf,MAX_LINE_SIZE,inFile)) {

View File

@@ -344,6 +344,10 @@ int callbackRequest(epicsCallback *pcallback)
epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " pcallback was NULL\n");
return S_db_notInit;
}
if (!pcallback->callback) {
epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " pcallback->callback was NULL\n");
return S_db_notInit;
}
priority = pcallback->priority;
if (priority < 0 || priority >= NUM_CALLBACK_PRIORITIES) {
epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " Bad priority\n");

View File

@@ -798,18 +798,13 @@ int dbLoadRecords(const char* file, const char* subs)
return -1;
}
status = dbReadDatabase(&pdbbase, file, 0, subs);
switch(status)
{
case 0:
if(status==0) {
if(dbLoadRecordsHook)
dbLoadRecordsHook(file, subs);
break;
case -2:
errlogPrintf("dbLoadRecords: failed to load '%s'\n"
" Records cannot be loaded after iocInit!\n", file);
break;
default:
errlogPrintf("dbLoadRecords: failed to load '%s'\n", file);
} else {
fprintf(stderr, ERL_ERROR " failed to load '%s'\n", file);
if(status==-2)
fprintf(stderr, " Records cannot be loaded after iocInit!\n");
}
return status;
}

View File

@@ -41,20 +41,35 @@ extern "C" {
/**
* event subscription
*/
typedef struct evSubscrip {
struct evSubscrip;
typedef struct evSubscrip evSubscrip;
#ifdef EPICS_PRIVATE_API
struct evSubscrip {
ELLNODE node;
struct dbChannel * chan;
/* user_sub==NULL used to indicate db_cancel_event() */
EVENTFUNC * user_sub;
void * user_arg;
/* associated queue, may be shared with other evSubscrip */
struct event_que * ev_que;
/* NULL if !npend. if npend!=0, pointer to last event added to event_que::valque */
db_field_log ** pLastLog;
unsigned long npend; /**< n times this event is on the queue */
unsigned long nreplace; /**< n times replacing event on the queue */
/* n times this event is on the queue */
unsigned long npend;
/* n times replacing event on the queue */
unsigned long nreplace;
/* DBE mask */
unsigned char select;
/* if set, subscription will yield dbfl_type_val */
char useValque;
/* event_task is handling this subscription */
char callBackInProgress;
/* this node added to dbCommon::mlis */
char enabled;
} evSubscrip;
};
#endif
typedef struct chFilter chFilter;

View File

@@ -18,6 +18,7 @@
* Ralph Lange <Ralph.Lange@bessy.de>
*/
#define EPICS_PRIVATE_API
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
@@ -75,23 +76,23 @@ struct event_que {
unsigned short getix;
unsigned short quota; /* the number of assigned entries*/
unsigned short nDuplicates; /* N events duplicated on this q */
unsigned short nCanceled; /* the number of canceled entries */
unsigned possibleStall;
};
struct event_user {
struct event_que firstque; /* the first event que */
ELLLIST waiters; /* event_waiter::node */
epicsMutexId lock;
epicsEventId ppendsem; /* Wait while empty */
epicsEventId pflush_sem; /* wait for flush */
epicsEventId pexitsem; /* wait for event task to join */
EXTRALABORFUNC *extralabor_sub;/* off load to event task */
void *extralabor_arg;/* parameter to above */
epicsThreadId taskid; /* event handler task id */
struct evSubscrip *pSuicideEvent; /* event that is deleting itself */
epicsUInt32 pflush_seq; /* worker cycle count for synchronization */
unsigned queovr; /* event que overflow count */
unsigned char pendexit; /* exit pend task */
unsigned char extra_labor; /* if set call extra labor func */
@@ -101,6 +102,11 @@ struct event_user {
epicsThreadId init_func_arg;
};
typedef struct {
ELLNODE node; /* event_user::waiters */
epicsEventId wake;
} event_waiter;
/*
* Reliable intertask communication requires copying the current value of the
* channel for later queuing so 3 stepper motor steps of 10 each do not turn
@@ -122,10 +128,9 @@ static void *dbevFieldLogFreeList;
static char *EVENT_PEND_NAME = "eventTask";
static struct evSubscrip canceledEvent;
static epicsMutexId stopSync;
/* unused space in queue (EVENTQUESIZE when empty) */
static unsigned short ringSpace ( const struct event_que *pevq )
{
if ( pevq->evque[pevq->putix] == EVENTQEMPTY ) {
@@ -139,17 +144,11 @@ static unsigned short ringSpace ( const struct event_que *pevq )
return 0;
}
/*
* db_event_list ()
*/
int db_event_list ( const char *pname, unsigned level )
{
return dbel ( pname, level );
}
/*
* dbel ()
*/
int dbel ( const char *pname, unsigned level )
{
DBADDR addr;
@@ -217,7 +216,6 @@ int dbel ( const char *pname, unsigned level )
if ( level > 2 ) {
unsigned nDuplicates;
unsigned nCanceled;
if ( pevent->nreplace ) {
printf (", discarded by replacement=%ld", pevent->nreplace);
}
@@ -226,14 +224,10 @@ int dbel ( const char *pname, unsigned level )
}
LOCKEVQUE(pevent->ev_que);
nDuplicates = pevent->ev_que->nDuplicates;
nCanceled = pevent->ev_que->nCanceled;
UNLOCKEVQUE(pevent->ev_que);
if ( nDuplicates ) {
printf (", duplicate count =%u\n", nDuplicates );
}
if ( nCanceled ) {
printf (", canceled count =%u\n", nCanceled );
}
}
if ( level > 3 ) {
@@ -318,9 +312,6 @@ dbEventCtx db_init_events (void)
evUser->ppendsem = epicsEventCreate(epicsEventEmpty);
if (!evUser->ppendsem)
goto fail;
evUser->pflush_sem = epicsEventCreate(epicsEventEmpty);
if (!evUser->pflush_sem)
goto fail;
evUser->lock = epicsMutexCreate();
if (!evUser->lock)
goto fail;
@@ -330,7 +321,6 @@ dbEventCtx db_init_events (void)
evUser->flowCtrlMode = FALSE;
evUser->extraLaborBusy = FALSE;
evUser->pSuicideEvent = NULL;
return (dbEventCtx) evUser;
fail:
if(evUser->lock)
@@ -339,8 +329,6 @@ fail:
epicsMutexDestroy (evUser->firstque.writelock);
if(evUser->ppendsem)
epicsEventDestroy (evUser->ppendsem);
if(evUser->pflush_sem)
epicsEventDestroy (evUser->pflush_sem);
if(evUser->pexitsem)
epicsEventDestroy (evUser->pexitsem);
freeListFree(dbevEventUserFreeList,evUser);
@@ -404,7 +392,6 @@ void db_close_events (dbEventCtx ctx)
epicsEventDestroy(evUser->pexitsem);
epicsEventDestroy(evUser->ppendsem);
epicsEventDestroy(evUser->pflush_sem);
epicsMutexDestroy(evUser->lock);
epicsMutexUnlock (stopSync);
@@ -461,8 +448,7 @@ dbEventSubscription db_add_event (
while ( TRUE ) {
int success = 0;
LOCKEVQUE ( ev_que );
success = ( ev_que->quota + ev_que->nCanceled <
EVENTQUESIZE - EVENTENTRIES );
success = ( ev_que->quota < EVENTQUESIZE - EVENTENTRIES );
if ( success ) {
ev_que->quota += EVENTENTRIES;
}
@@ -579,62 +565,62 @@ static void event_remove ( struct event_que *ev_que,
void db_cancel_event (dbEventSubscription event)
{
struct evSubscrip * const pevent = (struct evSubscrip *) event;
unsigned short getix;
struct event_que *que = pevent->ev_que;
char sync = 0;
db_event_disable ( event );
/*
* flag the event as canceled by NULLing out the callback handler
*
* make certain that the event isn't being accessed while
* its call back changes
*/
LOCKEVQUE (pevent->ev_que);
LOCKEVQUE (que);
pevent->user_sub = NULL;
pevent->user_sub = NULL; /* callback pointer doubles as canceled flag */
/*
* purge this event from the queue
*
* Its better to take this approach rather than waiting
* for the event thread to finish removing this event
* from the queue because the event thread will not
* process if we are in flow control mode. Since blocking
* here will block CA's TCP input queue then a dead lock
* would be possible.
*/
for ( getix = pevent->ev_que->getix;
pevent->ev_que->evque[getix] != EVENTQEMPTY; ) {
if ( pevent->ev_que->evque[getix] == pevent ) {
assert ( pevent->ev_que->nCanceled < USHRT_MAX );
pevent->ev_que->nCanceled++;
event_remove ( pevent->ev_que, getix, &canceledEvent );
}
getix = RNGINC ( getix );
if ( getix == pevent->ev_que->getix ) {
break;
}
}
assert ( pevent->npend == 0u );
if(pevent->callBackInProgress) {
/* this event callback is pending or in-progress in event_task. */
if(pevent->ev_que->evUser->taskid != epicsThreadGetIdSelf())
sync = 1; /* concurrent to event_task, so wait */
if ( pevent->ev_que->evUser->taskid == epicsThreadGetIdSelf() ) {
pevent->ev_que->evUser->pSuicideEvent = pevent;
}
else {
while ( pevent->callBackInProgress ) {
UNLOCKEVQUE (pevent->ev_que);
epicsEventMustWait ( pevent->ev_que->evUser->pflush_sem );
LOCKEVQUE (pevent->ev_que);
}
} else if(pevent->npend) {
/* some (now defunct) events in the queue, defer free() to event_task */
} else {
/* no other references, cleanup now */
pevent->ev_que->quota -= EVENTENTRIES;
freeListFree ( dbevEventSubscriptionFreeList, pevent );
}
pevent->ev_que->quota -= EVENTENTRIES;
UNLOCKEVQUE (que);
UNLOCKEVQUE (pevent->ev_que);
if(sync) {
/* cycle through worker */
struct event_user *evUser = que->evUser;
epicsUInt32 curSeq;
event_waiter wait;
wait.wake = epicsEventCreate(epicsEventEmpty); /* may fail */
freeListFree ( dbevEventSubscriptionFreeList, pevent );
epicsMutexMustLock ( evUser->lock );
ellAdd(&evUser->waiters, &wait.node);
/* grab current cycle counter, then wait for it to change */
curSeq = evUser->pflush_seq;
do {
epicsMutexUnlock( evUser->lock );
/* ensure worker will cycle at least once */
epicsEventMustTrigger(evUser->ppendsem);
return;
if(wait.wake) {
epicsEventMustWait(wait.wake);
} else {
epicsThreadSleep(0.01); /* ick. but better than cantProceed() */
}
epicsMutexMustLock ( evUser->lock );
} while(curSeq == evUser->pflush_seq);
ellDelete(&evUser->waiters, &wait.node);
/* destroy under lock to ensure epicsEventMustTrigger() has returned */
if(wait.wake)
epicsEventDestroy(wait.wake);
epicsMutexUnlock( evUser->lock );
}
}
/*
@@ -934,10 +920,7 @@ void db_post_single_event (dbEventSubscription event)
*/
static int event_read ( struct event_que *ev_que )
{
db_field_log *pfl;
int notifiedRemaining = 0;
void ( *user_sub ) ( void *user_arg, struct dbChannel *chan,
int eventsRemaining, db_field_log *pfl );
/*
* evUser ring buffer must be locked for the multiple
@@ -958,19 +941,7 @@ static int event_read ( struct event_que *ev_que )
while ( ev_que->evque[ev_que->getix] != EVENTQEMPTY ) {
struct evSubscrip *pevent = ev_que->evque[ev_que->getix];
int eventsRemaining;
pfl = ev_que->valque[ev_que->getix];
if ( pevent == &canceledEvent ) {
ev_que->evque[ev_que->getix] = EVENTQEMPTY;
if (ev_que->valque[ev_que->getix]) {
db_delete_field_log(ev_que->valque[ev_que->getix]);
ev_que->valque[ev_que->getix] = NULL;
}
ev_que->getix = RNGINC ( ev_que->getix );
assert ( ev_que->nCanceled > 0 );
ev_que->nCanceled--;
continue;
}
db_field_log *pfl = ev_que->valque[ev_que->getix];
/*
* Simple type values queued up for reliable interprocess
@@ -980,13 +951,7 @@ static int event_read ( struct event_que *ev_que )
event_remove ( ev_que, ev_que->getix, EVENTQEMPTY );
ev_que->getix = RNGINC ( ev_que->getix );
eventsRemaining = ev_que->evque[ev_que->getix] != EVENTQEMPTY && !ev_que->nCanceled;
/*
* create a local copy of the call back parameters while
* we still have the lock
*/
user_sub = pevent->user_sub;
eventsRemaining = ev_que->evque[ev_que->getix] != EVENTQEMPTY;
/*
* Next event pointer can be used by event tasks to determine
@@ -998,14 +963,12 @@ static int event_read ( struct event_que *ev_que )
* record lock, and it is calling db_post_events() waiting
* for the event queue lock (which this thread now has).
*/
if ( user_sub ) {
/*
* This provides a way to test to see if an event is in use
* despite the fact that the event queue does not point to
* it.
*/
if ( pevent->user_sub ) {
EVENTFUNC* user_sub = pevent->user_sub;
pevent->callBackInProgress = TRUE;
UNLOCKEVQUE (ev_que);
/* Run post-event-queue filter chain */
if (ellCount(&pevent->chan->post_chain)) {
pfl = dbChannelRunPostChain(pevent->chan, pfl);
@@ -1016,27 +979,15 @@ static int event_read ( struct event_que *ev_que )
eventsRemaining, pfl );
notifiedRemaining = eventsRemaining;
}
LOCKEVQUE (ev_que);
/*
* check to see if this event has been canceled each
* time that the callBackInProgress flag is set to false
* while we have the event queue lock, and post the flush
* complete sem if there are no longer any events on the
* queue
*/
if ( ev_que->evUser->pSuicideEvent == pevent ) {
ev_que->evUser->pSuicideEvent = NULL;
}
else {
if ( pevent->user_sub==NULL && pevent->npend==0u ) {
pevent->callBackInProgress = FALSE;
epicsEventSignal ( ev_que->evUser->pflush_sem );
}
else {
pevent->callBackInProgress = FALSE;
}
}
pevent->callBackInProgress = FALSE;
}
/* callback may have called db_cancel_event(), so must check user_sub again */
if(!pevent->user_sub && !pevent->npend) {
pevent->ev_que->quota -= EVENTENTRIES;
freeListFree ( dbevEventSubscriptionFreeList, pevent );
}
db_delete_field_log(pfl);
}
@@ -1051,9 +1002,6 @@ static int event_read ( struct event_que *ev_que )
return DB_EVENT_OK;
}
/*
* EVENT_TASK()
*/
static void event_task (void *pParm)
{
struct event_user * const evUser = (struct event_user *) pParm;
@@ -1094,13 +1042,25 @@ static void event_task (void *pParm)
}
evUser->extraLaborBusy = FALSE;
for ( ev_que = &evUser->firstque; ev_que;
ev_que = ev_que->nextque ) {
for ( ev_que = &evUser->firstque; ev_que; ev_que = ev_que->nextque ) {
/* unlock during iteration is safe as event_que will not be free'd */
epicsMutexUnlock ( evUser->lock );
event_read (ev_que);
epicsMutexMustLock ( evUser->lock );
}
pendexit = evUser->pendexit;
evUser->pflush_seq++;
if(ellCount(&evUser->waiters)) {
/* hold lock throughout to avoid race between event trigger and destroy */
ELLNODE *cur;
for(cur = ellFirst(&evUser->waiters); cur; cur = ellNext(cur)) {
event_waiter *w = CONTAINER(cur, event_waiter, node);
if(w->wake)
epicsEventMustTrigger(w->wake);
}
}
epicsMutexUnlock ( evUser->lock );
} while( ! pendexit );

View File

@@ -72,7 +72,9 @@ static void dbLoadRecordsCallFunc(const iocshArgBuf *args)
static const iocshArg dbbArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbbArgs[1] = {&dbbArg0};
static const iocshFuncDef dbbFuncDef = {"dbb",1,dbbArgs,
"Add breakpoint to a lock set.\n"};
"Set Breakpoint on a record\n"
"This command spawns one breakpoint continuation task per lockset,"
" in which further record execution is run\n"};
static void dbbCallFunc(const iocshArgBuf *args) { dbb(args[0].sval);}
/* dbd */
@@ -86,27 +88,36 @@ static void dbdCallFunc(const iocshArgBuf *args) { dbd(args[0].sval);}
static const iocshArg dbcArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbcArgs[1] = {&dbcArg0};
static const iocshFuncDef dbcFuncDef = {"dbc",1,dbcArgs,
"Continue processing in a lock set.\n"};
"Continue processing in a lockset until next breakpoint is found.\n"};
static void dbcCallFunc(const iocshArgBuf *args) { dbc(args[0].sval);}
/* dbs */
static const iocshArg dbsArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbsArgs[1] = {&dbsArg0};
static const iocshFuncDef dbsFuncDef = {"dbs",1,dbsArgs,
"Step through record processing.\n"};
"Step through record processing within a lockset.\n"
"If called without an argument, automatically steps with the last breakpoint.\n"};
static void dbsCallFunc(const iocshArgBuf *args) { dbs(args[0].sval);}
/* dbstat */
static const iocshFuncDef dbstatFuncDef = {"dbstat",0,0,
"print list of stopped records, and breakpoints set in locksets.\n"};
"Print list of suspended records, and breakpoints set in locksets.\n"};
static void dbstatCallFunc(const iocshArgBuf *args) { dbstat();}
/* dbp */
static const iocshArg dbpArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbpArg1 = { "interest level",iocshArgInt};
static const iocshArg * const dbpArgs[2] = {&dbpArg0,&dbpArg1};
static const iocshFuncDef dbpFuncDef = {"dbp",2,dbpArgs,
"print stopped record.\n"};
static const iocshFuncDef dbpFuncDef = {
"dbp",2,dbpArgs,
"Print Fields of a currently suspended record by a breakpoint.\n"
"interest level 0 - Fields of interest to an Application developer and\n"
" that can be changed as a result of record processing.\n"
" 1 - Fields of interest to an Application developer and\n"
" that do not change during record processing.\n"
" 2 - Fields of major interest to a System developer.\n"
" 3 - Fields of minor interest to a System developer.\n"
" 4 - Internal record fields.\n"};
static void dbpCallFunc(const iocshArgBuf *args)
{ dbp(args[0].sval,args[1].ival);}
@@ -114,14 +125,17 @@ static void dbpCallFunc(const iocshArgBuf *args)
static const iocshArg dbapArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbapArgs[1] = {&dbapArg0};
static const iocshFuncDef dbapFuncDef = {"dbap",1,dbapArgs,
"toggle printing after processing a certain record.\n"};
"Auto Print.\n"
"Toggle automatic printing after processing a record that has a breakpoint.\n"};
static void dbapCallFunc(const iocshArgBuf *args) { dbap(args[0].sval);}
/* dbsr */
static const iocshArg dbsrArg0 = { "interest level",iocshArgInt};
static const iocshArg * const dbsrArgs[1] = {&dbsrArg0};
static const iocshFuncDef dbsrFuncDef = {"dbsr",1,dbsrArgs,
"Database Server Report.\n"};
"Database Server Report.\n"
"Print current status of server and number of connected clients.\n"
"Level 0 prints summary information. Higher levels print more.\n"};
static void dbsrCallFunc(const iocshArgBuf *args) { dbsr(args[0].ival);}
/* dbcar */
@@ -131,9 +145,9 @@ static const iocshArg * const dbcarArgs[2] = {&dbcarArg0,&dbcarArg1};
static const iocshFuncDef dbcarFuncDef = {"dbcar",2,dbcarArgs,
"Database Channel Access Report.\n"
"Shows status of Channel Access links (CA_LINK).\n"
"interest level 0 - Shows statistics for all links.\n"
" 1 - Shows info. of only disconnected links.\n"
" 2 - Shows info. for all links.\n"};
" level 0 - Shows statistics for all links.\n"
" 1 - Shows info. of only disconnected links.\n"
" 2 - Shows info. for all links.\n"};
static void dbcarCallFunc(const iocshArgBuf *args)
{
dbcar(args[0].sval,args[1].ival);
@@ -144,7 +158,8 @@ static const iocshArg dbjlrArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbjlrArg1 = { "level",iocshArgInt};
static const iocshArg * const dbjlrArgs[2] = {&dbjlrArg0,&dbjlrArg1};
static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs,
"Database JSON link Report.\n"};
"Database JSON link Report.\n"
"List all JSON links in a record. If no record is specified, print for all\n"};
static void dbjlrCallFunc(const iocshArgBuf *args)
{
dbjlr(args[0].sval,args[1].ival);
@@ -156,7 +171,9 @@ static const iocshArg dbelArg1 = { "level",iocshArgInt};
static const iocshArg * const dbelArgs[2] = {&dbelArg0,&dbelArg1};
static const iocshFuncDef dbelFuncDef = {"dbel",2,dbelArgs,
"Database event list.\n"
"Show information on dbEvent subscriptions.\n"};
"Show information on dbEvent subscriptions.\n"
"Higher level shows more information (0 - 4)\n"
"Example: dbel aitest 2\n"};
static void dbelCallFunc(const iocshArgBuf *args)
{
dbel(args[0].sval, args[1].ival);
@@ -166,7 +183,10 @@ static void dbelCallFunc(const iocshArgBuf *args)
static const iocshArg dbaArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbaArgs[1] = {&dbaArg0};
static const iocshFuncDef dbaFuncDef = {"dba",1,dbaArgs,
"dbAddr info.\n"};
"Database Address.\n"
"Print information in the dbAddr structure for a specific field.\n"
"If no field is specified, VAL is assumed.\n\n"
"Example: dba(\"aitest.HIGH\")\n"};
static void dbaCallFunc(const iocshArgBuf *args) { dba(args[0].sval);}
/* dbl */
@@ -176,7 +196,12 @@ static const iocshArg * const dblArgs[] = {&dblArg0,&dblArg1};
static const iocshFuncDef dblFuncDef = {"dbl",2,dblArgs,
"Database list.\n"
"List record/field names.\n"
"With no arguments, lists all record names.\n"};
"With no arguments, lists all record names.\n"
"If record type is given, then only the names of records maching the type are printed\n"
"If a field list is given, then their values are also printed\n\n"
"Example: dbl(\"\")\n"
" dbl(\"ai\")\n"
" dbl(\"ai\",\"HIGH LOW VAL PREC\")\n"};
static void dblCallFunc(const iocshArgBuf *args)
{
dbl(args[0].sval,args[1].sval);
@@ -186,28 +211,35 @@ static void dblCallFunc(const iocshArgBuf *args)
static const iocshArg dbnrArg0 = { "verbose",iocshArgInt};
static const iocshArg * const dbnrArgs[1] = {&dbnrArg0};
static const iocshFuncDef dbnrFuncDef = {"dbnr",1,dbnrArgs,
"List stats on record alias()s.\n"};
"List number of records and aliases by type.\n"
"If verbose, list all record types regardless of being instanced\n"};
static void dbnrCallFunc(const iocshArgBuf *args) { dbnr(args[0].ival);}
/* dbli */
static const iocshArg dbliArg0 = { "pattern",iocshArgString};
static const iocshArg * const dbliArgs[1] = {&dbliArg0};
static const iocshFuncDef dbliFuncDef = {"dbli",1,dbliArgs,
"List info() tags with names matching pattern.\n"};
"List info() tags with names matching pattern.\n\n"
"Example: dbli(\"autosave*\")\n"};
static void dbliCallFunc(const iocshArgBuf *args) { dbli(args[0].sval);}
/* dbla */
static const iocshArg dblaArg0 = { "pattern",iocshArgStringRecord};
static const iocshArg * const dblaArgs[1] = {&dblaArg0};
static const iocshFuncDef dblaFuncDef = {"dbla",1,dblaArgs,
"List record alias()s by alias name pattern.\n"};
"List record alias()s by alias name pattern.\n\n"
"Example: dbla(\"alia*\")\n"};
static void dblaCallFunc(const iocshArgBuf *args) { dbla(args[0].sval);}
/* dbgrep */
static const iocshArg dbgrepArg0 = { "pattern",iocshArgStringRecord};
static const iocshArg * const dbgrepArgs[1] = {&dbgrepArg0};
static const iocshFuncDef dbgrepFuncDef = {"dbgrep",1,dbgrepArgs,
"List record names matching pattern.\n"};
"List record names matching pattern.\n"
"The pattern can contain any characters that are legal in record names as well as:\n"
" - \"?\", which matches 0 or one characters.\n"
" - \"*\", which matches 0 or more characters.\n\n"
"Example: dbgrep(\"*gpibAi*\")\n"};
static void dbgrepCallFunc(const iocshArgBuf *args) { dbgrep(args[0].sval);}
/* dbgf */
@@ -215,7 +247,9 @@ static const iocshArg dbgfArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbgfArgs[1] = {&dbgfArg0};
static const iocshFuncDef dbgfFuncDef = {"dbgf",1,dbgfArgs,
"Database Get Field.\n"
"Print current value of record field.\n"};
"Print current value of record field.\n"
"If no field name is specified, VAL is assumed.\n\n"
"Example: dbgf(\"aitest.VAL\")\n"};
static void dbgfCallFunc(const iocshArgBuf *args) { dbgf(args[0].sval);}
/* dbpf */
@@ -224,7 +258,8 @@ static const iocshArg dbpfArg1 = { "value",iocshArgString};
static const iocshArg * const dbpfArgs[2] = {&dbpfArg0,&dbpfArg1};
static const iocshFuncDef dbpfFuncDef = {"dbpf",2,dbpfArgs,
"Database Put Field.\n"
"Change value of record field.\n"};
"Change value of record field and read it back with dbgf.\n"
"If no field is specified, VAL is assumed\n"};
static void dbpfCallFunc(const iocshArgBuf *args)
{ dbpf(args[0].sval,args[1].sval);}
@@ -232,9 +267,17 @@ static void dbpfCallFunc(const iocshArgBuf *args)
static const iocshArg dbprArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbprArg1 = { "interest level",iocshArgInt};
static const iocshArg * const dbprArgs[2] = {&dbprArg0,&dbprArg1};
static const iocshFuncDef dbprFuncDef = {"dbpr",2,dbprArgs,
"Database Print Record.\n"
"Print values of record fields.\n"};
static const iocshFuncDef dbprFuncDef = {
"dbpr",2,dbprArgs,
"Database Print Record.\n"
"Print values of record fields for given interest level.\n"
"interest level 0 - Fields that can be changed as a result of record processing.\n"
" 1 - Fields that do not change during record processing.\n"
" 2 - Fields of major interest to a System developer.\n"
" 3 - Fields of minor interest to a System developer.\n"
" 4 - Internal record fields.\n\n"
"Example: dbpr aitest 3\n"
};
static void dbprCallFunc(const iocshArgBuf *args)
{ dbpr(args[0].sval,args[1].ival);}
@@ -250,7 +293,9 @@ static const iocshArg dbtgfArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbtgfArgs[1] = {&dbtgfArg0};
static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs,
"Database Test Get Field.\n"
"Get field with different DBR_* types\n"};
"Get and print the specified field with all possible DBR_* types\n"
"Example: dbtgf aitest\n"
"Example: dbtgf aitest.VAL\n"};
static void dbtgfCallFunc(const iocshArgBuf *args) { dbtgf(args[0].sval);}
/* dbtpf */
@@ -258,7 +303,10 @@ static const iocshArg dbtpfArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbtpfArg1 = { "value",iocshArgString};
static const iocshArg * const dbtpfArgs[2] = {&dbtpfArg0,&dbtpfArg1};
static const iocshFuncDef dbtpfFuncDef = {"dbtpf",2,dbtpfArgs,
"Database Test Put Field.\n"};
"Database Test Put Field.\n"
"Put the given value to the given PV, then get the value\n"
"for all possible DBR_* types\n\n"
"Example: dbtpf aitest 5.0\n"};
static void dbtpfCallFunc(const iocshArgBuf *args)
{ dbtpf(args[0].sval,args[1].sval);}
@@ -273,14 +321,21 @@ static void dbiorCallFunc(const iocshArgBuf *args)
/* dbhcr */
static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0,
"Database Report Device Config.\n"};
"Database Hardware Configuration Report.\n"
"Produce a report of all hardware links.\n"
"The produced report will probably not be in the sort order desired.\n"
"Use the UNIX sort command:\n"
"dbhcr > report\n"
"sort report > report.sorted\n"};
static void dbhcrCallFunc(const iocshArgBuf *args) { dbhcr();}
/* gft */
static const iocshArg gftArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const gftArgs[1] = {&gftArg0};
static const iocshFuncDef gftFuncDef = {"gft",1,gftArgs,
"Report dbChannel info and value.\n"};
"Report dbChannel info and value.\n"
"Example: gft aitest\n"
"Example: gft aitest.VAL\n"};
static void gftCallFunc(const iocshArgBuf *args) { gft(args[0].sval);}
/* pft */
@@ -288,7 +343,8 @@ static const iocshArg pftArg0 = { "record name",iocshArgStringRecord};
static const iocshArg pftArg1 = { "value",iocshArgString};
static const iocshArg * const pftArgs[2] = {&pftArg0,&pftArg1};
static const iocshFuncDef pftFuncDef = {"pft",2,pftArgs,
"dbChannel put value.\n"};
"dbChannel put value.\n"
"Example: pft aitest 5.0\n"};
static void pftCallFunc(const iocshArgBuf *args)
{ pft(args[0].sval,args[1].sval);}
@@ -297,9 +353,11 @@ static const iocshArg dbtpnArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbtpnArg1 = { "value",iocshArgString};
static const iocshArg * const dbtpnArgs[2] = {&dbtpnArg0,&dbtpnArg1};
static const iocshFuncDef dbtpnFuncDef = {"dbtpn",2,dbtpnArgs,
"Database Put Notify\n"
"Database Test Process Notify\n"
"Without value, begin async. processing and get\n"
"With value, begin put, process, and get\n"};
"With value, begin put, process, and get\n"
"Example: dbtpn aitest\n"
"Example: dbtpn aitest 5.0\n"};
static void dbtpnCallFunc(const iocshArgBuf *args)
{ dbtpn(args[0].sval,args[1].sval);}
@@ -314,9 +372,8 @@ static const iocshArg dbPutAttrArg1 = { "attribute name",iocshArgString};
static const iocshArg dbPutAttrArg2 = { "value",iocshArgString};
static const iocshArg * const dbPutAttrArgs[] =
{&dbPutAttrArg0, &dbPutAttrArg1, &dbPutAttrArg2};
static const iocshFuncDef dbPutAttrFuncDef =
{"dbPutAttribute",3,dbPutAttrArgs,
"Set/Create record attribute.\n"};
static const iocshFuncDef dbPutAttrFuncDef = {"dbPutAttribute",3,dbPutAttrArgs,
"Set/Create record attribute.\n"};
static void dbPutAttrCallFunc(const iocshArgBuf *args)
{ dbPutAttribute(args[0].sval,args[1].sval,args[2].sval);}
@@ -325,7 +382,8 @@ static const iocshArg tpnArg0 = { "record name",iocshArgStringRecord};
static const iocshArg tpnArg1 = { "value",iocshArgString};
static const iocshArg * const tpnArgs[2] = {&tpnArg0,&tpnArg1};
static const iocshFuncDef tpnFuncDef = {"tpn",2,tpnArgs,
"Begin async. process and get.\n"};
"Test Process Notify.\n\n"
"Example: tpn aitest 5.0\n"};
static void tpnCallFunc(const iocshArgBuf *args)
{ tpn(args[0].sval,args[1].sval);}
@@ -334,16 +392,25 @@ static const iocshArg dblsrArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dblsrArg1 = { "interest level",iocshArgInt};
static const iocshArg * const dblsrArgs[2] = {&dblsrArg0,&dblsrArg1};
static const iocshFuncDef dblsrFuncDef = {"dblsr",2,dblsrArgs,
"Database Lockset report.\n"};
"Database Lockset report.\n"
"Generate a report showing the lock set to which each record belongs.\n"
"interest level 0 - Show lock set information only.\n"
" 1 - Show each record in the lock set.\n"
" 2 - Show each record and all database links in the lock set.\n\n"
"Example: dblsr aitest 2\n"};
static void dblsrCallFunc(const iocshArgBuf *args)
{ dblsr(args[0].sval,args[1].ival);}
/* dbLockShowLocked */
static const iocshArg dbLockShowLockedArg0 = { "interest level",iocshArgInt};
static const iocshArg * const dbLockShowLockedArgs[1] = {&dbLockShowLockedArg0};
static const iocshFuncDef dbLockShowLockedFuncDef =
{"dbLockShowLocked",1,dbLockShowLockedArgs,
"Show Locksets which are currently locked.\n"};
static const iocshFuncDef dbLockShowLockedFuncDef = {
"dbLockShowLocked",1,dbLockShowLockedArgs,
"Show Locksets which are currently locked.\n"
"interest level argument is passed to epicsMutexShow to adjust reported\n"
"information.\n\n"
"Example: dbLockShowLocked 0\n"
};
static void dbLockShowLockedCallFunc(const iocshArgBuf *args)
{ dbLockShowLocked(args[0].ival);}
@@ -351,10 +418,9 @@ static void dbLockShowLockedCallFunc(const iocshArgBuf *args)
static const iocshArg scanOnceSetQueueSizeArg0 = { "size",iocshArgInt};
static const iocshArg * const scanOnceSetQueueSizeArgs[1] =
{&scanOnceSetQueueSizeArg0};
static const iocshFuncDef scanOnceSetQueueSizeFuncDef =
{"scanOnceSetQueueSize",1,scanOnceSetQueueSizeArgs,
"Change size of Scan once queue.\n"
"Must be called before iocInit().\n"};
static const iocshFuncDef scanOnceSetQueueSizeFuncDef = {"scanOnceSetQueueSize",1,scanOnceSetQueueSizeArgs,
"Change size of Scan once queue.\n"
"Must be called before iocInit().\n"};
static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args)
{
scanOnceSetQueueSize(args[0].ival);
@@ -364,9 +430,8 @@ static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args)
static const iocshArg scanOnceQueueShowArg0 = { "reset",iocshArgInt};
static const iocshArg * const scanOnceQueueShowArgs[1] =
{&scanOnceQueueShowArg0};
static const iocshFuncDef scanOnceQueueShowFuncDef =
{"scanOnceQueueShow",1,scanOnceQueueShowArgs,
"Show details and statitics of scan once queue processing.\n"};
static const iocshFuncDef scanOnceQueueShowFuncDef = {"scanOnceQueueShow",1,scanOnceQueueShowArgs,
"Show details and statitics of scan once queue processing.\n"};
static void scanOnceQueueShowCallFunc(const iocshArgBuf *args)
{
scanOnceQueueShow(args[0].ival);
@@ -376,7 +441,8 @@ static void scanOnceQueueShowCallFunc(const iocshArgBuf *args)
static const iocshArg scanpplArg0 = { "rate",iocshArgDouble};
static const iocshArg * const scanpplArgs[1] = {&scanpplArg0};
static const iocshFuncDef scanpplFuncDef = {"scanppl",1,scanpplArgs,
"print periodic scan lists.\n"};
"Print info for records with periodic scan.\n"
"If rate == 0.0, all periods are shown.\n"};
static void scanpplCallFunc(const iocshArgBuf *args)
{ scanppl(args[0].dval);}
@@ -408,10 +474,9 @@ static void scanpiolCallFunc(const iocshArgBuf *args) { scanpiol();}
static const iocshArg callbackSetQueueSizeArg0 = { "bufsize",iocshArgInt};
static const iocshArg * const callbackSetQueueSizeArgs[1] =
{&callbackSetQueueSizeArg0};
static const iocshFuncDef callbackSetQueueSizeFuncDef =
{"callbackSetQueueSize",1,callbackSetQueueSizeArgs,
"Change depth of queue for callback workers.\n"
"Must be called before iocInit().\n"};
static const iocshFuncDef callbackSetQueueSizeFuncDef = {"callbackSetQueueSize",1,callbackSetQueueSizeArgs,
"Change depth of queue for callback workers.\n"
"Must be called before iocInit().\n"};
static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args)
{
callbackSetQueueSize(args[0].ival);
@@ -421,9 +486,8 @@ static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args)
static const iocshArg callbackQueueShowArg0 = { "reset", iocshArgInt};
static const iocshArg * const callbackQueueShowArgs[1] =
{&callbackQueueShowArg0};
static const iocshFuncDef callbackQueueShowFuncDef =
{"callbackQueueShow",1,callbackQueueShowArgs,
"Show status of callback thread processing queue.\n"};
static const iocshFuncDef callbackQueueShowFuncDef = {"callbackQueueShow",1,callbackQueueShowArgs,
"Show status of callback thread processing queue.\n"};
static void callbackQueueShowCallFunc(const iocshArgBuf *args)
{
callbackQueueShow(args[0].ival);
@@ -434,11 +498,10 @@ static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgI
static const iocshArg callbackParallelThreadsArg1 = { "priority", iocshArgString};
static const iocshArg * const callbackParallelThreadsArgs[2] =
{&callbackParallelThreadsArg0,&callbackParallelThreadsArg1};
static const iocshFuncDef callbackParallelThreadsFuncDef =
{"callbackParallelThreads",2,callbackParallelThreadsArgs,
"Configure multiple workers for a given callback queue priority level.\n"
"priority may be omitted or \"*\" to act on all priorities\n"
"or one of LOW, MEDIUM, or HIGH.\n"};
static const iocshFuncDef callbackParallelThreadsFuncDef = {"callbackParallelThreads",2,callbackParallelThreadsArgs,
"Configure multiple workers for a given callback queue priority level.\n"
"priority may be omitted or \"*\" to act on all priorities\n"
"or one of LOW, MEDIUM, or HIGH.\n"};
static void callbackParallelThreadsCallFunc(const iocshArgBuf *args)
{
callbackParallelThreads(args[0].ival, args[1].sval);
@@ -447,8 +510,8 @@ static void callbackParallelThreadsCallFunc(const iocshArgBuf *args)
/* dbStateCreate */
static const iocshArg dbStateArgName = { "name", iocshArgString };
static const iocshArg * const dbStateCreateArgs[] = { &dbStateArgName };
static const iocshFuncDef dbStateCreateFuncDef = { "dbStateCreate", 1, dbStateCreateArgs,
"Allocate new state name for \"state\" filter.\n"};
static const iocshFuncDef dbStateCreateFuncDef = {"dbStateCreate", 1, dbStateCreateArgs,
"Allocate new state name for \"state\" filter.\n"};
static void dbStateCreateCallFunc (const iocshArgBuf *args)
{
dbStateCreate(args[0].sval);
@@ -456,8 +519,8 @@ static void dbStateCreateCallFunc (const iocshArgBuf *args)
/* dbStateSet */
static const iocshArg * const dbStateSetArgs[] = { &dbStateArgName };
static const iocshFuncDef dbStateSetFuncDef = { "dbStateSet", 1, dbStateSetArgs,
"Change state to set for \"state\" filter.\n"};
static const iocshFuncDef dbStateSetFuncDef = {"dbStateSet", 1, dbStateSetArgs,
"Change state to set for \"state\" filter.\n"};
static void dbStateSetCallFunc (const iocshArgBuf *args)
{
dbStateId sid = dbStateFind(args[0].sval);
@@ -468,8 +531,8 @@ static void dbStateSetCallFunc (const iocshArgBuf *args)
/* dbStateClear */
static const iocshArg * const dbStateClearArgs[] = { &dbStateArgName };
static const iocshFuncDef dbStateClearFuncDef = { "dbStateClear", 1, dbStateClearArgs,
"Change state to clear for \"state\" filter.\n" };
static const iocshFuncDef dbStateClearFuncDef = {"dbStateClear", 1, dbStateClearArgs,
"Change state to clear for \"state\" filter.\n"};
static void dbStateClearCallFunc (const iocshArgBuf *args)
{
dbStateId sid = dbStateFind(args[0].sval);
@@ -481,8 +544,8 @@ static void dbStateClearCallFunc (const iocshArgBuf *args)
/* dbStateShow */
static const iocshArg dbStateShowArg1 = { "level", iocshArgInt };
static const iocshArg * const dbStateShowArgs[] = { &dbStateArgName, &dbStateShowArg1 };
static const iocshFuncDef dbStateShowFuncDef = { "dbStateShow", 2, dbStateShowArgs,
"Show set/clear status of named state. (cf. \"state\" filter)\n" };
static const iocshFuncDef dbStateShowFuncDef = {"dbStateShow", 2, dbStateShowArgs,
"Show set/clear status of named state. (cf. \"state\" filter)\n"};
static void dbStateShowCallFunc (const iocshArgBuf *args)
{
dbStateId sid = dbStateFind(args[0].sval);
@@ -494,8 +557,8 @@ static void dbStateShowCallFunc (const iocshArgBuf *args)
/* dbStateShowAll */
static const iocshArg dbStateShowAllArg0 = { "level", iocshArgInt };
static const iocshArg * const dbStateShowAllArgs[] = { &dbStateShowAllArg0 };
static const iocshFuncDef dbStateShowAllFuncDef = { "dbStateShowAll", 1, dbStateShowAllArgs,
"Show set/clear status of all named states. (cf. \"state\" filter)\n" };
static const iocshFuncDef dbStateShowAllFuncDef = {"dbStateShowAll", 1, dbStateShowAllArgs,
"Show set/clear status of all named states. (cf. \"state\" filter)\n"};
static void dbStateShowAllCallFunc (const iocshArgBuf *args)
{
dbStateShowAll(args[0].ival);

View File

@@ -359,6 +359,8 @@ typedef struct lset {
* @param plink the link
* @param rtn routine to execute
* @returns status value
*
* @since 3.16.1
*/
long (*doLocked)(struct link *plink, dbLinkUserCallback rtn, void *priv);

View File

@@ -869,7 +869,7 @@ nosplit:
}
}
static char *msstring[4]={"NMS","MS","MSI","MSS"};
static const char *msstring[4]={"NMS","MS","MSI","MSS"};
long dblsr(char *recordname,int level)
{

View File

@@ -24,18 +24,64 @@ extern "C" {
struct dbCommon;
struct dbBase;
/** @brief Lock multiple records.
*
* A dbLocker allows a caller to simultaneously lock multiple records.
* The list of records is provided to dbLockerAlloc().
* And the resulting dbLocker can be locked/unlocked repeatedly.
*
* Each thread can only lock one dbLocker at a time.
* While locked, dbScanLock() may be called only on those records
* included in the dbLocker.
*
* @since 3.16.0.1
*/
struct dbLocker;
typedef struct dbLocker dbLocker;
/** @brief Lock a record for modification.
*
* While locked, caller may access record using eg. dbGet() or dbPut(),
* but not dbGetField() or dbPutField().
* The caller must later call dbScanUnlock().
* dbScanLock() may be called again as the record lock behaves as a recursive mutex.
*/
DBCORE_API void dbScanLock(struct dbCommon *precord);
/** @brief Unlock a record.
*
* Reverse the action of dbScanLock()
*/
DBCORE_API void dbScanUnlock(struct dbCommon *precord);
/** @brief Prepare to lock a set of records.
* @param precs Array of nrecs dbCommon pointers.
* @param nrecs Length of precs array
* @param flags Set to 0
* @return NULL on error
* @since 3.16.0.1
*/
DBCORE_API dbLocker *dbLockerAlloc(struct dbCommon * const *precs,
size_t nrecs,
unsigned int flags);
DBCORE_API void dbLockerFree(dbLocker *);
/** @brief Free dbLocker allocated by dbLockerAlloc()
* @param plocker Must not be NULL
* @since 3.16.0.1
*/
DBCORE_API void dbLockerFree(dbLocker *plocker);
/** @brief Lock all records of dbLocker
*
* While locked, caller may access any associated record passed to dbLockerAlloc() .
* dbScanLockMany() may not be called again (multi-lock is not recursive).
* dbScanLock()/dbScanUnlock() may be called on individual record.
* The caller must later call dbScanUnlockMany().
* @since 3.16.0.1
*/
DBCORE_API void dbScanLockMany(dbLocker*);
/** @brief Unlock all records of dbLocker
* @since 3.16.0.1
*/
DBCORE_API void dbScanUnlockMany(dbLocker*);
DBCORE_API unsigned long dbLockGetLockId(

View File

@@ -10,6 +10,7 @@
#define DBLOCKPVT_H
#include "dbLock.h"
#include "epicsMutex.h"
#include "epicsSpin.h"
/* Define to enable additional error checking */

View File

@@ -506,62 +506,62 @@ long dbtgf(const char *pname)
ret_options=0;
dbr_type = DBR_STRING;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/MAX_STRING_SIZE));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/MAX_STRING_SIZE));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_CHAR;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt8)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsInt8))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_UCHAR;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt8)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsUInt8))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_SHORT;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt16)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsInt16))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_USHORT;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt16)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsUInt16))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_LONG;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt32)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsInt32))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_ULONG;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt32)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsUInt32))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_INT64;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt64)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsInt64))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_UINT64;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt64)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsUInt64))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_FLOAT;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat32)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsFloat32))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_DOUBLE;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat64)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsFloat64))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_ENUM;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsEnum16)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsEnum16))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);

View File

@@ -1068,7 +1068,7 @@ int dbRecordNameValidate(const char *name)
const char *pos = name;
if (!*name) {
yyerrorAbort("Error: Record/Alias name can't be empty");
yyerrorAbort(ERL_ERROR ": Record/Alias name can't be empty");
return 1;
}
@@ -1086,7 +1086,7 @@ int dbRecordNameValidate(const char *name)
name, c);
} else if(c==' ' || c=='\t' || c=='"' || c=='\'' || c=='.' || c=='$') {
epicsPrintf("Error: Bad character '%c' in Record/Alias name \"%s\"\n",
epicsPrintf(ERL_ERROR ": Bad character '%c' in Record/Alias name \"%s\"\n",
c, name);
yyerrorAbort(NULL);
return 1;
@@ -1110,14 +1110,10 @@ static void dbRecordHead(char *recordType, char *name, int visible)
allocTemp(pdbentry);
if (recordType[0] == '*' && recordType[1] == 0) {
if (dbRecordsOnceOnly)
epicsPrintf("Record-type \"*\" not valid with dbRecordsOnceOnly\n");
else {
status = dbFindRecord(pdbentry, name);
if (status == 0)
return; /* done */
epicsPrintf("Record \"%s\" not found\n", name);
}
status = dbFindRecord(pdbentry, name);
if (status == 0)
return; /* done */
epicsPrintf(ERL_ERROR ": Record \"%s\" not found\n", name);
yyerror(NULL);
duplicate = TRUE;
return;
@@ -1136,15 +1132,16 @@ static void dbRecordHead(char *recordType, char *name, int visible)
status = dbCreateRecord(pdbentry,name);
if (status == S_dbLib_recExists) {
if (strcmp(recordType, dbGetRecordTypeName(pdbentry)) != 0) {
epicsPrintf("Record \"%s\" of type \"%s\" redefined with new type "
epicsPrintf(ERL_ERROR ": Record \"%s\" of type \"%s\" redefined with new type "
"\"%s\"\n", name, dbGetRecordTypeName(pdbentry), recordType);
yyerror(NULL);
duplicate = TRUE;
return;
}
else if (dbRecordsOnceOnly) {
epicsPrintf("Record \"%s\" already defined (dbRecordsOnceOnly is "
"set)\n", name);
epicsPrintf(ERL_ERROR ": Record \"%s\" already defined and dbRecordsOnceOnly set.\n"
"Used record type \"*\" to append.\n",
name);
yyerror(NULL);
duplicate = TRUE;
}
@@ -1170,8 +1167,28 @@ static void dbRecordField(char *name,char *value)
pdbentry = ptempListNode->item;
status = dbFindField(pdbentry,name);
if (status) {
epicsPrintf("Record \"%s\" does not have a field \"%s\"\n",
dbGetRecordName(pdbentry), name);
epicsPrintf("%s Record \"%s\" does not have a field \"%s\"\n",
dbGetRecordTypeName(pdbentry), dbGetRecordName(pdbentry), name);
if(dbGetRecordName(pdbentry)) {
DBENTRY temp;
double bestSim = -1.0;
const dbFldDes *bestFld = NULL;
dbCopyEntryContents(pdbentry, &temp);
for(status = dbFirstField(&temp, 0); !status; status = dbNextField(&temp, 0)) {
double sim = epicsStrSimilarity(name, temp.pflddes->name);
if(!bestFld || sim > bestSim) {
bestSim = sim;
bestFld = temp.pflddes;
}
}
dbFinishEntry(&temp);
if(bestSim>0.0) {
epicsPrintf(" Did you mean \"%s\"?", bestFld->name);
if(bestFld->prompt)
epicsPrintf(" (%s)", bestFld->prompt);
epicsPrintf("\n");
}
}
yyerror(NULL);
return;
}

View File

@@ -10,6 +10,7 @@
#include "iocsh.h"
#include "errSymTbl.h"
#include "errlog.h"
#include "dbStaticIocRegister.h"
#include "dbStaticLib.h"
@@ -24,7 +25,8 @@ static const iocshArg argRecType = { "recordTypeName", iocshArgString};
/* dbDumpPath */
static const iocshArg * const dbDumpPathArgs[] = {&argPdbbase};
static const iocshFuncDef dbDumpPathFuncDef = {"dbDumpPath",1,dbDumpPathArgs,
"Dump .db/.dbd file search path.\n"};
"Dump .db/.dbd file search path.\n"
"Example: dbDumpPath pdbbase\n"};
static void dbDumpPathCallFunc(const iocshArgBuf *args)
{
dbDumpPath(*iocshPpdbbase);
@@ -99,7 +101,8 @@ static void dbDumpDeviceCallFunc(const iocshArgBuf *args)
static const iocshArg * const dbDumpDriverArgs[] = { &argPdbbase};
static const iocshFuncDef dbDumpDriverFuncDef = {"dbDumpDriver",1,dbDumpDriverArgs,
"Dump device support information.\n"
"Example: dbDumpDriver pdbbase\n"};
"Example: dbDumpDriver pdbbase\n"
"If the last argument(s) are missing, dump all device support information.\n",};
static void dbDumpDriverCallFunc(const iocshArgBuf *args)
{
dbDumpDriver(*iocshPpdbbase);
@@ -124,7 +127,8 @@ static const iocshArg * const dbDumpRegistrarArgs[] = { &argPdbbase};
static const iocshFuncDef dbDumpRegistrarFuncDef = {"dbDumpRegistrar",1,dbDumpRegistrarArgs,
"Dump list of registered functions including ones for subroutine records,\n"
"and ones that can be invoked from iocsh.\n"
"Example: dbDumpRegistrar pdbbase\n"};
"Example: dbDumpRegistrar pdbbase\n"
"If last argument(s) are missing, dump all registered functions\n"};
static void dbDumpRegistrarCallFunc(const iocshArgBuf *args)
{
dbDumpRegistrar(*iocshPpdbbase);
@@ -134,7 +138,8 @@ static void dbDumpRegistrarCallFunc(const iocshArgBuf *args)
static const iocshArg * const dbDumpFunctionArgs[] = { &argPdbbase};
static const iocshFuncDef dbDumpFunctionFuncDef = {"dbDumpFunction",1,dbDumpFunctionArgs,
"Dump list of registered subroutine functions.\n"
"Example: dbDumpFunction pddbase\n"};
"Example: dbDumpFunction pddbase\n"
"If last argument(s) are missing, dump all registered subroutine functions\n"};
static void dbDumpFunctionCallFunc(const iocshArgBuf *args)
{
dbDumpFunction(*iocshPpdbbase);
@@ -144,7 +149,8 @@ static void dbDumpFunctionCallFunc(const iocshArgBuf *args)
static const iocshArg * const dbDumpVariableArgs[] = { &argPdbbase};
static const iocshFuncDef dbDumpVariableFuncDef = {"dbDumpVariable",1,dbDumpVariableArgs,
"Dump list of variables used in the database.\n"
"Example: dbDumpVariable pddbase\n"};
"Example: dbDumpVariable pddbase\n"
"If last argument(s) are missing, dump all variables.\n"};
static void dbDumpVariableCallFunc(const iocshArgBuf *args)
{
dbDumpVariable(*iocshPpdbbase);
@@ -159,7 +165,8 @@ static const iocshFuncDef dbDumpBreaktableFuncDef = {
2,
dbDumpBreaktableArgs,
"Dump the given break table\n"
"Example: dbDumpBreaktable pdbbase typeKdegC\n",
"Example: dbDumpBreaktable pdbbase typeKdegC\n"
"If last argument(s) are missing, dump all breakpoint tables.\n",
};
static void dbDumpBreaktableCallFunc(const iocshArgBuf *args)
{
@@ -176,7 +183,8 @@ static const iocshFuncDef dbPvdDumpFuncDef = {
dbPvdDumpArgs,
"Dump the various buckets of the process variable directory.\n"
"If verbose is greater than 0, also print the process variables in each bucket.\n"
"Example: dbPvdDump pdbbase 1\n",
"Example: dbPvdDump pdbbase 1\n"
"If the last argument(s) are missing, dump all buckets as though verbose is 0.\n",
};
static void dbPvdDumpCallFunc(const iocshArgBuf *args)
{
@@ -247,7 +255,7 @@ static void dbCreateAliasCallFunc(const iocshArgBuf *args)
}
dbFinishEntry(&ent);
if(status) {
fprintf(stderr, "Error: %ld %s\n", status, errSymMsg(status));
fprintf(stderr, ERL_ERROR ": %ld %s\n", status, errSymMsg(status));
iocshSetError(1);
}
}

View File

@@ -57,10 +57,10 @@ static char *pNullString = "";
*/
STATIC_ASSERT(messagesize >= 21);
static char *ppstring[5]={" NPP"," PP"," CA"," CP"," CPP"};
static char *msstring[4]={" NMS"," MS"," MSI"," MSS"};
static const char *ppstring[5]={" NPP"," PP"," CA"," CP"," CPP"};
static const char *msstring[4]={" NMS"," MS"," MSI"," MSS"};
maplinkType pamaplinkType[LINK_NTYPES] = {
const maplinkType pamaplinkType[LINK_NTYPES] = {
{"CONSTANT",CONSTANT},
{"PV_LINK",PV_LINK},
{"VME_IO",VME_IO},
@@ -89,7 +89,7 @@ static FILE *openOutstream(const char *filename)
errno = 0;
stream = fopen(filename,"w");
if(!stream) {
fprintf(stderr,"error opening %s %s\n",filename,strerror(errno));
fprintf(stderr,ERL_ERROR " opening %s %s\n",filename,strerror(errno));
return 0;
}
return stream;
@@ -637,7 +637,7 @@ void dbFinishEntry(DBENTRY *pdbentry)
}
}
DBENTRY * dbCopyEntry(DBENTRY *pdbentry)
DBENTRY * dbCopyEntry(const DBENTRY *pdbentry)
{
DBENTRY *pnew;
@@ -647,7 +647,7 @@ DBENTRY * dbCopyEntry(DBENTRY *pdbentry)
return(pnew);
}
void dbCopyEntryContents(DBENTRY *pfrom,DBENTRY *pto)
void dbCopyEntryContents(const DBENTRY *pfrom,DBENTRY *pto)
{
*pto = *pfrom;
pto->message = NULL;
@@ -2204,12 +2204,12 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec)
*/
} else if(dbCanSetLink(plink, &link_info, devsup)!=0) {
errlogPrintf("Error: %s.%s: can't initialize link type %s with \"%s\" (type %s)\n",
prec->name, pflddes->name, pamaplinkType[plink->type].strvalue, plink->text, pamaplinkType[link_info.ltype].strvalue);
errlogPrintf(ERL_ERROR ": %s.%s: can't initialize link type %d with \"%s\" (type %s)\n",
prec->name, pflddes->name, plink->type, plink->text, pamaplinkType[link_info.ltype].strvalue);
} else if(dbSetLink(plink, &link_info, devsup)) {
errlogPrintf("Error: %s.%s: failed to initialize link type %s with \"%s\" (type %s)\n",
prec->name, pflddes->name, pamaplinkType[plink->type].strvalue, plink->text, pamaplinkType[link_info.ltype].strvalue);
errlogPrintf(ERL_ERROR ": %s.%s: failed to initialize link type %d with \"%s\" (type %s)\n",
prec->name, pflddes->name, plink->type, plink->text, pamaplinkType[link_info.ltype].strvalue);
}
free(plink->text);
plink->text = NULL;

View File

@@ -51,9 +51,9 @@ DBCORE_API void dbInitEntry(DBBASE *pdbbase,
DBENTRY *pdbentry);
DBCORE_API void dbFinishEntry(DBENTRY *pdbentry);
DBCORE_API DBENTRY * dbCopyEntry(DBENTRY *pdbentry);
DBCORE_API void dbCopyEntryContents(DBENTRY *pfrom,
DBENTRY *pto);
DBCORE_API DBENTRY * dbCopyEntry(const DBENTRY *pdbentry);
DBCORE_API void dbCopyEntryContents(const DBENTRY *pfrom,
DBENTRY *pto);
DBCORE_API extern int dbBptNotMonotonic;

View File

@@ -143,7 +143,7 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName)
status = dbPutStringNum(pdbentry,pflddes->initial);
if(status)
epicsPrintf("Error initializing %s.%s initial %s\n",
epicsPrintf(ERL_ERROR " initializing %s.%s initial %s\n",
pdbRecordType->name,pflddes->name,pflddes->initial);
}
break;

View File

@@ -370,9 +370,9 @@ json_value: jsonNULL { $$ = dbmfStrdup("null"); }
static int yyerror(char *str)
{
if (str)
epicsPrintf("Error: %s\n", str);
epicsPrintf(ERL_ERROR ": %s\n", str);
else
epicsPrintf("Error");
epicsPrintf(ERL_ERROR "");
if (!yyFailed) { /* Only print this stuff once */
epicsPrintf(" at or before '%s'", yytext);
dbIncludePrint();

View File

@@ -43,11 +43,11 @@ extern "C" {
#define VXI_IO 15
#define LINK_NTYPES 16
typedef struct maplinkType {
char *strvalue;
const char *strvalue;
int value;
} maplinkType;
DBCORE_API extern maplinkType pamaplinkType[];
DBCORE_API extern const maplinkType pamaplinkType[LINK_NTYPES];
#define VXIDYNAMIC 0
#define VXISTATIC 1

View File

@@ -16,6 +16,7 @@
#include "osiUnistd.h"
#include "macLib.h"
#include "dbmf.h"
#include "errlog.h"
#include "epicsExport.h"
#include "dbAccess.h"
@@ -337,7 +338,7 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect)
if (dbTemplateMaxVars < 1)
{
fprintf(stderr,"Error: dbTemplateMaxVars = %d, must be +ve\n",
fprintf(stderr,ERL_ERROR ": dbTemplateMaxVars = %d, must be +ve\n",
dbTemplateMaxVars);
return -1;
}

View File

@@ -264,7 +264,7 @@ static void addMacroReplacements(MAC_HANDLE * const macPvt,
if (status) {
status = macInstallMacros(macPvt, pairs);
if (!status) {
fprintf(stderr, "Error from macInstallMacros\n");
fprintf(stderr, ERL_ERROR " from macInstallMacros\n");
usageExit(1);
}
free(pairs);
@@ -522,7 +522,7 @@ static void inputOpenFile(inputData *pinputData, const char * const filename)
}
if (!fp) {
fprintf(stderr, "msi: Can't open file '%s'\n", filename);
fprintf(stderr, ERL_ERROR " msi: Can't open file '%s'\n", filename);
inputErrPrint(pinputData);
abortExit(1);
}
@@ -672,7 +672,7 @@ static void substituteOpen(subInfo **ppvt, const std::string& substitutionName)
fp = fopen(substitutionName.c_str(), "r");
if (!fp) {
fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName.c_str());
fprintf(stderr, ERL_ERROR " msi: Can't open file '%s'\n", substitutionName.c_str());
abortExit(1);
}

View File

@@ -173,7 +173,7 @@ static int iocBuild_2(void)
scanInit();
if (asInit()) {
errlogPrintf("iocBuild: asInit Failed.\n");
errlogPrintf(ERL_ERROR " iocBuild: asInit Failed.\n");
return -1;
}
dbProcessNotifyInit();

View File

@@ -38,7 +38,7 @@ static void iocBuildCallFunc(const iocshArgBuf *args)
/* iocRun */
static const iocshFuncDef iocRunFuncDef = {"iocRun",0,NULL,
"Bring the IOC out of its initial quiescent state to the running state.\n"
"See more: iocBuild, iocPause"};
"See more: iocBuild, iocPause\n"};
static void iocRunCallFunc(const iocshArgBuf *args)
{
iocshSetError(iocRun());
@@ -47,7 +47,7 @@ static void iocRunCallFunc(const iocshArgBuf *args)
/* iocPause */
static const iocshFuncDef iocPauseFuncDef = {"iocPause",0,NULL,
"Brings a running IOC to a quiescent state with all record processing frozen.\n"
"See more: iocBuild, iocRub, iocInit"};
"See more: iocBuild, iocRub, iocInit\n"};
static void iocPauseCallFunc(const iocshArgBuf *args)
{
iocshSetError(iocPause());

View File

@@ -17,6 +17,7 @@
#include <epicsStdio.h>
#include <epicsFindSymbol.h>
#include <errlog.h>
#include <registryRecordType.h>
#include <registryDeviceSupport.h>
#include <registryDriverSupport.h>
@@ -248,7 +249,7 @@ registerAllRecordDeviceDrivers(DBBASE *pdbbase)
} catch(std::exception& e) {
dbFinishEntry(&entry);
fprintf(stderr, "Error: %s\n", e.what());
fprintf(stderr, ERL_ERROR ": %s\n", e.what());
return 2;
}
}

View File

@@ -51,6 +51,9 @@ static long readLocked(struct link *pinp, void *dummy)
if (status) return status;
if (prec->mask)
prec->rval &= prec->mask;
if (dbLinkIsConstant(&prec->tsel) &&
prec->tse == epicsTimeEventDeviceTime)
dbGetTimeStamp(pinp, &prec->time);

View File

@@ -17,6 +17,7 @@
#include <stdio.h>
#include "freeList.h"
#include "caeventmask.h"
#include "db_field_log.h"
#include "chfPlugin.h"
#include "epicsExit.h"
@@ -60,7 +61,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
myStruct *my = (myStruct*) pvt;
epicsInt32 i = my->i;
if (pfl->ctx == dbfl_context_read)
if (pfl->ctx == dbfl_context_read || (pfl->mask & DBE_PROPERTY))
return pfl;
if (i++ == 0)

View File

@@ -14,6 +14,7 @@
#include <stdio.h>
#include "freeList.h"
#include "caeventmask.h"
#include "db_field_log.h"
#include "chfPlugin.h"
#include "dbState.h"
@@ -94,7 +95,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
myStruct *my = (myStruct*) pvt;
int actstate;
if (pfl->ctx == dbfl_context_read)
if (pfl->ctx == dbfl_context_read || (pfl->mask & DBE_PROPERTY))
return pfl;
actstate = dbStateGet(my->id);

View File

@@ -60,8 +60,10 @@ this chapter for information on soft device support.
If the record gets its values from hardware or uses the C<Raw Soft Channel>
device support, the device support routines place the value in the RVAL
field which is then converted using the process described in the next
section.
field.
(Since UNRELEASED) If the MASK field is non-zero, then this MASK is applied to RVAL.
The value of RVAL is then converted using the process described in the
next section.
=fields INP, DTYP, ZNAM, ONAM, RVAL, VAL

View File

@@ -36,7 +36,7 @@ The desired output needs to be in engineering units.
The first field that determines where the desired output originates is the
output mode select (OMSL) field, which can have two possible values:
C<losed_loop> or C<supervisory>. If C<supervisory> is specified, the value
C<closed_loop> or C<supervisory>. If C<supervisory> is specified, the value
in the VAL field can be set externally via dbPuts at run-time. If
C<closed_loop> is specified, the VAL field's value is obtained from the
address specified in the Desired Output Link (DOL) field which can be a

View File

@@ -323,6 +323,7 @@ to alarms that are common to all record types.
field(B0,DBF_UCHAR) {
prompt("Bit 0")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -330,6 +331,7 @@ to alarms that are common to all record types.
field(B1,DBF_UCHAR) {
prompt("Bit 1")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -337,6 +339,7 @@ to alarms that are common to all record types.
field(B2,DBF_UCHAR) {
prompt("Bit 2")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -344,6 +347,7 @@ to alarms that are common to all record types.
field(B3,DBF_UCHAR) {
prompt("Bit 3")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -351,6 +355,7 @@ to alarms that are common to all record types.
field(B4,DBF_UCHAR) {
prompt("Bit 4")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -358,6 +363,7 @@ to alarms that are common to all record types.
field(B5,DBF_UCHAR) {
prompt("Bit 5")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -365,6 +371,7 @@ to alarms that are common to all record types.
field(B6,DBF_UCHAR) {
prompt("Bit 6")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -372,6 +379,7 @@ to alarms that are common to all record types.
field(B7,DBF_UCHAR) {
prompt("Bit 7")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -379,6 +387,7 @@ to alarms that are common to all record types.
field(B8,DBF_UCHAR) {
prompt("Bit 8")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -386,6 +395,7 @@ to alarms that are common to all record types.
field(B9,DBF_UCHAR) {
prompt("Bit 9")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -393,6 +403,7 @@ to alarms that are common to all record types.
field(BA,DBF_UCHAR) {
prompt("Bit 10")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -400,6 +411,7 @@ to alarms that are common to all record types.
field(BB,DBF_UCHAR) {
prompt("Bit 11")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -407,6 +419,7 @@ to alarms that are common to all record types.
field(BC,DBF_UCHAR) {
prompt("Bit 12")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -414,6 +427,7 @@ to alarms that are common to all record types.
field(BD,DBF_UCHAR) {
prompt("Bit 13")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -421,6 +435,7 @@ to alarms that are common to all record types.
field(BE,DBF_UCHAR) {
prompt("Bit 14")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -428,6 +443,7 @@ to alarms that are common to all record types.
field(BF,DBF_UCHAR) {
prompt("Bit 15")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -435,6 +451,7 @@ to alarms that are common to all record types.
field(B10,DBF_UCHAR) {
prompt("Bit 16")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -442,6 +459,7 @@ to alarms that are common to all record types.
field(B11,DBF_UCHAR) {
prompt("Bit 17")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -449,6 +467,7 @@ to alarms that are common to all record types.
field(B12,DBF_UCHAR) {
prompt("Bit 18")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -456,6 +475,7 @@ to alarms that are common to all record types.
field(B13,DBF_UCHAR) {
prompt("Bit 19")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -463,6 +483,7 @@ to alarms that are common to all record types.
field(B14,DBF_UCHAR) {
prompt("Bit 20")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -470,6 +491,7 @@ to alarms that are common to all record types.
field(B15,DBF_UCHAR) {
prompt("Bit 21")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -477,6 +499,7 @@ to alarms that are common to all record types.
field(B16,DBF_UCHAR) {
prompt("Bit 22")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -484,6 +507,7 @@ to alarms that are common to all record types.
field(B17,DBF_UCHAR) {
prompt("Bit 23")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -491,6 +515,7 @@ to alarms that are common to all record types.
field(B18,DBF_UCHAR) {
prompt("Bit 24")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -498,6 +523,7 @@ to alarms that are common to all record types.
field(B19,DBF_UCHAR) {
prompt("Bit 25")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -505,6 +531,7 @@ to alarms that are common to all record types.
field(B1A,DBF_UCHAR) {
prompt("Bit 26")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -512,6 +539,7 @@ to alarms that are common to all record types.
field(B1B,DBF_UCHAR) {
prompt("Bit 27")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -519,6 +547,7 @@ to alarms that are common to all record types.
field(B1C,DBF_UCHAR) {
prompt("Bit 28")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -526,6 +555,7 @@ to alarms that are common to all record types.
field(B1D,DBF_UCHAR) {
prompt("Bit 29")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -533,6 +563,7 @@ to alarms that are common to all record types.
field(B1E,DBF_UCHAR) {
prompt("Bit 30")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -540,6 +571,7 @@ to alarms that are common to all record types.
field(B1F,DBF_UCHAR) {
prompt("Bit 31")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)

View File

@@ -57,6 +57,14 @@
else \
flags |= F_BADLNK
#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wformat-security"
/* Intentionally passing non-const format string to epicsSnprintf() below.
* Older GCC does not allow pragma GCC within function body.
*/
#endif
static void doPrintf(printfRecord *prec)
{
const char *pfmt = prec->fmt;
@@ -314,6 +322,9 @@ static void doPrintf(printfRecord *prec)
prec->len = pval - prec->val;
}
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif
static long init_record(struct dbCommon *pcommon, int pass)
{

View File

@@ -4,6 +4,8 @@
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#define EPICS_PRIVATE_API
#include <string.h>
#include <errlog.h>

View File

@@ -37,7 +37,7 @@ int epicsNtpGetTime(char *ntpIp, struct timespec *now)
sockfd = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); // Create a UDP socket.
if ( sockfd < 0 ) {
perror( "epicsNtpGetTime" );
perror( "epicsNtpGetTime creating socket" );
return -1;
}
@@ -51,24 +51,29 @@ int epicsNtpGetTime(char *ntpIp, struct timespec *now)
// Call up the server using its IP address and port number.
if ( connect( sockfd, ( struct sockaddr * ) &serv_addr, sizeof( serv_addr) ) < 0 ) {
perror( "epicsNtpGetTime" );
perror( "epicsNtpGetTime connecting socket" );
close( sockfd );
return -1;
}
// Send it the NTP packet it wants. If n == -1, it failed.
n = write( sockfd, ( char* ) &packet, sizeof( ntp_packet ) );
if ( n < 0 ) {
perror( "epicsNtpGetTime" );
perror( "epicsNtpGetTime sending NTP request" );
close( sockfd );
return -1;
}
// Wait and receive the packet back from the server. If n == -1, it failed.
// Wait and receive the packet back from the server. If n == -1, it failed.
n = read( sockfd, ( char* ) &packet, sizeof( ntp_packet ) );
if ( n < 0 ) {
perror( "epicsNtpGetTime" );
perror( "epicsNtpGetTime reading NTP reply" );
close( sockfd );
return -1;
}
close( sockfd );
// These two fields contain the time-stamp seconds as the packet left the NTP server.
// The number of seconds correspond to the seconds passed since 1900.
// ntohl() converts the bit/byte order from the network's to host's "endianness".

View File

@@ -209,7 +209,7 @@ static int yyerror(char *str)
if (strlen(str))
errlogPrintf("%s at line %d\n", str, line_num);
else
errlogPrintf("Error at line %d\n", line_num);
errlogPrintf(ERL_ERROR " at line %d\n", line_num);
yyFailed = TRUE;
return 0;
}

View File

@@ -176,12 +176,12 @@ long epicsStdCall asInitFile(const char *filename,const char *substitutions)
fp = fopen(filename,"r");
if(!fp) {
errlogPrintf("asInitFile: Can't open file '%s'\n", filename);
fprintf(stderr, ERL_ERROR " asInitFile: Can't open file '%s'\n", filename);
return(S_asLib_badConfig);
}
status = asInitFP(fp,substitutions);
if(fclose(fp)==EOF) {
errMessage(0,"asInitFile: fclose failed!");
fprintf(stderr, ERL_ERROR " asInitFile: fclose failed!");
if(!status) status = S_asLib_badConfig;
}
return(status);
@@ -359,7 +359,6 @@ void epicsStdCall asPutMemberPvt(ASMEMBERPVT asMemberPvt,void *userPvt)
if(!asActive) return;
if(!pasgmember) return;
pasgmember->userPvt = userPvt;
return;
}
long epicsStdCall asAddClient(ASCLIENTPVT *pasClientPvt,ASMEMBERPVT asMemberPvt,

View File

@@ -336,6 +336,10 @@ LIBCOM_API long
break;
case SEPERATOR:
if (pstacktop == stack) {
*perror = CALC_ERR_BAD_SEPERATOR;
goto bad;
}
/* Move operators to the output until open paren */
while (pstacktop->name[0] != '(') {
if (pstacktop <= stack+1) {
@@ -354,6 +358,10 @@ LIBCOM_API long
break;
case CLOSE_PAREN:
if (pstacktop == stack) {
*perror = CALC_ERR_PAREN_NOT_OPEN;
goto bad;
}
/* Move operators to the output until matching paren */
while (pstacktop->name[0] != '(') {
if (pstacktop <= stack+1) {

View File

@@ -25,8 +25,8 @@
namespace {
static epicsThreadOnceId epicsSigletonOnceFlag ( EPICS_THREAD_ONCE_INIT );
static epicsMutex * pEPICSSigletonMutex = 0;
epicsThreadOnceId epicsSigletonOnceFlag ( EPICS_THREAD_ONCE_INIT );
epicsMutex * pEPICSSigletonMutex = 0;
extern "C" void SingletonMutexOnce ( void * /* pParm */ )
{

View File

@@ -34,8 +34,6 @@ void ellAdd (ELLLIST *pList, ELLNODE *pNode)
pList->node.previous = pNode;
pList->count++;
return;
}
/****************************************************************************
*
@@ -65,8 +63,6 @@ void ellConcat (ELLLIST *pDstList, ELLLIST *pAddList)
pAddList->count = 0;
pAddList->node.next = NULL;
pAddList->node.previous = NULL;
return;
}
/****************************************************************************
*
@@ -86,8 +82,6 @@ void ellDelete (ELLLIST *pList, ELLNODE *pNode)
pNode->previous->next = pNode->next;
pList->count--;
return;
}
/****************************************************************************
*
@@ -136,8 +130,6 @@ void ellExtract (ELLLIST *pSrcList, ELLNODE *pStartNode, ELLNODE *pEndNode, ELLL
}
pSrcList->count -= count;
pDstList->count += count;
return;
}
/****************************************************************************
*
@@ -194,8 +186,6 @@ void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode)
pNode->next->previous = pNode;
plist->count++;
return;
}
/****************************************************************************
*

View File

@@ -105,14 +105,13 @@ int errSymbolAdd(long errNum, const char *name)
ERRNUMNODE **phashnode = NULL;
ERRNUMNODE *pNew = NULL;
int modnum = (epicsUInt16) (errNum >> 16);
epicsUInt16 hashInd = errhash(errNum);
if (modnum < MIN_MODULE_NUM)
return S_err_invCode;
initErrorHashTable();
epicsUInt16 hashInd = errhash(errNum);
epicsMutexLock(errHashTable.tableMutexId);
phashnode = (ERRNUMNODE**)&errHashTable.table[hashInd];
pNextNode = (ERRNUMNODE*) *phashnode;
@@ -271,7 +270,6 @@ void errSymTestPrint(long errNum)
if ( message[0] == '\0' ) return;
printf("module %hu number %hu message=\"%s\"\n",
modnum, errnum, message);
return;
}
/****************************************************************

View File

@@ -38,13 +38,38 @@ typedef ERRSYMTAB *ERRSYMTAB_ID;
extern "C" {
#endif
/** \brief Lookup message from error/status code.
* \param status Input code
* \param pBuf Output string buffer
* \param bufLength Length of output buffer in bytes. Must be non-zero.
*
* Handles EPICS message codes, and "errno" codes.
*
* Copies in a mesage for any status code. Unknown status codes
* are printed numerically.
*/
LIBCOM_API void errSymLookup(long status, char *pBuf, size_t bufLength);
/** \brief Lookup message from error/status code.
* \param status Input code
* \returns A statically allocated message string. Never NULL.
*
* Handles EPICS message codes, and "errno" codes.
*
* For any unknown status codes, a generic "Unknown code" message is returned.
*
* \since 3.16.1
*/
LIBCOM_API const char* errSymMsg(long status);
LIBCOM_API void errSymTest(epicsUInt16 modnum, epicsUInt16 begErrNum,
epicsUInt16 endErrNum);
LIBCOM_API void errSymTestPrint(long errNum);
LIBCOM_API int errSymBld(void);
LIBCOM_API int errSymbolAdd(long errNum, const char *name);
/** @brief Define new custom error code and associate message string.
* @param errNum New error code. Caller is reponsible for avoiding reuse of existing codes.
* @param message New message. Pointer stored. Caller must not free pointed storage.
* @return 0 on success
*/
LIBCOM_API int errSymbolAdd(long errNum, const char *message);
LIBCOM_API void errSymDump(void);
#ifdef __cplusplus

View File

@@ -247,7 +247,7 @@ int isATTY(FILE* fp)
else if(fp==stderr)
hand = GetStdHandle(STD_ERROR_HANDLE);
#ifdef ENABLE_VIRTUAL_TERMINAL_PROCESSING
if(hand && GetConsoleMode(hand, &mode)) {
if(hand!=INVALID_HANDLE_VALUE && GetConsoleMode(hand, &mode)) {
(void)SetConsoleMode(hand, mode|ENABLE_VIRTUAL_TERMINAL_PROCESSING);
mode = 0u;
if(GetConsoleMode(hand, &mode) && (mode&ENABLE_VIRTUAL_TERMINAL_PROCESSING))

View File

@@ -285,14 +285,16 @@ LIBCOM_API void errSymLookup(long status, char *pBuf, size_t bufLength);
#define ANSI_ESC_MAGENTA "\033[35;1m"
#define ANSI_ESC_CYAN "\033[36;1m"
#define ANSI_ESC_BOLD "\033[1m"
#define ANSI_ESC_UNDERLINE "\033[4m"
#define ANSI_ESC_RESET "\033[0m"
#define ANSI_RED(STR) ANSI_ESC_RED STR ANSI_ESC_RESET
#define ANSI_GREEN(STR) ANSI_ESC_GREEN STR ANSI_ESC_RESET
#define ANSI_YELLOW(STR) ANSI_ESC_YELLOW STR ANSI_ESC_RESET
#define ANSI_BLUE(STR) ANSI_ESC_BLUE STR ANSI_ESC_RESET
#define ANSI_MAGENTA(STR) ANSI_ESC_MAGENTA STR ANSI_ESC_RESET
#define ANSI_CYAN(STR) ANSI_ESC_CYAN STR ANSI_ESC_RESET
#define ANSI_BOLD(STR) ANSI_ESC_BOLD STR ANSI_ESC_RESET
#define ANSI_RED(STR) ANSI_ESC_RED STR ANSI_ESC_RESET
#define ANSI_GREEN(STR) ANSI_ESC_GREEN STR ANSI_ESC_RESET
#define ANSI_YELLOW(STR) ANSI_ESC_YELLOW STR ANSI_ESC_RESET
#define ANSI_BLUE(STR) ANSI_ESC_BLUE STR ANSI_ESC_RESET
#define ANSI_MAGENTA(STR) ANSI_ESC_MAGENTA STR ANSI_ESC_RESET
#define ANSI_CYAN(STR) ANSI_ESC_CYAN STR ANSI_ESC_RESET
#define ANSI_BOLD(STR) ANSI_ESC_BOLD STR ANSI_ESC_RESET
#define ANSI_UNDERLINE(STR) ANSI_ESC_UNDERLINE STR ANSI_ESC_RESET
#define ERL_ERROR ANSI_RED("ERROR")
#define ERL_WARNING ANSI_MAGENTA("WARNING")
/** @} */

View File

@@ -206,7 +206,6 @@ LIBCOM_API void fdManager::process (double delay)
this->pTimerQueue->process(epicsTime::getCurrent());
}
this->processInProg = false;
return;
}
//

View File

@@ -38,10 +38,9 @@ public:
// exceptions
//
class noFunctionSpecified {};
class doubleDelete {};
LIBCOM_API fdRegForOldFdmgr (const SOCKET fdIn, const fdRegType type,
const bool onceOnly, fdManager &manager, pCallBackFDMgr pFunc, void *pParam);
LIBCOM_API fdRegForOldFdmgr (SOCKET fdIn, fdRegType type,
bool onceOnly, fdManager &manager, pCallBackFDMgr pFunc, void *pParam);
LIBCOM_API ~fdRegForOldFdmgr ();
private:
@@ -66,7 +65,6 @@ public:
// exceptions
//
class noFunctionSpecified {};
class doubleDelete {};
private:
epicsTimer &timer;
oldFdmgr &fdmgr;
@@ -116,11 +114,7 @@ LIBCOM_API fdRegForOldFdmgr::fdRegForOldFdmgr
}
LIBCOM_API fdRegForOldFdmgr::~fdRegForOldFdmgr ()
{
if (this->pFunc==NULL) {
throwWithLocation ( doubleDelete () );
}
}
{}
LIBCOM_API void fdRegForOldFdmgr::callBack ()
{

View File

@@ -83,7 +83,6 @@ LIBCOM_API void epicsStdCall
pfl->lock = epicsMutexMustCreate();
*ppvt = (void *)pfl;
VALGRIND_CREATE_MEMPOOL(pfl, REDZONE, 0);
return;
}
LIBCOM_API void * epicsStdCall freeListCalloc(void *pvt)

View File

@@ -56,7 +56,6 @@ void epicsStdCall gphInitPvt(gphPvt **ppvt, int size)
pgphPvt->paplist = callocMustSucceed(size, sizeof(ELLLIST *), "gphInitPvt");
pgphPvt->lock = epicsMutexMustCreate();
*ppvt = pgphPvt;
return;
}
GPHENTRY * epicsStdCall gphFindParse(gphPvt *pgphPvt, const char *name, size_t len, void *pvtid)
@@ -171,7 +170,6 @@ void epicsStdCall gphDelete(gphPvt *pgphPvt, const char *name, void *pvtid)
}
epicsMutexUnlock(pgphPvt->lock);
return;
}
void epicsStdCall gphFreeMem(gphPvt *pgphPvt)

View File

@@ -142,6 +142,7 @@ const char *initHookName(int state)
"initHookAfterInterruptAccept",
"initHookAtEnd"
};
STATIC_ASSERT(NELEMENTS(stateName)==initHookAtEnd+1);
if (state < 0 || state >= NELEMENTS(stateName)) {
return "Not an initHookState";
}

View File

@@ -8,42 +8,44 @@
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/**
* \file initHooks.h
/** @page inithooks IOC lifecycle callback hooks.
*
* \author Benjamin Franksen (BESSY)
* \author Marty Kraimer (ANL)
* initHookRegister() allows external code to be called at certains points (initHookState)
* during IOC startup and shutdown.
*
* \brief Facility to call functions during iocInit()
* The overall states (see getIocState() ) of an IOC are:
*
* The initHooks facility allows application functions to be called at various
* stages/states during IOC initialization, pausing, restart and shutdown.
*
* All registered application functions will be called whenever the IOC
* initialization, pause/resume or shutdown process reaches a new state.
* - Void
* - From process start until iocInit()
* - After iocShutdown()
* - Building
* - Transiant state during iocInit()
* - Built
* - Transiant state during iocInit()
* - Running
* - After iocInit() or iocRun()
* - Paused
* - After iocPause()
*
* The following C++ example shows how to use this facility:
*
* \code{.cpp}
* #include <initHooks.h>
* #include <epicsExport.h>
* static void myHookFunction(initHookState state)
* {
* switch (state) {
* case initHookAfterInitRecSup:
* ...
* break;
* case initHookAfterDatabaseRunning:
* ...
* break;
* default:
* break;
* if(state == initHookAfterDatabaseRunning) {
* // a good point for driver worker threads to be started.
* }
* }
*
* // A static constructor registers hook function at startup:
* static int myHookStatus = initHookRegister(myHookFunction);
* static void myRegistrar(void) {
* initHookRegister(&myHookFunction);
* }
* extern "C" {
* epicsExportRegistrar(myRegistrar);
* }
* // in some .dbd file add "registrar(myRegistrar)".
* \endcode
*
* An arbitrary number of functions can be registered.
*/
@@ -69,41 +71,73 @@ extern "C" {
* if the IOC is later paused and restarted.
*/
typedef enum {
// iocInit() begins
initHookAtIocBuild = 0, /**< Start of iocBuild() / iocInit() */
initHookAtBeginning, /**< Database sanity checks passed */
initHookAfterCallbackInit, /**< Callbacks, generalTime & taskwd init */
initHookAfterCaLinkInit, /**< CA links init */
initHookAfterInitDrvSup, /**< Driver support init */
initHookAfterInitRecSup, /**< Record support init */
initHookAfterInitDevSup, /**< Device support init pass 0 */
initHookAfterInitDatabase, /**< Records and locksets init */
initHookAfterInitDevSup, /**< Device support init pass 0 (also autosave pass 0) */
initHookAfterInitDatabase, /**< Records and locksets init (also autosave pass 1) */
initHookAfterFinishDevSup, /**< Device support init pass 1 */
initHookAfterScanInit, /**< Scan, AS, ProcessNotify init */
initHookAfterInitialProcess, /**< Records with PINI = YES processsed */
initHookAfterCaServerInit, /**< RSRV init */
initHookAfterIocBuilt, /**< End of iocBuild() */
// iocInit() continues, and iocRun() begins
initHookAtIocRun, /**< Start of iocRun() */
initHookAfterDatabaseRunning, /**< Scan tasks and CA links running */
initHookAfterCaServerRunning, /**< RSRV running */
initHookAfterIocRunning, /**< End of iocRun() / iocInit() */
// iocInit() or iocRun() ends
// iocPause() begins
initHookAtIocPause, /**< Start of iocPause() */
initHookAfterCaServerPaused, /**< RSRV paused */
initHookAfterDatabasePaused, /**< CA links and scan tasks paused */
initHookAfterIocPaused, /**< End of iocPause() */
// iocPause() ends
initHookAtShutdown, /**< Start of iocShutdown() (unit tests only) */
initHookAfterCloseLinks, /**< Links disabled/deleted */
initHookAfterStopScan, /**< Scan tasks stopped. Prior to UNRELEASED, triggered only by unittest code. */
initHookAfterStopCallback, /**< Callback tasks stopped */
initHookAfterStopLinks, /**< CA links stopped. Prior to UNRELEASED, triggered only by unittest code. */
initHookBeforeFree, /**< Resource cleanup about to happen */
initHookAfterShutdown, /**< End of iocShutdown() */
// iocShutdown() begins
/** \brief Start of iocShutdown() (unit tests only)
* \since 7.0.3.1 Added
*/
initHookAtShutdown,
/** \brief Links disabled/deleted
* \since 7.0.3.1 Added
*/
initHookAfterCloseLinks,
/** \brief Scan tasks stopped.
* \since UNRELEASED Triggered during normal IOC shutdown
* \since 7.0.3.1 Added, triggered only by unittest code.
*/
initHookAfterStopScan,
/** \brief Callback tasks stopped
* \since 7.0.3.1 Added
*/
initHookAfterStopCallback,
/** \brief CA links stopped.
* \since UNRELEASED Triggered during normal IOC shutdown
* \since 7.0.3.1 Added, triggered only by unittest code.
*/
initHookAfterStopLinks,
/** \brief Resource cleanup about to happen
* \since 7.0.3.1 Added
*/
initHookBeforeFree,
/** \brief End of iocShutdown()
* \since 7.0.3.1 Added
*/
initHookAfterShutdown,
// iocShutdown() ends
/* Deprecated states: */
initHookAfterInterruptAccept, /**< After initHookAfterDatabaseRunning */
initHookAtEnd, /**< Before initHookAfterIocRunning */
/** Only announced once. Deprecated in favor of initHookAfterDatabaseRunning */
initHookAfterInterruptAccept,
/** Only announced once. Deprecated in favor of initHookAfterIocRunning */
initHookAtEnd,
} initHookState;
/** \brief Type for application callback functions

View File

@@ -16,6 +16,7 @@
#include <vector>
#include <map>
#include <string>
#include <sstream>
#include <stddef.h>
#include <string.h>
@@ -43,6 +44,7 @@
#if EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_READLINE
# include <readline/readline.h>
# include <readline/history.h>
# define USE_READLINE
/* libedit also provides readline.h, but isn't fully compatible with
* GNU readline. It also doesn't specifically identify itself.
@@ -84,9 +86,10 @@ static struct iocshVariable *iocshVariableHead;
static char iocshVarID[] = "iocshVar";
extern "C" { static void varCallFunc(const iocshArgBuf *); }
static epicsMutexId iocshTableMutex;
static epicsThreadOnceId iocshOnceId = EPICS_THREAD_ONCE_INIT;
static epicsThreadPrivateId iocshContextId;
static void iocshInit (void);
/*
* I/O redirection
*/
@@ -121,20 +124,6 @@ struct iocshRedirect {
};
} // namespace
/*
* Set up module variables
*/
static void iocshOnce (void *)
{
iocshTableMutex = epicsMutexMustCreate ();
iocshContextId = epicsThreadPrivateCreate();
}
static void iocshInit (void)
{
epicsThreadOnce (&iocshOnceId, iocshOnce, NULL);
}
/*
* Lock the table mutex
*/
@@ -157,19 +146,18 @@ iocshTableUnlock (void)
/*
* Register a command
*/
void epicsStdCall iocshRegister (const iocshFuncDef *piocshFuncDef,
static
void iocshRegisterImpl (const iocshFuncDef *piocshFuncDef,
iocshCallFunc func)
{
struct iocshCommand *l, *p, *n;
int i;
iocshTableLock ();
for (l = NULL, p = iocshCommandHead ; p != NULL ; l = p, p = p->next) {
i = strcmp (piocshFuncDef->name, p->def.pFuncDef->name);
if (i == 0) {
p->def.pFuncDef = piocshFuncDef;
p->def.func = func;
iocshTableUnlock ();
return;
}
if (i < 0)
@@ -179,7 +167,6 @@ void epicsStdCall iocshRegister (const iocshFuncDef *piocshFuncDef,
"iocshRegister");
if (!registryAdd(iocshCmdID, piocshFuncDef->name, (void *)n)) {
free (n);
iocshTableUnlock ();
errlogPrintf ("iocshRegister failed to add %s\n", piocshFuncDef->name);
return;
}
@@ -193,10 +180,15 @@ void epicsStdCall iocshRegister (const iocshFuncDef *piocshFuncDef,
}
n->def.pFuncDef = piocshFuncDef;
n->def.func = func;
iocshTableUnlock ();
}
void epicsStdCall iocshRegister (const iocshFuncDef *piocshFuncDef,
iocshCallFunc func)
{
iocshTableLock ();
iocshRegisterImpl (piocshFuncDef, func);
iocshTableUnlock ();
}
/*
* Retrieves a previously registered function with the given name.
*/
@@ -212,7 +204,7 @@ showError (const char *filename, int lineno, const char *msg, ...)
va_start (ap, msg);
if (filename)
fprintf(epicsGetStderr(), "%s line %d: ", filename, lineno);
fprintf(epicsGetStderr(), ERL_ERROR " %s line %d: ", filename, lineno);
vfprintf (epicsGetStderr(), msg, ap);
fputc ('\n', epicsGetStderr());
va_end (ap);
@@ -647,6 +639,15 @@ struct ReadlineContext {
rl_completer_quote_characters = (char*)"\"";
rl_attempted_completion_function = &iocsh_attempt_completion;
rl_bind_key('\t', rl_complete);
compute_hist_file();
if(!hist_file.empty()) {
if(int err = read_history(hist_file.c_str())) {
if(err!=ENOENT)
fprintf(stderr, ERL_ERROR " %s (%d) loading '%s'\n",
strerror(err), err, hist_file.c_str());
}
stifle_history(1024); // some limit...
}
}
#endif
return context;
@@ -655,6 +656,12 @@ struct ReadlineContext {
~ReadlineContext() {
if(context) {
#ifdef USE_READLINE
if(!hist_file.empty()) {
if(int err = write_history(hist_file.c_str())) {
fprintf(stderr, ERL_ERROR " %s (%d) writing '%s'\n",
strerror(err), err, hist_file.c_str());
}
}
rl_readline_name = prev_rl_readline_name;
rl_basic_word_break_characters = prev_rl_basic_word_break_characters;
rl_completer_word_break_characters = prev_rl_completer_word_break_characters;
@@ -667,6 +674,26 @@ struct ReadlineContext {
epicsReadlineEnd(context);
}
}
#ifdef USE_READLINE
std::string hist_file;
void compute_hist_file() {
std::string scratch;
if(const char *env = getenv("EPICS_IOCSH_HISTFILE")) {
scratch = env;
} else {
scratch = ".iocsh_history";
}
const char *home = getenv("HOME");
if(home && scratch.size()>=2 && scratch[0]=='~' && scratch[1]=='/') {
std::ostringstream strm;
strm<<home<<'/'<<scratch.substr(2);
scratch = strm.str();
}
hist_file.swap(scratch);
}
#endif // USE_READLINE
};
} // namespace
@@ -769,6 +796,13 @@ void epicsStdCall iocshFree(void)
iocshTableUnlock ();
}
/*
* Parse argument input based on the arg type specified.
* It is worth noting that depending on type this argument may
* be defaulted if a value is not specified. For example, a
* double/int with no value will default to 0 which may allow
* you to add optional arguments to the end of your argument list.
*/
static int
cvtArg (const char *filename, int lineno, char *arg, iocshArgBuf *argBuf,
const iocshArg *piocshArg)
@@ -902,15 +936,19 @@ static void helpCallFunc(const iocshArgBuf *args)
"Type 'help <command>' to see the arguments of <command>. eg. 'help db*'\n");
}
else {
bool firstFunction = true;
for (int iarg = 1 ; iarg < argc ; iarg++) {
for (pcmd = iocshCommandHead ; pcmd != NULL ; pcmd = pcmd->next) {
piocshFuncDef = pcmd->def.pFuncDef;
if (epicsStrGlobMatch(piocshFuncDef->name, argv[iarg]) != 0) {
if(piocshFuncDef->usage) {
fputs("\nUsage: ", epicsGetStdout());
if (! firstFunction) {
fprintf(epicsGetStdout(),
ANSI_UNDERLINE(" \n"));
}
fprintf(epicsGetStdout(),
ANSI_BOLD("%s"),
ANSI_BOLD("\n%s"),
piocshFuncDef->name);
for (int a = 0 ; a < piocshFuncDef->nargs ; a++) {
@@ -923,11 +961,14 @@ static void helpCallFunc(const iocshArgBuf *args)
fprintf(epicsGetStdout(), " '%s'", cp);
}
}
fprintf(epicsGetStdout(),"\n");;
fprintf(epicsGetStdout(),"\n");
if(piocshFuncDef->usage) {
fprintf(epicsGetStdout(), "\n%s", piocshFuncDef->usage);
}
firstFunction = false;
}
}
}
}
@@ -1397,12 +1438,12 @@ static void varCallFunc(const iocshArgBuf *args)
found = 1;
}
if (!found && name != NULL)
fprintf(epicsGetStderr(), "No var matching %s found.\n", name);
fprintf(epicsGetStderr(), ANSI_RED("No var matching") " %s found.\n", name);
}
else {
v = (iocshVariable *)registryFind(iocshVarID, args[0].sval);
if (v == NULL) {
fprintf(epicsGetStderr(), "Var %s not found.\n", name);
fprintf(epicsGetStderr(), "Var %s " ANSI_RED("not found.") "\n", name);
}
else {
varHandler(v->pVarDef, value);
@@ -1455,7 +1496,7 @@ static const iocshFuncDef onFuncDef = {"on", 1, onArgs,
" continue (default) - Ignores error and continue with next commands.\n"
" break - Return to caller without executing futher commands.\n"
" halt - Suspend process.\n"
" wait - stall process for [value] seconds, the continue.\n"};
" wait - stall process for [value] seconds, then continue.\n"};
static void onCallFunc(const iocshArgBuf *args)
{
iocshContext *context = (iocshContext *) epicsThreadPrivateGet(iocshContextId);
@@ -1527,24 +1568,25 @@ static void exitCallFunc(const iocshArgBuf *)
{
}
static void localRegister (void)
static void iocshOnce (void *)
{
iocshRegister(&commentFuncDef,commentCallFunc);
iocshRegister(&exitFuncDef,exitCallFunc);
iocshRegister(&helpFuncDef,helpCallFunc);
iocshRegister(&iocshCmdFuncDef,iocshCmdCallFunc);
iocshRegister(&iocshLoadFuncDef,iocshLoadCallFunc);
iocshRegister(&iocshRunFuncDef,iocshRunCallFunc);
iocshRegister(&onFuncDef, onCallFunc);
iocshTableMutex = epicsMutexMustCreate ();
iocshContextId = epicsThreadPrivateCreate();
epicsMutexMustLock (iocshTableMutex);
iocshRegisterImpl(&commentFuncDef,commentCallFunc);
iocshRegisterImpl(&exitFuncDef,exitCallFunc);
iocshRegisterImpl(&helpFuncDef,helpCallFunc);
iocshRegisterImpl(&iocshCmdFuncDef,iocshCmdCallFunc);
iocshRegisterImpl(&iocshLoadFuncDef,iocshLoadCallFunc);
iocshRegisterImpl(&iocshRunFuncDef,iocshRunCallFunc);
iocshRegisterImpl(&onFuncDef, onCallFunc);
iocshTableUnlock();
}
static void iocshInit (void)
{
static epicsThreadOnceId iocshOnceId = EPICS_THREAD_ONCE_INIT;
epicsThreadOnce (&iocshOnceId, iocshOnce, NULL);
}
} /* extern "C" */
/*
* Register local commands on application startup
*/
class IocshRegister {
public:
IocshRegister() { localRegister(); }
};
static IocshRegister iocshRegisterObj;

View File

@@ -191,7 +191,9 @@ static const iocshArg * const epicsEnvShowArgs[1] = {&epicsEnvShowArg0};
static const iocshFuncDef epicsEnvShowFuncDef = {"epicsEnvShow",1,epicsEnvShowArgs,
"Show environment variables on your system\n"
" (default) - show all environment variables\n"
" name - show value of specific environment variable\n"};
" name - show value of specific environment variable\n"
"Example: epicsEnvShow\n"
"Example: epicsEnvShow PATH"};
static void epicsEnvShowCallFunc(const iocshArgBuf *args)
{
epicsEnvShow (args[0].sval);
@@ -258,10 +260,11 @@ static void eltcCallFunc(const iocshArgBuf *args)
/* errlogInit */
static const iocshArg errlogInitArg0 = { "bufSize",iocshArgInt};
static const iocshArg * const errlogInitArgs[1] = {&errlogInitArg0};
static const iocshFuncDef errlogInitFuncDef =
{"errlogInit",1,errlogInitArgs,
"Initialize error log client buffer size\n"
" bufSize - size of circular buffer (default = 1280 bytes)\n"};
static const iocshFuncDef errlogInitFuncDef = {
"errlogInit",1,errlogInitArgs,
"Initialize error log client buffer size\n"
" bufSize - size of circular buffer (default = 1280 bytes)\n"
};
static void errlogInitCallFunc(const iocshArgBuf *args)
{
errlogInit(args[0].ival);
@@ -272,11 +275,12 @@ static const iocshArg errlogInit2Arg0 = { "bufSize",iocshArgInt};
static const iocshArg errlogInit2Arg1 = { "maxMsgSize",iocshArgInt};
static const iocshArg * const errlogInit2Args[] =
{&errlogInit2Arg0, &errlogInit2Arg1};
static const iocshFuncDef errlogInit2FuncDef =
{"errlogInit2", 2, errlogInit2Args,
"Initialize error log client buffer size and maximum message size\n"
" bufSize - size of circular buffer (default = 1280 bytes)\n"
" maxMsgSize - maximum size of error message (default = 256 bytes)\n"};
static const iocshFuncDef errlogInit2FuncDef = {
"errlogInit2", 2, errlogInit2Args,
"Initialize error log client buffer size and maximum message size\n"
" bufSize - size of circular buffer (default = 1280 bytes)\n"
" maxMsgSize - maximum size of error message (default = 256 bytes)\n"
};
static void errlogInit2CallFunc(const iocshArgBuf *args)
{
errlogInit2(args[0].ival, args[1].ival);
@@ -311,9 +315,8 @@ static void iocLogPrefixCallFunc(const iocshArgBuf *args)
/* epicsThreadShowAll */
static const iocshArg epicsThreadShowAllArg0 = { "level",iocshArgInt};
static const iocshArg * const epicsThreadShowAllArgs[1] = {&epicsThreadShowAllArg0};
static const iocshFuncDef epicsThreadShowAllFuncDef =
{"epicsThreadShowAll",1,epicsThreadShowAllArgs,
"Display info about all threads\n"};
static const iocshFuncDef epicsThreadShowAllFuncDef = {"epicsThreadShowAll",1,epicsThreadShowAllArgs,
"Display info about all threads\n"};
static void epicsThreadShowAllCallFunc(const iocshArgBuf *args)
{
epicsThreadShowAll(args[0].ival);
@@ -368,9 +371,8 @@ static void threadCallFunc(const iocshArgBuf *args)
/* taskwdShow */
static const iocshArg taskwdShowArg0 = { "level",iocshArgInt};
static const iocshArg * const taskwdShowArgs[1] = {&taskwdShowArg0};
static const iocshFuncDef taskwdShowFuncDef =
{"taskwdShow",1,taskwdShowArgs,
"Show number of tasks and monitors registered\n"};
static const iocshFuncDef taskwdShowFuncDef = {"taskwdShow",1,taskwdShowArgs,
"Show number of tasks and monitors registered\n"};
static void taskwdShowCallFunc(const iocshArgBuf *args)
{
taskwdShow(args[0].ival);
@@ -381,11 +383,12 @@ static const iocshArg epicsMutexShowAllArg0 = { "onlyLocked",iocshArgInt};
static const iocshArg epicsMutexShowAllArg1 = { "level",iocshArgInt};
static const iocshArg * const epicsMutexShowAllArgs[2] =
{&epicsMutexShowAllArg0,&epicsMutexShowAllArg1};
static const iocshFuncDef epicsMutexShowAllFuncDef =
{"epicsMutexShowAll",2,epicsMutexShowAllArgs,
"Display information about all epicsMutex semaphores\n"
" onlyLocked - non-zero to show only locked semaphores\n"
" level - desired information level to report\n"};
static const iocshFuncDef epicsMutexShowAllFuncDef = {
"epicsMutexShowAll",2,epicsMutexShowAllArgs,
"Display information about all epicsMutex semaphores\n"
" onlyLocked - non-zero to show only locked semaphores\n"
" level - desired information level to report\n"
};
static void epicsMutexShowAllCallFunc(const iocshArgBuf *args)
{
epicsMutexShowAll(args[0].ival,args[1].ival);
@@ -394,9 +397,8 @@ static void epicsMutexShowAllCallFunc(const iocshArgBuf *args)
/* epicsThreadSleep */
static const iocshArg epicsThreadSleepArg0 = { "seconds",iocshArgDouble};
static const iocshArg * const epicsThreadSleepArgs[1] = {&epicsThreadSleepArg0};
static const iocshFuncDef epicsThreadSleepFuncDef =
{"epicsThreadSleep",1,epicsThreadSleepArgs,
"Pause execution of IOC shell for <seconds> seconds\n"};
static const iocshFuncDef epicsThreadSleepFuncDef = {"epicsThreadSleep",1,epicsThreadSleepArgs,
"Pause execution of IOC shell for <seconds> seconds\n"};
static void epicsThreadSleepCallFunc(const iocshArgBuf *args)
{
epicsThreadSleep(args[0].dval);
@@ -451,18 +453,18 @@ static void epicsThreadResumeCallFunc(const iocshArgBuf *args)
static const iocshArg generalTimeReportArg0 = { "interest_level", iocshArgInt};
static const iocshArg * const generalTimeReportArgs[1] = { &generalTimeReportArg0 };
static const iocshFuncDef generalTimeReportFuncDef = {"generalTimeReport",1,generalTimeReportArgs,
"Display time providers and their priority levels"
" for current and event times\n"};
"Display time providers information for given interest level.\n"
"interest level 0 - List providers and their priorities.\n"
" 1 - Additionally show current time obtained from each provider.\n"};
static void generalTimeReportCallFunc(const iocshArgBuf *args)
{
generalTimeReport(args[0].ival);
}
/* installLastResortEventProvider */
static const iocshFuncDef installLastResortEventProviderFuncDef = {"installLastResortEventProvider", 0, NULL,
"Installs the optional Last Resort event provider"
" at priority 999,\nwhich returns the current time"
" for every event number\n"};
static const iocshFuncDef installLastResortEventProviderFuncDef = {"installLastResortEventProvider",0,NULL,
"Installs the optional Last Resort event provider at priority 999,\n"
"which returns the current time for every event number\n"};
static void installLastResortEventProviderCallFunc(const iocshArgBuf *args)
{
installLastResortEventProvider();

View File

@@ -711,8 +711,6 @@ static void freeLogClient(struct iocLogClient *pclient)
epicsSocketDestroy ( pclient->insock );
free (pclient);
return;
}

View File

@@ -559,7 +559,7 @@ void epicsStdCall iocLogPrefix(const char * prefix)
if (logClientPrefix) {
/* No error message if the new prefix is identical to the old one */
if (strcmp(logClientPrefix, prefix))
if (strcmp(logClientPrefix, prefix)!=0)
printf (ERL_WARNING " iocLogPrefix: The prefix was already set to "
"\"%s\" and can't be changed.\n", logClientPrefix);
return;

View File

@@ -72,7 +72,7 @@ static void trans ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
const char **rawval, char **value, char *valend );
static void cpy2val( const char *src, char **value, char *valend );
static void cpy2val( const char *src, char **value, const char *valend );
static char *Strdup( const char *string );
@@ -948,8 +948,6 @@ static void trans( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
still there to be seen) */
*rawval = ( *r == '\0' ) ? r - 1 : r;
*value = v;
return;
}
/*
@@ -1110,14 +1108,13 @@ cleanup:
*rawval = r;
*value = v;
return;
}
/*
* Copy a string, honoring the 'end of destination string' pointer
* Returns with **value pointing to the '\0' terminator
*/
static void cpy2val(const char *src, char **value, char *valend)
static void cpy2val(const char *src, char **value, const char *valend)
{
char *v = *value;
while ((v < valend) && (*v = *src++)) { v++; }

View File

@@ -15,13 +15,10 @@
#include <stdio.h>
#include <string.h>
#include "dbDefs.h"
#include "epicsTypes.h"
#include "osiSock.h"
#ifndef NELEMENTS
#define NELEMENTS(A) (sizeof(A)/sizeof(A[0]))
#endif /*NELEMENTS*/
/*
* addrArrayToUL ()
*/

View File

@@ -503,7 +503,7 @@ long devUnregisterAddress(
return S_dev_addressNotFound;
}
if (strcmp(pOwnerName,pRange->pOwnerName)) {
if (strcmp(pOwnerName,pRange->pOwnerName)!=0) {
s = S_dev_addressOverlap;
errPrintf (
s,

View File

@@ -78,14 +78,22 @@ LIBCOM_API void epicsAssert (const char *pFile, const unsigned line,
#if __cplusplus>=201103L
#define STATIC_ASSERT(expr) static_assert(expr, #expr)
#else
#define STATIC_JOIN(x, y) STATIC_JOIN2(x, y)
#define STATIC_JOIN2(x, y) x ## y
#define STATIC_JOIN(x, y, z, w) STATIC_JOIN4(x, y, z, w)
#define STATIC_JOIN4(x, y, z, w) x ## y ## z ## w
/* Compilers that do not support __COUNTER__ (e.g. GCC 4.1) will not be able to use unique static assert typedefs */
#ifdef __COUNTER__
# define STATIC_ASSERT_MSG(l) STATIC_JOIN(static_assert_, __COUNTER__, _failed_at_line_, l)
#else
# define STATIC_ASSERT_MSG(l) STATIC_JOIN(static_assert_, 0, _failed_at_line_, l)
#endif
/**\brief Declare a condition that should be true at compile-time.
* \param expr A C/C++ const-expression that should evaluate to True.
*/
#define STATIC_ASSERT(expr) \
typedef int STATIC_JOIN(static_assert_failed_at_line_, __LINE__) \
typedef int STATIC_ASSERT_MSG(__LINE__) \
[ (expr) ? 1 : -1 ] EPICS_UNUSED
#endif

View File

@@ -25,6 +25,8 @@ extern "C" {
/** \brief Dump a stack trace
*
* Dump a stack trace to the errlog.
*
* \since 3.15.0.2 Added
*/
LIBCOM_API void epicsStackTrace(void);

View File

@@ -275,18 +275,12 @@ void epicsThread::setPriority (unsigned int priority) throw ()
bool epicsThread::priorityIsEqual (const epicsThread &otherThread) const throw ()
{
if ( epicsThreadIsEqual (this->id, otherThread.id) ) {
return true;
}
return false;
return epicsThreadIsEqual (this->id, otherThread.id)!=0;
}
bool epicsThread::isSuspended () const throw ()
{
if ( epicsThreadIsSuspended (this->id) ) {
return true;
}
return false;
return epicsThreadIsSuspended (this->id)!=0;
}
bool epicsThread::operator == (const epicsThread &rhs) const throw ()

View File

@@ -44,8 +44,9 @@ void epicsTime::throwError(int code)
}
epicsTime::epicsTime ( const epicsTimeStamp & replace ) {
ts = replace;
epicsTime::epicsTime ( const epicsTimeStamp & replace )
:ts(replace)
{
if(ts.nsec >= nSecPerSec)
throw std::logic_error("epicsTimeStamp has overflow in nano-seconds field");
}

View File

@@ -36,6 +36,7 @@ typedef struct epicsThreadOSD {
int isEpicsThread;
int isRealTimeScheduled;
int isOnThreadList;
int isRunning;
unsigned int osiPriority;
int joinable;
char name[1]; /* actually larger */

View File

@@ -23,6 +23,7 @@
#include <sys/types.h>
#include <sys/prctl.h>
#include "epicsAtomic.h"
#include "epicsStdio.h"
#include "ellLib.h"
#include "epicsEvent.h"
@@ -45,11 +46,12 @@ void epicsThreadShowInfo(epicsThreadId pthreadInfo, unsigned int level)
if (!status)
priority = param.sched_priority;
}
fprintf(epicsGetStdout(),"%16.16s %14p %8lu %3d%8d %8.8s\n",
fprintf(epicsGetStdout(),"%16.16s %14p %8lu %3d%8d %8.8s%s\n",
pthreadInfo->name,(void *)
pthreadInfo,(unsigned long)pthreadInfo->lwpId,
pthreadInfo->osiPriority,priority,
pthreadInfo->isSuspended ? "SUSPEND" : "OK");
pthreadInfo->isSuspended ? "SUSPEND" : "OK",
epicsAtomicGetIntT(&pthreadInfo->isRunning) ? "" : " ZOMBIE");
}
}
@@ -58,7 +60,7 @@ static void thread_hook(epicsThreadId pthreadInfo)
/* Set the name of the thread's process. Limited to 16 characters. */
char comm[16];
if (strcmp(pthreadInfo->name, "_main_")) {
if (strcmp(pthreadInfo->name, "_main_") != 0) {
snprintf(comm, sizeof(comm), "%s", pthreadInfo->name);
prctl(PR_SET_NAME, comm, 0l, 0l, 0l);
}

View File

@@ -53,6 +53,7 @@ struct taskVar {
rtems_id join_barrier; /* only valid if joinable */
int refcnt;
int joinable;
int isRunning;
EPICSTHREADFUNC funptr;
void *parm;
unsigned int threadVariableCapacity;
@@ -197,6 +198,7 @@ threadWrapper (rtems_task_argument arg)
osdThreadHooksRun((epicsThreadId)v->id);
(*v->funptr)(v->parm);
epicsExitCallAtThreadExits ();
epicsAtomicSetIntT(&v->isRunning, 0);
taskVarLock ();
if (v->back)
v->back->forw = v->forw;
@@ -239,6 +241,7 @@ setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr,
v->refcnt = joinable ? 2 : 1;
v->threadVariableCapacity = 0;
v->threadVariables = NULL;
v->isRunning = 1;
if (joinable) {
char c[3];
strncpy(c, v->name, 3);
@@ -797,7 +800,11 @@ epicsThreadShowInfo (struct taskVar *v, unsigned int level)
fprintf(epicsGetStdout(),"+--------+-----------+--------+--------+---------------------+\n");
} else {
fprintf(epicsGetStdout(),"%9.8x", (int)v->id);
showInternalTaskInfo (v->id);
if(epicsAtomicGetIntT(&v->isRunning)) {
showInternalTaskInfo (v->id);
} else {
fprintf(epicsGetStdout(),"%-30s", " *** ZOMBIE task! ***");
}
fprintf(epicsGetStdout()," %s\n", v->name);
}
}

View File

@@ -102,6 +102,7 @@ typedef struct epicsThreadOSD {
unsigned epicsPriority;
char isSuspended;
int joinable;
int isRunning;
HANDLE timer; /* waitable timer */
} win32ThreadParam;
@@ -519,6 +520,8 @@ static unsigned WINAPI epicsWin32ThreadEntry ( LPVOID lpParameter )
epicsExitCallAtThreadExits ();
epicsAtomicSetIntT(&pParm->isRunning, 0);
/* On Windows we could omit this and rely on the callback given to FlsAlloc() to free.
* However < vista doesn't implement FLS at all, and WINE (circa 5.0.3) doesn't
* implement fully (dtor never runs). So for EPICS threads, we explicitly
@@ -540,6 +543,7 @@ static win32ThreadParam * epicsThreadParmCreate ( const char *pName )
pParmWIN32->pName = (char *) ( pParmWIN32 + 1 );
strcpy ( pParmWIN32->pName, pName );
pParmWIN32->isSuspended = 0;
pParmWIN32->isRunning = 1;
epicsAtomicIncrIntT(&pParmWIN32->refcnt);
#ifdef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
pParmWIN32->timer = CreateWaitableTimerEx(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
@@ -1044,6 +1048,8 @@ static void epicsThreadShowInfo ( epicsThreadId id, unsigned level )
fprintf (epicsGetStdout(), " %-8p %-8p ",
(void *) pParm->handle, (void *) pParm->parm );
}
if(!epicsAtomicGetIntT(&pParm->isRunning))
fprintf (epicsGetStdout(), " ZOMBIE");
}
else {
fprintf (epicsGetStdout(),

View File

@@ -12,10 +12,13 @@
#include <stdlib.h>
// execinfo.h may not be present if uclibc is configured to omit backtrace()
#if !defined(__UCLIBC_MAJOR__) || defined(__UCLIBC_HAS_EXECINFO__)
// some C libraries, such as musl, don't have execinfo.h at all
#if defined(__has_include)
# if __has_include(<execinfo.h>)
# define HAS_EXECINFO 1
# endif
#elif !defined(__UCLIBC_MAJOR__) || defined(__UCLIBC_HAS_EXECINFO__)
# define HAS_EXECINFO 1
#else
# define HAS_EXECINFO 0
#endif
#if HAS_EXECINFO

View File

@@ -173,6 +173,7 @@ static epicsThreadOSD * create_threadInfo(const char *name)
pthreadInfo = calloc(1,sizeof(*pthreadInfo) + strlen(name));
if(!pthreadInfo)
return NULL;
pthreadInfo->isRunning = 1;
pthreadInfo->suspendEvent = epicsEventCreate(epicsEventEmpty);
if(!pthreadInfo->suspendEvent){
free(pthreadInfo);
@@ -441,6 +442,8 @@ static void * start_routine(void *arg)
(*pthreadInfo->createFunc)(pthreadInfo->createArg);
epicsExitCallAtThreadExits ();
epicsAtomicSetIntT(&pthreadInfo->isRunning, 0);
return(0);
}

View File

@@ -34,6 +34,7 @@ typedef struct epicsThreadOSD {
int isEpicsThread;
int isRealTimeScheduled;
int isOnThreadList;
int isRunning;
unsigned int osiPriority;
int joinable;
char name[1]; /* actually larger */

View File

@@ -12,6 +12,7 @@
/* This is part of the posix implementation of epicsThread */
#include "epicsAtomic.h"
#include "epicsStdio.h"
#include "ellLib.h"
#include "epicsEvent.h"
@@ -35,11 +36,12 @@ void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level)
status = pthread_getschedparam(pthreadInfo->tid,&policy,&param);
if(!status) priority = param.sched_priority;
}
fprintf(epicsGetStdout(),"%16.16s %14p %12lu %3d%8d %8.8s\n",
fprintf(epicsGetStdout(),"%16.16s %14p %12lu %3d%8d %8.8s%s\n",
pthreadInfo->name,(void *)
pthreadInfo,(unsigned long)pthreadInfo->tid,
pthreadInfo->osiPriority,priority,
pthreadInfo->isSuspended?"SUSPEND":"OK");
pthreadInfo->isSuspended?"SUSPEND":"OK",
epicsAtomicGetIntT(&pthreadInfo->isRunning) ? "" : " ZOMBIE");
}
}

View File

@@ -25,7 +25,7 @@
#define nDigitsDottedIP 4u
#define chunkSize 8u
#define makeMask(NBITS) ( ( 1u << ( (unsigned) NBITS) ) - 1u )
#define makeMask(NBITS) ( ( 1u << ( (unsigned) (NBITS)) ) - 1u )
/*
* sockAddrAreIdentical()

View File

@@ -42,7 +42,7 @@ epicsThreadPool* epicsThreadPoolCreate(epicsThreadPoolConfig *opts)
/* caller likely didn't initialize the options structure */
if (opts && opts->maxThreads == 0) {
errlogMessage("Error: epicsThreadPoolCreate() options provided, but not initialized");
errlogMessage(ERL_ERROR ": epicsThreadPoolCreate() options provided, but not initialized");
return NULL;
}
@@ -78,7 +78,7 @@ epicsThreadPool* epicsThreadPoolCreate(epicsThreadPoolConfig *opts)
if (pool->threadsRunning == 0 && pool->conf.initialThreads != 0) {
epicsMutexUnlock(pool->guard);
errlogPrintf("Error: Unable to create any threads for thread pool\n");
errlogPrintf(ERL_ERROR ": Unable to create any threads for thread pool\n");
goto cleanup;
}

View File

@@ -210,7 +210,7 @@ extern "C" epicsTimerQueueId epicsStdCall
epicsSingleton < timerQueueActiveMgr > :: reference ref =
timerQueueMgrEPICS.getReference ();
epicsTimerQueueActiveForC & tmr =
ref->allocate ( ref, okToShare ? true : false, threadPriority );
ref->allocate ( ref, okToShare != 0, threadPriority );
return &tmr;
}
catch ( ... ) {

View File

@@ -24,6 +24,11 @@
#include "epicsTimer.h"
#include "compilerDependencies.h"
#if __cplusplus<201103L
# define final
# define override
#endif
#ifdef DEBUG
# define debugPrintf(ARGSINPAREN) printf ARGSINPAREN
#else
@@ -34,12 +39,12 @@ template < class T > class epicsGuard;
class timer : public epicsTimer, public tsDLNode < timer > {
public:
void destroy ();
void start ( class epicsTimerNotify &, const epicsTime & );
void start ( class epicsTimerNotify &, double delaySeconds );
void cancel ();
expireInfo getExpireInfo () const;
void show ( unsigned int level ) const;
void destroy () override;
void start ( class epicsTimerNotify &, const epicsTime & ) override final;
void start ( class epicsTimerNotify &, double delaySeconds ) override final;
void cancel () override final;
expireInfo getExpireInfo () const override final;
void show ( unsigned int level ) const override final;
void * operator new ( size_t size, tsFreeList < timer, 0x20 > & );
epicsPlacementDeleteOperator (( void *, tsFreeList < timer, 0x20 > & ))
protected:
@@ -62,9 +67,9 @@ private:
friend class timerQueue;
};
struct epicsTimerForC : public epicsTimerNotify, public timer {
struct epicsTimerForC final : public epicsTimerNotify, public timer {
public:
void destroy ();
void destroy () override final;
protected:
epicsTimerForC ( timerQueue &, epicsTimerCallback, void *pPrivateIn );
~epicsTimerForC ();
@@ -73,7 +78,7 @@ protected:
private:
epicsTimerCallback pCallBack;
void * pPrivate;
expireStatus expire ( const epicsTime & currentTime );
expireStatus expire ( const epicsTime & currentTime ) override final;
epicsTimerForC & operator = ( const epicsTimerForC & );
// Visual C++ .net appears to require operator delete if
// placement operator delete is defined? I smell a ms rat
@@ -90,10 +95,10 @@ class timerQueue : public epicsTimerQueue {
public:
timerQueue ( epicsTimerQueueNotify &notify );
virtual ~timerQueue ();
epicsTimer & createTimer ();
epicsTimer & createTimer () override final;
epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg );
double process ( const epicsTime & currentTime );
void show ( unsigned int level ) const;
void show ( unsigned int level ) const override final;
private:
tsFreeList < timer, 0x20 > timerFreeList;
tsFreeList < epicsTimerForC, 0x20 > timerForCFreeList;
@@ -133,9 +138,9 @@ public:
typedef epicsSingleton < timerQueueActiveMgr > :: reference RefMgr;
timerQueueActive ( RefMgr &, bool okToShare, unsigned priority );
void start ();
epicsTimer & createTimer ();
epicsTimer & createTimer () override final;
epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg );
void show ( unsigned int level ) const;
void show ( unsigned int level ) const override final;
bool sharingOK () const;
unsigned threadPriority () const;
protected:
@@ -150,9 +155,9 @@ private:
bool okToShare;
int exitFlag; // use atomic ops
bool terminateFlag;
void run ();
void reschedule ();
double quantum ();
void run () override final;
void reschedule () override final;
double quantum () override final;
void _printLastChanceExceptionMessage (
const char * pExceptionTypeName,
const char * pExceptionContext );
@@ -161,7 +166,7 @@ private:
timerQueueActive & operator = ( const timerQueueActive & );
};
class timerQueueActiveMgr {
class timerQueueActiveMgr final {
public:
typedef epicsSingleton < timerQueueActiveMgr > :: reference RefThis;
timerQueueActiveMgr ();
@@ -181,10 +186,10 @@ extern epicsSingleton < timerQueueActiveMgr > timerQueueMgrEPICS;
class timerQueuePassive : public epicsTimerQueuePassive {
public:
timerQueuePassive ( epicsTimerQueueNotify & );
epicsTimer & createTimer ();
epicsTimer & createTimer () override final;
epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg );
void show ( unsigned int level ) const;
double process ( const epicsTime & currentTime );
void show ( unsigned int level ) const override final;
double process ( const epicsTime & currentTime ) override final;
protected:
timerQueue queue;
~timerQueuePassive ();
@@ -193,7 +198,7 @@ protected:
timerQueuePassive & operator = ( const timerQueuePassive & );
};
struct epicsTimerQueuePassiveForC :
struct epicsTimerQueuePassiveForC final :
public epicsTimerQueueNotify, public timerQueuePassive {
public:
epicsTimerQueuePassiveForC (
@@ -208,15 +213,15 @@ private:
epicsTimerQueueNotifyQuantum pSleepQuantumCallback;
void * pPrivate;
static epicsSingleton < tsFreeList < epicsTimerQueuePassiveForC, 0x10 > > pFreeList;
void reschedule ();
double quantum ();
void reschedule () override final;
double quantum () override final;
};
struct epicsTimerQueueActiveForC : public timerQueueActive,
struct epicsTimerQueueActiveForC final : public timerQueueActive,
public tsDLNode < epicsTimerQueueActiveForC > {
public:
epicsTimerQueueActiveForC ( RefMgr &, bool okToShare, unsigned priority );
void release ();
void release () override final;
void * operator new ( size_t );
void operator delete ( void * );
protected:

View File

@@ -269,6 +269,7 @@ TESTPROD_HOST += iocshTest
iocshTest_SRCS += iocshTest.cpp
TESTS += iocshTest
TESTFILES += $(wildcard ../iocshTest*.cmd)
TESTFILES += ../iocshTestHelpFunction1 ../iocshTestHelpFunctions
TESTPROD_HOST += epicsLoadTest
epicsLoadTest_SRCS += epicsLoadTest.cpp

View File

@@ -298,7 +298,7 @@ MAIN(epicsCalcTest)
const double a=1.0, b=2.0, c=3.0, d=4.0, e=5.0, f=6.0,
g=7.0, h=8.0, i=9.0, j=10.0, k=11.0, l=12.0;
testPlan(/*635*/633);
testPlan(637-2);
/* LITERAL_OPERAND elements */
testExpr(0);
@@ -953,6 +953,8 @@ MAIN(epicsCalcTest)
// testBadExpr("1?", CALC_ERR_CONDITIONAL);
// testBadExpr("1?1", CALC_ERR_CONDITIONAL);
testBadExpr(":1", CALC_ERR_SYNTAX);
testBadExpr("0,", CALC_ERR_BAD_SEPERATOR);
testBadExpr("0)", CALC_ERR_PAREN_NOT_OPEN);
// Bit manipulations wrt bit 31 (bug lp:1514520)
// using integer literals

View File

@@ -14,6 +14,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include "epicsUnitTest.h"
#include "epicsString.h"
@@ -198,10 +199,22 @@ void testStrTok(void)
static
void testEpicsStrPrintEscaped(void)
{
const char *filename = "testEpicsStrPrintEscaped";
const char *filename;
FILE *testFile;
FILE *readOnly;
#ifdef __rtems__
/* ensure there is a writable area */
mkdir( "/tmp", S_IRWXU );
filename = "/tmp/testEpicsStrPrintEscaped";
#else
filename = "testEpicsStrPrintEscaped";
#endif
// Avoid printing to stdout by redirecting everything to a file
FILE *testFile = fopen(filename, "a");
FILE *readOnly = fopen(filename, "r");
testFile = fopen(filename, "a");
readOnly = fopen(filename, "r");
testDiag("testEpicsStrPrintEscaped()");

View File

@@ -8,6 +8,8 @@
#include <string>
#include <fstream>
#include <set>
#include <sstream>
#include <stdexcept>
#include <osiUnistd.h>
#include <osiFileName.h>
@@ -83,14 +85,117 @@ void assertCallFunc(const iocshArgBuf *args)
iocshSetError(args[0].ival);
}
const iocshFuncDef testHelpFunction1Def = {"testHelpFunction1",0,0,
"Usage message of testHelpFunction1\n"};
const iocshFuncDef testHelpFunction2Def = {"testHelpFunction2",0,0,
"Usage message of testHelpFunction2\n"};
const iocshFuncDef testHelpFunction3Def = {"testHelpFunction3",0,0,
"Usage message of testHelpFunction3\n"};
void doNothing(const iocshArgBuf *args)
{
return;
}
std::string readFile(std::string filename)
{
std::ifstream t(filename.c_str());
std::stringstream buffer;
if (!t.is_open()) {
throw std::invalid_argument("Could not open filename " + filename);
}
buffer << t.rdbuf();
return buffer.str();
}
bool compareFiles(const std::string& p1, const std::string& p2)
{
std::ifstream f1(p1.c_str(), std::ifstream::binary|std::ifstream::ate);
std::ifstream f2(p2.c_str(), std::ifstream::binary|std::ifstream::ate);
if (f1.fail() || f2.fail()) {
testDiag("One or more files failed to open");
testDiag("f1.fail(): %d f2.fail(): %d", f1.fail(), f2.fail());
return false; // File problem
}
if (f1.tellg() != f2.tellg()) {
testDiag("File sizes did not match");
return false; // Size mismatch
}
// Seek back to beginning and use std::equal to compare contents
f1.seekg(0, std::ifstream::beg);
f2.seekg(0, std::ifstream::beg);
bool are_equal = std::equal(
std::istreambuf_iterator<char>(f1.rdbuf()),
std::istreambuf_iterator<char>(),
std::istreambuf_iterator<char>(f2.rdbuf())
);
if (! are_equal) {
testDiag("File contents did not match");
std::string line;
f1.seekg(0, std::ifstream::beg);
f2.seekg(0, std::ifstream::beg);
testDiag("File1 contents: ");
while(std::getline(f1, line)) {
testDiag("%s", line.c_str());
}
testDiag("File2 contents: ");
while(std::getline(f2, line)) {
testDiag("%s", line.c_str());
}
}
return are_equal;
}
void testHelp(void)
{
testDiag("iocshTest testHelp start");
// Filename to save help output to
const std::string filename = "testHelpOutput";
// Verify help lists expected commands
iocshCmd(("help > " + filename).c_str());
std::string contents = readFile(filename);
testOk1(contents.find("help") != std::string::npos);
testOk1(contents.find("testHelpFunction1") != std::string::npos);
testOk1(contents.find("testHelpFunction2") != std::string::npos);
testOk1(contents.find("testHelpFunction3") != std::string::npos);
// Confirm formatting of a single command
iocshCmd(("help testHelpFunction1 > " + filename).c_str());
testOk1(compareFiles(filename, "iocshTestHelpFunction1") == true);
// Confirm formatting of multiple commands
iocshCmd(("help testHelp* > " + filename).c_str());
testOk1(compareFiles(filename, "iocshTestHelpFunctions") == true);
remove(filename.c_str());
}
} // namespace
MAIN(iocshTest)
{
testPlan(19);
testPlan(25);
libComRegister();
iocshRegister(&positionFuncDef, &positionCallFunc);
iocshRegister(&assertFuncDef, &assertCallFunc);
iocshRegister(&testHelpFunction1Def, &doNothing);
iocshRegister(&testHelpFunction2Def, &doNothing);
iocshRegister(&testHelpFunction3Def, &doNothing);
findTestData();
testFile("iocshTestSuccess.cmd");
@@ -129,6 +234,8 @@ MAIN(iocshTest)
testPosition("after_error_1", false);
reached.clear();
testHelp();
// cleanup after macLib to avoid valgrind false positives
dbmfFreeChunks();
return testDone();

View File

@@ -0,0 +1,4 @@

testHelpFunction1
Usage message of testHelpFunction1

View File

@@ -0,0 +1,14 @@

testHelpFunction1
Usage message of testHelpFunction1


testHelpFunction2
Usage message of testHelpFunction2


testHelpFunction3
Usage message of testHelpFunction3

View File

@@ -17,6 +17,9 @@
/iocBoot/*ioc*/envPaths
/iocBoot/*ioc*/relPaths.sh
# iocsh
.iocsh_history
# Build directories
O.*/

View File

@@ -8,6 +8,9 @@
# Local configuration files
/configure/*.local
# iocsh
.iocsh_history
# Build directories
O.*/

View File

@@ -55,7 +55,7 @@ if (-d '_darcs') { # Darcs
# v1-4-dirty
# is tag 'v1' plus 4 patches
# with uncommited modifications
my @tags = `darcs show tags`;
my @tags = split('\n', `darcs show tags`);
my $count = `darcs changes --count --from-tag .` - 1;
my $result = $tags[0] . '-' . $count;
print "== darcs show tags, changes:\n$result\n==\n" if $opt_v;