Compare commits
36 Commits
PSI-7.0.8.
...
R7.0.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86154953f5 | ||
|
|
07572ab025 | ||
|
|
0733beae50 | ||
|
|
a3d8531008 | ||
|
|
7a6e11cae0 | ||
|
|
7384351181 | ||
|
|
890cbc2c0d | ||
|
|
2612b47c3f | ||
|
|
f4c474eb77 | ||
|
|
1735a821db | ||
|
|
1d19ba4cc2 | ||
|
|
144f9756ea | ||
|
|
48eed22f3b | ||
|
|
8ac2c87156 | ||
|
|
0d2ffcd97f | ||
|
|
c8eccfcb8f | ||
|
|
6fb40b02fe | ||
|
|
dad0ee9c89 | ||
|
|
065fe7cab6 | ||
|
|
e4ad4becde | ||
|
|
1cd141c540 | ||
| 72026a27a0 | |||
|
|
9fb820b46e | ||
|
|
721e9cc3a7 | ||
|
|
0186836449 | ||
|
|
b90ab7de13 | ||
|
|
333be085c0 | ||
|
|
169948967f | ||
|
|
5a11954c51 | ||
|
|
9f8a8b9c1f | ||
|
|
d0cf47cd6f | ||
|
|
f4aee8e6b7 | ||
| dac620a708 | |||
| 4ee766b6b1 | |||
| 5143258011 | |||
|
|
ead8b7e82b |
@@ -48,15 +48,15 @@ EPICS_VERSION = 7
|
||||
EPICS_REVISION = 0
|
||||
|
||||
# EPICS_MODIFICATION must be a number >=0 and <256
|
||||
EPICS_MODIFICATION = 8
|
||||
EPICS_MODIFICATION = 9
|
||||
|
||||
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
|
||||
# Not included in the official EPICS version number if zero
|
||||
EPICS_PATCH_LEVEL = 2
|
||||
EPICS_PATCH_LEVEL = 0
|
||||
|
||||
# Immediately after an official release the EPICS_PATCH_LEVEL is incremented
|
||||
# and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions)
|
||||
EPICS_DEV_SNAPSHOT=-DEV
|
||||
EPICS_DEV_SNAPSHOT=
|
||||
|
||||
# No changes should be needed below here
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ EPICS_CA_MAINTENANCE_VERSION = 5
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
EPICS_CA_DEVELOPMENT_FLAG = 1
|
||||
EPICS_CA_DEVELOPMENT_FLAG = 0
|
||||
|
||||
# Immediately after a release the MAINTENANCE_VERSION
|
||||
# will be incremented and the DEVELOPMENT_FLAG set to 1
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Version number for the database APIs and shared library
|
||||
|
||||
EPICS_DATABASE_MAJOR_VERSION = 3
|
||||
EPICS_DATABASE_MINOR_VERSION = 23
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 2
|
||||
EPICS_DATABASE_MINOR_VERSION = 24
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 0
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
EPICS_DATABASE_DEVELOPMENT_FLAG = 1
|
||||
EPICS_DATABASE_DEVELOPMENT_FLAG = 0
|
||||
|
||||
# Immediately after a release the MAINTENANCE_VERSION
|
||||
# will be incremented and the DEVELOPMENT_FLAG set to 1
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Version number for the libcom APIs and shared library
|
||||
|
||||
EPICS_LIBCOM_MAJOR_VERSION = 3
|
||||
EPICS_LIBCOM_MINOR_VERSION = 23
|
||||
EPICS_LIBCOM_MAINTENANCE_VERSION = 2
|
||||
EPICS_LIBCOM_MINOR_VERSION = 24
|
||||
EPICS_LIBCOM_MAINTENANCE_VERSION = 0
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
EPICS_LIBCOM_DEVELOPMENT_FLAG = 1
|
||||
EPICS_LIBCOM_DEVELOPMENT_FLAG = 0
|
||||
|
||||
# Immediately after a release the MAINTENANCE_VERSION
|
||||
# will be incremented and the DEVELOPMENT_FLAG set to 1
|
||||
|
||||
@@ -92,3 +92,6 @@ EPICS_IOC_LOG_FILE_NAME=
|
||||
EPICS_IOC_LOG_FILE_COMMAND=
|
||||
EPICS_IOC_LOG_FILE_LIMIT=1000000
|
||||
|
||||
# Set to 'YES' to call abort() rather than suspend the current thread
|
||||
# when an assert() fails
|
||||
EPICS_ABORT_ON_ASSERT=NO
|
||||
|
||||
11
configure/os/CONFIG.darwin-aarch64-debug.Common
Normal file
11
configure/os/CONFIG.darwin-aarch64-debug.Common
Normal file
@@ -0,0 +1,11 @@
|
||||
# CONFIG.darwin-aarch64-debug.Common
|
||||
#
|
||||
# Definitions for darwin-aarch64-debug host builds - darwin-aarch64 target build with debug compiler flags
|
||||
# Sites may override these definitions in CONFIG_SITE.darwin-aarch64-debug.Common
|
||||
#-------------------------------------------------------
|
||||
|
||||
include $(CONFIG)/os/CONFIG.darwin-aarch64.Common
|
||||
|
||||
# Removes -O optimization and adds -g compile option
|
||||
HOST_OPT=NO
|
||||
|
||||
14
configure/os/CONFIG.darwin-aarch64.darwin-aarch64-debug
Normal file
14
configure/os/CONFIG.darwin-aarch64.darwin-aarch64-debug
Normal file
@@ -0,0 +1,14 @@
|
||||
# CONFIG.darwin-aarch64.darwin-aarch64-debug
|
||||
#
|
||||
# Definitions for darwin-aarch64 host - darwin-aarch64-debug target build with debug compiler flags
|
||||
# Sites may override these definitions in CONFIG_SITE.darwin-aarch64.darwin-aarch64-debug
|
||||
#-------------------------------------------------------
|
||||
|
||||
-include $(CONFIG)/os/CONFIG.Common.darwin-aarch64
|
||||
-include $(CONFIG)/os/CONFIG.darwin-aarch64.darwin-aarch64
|
||||
-include $(CONFIG)/os/CONFIG_SITE.Common.darwin-aarch64
|
||||
-include $(CONFIG)/os/CONFIG_SITE.darwin-aarch64.darwin-aarch64
|
||||
|
||||
|
||||
BUILD_CLASS=HOST
|
||||
HOST_OPT = NO
|
||||
11
configure/os/CONFIG.darwin-x86-debug.Common
Normal file
11
configure/os/CONFIG.darwin-x86-debug.Common
Normal file
@@ -0,0 +1,11 @@
|
||||
# CONFIG.darwin-x86-debug.Common
|
||||
#
|
||||
# Definitions for darwin-x86-debug host builds - darwin-x86 target build with debug compiler flags
|
||||
# Sites may override these definitions in CONFIG_SITE.darwin-x86-debug.Common
|
||||
#-------------------------------------------------------
|
||||
|
||||
include $(CONFIG)/os/CONFIG.darwin-x86.Common
|
||||
|
||||
# Removes -O optimization and adds -g compile option
|
||||
HOST_OPT=NO
|
||||
|
||||
@@ -64,7 +64,3 @@ COMMANDLINE_LIBRARY ?= EPICS
|
||||
#else
|
||||
COMMANDLINE_LIBRARY ?= $(strip $(if $(wildcard $(if $(GNU_DIR),$(GNU_DIR)/include/readline/readline.h)), READLINE, EPICS))
|
||||
#endif
|
||||
|
||||
#if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE>2
|
||||
OP_SYS_CPPFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2
|
||||
#endif
|
||||
|
||||
@@ -8,41 +8,134 @@ under the 3.15 release to which they were originally committed.** Thus it is
|
||||
important to read more than just the first section to understand everything that
|
||||
has changed in each release.
|
||||
|
||||
The PVA submodules each have their own individual sets of release notes which
|
||||
should also be read to understand what has changed since earlier releases:
|
||||
The external PVA submodules each have their own individual release notes files.
|
||||
However the entries describing changes included in those submodules since EPICS
|
||||
7.0.5 have now been copied into the appropriate place of this file.
|
||||
|
||||
- [normativeTypes](https://github.com/epics-base/normativeTypesCPP/blob/master/documentation/RELEASE_NOTES.md)
|
||||
- [pvAccess](http://epics-base.github.io/pvAccessCPP/pvarelease_notes.html)
|
||||
- [pvData](http://epics-base.github.io/pvDataCPP/release_notes.html)
|
||||
- [pvDatabase](https://github.com/epics-base/pvDatabaseCPP/blob/master/documentation/RELEASE_NOTES.md)
|
||||
- [pva2pva](https://epics-base.github.io/pva2pva/release_notes.html)
|
||||
- [pvaClient](https://github.com/epics-base/pvaClientCPP/blob/master/documentation/RELEASE_NOTES.md)
|
||||
## EPICS Release 7.0.9
|
||||
|
||||
**This version of EPICS has not been released yet.**
|
||||
### Core documentation published at ReadTheDocs
|
||||
|
||||
## Changes made on the 7.0 branch since 7.0.8.1
|
||||
The `documentation` directory's `Makefile` can now run various publication scripts including Sphinx and Doxygen to generate formatted documentation that is now being published
|
||||
[at docs.epics-controls.org](https://docs.epics-controls.org/projects/base/en/latest/index.html)
|
||||
and integrated into the main [EPICS Documentation website](https://docs.epics-controls.org/en/latest/index.html).
|
||||
The best place to find out more about these mechanisms is the
|
||||
[Contribution Guide](https://docs.epics-controls.org/en/latest/CONTRIBUTING.html)
|
||||
although it doesn't currently cover the new processes added to epics-base.
|
||||
|
||||
### DBE_PROPERTY event rate changed
|
||||
Much of the documentation generated from .dbd.pod files at build time is now
|
||||
also being converted into MarkDown (.md) files and installed into the top-level
|
||||
`doc` directory. Some users might find it quicker to look up information about a
|
||||
record type by opening these files in a text editor intead of opening a browser
|
||||
and loading the HTML versions or finding and opening the files from the EPICS
|
||||
Documentation site.
|
||||
|
||||
Updating property fields now only post DBE_PROPERTY events if the
|
||||
### Post monitors from compress record when it's reset
|
||||
|
||||
Writing into a compress record's `RES` field now posts a monitor event instead
|
||||
of only changing `VAL`. Monitor clients will therefore receive an empty array.
|
||||
|
||||
### The AMSG error message propagates through MSS links
|
||||
|
||||
A database link with the MSS attribute will now propagate not only SEVR and
|
||||
STAT, but also AMSG. This field contains additional information that complements
|
||||
STAT. Links with MS or MSI attributes do not propagate STAT, and therefore do
|
||||
not propagate AMSG, either.
|
||||
|
||||
Channel Access links do not propagate AMSG, regardless of the MSS attribute,
|
||||
because the message is not available as Channel metadata.
|
||||
|
||||
### Reloading record aliases
|
||||
|
||||
Aliases can now be defined more than once as long as they still refer to the
|
||||
same record, unless the global variable `dbRecordsOnceOnly` is non-zero.
|
||||
This allows database files to be loaded multiple times, even if they contain
|
||||
alias definitions.
|
||||
|
||||
### `DBE_PROPERTY` event rate changed
|
||||
|
||||
Updating property fields now only posts `DBE_PROPERTY` events if the
|
||||
field actually changed.
|
||||
|
||||
### Changes to msi related to include paths
|
||||
|
||||
There are two changes to `msi` included here.
|
||||
|
||||
`msi` now treats files included by .template or .substutiions files in a more
|
||||
consistent way: for relative paths, it will always look relative to the current
|
||||
working directory if no `-I` flags are passed, and if they are passed then it
|
||||
will search for the _relative_ path from each of those flags. That is, the
|
||||
following will now find the file `bar.template` located at
|
||||
`/some/path/rel/path/bar.template`
|
||||
```
|
||||
$ cat foo.substitutions
|
||||
file rel/path/bar.template {
|
||||
# contents
|
||||
}
|
||||
$ msi -I /some/path foo.substitutions
|
||||
```
|
||||
|
||||
Note that this does provide one change from previous behaviour: when opening a
|
||||
file from the command line, `msi` will not use the `-I`-specified paths to
|
||||
search for the file, but will only work relative to the current working
|
||||
directory, consistent with most commandline utilities.
|
||||
|
||||
### Allow users to delete previously created records from the database
|
||||
|
||||
From this release, record instances and aliases that have already been loaded
|
||||
by an IOC can be removed from the database again before the call to iocInit
|
||||
by loading a second instance of the named records but using `"#"` in place of
|
||||
the record type. Values for the fields are not required or advised, just use
|
||||
an empty record body { }. This is useful when a template defines records that
|
||||
an empty record body `{}`. This is useful when a template defines records that
|
||||
are not wanted in some IOCs, without having to split or duplicate the original
|
||||
template.
|
||||
|
||||
For example this will remove the record named "unwanted":
|
||||
|
||||
```
|
||||
record("#", "unwanted") { }
|
||||
record("#", "unwanted") {}
|
||||
```
|
||||
|
||||
### Only keep readline history for interactive sessions
|
||||
|
||||
Previously, all IOCsh commands were persisited in the libreadline history
|
||||
(when readline support is included).
|
||||
Going forward, only interactive commands are saved.
|
||||
|
||||
### Type change to asTrap serverSpecific data
|
||||
|
||||
Change `void*` to `dbChannel*` in `asTrapWriteBeforeWithData()` and
|
||||
`asTrapWriteMessage::serverSpecific` to reflect the reality since
|
||||
the `dbAddr*` to `dbChannel*` migration.
|
||||
External code wishing to support both before and after 3.15 should
|
||||
already be conditionally casting to/from the appropriate type.
|
||||
|
||||
### Fix issues with `_FORTIFY_SOURCE=3`
|
||||
|
||||
This release fixes the false positives failures whhen building with `_FORTIFY_SOURCE` level 3.
|
||||
The override introduced in 7.0.8.1 has been removed.
|
||||
|
||||
### Other
|
||||
|
||||
- genVersionHeader: work with git submodules and worktrees.
|
||||
- avoid UB with self `pthread_join()`
|
||||
- freebsd: Add support for x86 and amd64 builds
|
||||
- Clear AMSG when SEVR becomes zero.
|
||||
- `seqRecord` fix support for link `DLY0`
|
||||
- Add `ABORT_ON_ASSERT` flag to `CONFIG_SITE_ENV`
|
||||
- rationalize osdMutex
|
||||
|
||||
### Submodule updates
|
||||
|
||||
The pvDatabase module was updated to version 4.7.2:
|
||||
|
||||
* Resolved issue with changed field set in the case where the top level (master)
|
||||
field ("_") is not requested by the client, but the master field callback causes
|
||||
all fields to be marked as updated, rather than only those fields that have
|
||||
actually been modified.
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.8.1
|
||||
|
||||
### Limit to `_FORTIFY_SOURCE=2`
|
||||
@@ -280,6 +373,50 @@ The floating point modulo function `FMOD(NUM,DEN)` has been added to the CALC
|
||||
expression engine and is available to all software using that (calc and calcout
|
||||
record types, access security library and some extensions).
|
||||
|
||||
### Submodule updates
|
||||
|
||||
The pvData module was updated to version 8.0.6:
|
||||
|
||||
- Compatible changes
|
||||
- Actually enable JSON-5 output in PVStructure::Formatter::JSON when available.
|
||||
- Fix unaligned access issues for some ARM/Linux targets.
|
||||
|
||||
The pvAccess module was updated to version 7.1.7:
|
||||
|
||||
- Changes
|
||||
- Registering the PVA server with the IOC now sets the `PVAS_SERVER_PORT`
|
||||
variable in the environment.
|
||||
|
||||
The pva2pva module was updated to version 1.4.1:
|
||||
|
||||
- Bug Fixes
|
||||
- `dbLoadGroup` was fixed
|
||||
- Additions
|
||||
- Support for "meta" member at top of array of structs
|
||||
|
||||
The pvDatabase module was updated to version 4.7.1:
|
||||
|
||||
* Added data distributor plugin which can be used for distributing data between
|
||||
a group of clients. The plugin is triggered by the request string of the
|
||||
form:
|
||||
|
||||
`_[distributor=group:<group id>;set:<set_id>;trigger:<field_name>;updates:<n_updates>;mode:<update_mode>]`
|
||||
|
||||
The plugin parameters are optional and are described bellow:
|
||||
|
||||
- group: this parameter indicates a group that client application belongs to (default value: "default"); groups of clients are completely independent of each other
|
||||
|
||||
- set: this parameter designates a client set that application belongs to within its group (default value: "default")
|
||||
|
||||
- trigger: this is the PV structure field that distinguishes different channel updates (default value: "timeStamp"); for example, for area detector images one could use the "uniqueId" field of the NTND structure
|
||||
|
||||
- updates: this parameter configures how many sequential updates a client (or a set of clients) will receive before the data distributor starts updating the next one (default value: "1")
|
||||
|
||||
- mode: this parameter configures how channel updates are to be distributed between clients in a set:
|
||||
- one: update goes to one client per set
|
||||
- all: update goes to all clients in a set
|
||||
- default is "one" if client set id is not specified, and "all" if set id is specified
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.7
|
||||
@@ -358,10 +495,10 @@ changed to `(p)->dtor`.
|
||||
The order over operations when processing a waveformRecord is adjusted
|
||||
so that updates to NORD is posted with the correct timestamp.
|
||||
|
||||
### Automatic COMMANDLINE_LIBRARY w/ newer compilers
|
||||
### Automatic `COMMANDLINE_LIBRARY` with newer compilers
|
||||
|
||||
When built with a compiler supporting `__has_include<>`, the presence
|
||||
of the `<readline/readline.h>` will be used to automatically determine
|
||||
of a `readline/readline.h` header will be used to automatically determine
|
||||
a default value for `COMMANDLINE_LIBRARY`.
|
||||
|
||||
Mingw builds with readline support now link `-ltermcap` instead of `-lcurses`.
|
||||
@@ -530,6 +667,44 @@ or if unsupported (`$TERM` not set, or Windows < 10).
|
||||
The `dbnd` server side filter now passes through alarm and property
|
||||
change events, even when not exceeding the deadband.
|
||||
|
||||
### Submodule updates
|
||||
|
||||
The pvData module was updated to version 8.0.5:
|
||||
|
||||
- Compatible changes
|
||||
- Internal changes to use the YAJL API for generating JSON and JSON-5 output.
|
||||
|
||||
The pvAccess module was updated to version 7.1.6:
|
||||
|
||||
- Changes to caProvider
|
||||
- Bug fix related to enum values.
|
||||
- More internal changes to improve performance when connecting tens of
|
||||
thousands of CA channels.
|
||||
- Several minor internal improvements.
|
||||
|
||||
The pva2pva module was updated to version 1.4.0:
|
||||
|
||||
- Bug Fixes
|
||||
- Apply ACF when writing to atomic group
|
||||
- Additions
|
||||
- Add new "structure" to @ref qsrv_group_map_types
|
||||
- Changes
|
||||
- Add Access Security hooks for single and group writes.
|
||||
- Enable "Async Soft Channel" for output links
|
||||
- When built against Base 7.0.6.1, set timeStamp.userTag from UTAG field.
|
||||
- Add DTYP="QSRV Set UTag" for longin, which sets UTAG=VAL.
|
||||
|
||||
The pvDatabase module was updated to version 4.7.0:
|
||||
|
||||
* Added support for the whole structure (master field) server side plugins.
|
||||
The whole structure is identified as the `_` string, and a pvRequest string
|
||||
that applies a plugin to it takes the form:
|
||||
|
||||
`field(_[XYZ=A:3;B:uniqueId])`
|
||||
|
||||
where `XYZ` is the name of a specific filter plugin that takes parameters
|
||||
`A` and `B` with values `3` and `uniqueId` respectively.
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.6.1
|
||||
@@ -596,6 +771,15 @@ This was done to simplify the code and may have improved performance slightly fo
|
||||
|
||||
Many of the built-in record types have had improvements to their documentation with additional fields added to the tables, rewrites of descriptions and links to other documents added or fixed.
|
||||
|
||||
### Submodule updates
|
||||
|
||||
The pvAccess module was updated to version 7.1.4:
|
||||
|
||||
- Changes to caProvider
|
||||
- Resolve issues with pv structures that don't have a value field
|
||||
- Add NULL checks for handling unusual structures
|
||||
- Speed up channel creation when using large numbers of channels
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.6
|
||||
@@ -606,7 +790,7 @@ These target architectures have been removed:
|
||||
|
||||
+ darwin-ppc, darwin-ppcx86
|
||||
+ linux-386, linux-486, linux-586, linux-686, linux-athlon (cross-build)
|
||||
+ linux-cris, linux-cris_v10, linux-cris_v32 (cross-build)
|
||||
+ linux-cris, linux-cris\_v10, linux-cris\_v32 (cross-build)
|
||||
+ RTEMS-at91rm9200ek, RTEMS-gen68360, RTEMS-mcp750, RTEMS-mvme167,
|
||||
RTEMS-psim (cross-build)
|
||||
|
||||
@@ -624,9 +808,9 @@ running on RTEMS 5:
|
||||
|
||||
- RTEMS-beagleboneblack
|
||||
- RTEMS-pc686
|
||||
- RTEMS-qoriq_e500 (MVME2500)
|
||||
- RTEMS-xilinx_zynq_a9_qemu
|
||||
- RTEMS-xilinx_zynq_zedboard
|
||||
- RTEMS-qoriq\_e500 (MVME2500)
|
||||
- RTEMS-xilinx\_zynq\_a9\_qemu
|
||||
- RTEMS-xilinx\_zynq\_zedboard
|
||||
|
||||
The EPICS support for RTEMS 4 has always relied on RTEMS-specific
|
||||
kernel APIs which cannot be used on an SMP system, so a new port was
|
||||
@@ -638,7 +822,7 @@ to run `make distclean` if switching a single source tree from one
|
||||
to the other (both header files and dependency files are different
|
||||
between the two and must be cleaned out).
|
||||
|
||||
The configuration variable RTEMS_VERSION in the EPICS config file
|
||||
The configuration variable `RTEMS_VERSION` in the EPICS config file
|
||||
`configure/os/CONFIG_SITE.Common.RTEMS` must be set to the full 3-
|
||||
part version number for RTEMS 4 releases, e.g. `4.9.1`, `4.10.2`
|
||||
but for RTEMS 5.1 and later it must only contain the major version
|
||||
@@ -836,6 +1020,39 @@ Test programs written directly in Perl as a `.plt` script should implement a
|
||||
similar timeout for themselves. The "netget" test in Base does this in a way
|
||||
that works on Windows as well as Unix-like hosts.
|
||||
|
||||
### Submodule updates
|
||||
|
||||
The pvAccess module was updated to version 7.1.4:
|
||||
|
||||
- Changes
|
||||
- Adjust argument parsing with pvput (Jesus Vasquez).
|
||||
|
||||
The pva2pva module was updated to version 1.3.1:
|
||||
|
||||
- Bug Fixes
|
||||
- Correct handling for server side filters.
|
||||
- Changes
|
||||
- Syncing softMain.cpp with epics-base
|
||||
|
||||
The pvDatabase module was updated to version 4.6.0:
|
||||
|
||||
* Access Security is now supported.
|
||||
* <b>special</b> has been revised and extended.
|
||||
* addRecord, removeRecord, processRecord, and traceRecord are replaced by pvdbcr versions.
|
||||
* <b>support</b> is DEPRECATED
|
||||
|
||||
The pvaClient module was updated to version 4.8.0:
|
||||
|
||||
* `PvaClientNTMultiData::getChannelChangeFlags` is a new method. It fixes
|
||||
issue #66.
|
||||
* Fix for issue #68. Both `PvaClientArray` and `PvaClientField` are not longer
|
||||
present. Neither was previously implemented.
|
||||
* Several public methods are now protected. They were never meant to be called
|
||||
by clients.
|
||||
* Issue #70 has been fixed.
|
||||
* Changes was made to increase the performance of `pvaMultiChannel`.
|
||||
* doxygen changes were made.
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.5
|
||||
@@ -873,7 +1090,7 @@ compile device supports as loadable modules.
|
||||
### Priority inversion safe Posix mutexes
|
||||
|
||||
On Posix systems, epicsMutex now support priority inheritance if available.
|
||||
The IOC needs to run with SCHED_FIFO engaged to use these.
|
||||
The IOC needs to run with `SCHED_FIFO` engaged to use these.
|
||||
Support for Posix implementations before POSIX.1-2001 (`_XOPEN_SOURCE < 500`,
|
||||
glibc version < 2.3.3) has been dropped.
|
||||
|
||||
@@ -1006,14 +1223,14 @@ properly handle zero-length arrays. The `caget`, `caput` and `camonitor`
|
||||
client programs are known to work with empty arrays as long as they were
|
||||
built with this or a later version of EPICS.
|
||||
|
||||
#### Change to the db_access.h `dbr_size_n(TYPE, COUNT)` macro
|
||||
#### Change to the db\_access.h `dbr_size_n(TYPE, COUNT)` macro
|
||||
|
||||
When called with COUNT=0 this macro no longer returns the number of bytes
|
||||
required for a scalar (1 element) but for an empty array (0 elements).
|
||||
Make sure code that uses this doesn't call it with COUNT=0 when it really
|
||||
means COUNT=1.
|
||||
|
||||
Note that the db_access.h header file is included by cadef.h so the change
|
||||
Note that the db\_access.h header file is included by cadef.h so the change
|
||||
can impact Channel Access client programs that use this macro.
|
||||
|
||||
#### Channel Access support for zero-length arrays
|
||||
@@ -1123,6 +1340,35 @@ GNUmake added the directive `undefine` in version 3.82 to allow variables to
|
||||
be undefined. Support for this has been added to the EPICS Release file parser,
|
||||
so `undefine` can now be used in configure/RELEASE files to unset variables.
|
||||
|
||||
|
||||
### Submodule updates
|
||||
|
||||
The pvData module was updated to version 8.0.4:
|
||||
|
||||
- Incompatible changes
|
||||
- Remove `ByteBuffer::align()`
|
||||
- Compatible changes
|
||||
- Deprecate `SerializableControl::alignBuffer()` and
|
||||
`DeserializableControl::alignData()`
|
||||
- `shared_vector_convert<>()` fix convert of empty, untyped, array
|
||||
|
||||
The pvAccess module was updated to version 7.1.3:
|
||||
|
||||
- Bug fixes
|
||||
- Increase default TCP timeout to 40 seconds.
|
||||
Applies a 4/3 multiplier on `$EPICS_PVA_CONN_TMO` for compatibility.
|
||||
- CA Provider implementation restructured to simplify, reduce duplication
|
||||
and fix issues #163 and #165.
|
||||
- Changes
|
||||
- Enable building of pvtools to all except vxWorks, RTEMS and iOS.
|
||||
|
||||
The pva2pva module was updated to version 1.3.0:
|
||||
|
||||
- Changes
|
||||
- Add `dbLoadGroup()` iocsh function to read group JSON definitions
|
||||
from a file. Mappings in files must refer to full record names
|
||||
instead of fields. eg. 'recname.VAL' instead of 'VAL'.
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.4.1
|
||||
@@ -1188,7 +1434,7 @@ The following launchpad bugs have fixes included in this release:
|
||||
operators on aarch64
|
||||
- [lp: 1853148](https://bugs.launchpad.net/bugs/1853148), mingw compiler
|
||||
problem with printf/scanf formats
|
||||
- [lp: 1852653](https://bugs.launchpad.net/bugs/1852653), USE_TYPED_DSET
|
||||
- [lp: 1852653](https://bugs.launchpad.net/bugs/1852653), `USE_TYPED_DSET`
|
||||
incompatible with C++
|
||||
- [lp: 1862328](https://bugs.launchpad.net/bugs/1862328), Race condition on
|
||||
IOC start leaves rsrv unresponsive
|
||||
@@ -1198,7 +1444,7 @@ The following launchpad bugs have fixes included in this release:
|
||||
- [lp: 1868680](https://bugs.launchpad.net/bugs/1868680), Access Security file
|
||||
reload (asInit) fails
|
||||
|
||||
### \*_API macros in EPICS headers
|
||||
### `*_API` macros in EPICS headers
|
||||
|
||||
Internally, the Com and ca libraries now express dllimport/export (Windows)
|
||||
and symbol visibility (GCC) using library-specific macros (eg. `LIBCOM_API`)
|
||||
@@ -1432,7 +1678,7 @@ The API functions `epicsGetExecDir()` and `epicsGetExecName()` are also
|
||||
added to `osiFileName.h` to provide runtime access to the directory or
|
||||
filename of the executable with which the process was started.
|
||||
|
||||
### Decouple LINKER_USE_RPATH and STATIC_BUILD
|
||||
### Decouple `LINKER_USE_RPATH` and `STATIC_BUILD`
|
||||
|
||||
Previously, setting `STATIC_BUILD=NO` implied `LINKER_USE_RPATH=NO`.
|
||||
This is no longer the case. Setting `LINKER_USE_RPATH=YES` will
|
||||
@@ -2011,7 +2257,7 @@ number instead, like this:
|
||||
Channel Access does not (and probably never will) directly support 64-bit
|
||||
integer types, so the new field types are presented to the CA server as
|
||||
`DBF_DOUBLE` values. This means that field values larger than 2^52
|
||||
(0x10_0000_0000_0000 = 4503599627370496) cannot be transported over Channel
|
||||
(0x10\_0000\_0000\_0000 = 4503599627370496) cannot be transported over Channel
|
||||
Access without their least significant bits being truncated. The EPICS V4
|
||||
pvAccess network protocol _can_ transport 64-bit data types however, and a
|
||||
future release of the pvaSrv module will connect this ability to the fields of
|
||||
|
||||
@@ -285,7 +285,11 @@ Inf (Infinite) value. UDF defaults to TRUE but can be set in a database file.
|
||||
Record and device support routines which write to the VAL field are generally
|
||||
responsible for setting and clearing UDF.
|
||||
|
||||
=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF
|
||||
The B<UDFS> field specifies the alarm severity that the record will be set to
|
||||
whenever its value is undefined (i.e., the UDF field is 1). This includes the
|
||||
initial severity of the record being undefined after the IOC boots.
|
||||
|
||||
=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF, UDFS
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
@@ -226,25 +226,48 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
}
|
||||
|
||||
if (!status && precord != dbChannelRecord(chan))
|
||||
recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode,
|
||||
recGblInheritSevrMsg(plink->value.pv_link.pvlMask & pvlOptMsMode,
|
||||
plink->precord,
|
||||
dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr);
|
||||
dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr,
|
||||
dbChannelRecord(chan)->amsg);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Some records get options (precsision, units, ...) for some fields
|
||||
* from an input link. We need to catch the case that this link
|
||||
* points back to the same field or we will end in an infinite recursion.
|
||||
*/
|
||||
static long dbDbGetOptionLoopSafe(const struct link *plink, short dbrType,
|
||||
void *pbuffer, long option)
|
||||
{
|
||||
/* We need to cast away const to set the flags.
|
||||
That's ok because we know that plink is never actually readonly.
|
||||
And we reset everything to its original state.
|
||||
*/
|
||||
struct link *mutable_plink = (struct link *)plink;
|
||||
long status = S_dbLib_badLink;
|
||||
dbChannel *chan = linkChannel(plink);
|
||||
DBADDR *paddr = &chan->addr;
|
||||
long number_elements = 0;
|
||||
|
||||
dbScanLock(paddr->precord);
|
||||
if (!(mutable_plink->flags & DBLINK_FLAG_VISITED)) {
|
||||
mutable_plink->flags |= DBLINK_FLAG_VISITED;
|
||||
status = dbGet(paddr, dbrType, pbuffer, &option, &number_elements, NULL);
|
||||
mutable_plink->flags &= ~DBLINK_FLAG_VISITED;
|
||||
}
|
||||
dbScanUnlock(paddr->precord);
|
||||
return status;
|
||||
}
|
||||
|
||||
static long dbDbGetControlLimits(const struct link *plink, double *low,
|
||||
double *high)
|
||||
{
|
||||
dbChannel *chan = linkChannel(plink);
|
||||
DBADDR *paddr = &chan->addr;
|
||||
struct buffer {
|
||||
DBRctrlDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_CTRL_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
NULL);
|
||||
long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_CTRL_DOUBLE);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
@@ -257,16 +280,11 @@ static long dbDbGetControlLimits(const struct link *plink, double *low,
|
||||
static long dbDbGetGraphicLimits(const struct link *plink, double *low,
|
||||
double *high)
|
||||
{
|
||||
dbChannel *chan = linkChannel(plink);
|
||||
DBADDR *paddr = &chan->addr;
|
||||
struct buffer {
|
||||
DBRgrDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_GR_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
NULL);
|
||||
long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_GR_DOUBLE);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
@@ -279,16 +297,11 @@ static long dbDbGetGraphicLimits(const struct link *plink, double *low,
|
||||
static long dbDbGetAlarmLimits(const struct link *plink, double *lolo,
|
||||
double *low, double *high, double *hihi)
|
||||
{
|
||||
dbChannel *chan = linkChannel(plink);
|
||||
DBADDR *paddr = &chan->addr;
|
||||
struct buffer {
|
||||
DBRalDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_AL_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_AL_DOUBLE);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
@@ -302,16 +315,11 @@ static long dbDbGetAlarmLimits(const struct link *plink, double *lolo,
|
||||
|
||||
static long dbDbGetPrecision(const struct link *plink, short *precision)
|
||||
{
|
||||
dbChannel *chan = linkChannel(plink);
|
||||
DBADDR *paddr = &chan->addr;
|
||||
struct buffer {
|
||||
DBRprecision
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_PRECISION;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_PRECISION);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
@@ -322,16 +330,11 @@ static long dbDbGetPrecision(const struct link *plink, short *precision)
|
||||
|
||||
static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize)
|
||||
{
|
||||
dbChannel *chan = linkChannel(plink);
|
||||
DBADDR *paddr = &chan->addr;
|
||||
struct buffer {
|
||||
DBRunits
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_UNITS;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_UNITS);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
@@ -376,8 +379,8 @@ static long dbDbPutValue(struct link *plink, short dbrType,
|
||||
dbCommon *pdest = dbChannelRecord(chan);
|
||||
long status = dbPut(paddr, dbrType, pbuffer, nRequest);
|
||||
|
||||
recGblInheritSevr(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta,
|
||||
psrce->nsev);
|
||||
recGblInheritSevrMsg(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta,
|
||||
psrce->nsev, psrce->namsg);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
|
||||
@@ -75,21 +75,21 @@ static const iocshFuncDef dbbFuncDef = {"dbb",1,dbbArgs,
|
||||
"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);}
|
||||
static void dbbCallFunc(const iocshArgBuf *args) { iocshSetError(dbb(args[0].sval));}
|
||||
|
||||
/* dbd */
|
||||
static const iocshArg dbdArg0 = { "record name",iocshArgStringRecord};
|
||||
static const iocshArg * const dbdArgs[1] = {&dbdArg0};
|
||||
static const iocshFuncDef dbdFuncDef = {"dbd",1,dbdArgs,
|
||||
"Remove breakpoint from a record.\n"};
|
||||
static void dbdCallFunc(const iocshArgBuf *args) { dbd(args[0].sval);}
|
||||
static void dbdCallFunc(const iocshArgBuf *args) { iocshSetError(dbd(args[0].sval));}
|
||||
|
||||
/* dbc */
|
||||
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 lockset until next breakpoint is found.\n"};
|
||||
static void dbcCallFunc(const iocshArgBuf *args) { dbc(args[0].sval);}
|
||||
static void dbcCallFunc(const iocshArgBuf *args) { iocshSetError(dbc(args[0].sval));}
|
||||
|
||||
/* dbs */
|
||||
static const iocshArg dbsArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -97,12 +97,12 @@ static const iocshArg * const dbsArgs[1] = {&dbsArg0};
|
||||
static const iocshFuncDef dbsFuncDef = {"dbs",1,dbsArgs,
|
||||
"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);}
|
||||
static void dbsCallFunc(const iocshArgBuf *args) { iocshSetError(dbs(args[0].sval));}
|
||||
|
||||
/* dbstat */
|
||||
static const iocshFuncDef dbstatFuncDef = {"dbstat",0,0,
|
||||
"Print list of suspended records, and breakpoints set in locksets.\n"};
|
||||
static void dbstatCallFunc(const iocshArgBuf *args) { dbstat();}
|
||||
static void dbstatCallFunc(const iocshArgBuf *args) { iocshSetError(dbstat());}
|
||||
|
||||
/* dbp */
|
||||
static const iocshArg dbpArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -119,7 +119,9 @@ static const iocshFuncDef dbpFuncDef = {
|
||||
" 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);}
|
||||
{
|
||||
iocshSetError(dbp(args[0].sval,args[1].ival));
|
||||
}
|
||||
|
||||
/* dbap */
|
||||
static const iocshArg dbapArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -127,7 +129,7 @@ static const iocshArg * const dbapArgs[1] = {&dbapArg0};
|
||||
static const iocshFuncDef dbapFuncDef = {"dbap",1,dbapArgs,
|
||||
"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);}
|
||||
static void dbapCallFunc(const iocshArgBuf *args) { iocshSetError(dbap(args[0].sval));}
|
||||
|
||||
/* dbsr */
|
||||
static const iocshArg dbsrArg0 = { "interest level",iocshArgInt};
|
||||
@@ -150,7 +152,7 @@ static const iocshFuncDef dbcarFuncDef = {"dbcar",2,dbcarArgs,
|
||||
" 2 - Shows info. for all links.\n"};
|
||||
static void dbcarCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbcar(args[0].sval,args[1].ival);
|
||||
iocshSetError(dbcar(args[0].sval,args[1].ival));
|
||||
}
|
||||
|
||||
/* dbjlr */
|
||||
@@ -162,7 +164,7 @@ static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs,
|
||||
"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);
|
||||
iocshSetError(dbjlr(args[0].sval,args[1].ival));
|
||||
}
|
||||
|
||||
/* dbel */
|
||||
@@ -176,7 +178,7 @@ static const iocshFuncDef dbelFuncDef = {"dbel",2,dbelArgs,
|
||||
"Example: dbel aitest 2\n"};
|
||||
static void dbelCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbel(args[0].sval, args[1].ival);
|
||||
iocshSetError(dbel(args[0].sval, args[1].ival));
|
||||
}
|
||||
|
||||
/* dba */
|
||||
@@ -187,7 +189,7 @@ static const iocshFuncDef dbaFuncDef = {"dba",1,dbaArgs,
|
||||
"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);}
|
||||
static void dbaCallFunc(const iocshArgBuf *args) { iocshSetError(dba(args[0].sval));}
|
||||
|
||||
/* dbl */
|
||||
static const iocshArg dblArg0 = { "record type",iocshArgString};
|
||||
@@ -204,7 +206,7 @@ static const iocshFuncDef dblFuncDef = {"dbl",2,dblArgs,
|
||||
" dbl(\"ai\",\"HIGH LOW VAL PREC\")\n"};
|
||||
static void dblCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbl(args[0].sval,args[1].sval);
|
||||
iocshSetError(dbl(args[0].sval,args[1].sval));
|
||||
}
|
||||
|
||||
/* dbnr */
|
||||
@@ -213,7 +215,7 @@ static const iocshArg * const dbnrArgs[1] = {&dbnrArg0};
|
||||
static const iocshFuncDef dbnrFuncDef = {"dbnr",1,dbnrArgs,
|
||||
"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);}
|
||||
static void dbnrCallFunc(const iocshArgBuf *args) { iocshSetError(dbnr(args[0].ival));}
|
||||
|
||||
/* dbli */
|
||||
static const iocshArg dbliArg0 = { "pattern",iocshArgString};
|
||||
@@ -221,7 +223,7 @@ static const iocshArg * const dbliArgs[1] = {&dbliArg0};
|
||||
static const iocshFuncDef dbliFuncDef = {"dbli",1,dbliArgs,
|
||||
"List info() tags with names matching pattern.\n\n"
|
||||
"Example: dbli(\"autosave*\")\n"};
|
||||
static void dbliCallFunc(const iocshArgBuf *args) { dbli(args[0].sval);}
|
||||
static void dbliCallFunc(const iocshArgBuf *args) { iocshSetError(dbli(args[0].sval));}
|
||||
|
||||
/* dbla */
|
||||
static const iocshArg dblaArg0 = { "pattern",iocshArgStringRecord};
|
||||
@@ -229,7 +231,7 @@ static const iocshArg * const dblaArgs[1] = {&dblaArg0};
|
||||
static const iocshFuncDef dblaFuncDef = {"dbla",1,dblaArgs,
|
||||
"List record alias()s by alias name pattern.\n\n"
|
||||
"Example: dbla(\"alia*\")\n"};
|
||||
static void dblaCallFunc(const iocshArgBuf *args) { dbla(args[0].sval);}
|
||||
static void dblaCallFunc(const iocshArgBuf *args) { iocshSetError(dbla(args[0].sval));}
|
||||
|
||||
/* dbgrep */
|
||||
static const iocshArg dbgrepArg0 = { "pattern",iocshArgStringRecord};
|
||||
@@ -240,7 +242,7 @@ static const iocshFuncDef dbgrepFuncDef = {"dbgrep",1,dbgrepArgs,
|
||||
" - \"?\", 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);}
|
||||
static void dbgrepCallFunc(const iocshArgBuf *args) { iocshSetError(dbgrep(args[0].sval));}
|
||||
|
||||
/* dbgf */
|
||||
static const iocshArg dbgfArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -250,7 +252,7 @@ static const iocshFuncDef dbgfFuncDef = {"dbgf",1,dbgfArgs,
|
||||
"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);}
|
||||
static void dbgfCallFunc(const iocshArgBuf *args) { iocshSetError(dbgf(args[0].sval));}
|
||||
|
||||
/* dbpf */
|
||||
static const iocshArg dbpfArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -261,7 +263,7 @@ static const iocshFuncDef dbpfFuncDef = {"dbpf",2,dbpfArgs,
|
||||
"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);}
|
||||
{ iocshSetError(dbpf(args[0].sval,args[1].sval));}
|
||||
|
||||
/* dbpr */
|
||||
static const iocshArg dbprArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -279,14 +281,14 @@ static const iocshFuncDef dbprFuncDef = {
|
||||
"Example: dbpr aitest 3\n"
|
||||
};
|
||||
static void dbprCallFunc(const iocshArgBuf *args)
|
||||
{ dbpr(args[0].sval,args[1].ival);}
|
||||
{ iocshSetError(dbpr(args[0].sval,args[1].ival));}
|
||||
|
||||
/* dbtr */
|
||||
static const iocshArg dbtrArg0 = { "record name",iocshArgStringRecord};
|
||||
static const iocshArg * const dbtrArgs[1] = {&dbtrArg0};
|
||||
static const iocshFuncDef dbtrFuncDef = {"dbtr",1,dbtrArgs,
|
||||
"Process record and then some fields.\n"};
|
||||
static void dbtrCallFunc(const iocshArgBuf *args) { dbtr(args[0].sval);}
|
||||
static void dbtrCallFunc(const iocshArgBuf *args) { iocshSetError(dbtr(args[0].sval));}
|
||||
|
||||
/* dbtgf */
|
||||
static const iocshArg dbtgfArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -296,7 +298,7 @@ static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs,
|
||||
"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);}
|
||||
static void dbtgfCallFunc(const iocshArgBuf *args) { iocshSetError(dbtgf(args[0].sval));}
|
||||
|
||||
/* dbtpf */
|
||||
static const iocshArg dbtpfArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -308,7 +310,7 @@ static const iocshFuncDef dbtpfFuncDef = {"dbtpf",2,dbtpfArgs,
|
||||
"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);}
|
||||
{ iocshSetError(dbtpf(args[0].sval,args[1].sval));}
|
||||
|
||||
/* dbior */
|
||||
static const iocshArg dbiorArg0 = { "driver name",iocshArgString};
|
||||
@@ -317,7 +319,7 @@ static const iocshArg * const dbiorArgs[] = {&dbiorArg0,&dbiorArg1};
|
||||
static const iocshFuncDef dbiorFuncDef = {"dbior",2,dbiorArgs,
|
||||
"Driver Report.\n"};
|
||||
static void dbiorCallFunc(const iocshArgBuf *args)
|
||||
{ dbior(args[0].sval,args[1].ival);}
|
||||
{ iocshSetError(dbior(args[0].sval,args[1].ival));}
|
||||
|
||||
/* dbhcr */
|
||||
static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0,
|
||||
@@ -327,7 +329,7 @@ static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0,
|
||||
"Use the UNIX sort command:\n"
|
||||
"dbhcr > report\n"
|
||||
"sort report > report.sorted\n"};
|
||||
static void dbhcrCallFunc(const iocshArgBuf *args) { dbhcr();}
|
||||
static void dbhcrCallFunc(const iocshArgBuf *args) { iocshSetError(dbhcr());}
|
||||
|
||||
/* gft */
|
||||
static const iocshArg gftArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -336,7 +338,7 @@ static const iocshFuncDef gftFuncDef = {"gft",1,gftArgs,
|
||||
"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);}
|
||||
static void gftCallFunc(const iocshArgBuf *args) { iocshSetError(gft(args[0].sval));}
|
||||
|
||||
/* pft */
|
||||
static const iocshArg pftArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -346,7 +348,7 @@ static const iocshFuncDef pftFuncDef = {"pft",2,pftArgs,
|
||||
"dbChannel put value.\n"
|
||||
"Example: pft aitest 5.0\n"};
|
||||
static void pftCallFunc(const iocshArgBuf *args)
|
||||
{ pft(args[0].sval,args[1].sval);}
|
||||
{ iocshSetError(pft(args[0].sval,args[1].sval));}
|
||||
|
||||
/* dbtpn */
|
||||
static const iocshArg dbtpnArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -359,12 +361,12 @@ static const iocshFuncDef dbtpnFuncDef = {"dbtpn",2,dbtpnArgs,
|
||||
"Example: dbtpn aitest\n"
|
||||
"Example: dbtpn aitest 5.0\n"};
|
||||
static void dbtpnCallFunc(const iocshArgBuf *args)
|
||||
{ dbtpn(args[0].sval,args[1].sval);}
|
||||
{ iocshSetError(dbtpn(args[0].sval,args[1].sval));}
|
||||
|
||||
/* dbNotifyDump */
|
||||
static const iocshFuncDef dbNotifyDumpFuncDef = {"dbNotifyDump",0,0,
|
||||
"Report status of any active async processing with completion notification.\n"};
|
||||
static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { dbNotifyDump();}
|
||||
static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { iocshSetError(dbNotifyDump());}
|
||||
|
||||
/* dbPutAttribute */
|
||||
static const iocshArg dbPutAttrArg0 = { "record type",iocshArgString};
|
||||
@@ -375,7 +377,7 @@ static const iocshArg * const dbPutAttrArgs[] =
|
||||
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);}
|
||||
{ iocshSetError(dbPutAttribute(args[0].sval,args[1].sval,args[2].sval));}
|
||||
|
||||
/* tpn */
|
||||
static const iocshArg tpnArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -385,7 +387,7 @@ static const iocshFuncDef tpnFuncDef = {"tpn",2,tpnArgs,
|
||||
"Test Process Notify.\n\n"
|
||||
"Example: tpn aitest 5.0\n"};
|
||||
static void tpnCallFunc(const iocshArgBuf *args)
|
||||
{ tpn(args[0].sval,args[1].sval);}
|
||||
{ iocshSetError(tpn(args[0].sval,args[1].sval));}
|
||||
|
||||
/* dblsr */
|
||||
static const iocshArg dblsrArg0 = { "record name",iocshArgStringRecord};
|
||||
@@ -399,7 +401,7 @@ static const iocshFuncDef dblsrFuncDef = {"dblsr",2,dblsrArgs,
|
||||
" 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);}
|
||||
{ iocshSetError(dblsr(args[0].sval,args[1].ival));}
|
||||
|
||||
/* dbLockShowLocked */
|
||||
static const iocshArg dbLockShowLockedArg0 = { "interest level",iocshArgInt};
|
||||
@@ -412,7 +414,7 @@ static const iocshFuncDef dbLockShowLockedFuncDef = {
|
||||
"Example: dbLockShowLocked 0\n"
|
||||
};
|
||||
static void dbLockShowLockedCallFunc(const iocshArgBuf *args)
|
||||
{ dbLockShowLocked(args[0].ival);}
|
||||
{ iocshSetError(dbLockShowLocked(args[0].ival));}
|
||||
|
||||
/* scanOnceSetQueueSize */
|
||||
static const iocshArg scanOnceSetQueueSizeArg0 = { "size",iocshArgInt};
|
||||
@@ -423,7 +425,7 @@ static const iocshFuncDef scanOnceSetQueueSizeFuncDef = {"scanOnceSetQueueSize",
|
||||
"Must be called before iocInit().\n"};
|
||||
static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
scanOnceSetQueueSize(args[0].ival);
|
||||
iocshSetError(scanOnceSetQueueSize(args[0].ival));
|
||||
}
|
||||
|
||||
/* scanOnceQueueShow */
|
||||
@@ -444,7 +446,7 @@ static const iocshFuncDef scanpplFuncDef = {"scanppl",1,scanpplArgs,
|
||||
"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);}
|
||||
{ iocshSetError(scanppl(args[0].dval));}
|
||||
|
||||
/* scanpel */
|
||||
static const iocshArg scanpelArg0 = { "event name",iocshArgString};
|
||||
@@ -452,7 +454,7 @@ static const iocshArg * const scanpelArgs[1] = {&scanpelArg0};
|
||||
static const iocshFuncDef scanpelFuncDef = {"scanpel",1,scanpelArgs,
|
||||
"Print info for records with SCAN = \"Event\".\n"};
|
||||
static void scanpelCallFunc(const iocshArgBuf *args)
|
||||
{ scanpel(args[0].sval);}
|
||||
{ iocshSetError(scanpel(args[0].sval));}
|
||||
|
||||
/* postEvent */
|
||||
static const iocshArg postEventArg0 = { "event name",iocshArgString};
|
||||
@@ -468,7 +470,7 @@ static void postEventCallFunc(const iocshArgBuf *args)
|
||||
/* scanpiol */
|
||||
static const iocshFuncDef scanpiolFuncDef = {"scanpiol",0,0,
|
||||
"Print info for records with SCAN = \"I/O Intr\".\n"};
|
||||
static void scanpiolCallFunc(const iocshArgBuf *args) { scanpiol();}
|
||||
static void scanpiolCallFunc(const iocshArgBuf *args) { iocshSetError(scanpiol());}
|
||||
|
||||
/* callbackSetQueueSize */
|
||||
static const iocshArg callbackSetQueueSizeArg0 = { "bufsize",iocshArgInt};
|
||||
@@ -479,7 +481,7 @@ static const iocshFuncDef callbackSetQueueSizeFuncDef = {"callbackSetQueueSize",
|
||||
"Must be called before iocInit().\n"};
|
||||
static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
callbackSetQueueSize(args[0].ival);
|
||||
iocshSetError(callbackSetQueueSize(args[0].ival));
|
||||
}
|
||||
|
||||
/* callbackQueueShow */
|
||||
@@ -504,7 +506,7 @@ static const iocshFuncDef callbackParallelThreadsFuncDef = {"callbackParallelThr
|
||||
"or one of LOW, MEDIUM, or HIGH.\n"};
|
||||
static void callbackParallelThreadsCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
callbackParallelThreads(args[0].ival, args[1].sval);
|
||||
iocshSetError(callbackParallelThreads(args[0].ival, args[1].sval));
|
||||
}
|
||||
|
||||
/* dbStateCreate */
|
||||
@@ -514,7 +516,8 @@ static const iocshFuncDef dbStateCreateFuncDef = {"dbStateCreate", 1, dbStateCre
|
||||
"Allocate new state name for \"state\" filter.\n"};
|
||||
static void dbStateCreateCallFunc (const iocshArgBuf *args)
|
||||
{
|
||||
dbStateCreate(args[0].sval);
|
||||
if (!dbStateCreate(args[0].sval))
|
||||
iocshSetError(-1);
|
||||
}
|
||||
|
||||
/* dbStateSet */
|
||||
@@ -527,6 +530,8 @@ static void dbStateSetCallFunc (const iocshArgBuf *args)
|
||||
|
||||
if (sid)
|
||||
dbStateSet(sid);
|
||||
else
|
||||
iocshSetError(-1);
|
||||
}
|
||||
|
||||
/* dbStateClear */
|
||||
@@ -539,6 +544,8 @@ static void dbStateClearCallFunc (const iocshArgBuf *args)
|
||||
|
||||
if (sid)
|
||||
dbStateClear(sid);
|
||||
else
|
||||
iocshSetError(-1);
|
||||
}
|
||||
|
||||
/* dbStateShow */
|
||||
@@ -552,6 +559,8 @@ static void dbStateShowCallFunc (const iocshArgBuf *args)
|
||||
|
||||
if (sid)
|
||||
dbStateShow(sid, args[1].ival);
|
||||
else
|
||||
iocshSetError(-1);
|
||||
}
|
||||
|
||||
/* dbStateShowAll */
|
||||
|
||||
@@ -391,6 +391,10 @@ typedef struct lset {
|
||||
#define dbGetSevr(link, sevr) \
|
||||
dbGetAlarm(link, NULL, sevr)
|
||||
|
||||
/** @brief Lookup link field name from pointer.
|
||||
* Returns only field name. aka. value of ``dbFldDes::name``
|
||||
* @since 3.16.2
|
||||
*/
|
||||
DBCORE_API const char * dbLinkFieldName(const struct link *plink);
|
||||
|
||||
DBCORE_API void dbInitLink(struct link *plink, short dbfType);
|
||||
|
||||
@@ -260,8 +260,8 @@ int recGblSetSevr(void *precord, epicsEnum16 new_stat, epicsEnum16 new_sevr)
|
||||
return recGblSetSevrMsg(precord, new_stat, new_sevr, NULL);
|
||||
}
|
||||
|
||||
void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
|
||||
epicsEnum16 sevr)
|
||||
void recGblInheritSevrMsg(int msMode, void *precord, epicsEnum16 stat,
|
||||
epicsEnum16 sevr, const char *msg)
|
||||
{
|
||||
switch (msMode) {
|
||||
case pvlOptNMS:
|
||||
@@ -274,11 +274,17 @@ void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
|
||||
recGblSetSevr(precord, LINK_ALARM, sevr);
|
||||
break;
|
||||
case pvlOptMSS:
|
||||
recGblSetSevr(precord, stat, sevr);
|
||||
/* Only MSS inherits msg */
|
||||
recGblSetSevrMsg(precord, stat, sevr, "%s", msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
|
||||
epicsEnum16 sevr)
|
||||
{
|
||||
recGblInheritSevrMsg(msMode, precord, stat, sevr, NULL);
|
||||
}
|
||||
|
||||
void recGblFwdLink(void *precord)
|
||||
{
|
||||
|
||||
@@ -73,6 +73,8 @@ DBCORE_API int recGblSetSevr(void *precord, epicsEnum16 new_stat,
|
||||
epicsEnum16 new_sevr);
|
||||
DBCORE_API void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
|
||||
epicsEnum16 sevr);
|
||||
DBCORE_API void recGblInheritSevrMsg(int msMode, void *precord, epicsEnum16 stat,
|
||||
epicsEnum16 sevr, const char *msg);
|
||||
DBCORE_API int recGblSetSevrMsg(void *precord, epicsEnum16 new_stat,
|
||||
epicsEnum16 new_sevr,
|
||||
EPICS_PRINTF_FMT(const char *msg), ...) EPICS_PRINTF_STYLE(4,5);
|
||||
|
||||
@@ -1263,24 +1263,50 @@ static void dbRecordInfo(char *name, char *value)
|
||||
}
|
||||
}
|
||||
|
||||
static long createAlias(DBENTRY *pdbentry, const char *alias)
|
||||
{
|
||||
DBENTRY tempEntry;
|
||||
long status;
|
||||
dbRecordNode *precnode = pdbentry->precnode;
|
||||
|
||||
if (precnode->aliasedRecnode) precnode = precnode->aliasedRecnode;
|
||||
dbInitEntry(pdbentry->pdbbase, &tempEntry);
|
||||
status = dbFindRecord(&tempEntry, alias);
|
||||
if (status == 0) {
|
||||
if (tempEntry.precnode->aliasedRecnode != precnode) {
|
||||
if (tempEntry.precnode->aliasedRecnode)
|
||||
fprintf(stderr, ERL_ERROR ": Alias \"%s\" for \"%s\": name already used by an alias for \"%s\"\n",
|
||||
alias, dbGetRecordName(pdbentry),
|
||||
tempEntry.precnode->aliasedRecnode->recordname);
|
||||
else
|
||||
fprintf(stderr, ERL_ERROR ": Alias \"%s\" for \"%s\": name already used by a record\n",
|
||||
alias, dbGetRecordName(pdbentry));
|
||||
status = S_dbLib_recExists;
|
||||
} else if (dbRecordsOnceOnly) {
|
||||
fprintf(stderr, ERL_ERROR ": Alias \"%s\" already defined and dbRecordsOnceOnly set.\n",
|
||||
alias);
|
||||
status = S_dbLib_recExists;
|
||||
}
|
||||
} else {
|
||||
status = dbCreateAlias(pdbentry, alias);
|
||||
}
|
||||
dbFinishEntry(&tempEntry);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void dbRecordAlias(char *name)
|
||||
{
|
||||
DBENTRY *pdbentry;
|
||||
tempListNode *ptempListNode;
|
||||
long status;
|
||||
|
||||
if(dbRecordNameValidate(name))
|
||||
return;
|
||||
|
||||
if (duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbentry = ptempListNode->item;
|
||||
status = dbCreateAlias(pdbentry, name);
|
||||
if (status) {
|
||||
fprintf(stderr, "Can't create alias \"%s\" for \"%s\"\n",
|
||||
name, dbGetRecordName(pdbentry));
|
||||
|
||||
if (createAlias(pdbentry, name) != 0) {
|
||||
yyerror(NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1298,9 +1324,7 @@ static void dbAlias(char *name, char *alias)
|
||||
alias, name);
|
||||
yyerror(NULL);
|
||||
}
|
||||
else if (dbCreateAlias(pdbEntry, alias)) {
|
||||
fprintf(stderr, "Can't create alias \"%s\" referring to \"%s\"\n",
|
||||
alias, name);
|
||||
else if (createAlias(pdbEntry, alias) != 0) {
|
||||
yyerror(NULL);
|
||||
}
|
||||
dbFinishEntry(pdbEntry);
|
||||
|
||||
@@ -207,7 +207,7 @@ static const iocshFuncDef dbPvdTableSizeFuncDef = {
|
||||
};
|
||||
static void dbPvdTableSizeCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbPvdTableSize(args[0].ival);
|
||||
iocshSetError(dbPvdTableSize(args[0].ival));
|
||||
}
|
||||
|
||||
/* dbReportDeviceConfig */
|
||||
|
||||
@@ -72,6 +72,7 @@ DBCORE_API extern const maplinkType pamaplinkType[LINK_NTYPES];
|
||||
/* DBLINK Flag bits */
|
||||
#define DBLINK_FLAG_INITIALIZED 1 /* dbInitLink() called */
|
||||
#define DBLINK_FLAG_TSELisTIME 2 /* Use TSEL to get timeStamp */
|
||||
#define DBLINK_FLAG_VISITED 4 /* Used in loop detection */
|
||||
|
||||
struct macro_link {
|
||||
char *macroStr;
|
||||
|
||||
@@ -14,6 +14,7 @@ SRC_DIRS += $(IOCDIR)/dbtemplate
|
||||
PROD_HOST += msi
|
||||
|
||||
msi_SRCS = msi.cpp
|
||||
msi_SYS_LIBS_WIN32 = shlwapi
|
||||
DOCS += msi.md
|
||||
|
||||
INC += dbLoadTemplate.h
|
||||
|
||||
@@ -41,6 +41,17 @@ static int var_count, sub_count;
|
||||
int dbTemplateMaxVars = 100;
|
||||
epicsExportAddress(int, dbTemplateMaxVars);
|
||||
|
||||
static
|
||||
int msiLoadRecords(const char *fname, const char *subs)
|
||||
{
|
||||
int ret = dbLoadRecords(fname, subs);
|
||||
if(ret) {
|
||||
fprintf(stderr, "dbLoadRecords(\"%s\", %s)\n", fname, subs);
|
||||
yyerror("Error while reading included file");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%start substitution_file
|
||||
@@ -167,7 +178,7 @@ pattern_definition: global_definitions
|
||||
fprintf(stderr, "pattern_definition: pattern_values empty\n");
|
||||
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
|
||||
#endif
|
||||
dbLoadRecords(db_file_name, sub_collect+1);
|
||||
if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
|
||||
}
|
||||
| O_BRACE pattern_values C_BRACE
|
||||
{
|
||||
@@ -175,7 +186,7 @@ pattern_definition: global_definitions
|
||||
fprintf(stderr, "pattern_definition:\n");
|
||||
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
|
||||
#endif
|
||||
dbLoadRecords(db_file_name, sub_collect+1);
|
||||
if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
|
||||
*sub_locals = '\0';
|
||||
sub_count = 0;
|
||||
}
|
||||
@@ -190,7 +201,7 @@ pattern_definition: global_definitions
|
||||
fprintf(stderr, "pattern_definition:\n");
|
||||
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
|
||||
#endif
|
||||
dbLoadRecords(db_file_name, sub_collect+1);
|
||||
if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
|
||||
dbmfFree($1);
|
||||
*sub_locals = '\0';
|
||||
sub_count = 0;
|
||||
@@ -250,7 +261,7 @@ variable_substitution: global_definitions
|
||||
fprintf(stderr, "variable_substitution: variable_definitions empty\n");
|
||||
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
|
||||
#endif
|
||||
dbLoadRecords(db_file_name, sub_collect+1);
|
||||
if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
|
||||
}
|
||||
| O_BRACE variable_definitions C_BRACE
|
||||
{
|
||||
@@ -258,7 +269,7 @@ variable_substitution: global_definitions
|
||||
fprintf(stderr, "variable_substitution:\n");
|
||||
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
|
||||
#endif
|
||||
dbLoadRecords(db_file_name, sub_collect+1);
|
||||
if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
|
||||
*sub_locals = '\0';
|
||||
}
|
||||
| WORD O_BRACE variable_definitions C_BRACE
|
||||
@@ -272,7 +283,7 @@ variable_substitution: global_definitions
|
||||
fprintf(stderr, "variable_substitution:\n");
|
||||
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
|
||||
#endif
|
||||
dbLoadRecords(db_file_name, sub_collect+1);
|
||||
if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
|
||||
dbmfFree($1);
|
||||
*sub_locals = '\0';
|
||||
}
|
||||
@@ -328,6 +339,7 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect)
|
||||
{
|
||||
FILE *fp;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
line_num = 1;
|
||||
|
||||
@@ -377,7 +389,7 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect)
|
||||
yyrestart(fp);
|
||||
}
|
||||
|
||||
yyparse();
|
||||
err = yyparse();
|
||||
|
||||
for (i = 0; i < var_count; i++) {
|
||||
dbmfFree(vars[i]);
|
||||
@@ -390,5 +402,5 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect)
|
||||
dbmfFree(db_file_name);
|
||||
db_file_name = NULL;
|
||||
}
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
#include <osiFileName.h>
|
||||
#include <osiUnistd.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <shlwapi.h>
|
||||
#endif
|
||||
|
||||
#define MAX_BUFFER_SIZE 4096
|
||||
#define MAX_DEPS 1024
|
||||
|
||||
@@ -61,7 +65,7 @@ typedef struct inputData inputData;
|
||||
static void inputConstruct(inputData **ppvt);
|
||||
static void inputDestruct(inputData * const pvt);
|
||||
static void inputAddPath(inputData * const pvt, const char * const pval);
|
||||
static void inputBegin(inputData * const pvt, const char * const fileName);
|
||||
static void inputBegin(inputData * const pvt, const char * const fileName, bool fromCmdLine);
|
||||
static char *inputNextLine(inputData * const pvt);
|
||||
static void inputNewIncludeFile(inputData * const pvt, const char * const name);
|
||||
static void inputErrPrint(const inputData * const pvt);
|
||||
@@ -83,7 +87,8 @@ static void addMacroReplacements(MAC_HANDLE * const macPvt,
|
||||
const char * const pval);
|
||||
static void makeSubstitutions(inputData * const inputPvt,
|
||||
MAC_HANDLE * const macPvt,
|
||||
const char * const templateName);
|
||||
const char * const templateName,
|
||||
bool fromCmdLine);
|
||||
|
||||
/*Global variables */
|
||||
static int opt_V = 0;
|
||||
@@ -171,7 +176,7 @@ int main(int argc,char **argv)
|
||||
|
||||
if (substitutionName.empty()) {
|
||||
STEP("Single template+substitutions file");
|
||||
makeSubstitutions(inputPvt, macPvt, templateName);
|
||||
makeSubstitutions(inputPvt, macPvt, templateName, true);
|
||||
}
|
||||
else {
|
||||
subInfo *substitutePvt;
|
||||
@@ -203,7 +208,7 @@ int main(int argc,char **argv)
|
||||
macPushScope(macPvt);
|
||||
|
||||
addMacroReplacements(macPvt, macStr);
|
||||
makeSubstitutions(inputPvt, macPvt, filename);
|
||||
makeSubstitutions(inputPvt, macPvt, filename, false);
|
||||
|
||||
if (localScope)
|
||||
macPopScope(macPvt);
|
||||
@@ -276,14 +281,15 @@ static const char *cmdNames[] = {"include","substitute"};
|
||||
|
||||
static void makeSubstitutions(inputData * const inputPvt,
|
||||
MAC_HANDLE * const macPvt,
|
||||
const char * const templateName)
|
||||
const char * const templateName,
|
||||
bool fromCmdLine)
|
||||
{
|
||||
char *input;
|
||||
static char buffer[MAX_BUFFER_SIZE];
|
||||
int n;
|
||||
|
||||
ENTER;
|
||||
inputBegin(inputPvt, templateName);
|
||||
inputBegin(inputPvt, templateName, fromCmdLine);
|
||||
while ((input = inputNextLine(inputPvt))) {
|
||||
int expand=1;
|
||||
char *p;
|
||||
@@ -378,7 +384,7 @@ struct inputData {
|
||||
inputData() { memset(inputBuffer, 0, sizeof(inputBuffer) * sizeof(inputBuffer[0])); };
|
||||
};
|
||||
|
||||
static void inputOpenFile(inputData *pinputData, const char * const filename);
|
||||
static void inputOpenFile(inputData *pinputData, const char * const filename, bool fromCmdLine);
|
||||
static void inputCloseFile(inputData *pinputData);
|
||||
static void inputCloseAllFiles(inputData *pinputData);
|
||||
|
||||
@@ -431,11 +437,11 @@ static void inputAddPath(inputData * const pinputData, const char * const path)
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void inputBegin(inputData * const pinputData, const char * const fileName)
|
||||
static void inputBegin(inputData * const pinputData, const char * const fileName, bool fromCmdLine)
|
||||
{
|
||||
ENTER;
|
||||
inputCloseAllFiles(pinputData);
|
||||
inputOpenFile(pinputData, fileName);
|
||||
inputOpenFile(pinputData, fileName, fromCmdLine);
|
||||
EXIT;
|
||||
}
|
||||
|
||||
@@ -462,7 +468,7 @@ static void inputNewIncludeFile(inputData * const pinputData,
|
||||
const char * const name)
|
||||
{
|
||||
ENTER;
|
||||
inputOpenFile(pinputData,name);
|
||||
inputOpenFile(pinputData, name, false);
|
||||
EXIT;
|
||||
}
|
||||
|
||||
@@ -493,7 +499,15 @@ static void inputErrPrint(const inputData *const pinputData)
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void inputOpenFile(inputData *pinputData, const char * const filename)
|
||||
static int isPathRelative(const char * const path) {
|
||||
#ifdef _WIN32
|
||||
return path && PathIsRelativeA(path);
|
||||
#else
|
||||
return path && path[0] != '/';
|
||||
#endif
|
||||
}
|
||||
|
||||
static void inputOpenFile(inputData *pinputData, const char * const filename, bool fromCmdLine)
|
||||
{
|
||||
std::list<std::string>& pathList = pinputData->pathList;
|
||||
std::list<std::string>::iterator pathIt = pathList.end();
|
||||
@@ -505,7 +519,7 @@ static void inputOpenFile(inputData *pinputData, const char * const filename)
|
||||
STEP("Using stdin");
|
||||
fp = stdin;
|
||||
}
|
||||
else if (pathList.empty() || strchr(filename, '/')){
|
||||
else if (fromCmdLine || pathList.empty() || !isPathRelative(filename)){
|
||||
STEPS("Opening ", filename);
|
||||
fp = fopen(filename, "r");
|
||||
}
|
||||
|
||||
@@ -60,6 +60,18 @@ Switches have the following meanings:
|
||||
2. . (the current directory)
|
||||
3. .. (the parent of the current directory)
|
||||
|
||||
Note that relative path searching is handled as
|
||||
|
||||
$ cat foo.substitutions
|
||||
file rel/path/bar.template {
|
||||
# contents
|
||||
}
|
||||
$ msi -I . -I /some/path foo.substitutions
|
||||
|
||||
which will try to find `bar.template` at the path `./rel/path/` followed by
|
||||
`/some/path/rel/path`.
|
||||
|
||||
|
||||
- **-M _substitutions_**
|
||||
|
||||
This parameter specifies macro values for the template instance.
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
#include "iocsh.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
IOCSH_STATIC_FUNC void dlload(const char* name)
|
||||
IOCSH_STATIC_FUNC int dlload(const char* name)
|
||||
{
|
||||
if (!epicsLoadLibrary(name)) {
|
||||
printf("epicsLoadLibrary failed: %s\n", epicsLoadError());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const iocshArg dlloadArg0 = { "path/library.so", iocshArgStringPath};
|
||||
@@ -28,7 +30,7 @@ static const iocshFuncDef dlloadFuncDef = {
|
||||
};
|
||||
static void dlloadCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dlload(args[0].sval);
|
||||
iocshSetError(dlload(args[0].sval));
|
||||
}
|
||||
|
||||
static void dlloadRegistar(void) {
|
||||
|
||||
@@ -22,6 +22,9 @@ enum iocStateEnum {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Query present IOC run state
|
||||
* @since 3.15.8
|
||||
*/
|
||||
DBCORE_API enum iocStateEnum getIocState(void);
|
||||
DBCORE_API int iocInit(void);
|
||||
DBCORE_API int iocBuild(void);
|
||||
|
||||
@@ -163,9 +163,11 @@ void camsgtask ( void *pParm )
|
||||
|
||||
int casClientInitiatingCurrentThread ( char * pBuf, size_t bufSize )
|
||||
{
|
||||
struct client * pClient = ( struct client * )
|
||||
epicsThreadPrivateGet ( rsrvCurrentClient );
|
||||
struct client * pClient;
|
||||
if ( ! rsrvCurrentClient )
|
||||
return RSRV_ERROR; /* not yet initialized, or disabled via dbServer */
|
||||
|
||||
pClient = ( struct client * ) epicsThreadPrivateGet ( rsrvCurrentClient );
|
||||
if ( ! pClient )
|
||||
return RSRV_ERROR;
|
||||
|
||||
|
||||
@@ -1536,6 +1536,13 @@ struct client *create_tcp_client (SOCKET sock , const osiSockAddr *peerAddr)
|
||||
|
||||
void casStatsFetch ( unsigned *pChanCount, unsigned *pCircuitCount )
|
||||
{
|
||||
if(!clientQlock) { /* not yet initialized, or disabled via dbServer */
|
||||
if(pChanCount)
|
||||
*pChanCount = 0;
|
||||
if(pCircuitCount)
|
||||
*pCircuitCount = 0;
|
||||
return;
|
||||
}
|
||||
LOCK_CLIENTQ;
|
||||
{
|
||||
int circuitCount = ellCount ( &clientQ );
|
||||
|
||||
@@ -33,7 +33,7 @@ These fields control where the record will read data from when it is processed:
|
||||
The DTYP field selects which device support layer should be responsible for
|
||||
providing input data to the record.
|
||||
The ai device support layers provided by EPICS Base are documented in the
|
||||
L<Device Support|devSoft> section.
|
||||
L<Device Support Interface> section.
|
||||
External support modules may provide additional device support for this record
|
||||
type.
|
||||
If not set explicitly, the DTYP value defaults to the first device support that
|
||||
|
||||
@@ -384,6 +384,7 @@ static long special(DBADDR *paddr, int after)
|
||||
|
||||
if (special_type == SPC_RESET) {
|
||||
reset(prec);
|
||||
monitor(prec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -282,10 +282,10 @@ static void processCallback(epicsCallback *arg)
|
||||
static long get_units(DBADDR *paddr, char *units)
|
||||
{
|
||||
seqRecord *prec = (seqRecord *) paddr->precord;
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0);
|
||||
|
||||
if (fieldOffset >= 0)
|
||||
switch (fieldOffset & 2) {
|
||||
switch (fieldOffset & 3) {
|
||||
case 0: /* DLYn */
|
||||
strcpy(units, "s");
|
||||
break;
|
||||
@@ -299,11 +299,11 @@ static long get_units(DBADDR *paddr, char *units)
|
||||
static long get_precision(const DBADDR *paddr, long *pprecision)
|
||||
{
|
||||
seqRecord *prec = (seqRecord *) paddr->precord;
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0);
|
||||
short precision;
|
||||
|
||||
if (fieldOffset >= 0)
|
||||
switch (fieldOffset & 2) {
|
||||
switch (fieldOffset & 3) {
|
||||
case 0: /* DLYn */
|
||||
*pprecision = seqDLYprecision;
|
||||
return 0;
|
||||
@@ -321,10 +321,10 @@ static long get_precision(const DBADDR *paddr, long *pprecision)
|
||||
static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
|
||||
{
|
||||
seqRecord *prec = (seqRecord *) paddr->precord;
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0);
|
||||
|
||||
if (fieldOffset >= 0)
|
||||
switch (fieldOffset & 2) {
|
||||
switch (fieldOffset & 3) {
|
||||
case 0: /* DLYn */
|
||||
pgd->lower_disp_limit = 0.0;
|
||||
pgd->lower_disp_limit = 10.0;
|
||||
@@ -341,9 +341,9 @@ static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
|
||||
|
||||
static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
|
||||
{
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0);
|
||||
|
||||
if (fieldOffset >= 0 && (fieldOffset & 2) == 0) { /* DLYn */
|
||||
if (fieldOffset >= 0 && (fieldOffset & 3) == 0) { /* DLYn */
|
||||
pcd->lower_ctrl_limit = 0.0;
|
||||
pcd->upper_ctrl_limit = seqDLYlimit;
|
||||
}
|
||||
@@ -355,9 +355,9 @@ static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
|
||||
static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
|
||||
{
|
||||
seqRecord *prec = (seqRecord *) paddr->precord;
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
|
||||
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0);
|
||||
|
||||
if (fieldOffset >= 0 && (fieldOffset & 2) == 2) /* DOn */
|
||||
if (fieldOffset >= 0 && (fieldOffset & 3) == 2) /* DOn */
|
||||
dbGetAlarmLimits(get_dol(prec, fieldOffset),
|
||||
&pad->lower_alarm_limit, &pad->lower_warning_limit,
|
||||
&pad->upper_warning_limit, &pad->upper_alarm_limit);
|
||||
|
||||
@@ -36,6 +36,7 @@ dbTestIoc_DBD += xLink.dbd
|
||||
dbTestIoc_DBD += devx.dbd
|
||||
dbTestIoc_DBD += jlinkz.dbd
|
||||
dbTestIoc_DBD += dbLinkdset.dbd
|
||||
dbTestIoc_DBD += dbCore.dbd
|
||||
TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db
|
||||
|
||||
testHarness_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
@@ -183,6 +184,11 @@ testHarness_SRCS += dbStaticTest.c
|
||||
TESTFILES += ../dbStaticTest.db
|
||||
TESTFILES += ../dbStaticTestAlias1.db
|
||||
TESTFILES += ../dbStaticTestAlias2.db
|
||||
TESTFILES += ../dbStaticTestAliasAgain1.db
|
||||
TESTFILES += ../dbStaticTestAliasAgain2.db
|
||||
TESTFILES += ../dbStaticTestAliasAgain3.db
|
||||
TESTFILES += ../dbStaticTestAliasAgainError1.db
|
||||
TESTFILES += ../dbStaticTestAliasAgainError2.db
|
||||
TESTFILES += ../dbStaticTestRemove.db
|
||||
TESTS += dbStaticTest
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <dbUnitTest.h>
|
||||
#include <testMain.h>
|
||||
#include <epicsString.h>
|
||||
#include <iocsh.h>
|
||||
|
||||
|
||||
static void testEntryRemoved(const char *pv)
|
||||
@@ -321,16 +322,19 @@ static void testDbVerify(const char *record)
|
||||
dbFinishEntry(&entry);
|
||||
}
|
||||
|
||||
static void testWrongAliasRecord(const char *filename)
|
||||
static void testReadDatabase(const char *filename, int expectToFail)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
long status;
|
||||
dbPath(pdbbase,"." OSI_PATH_LIST_SEPARATOR "..");
|
||||
dbOpenFile(pdbbase, filename, &fp);
|
||||
if(!fp) {
|
||||
testAbort("Unable to read %s", filename);
|
||||
testAbort("Unable to open %s", filename);
|
||||
}
|
||||
testOk(dbReadDatabaseFP(&pdbbase, fp, NULL, NULL) != 0,
|
||||
"Wrong alias record in %s is expected to fail", filename);
|
||||
status = dbReadDatabaseFP(&pdbbase, fp, NULL, NULL);
|
||||
testOk(!status == !expectToFail,
|
||||
"Reading %s%s", filename,
|
||||
expectToFail ? " is expected to fail" : "");
|
||||
}
|
||||
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
@@ -341,7 +345,7 @@ MAIN(dbStaticTest)
|
||||
char *ldirDup;
|
||||
FILE *fp = NULL;
|
||||
|
||||
testPlan(340);
|
||||
testPlan(350);
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
@@ -370,8 +374,21 @@ MAIN(dbStaticTest)
|
||||
}
|
||||
free(ldirDup);
|
||||
|
||||
testWrongAliasRecord("dbStaticTestAlias1.db");
|
||||
testWrongAliasRecord("dbStaticTestAlias2.db");
|
||||
testReadDatabase("dbStaticTestAlias1.db", 1);
|
||||
testReadDatabase("dbStaticTestAlias2.db", 1);
|
||||
|
||||
/* Test re-defining aliases */
|
||||
testReadDatabase("dbStaticTestAliasAgain1.db", 0);
|
||||
testReadDatabase("dbStaticTestAliasAgain1.db", 0);
|
||||
testReadDatabase("dbStaticTestAliasAgain2.db", 0);
|
||||
testReadDatabase("dbStaticTestAliasAgain2.db", 0);
|
||||
testReadDatabase("dbStaticTestAliasAgain3.db", 0);
|
||||
testReadDatabase("dbStaticTestAliasAgain3.db", 0);
|
||||
testReadDatabase("dbStaticTestAliasAgainError1.db", 1);
|
||||
testReadDatabase("dbStaticTestAliasAgainError2.db", 1);
|
||||
iocshCmd("var dbRecordsOnceOnly 1");
|
||||
testReadDatabase("dbStaticTestAliasAgain2.db", 1);
|
||||
testReadDatabase("dbStaticTestAliasAgain3.db", 1);
|
||||
|
||||
testEntry("testrec.VAL");
|
||||
testEntry("testalias.VAL");
|
||||
|
||||
4
modules/database/test/ioc/db/dbStaticTestAliasAgain1.db
Normal file
4
modules/database/test/ioc/db/dbStaticTestAliasAgain1.db
Normal file
@@ -0,0 +1,4 @@
|
||||
# Test re-load alias in record
|
||||
record(x, "testrecAgain") {
|
||||
alias("testaliasAgain1")
|
||||
}
|
||||
2
modules/database/test/ioc/db/dbStaticTestAliasAgain2.db
Normal file
2
modules/database/test/ioc/db/dbStaticTestAliasAgain2.db
Normal file
@@ -0,0 +1,2 @@
|
||||
# Test re-load alias for record
|
||||
alias("testrecAgain", "testaliasAgain2")
|
||||
2
modules/database/test/ioc/db/dbStaticTestAliasAgain3.db
Normal file
2
modules/database/test/ioc/db/dbStaticTestAliasAgain3.db
Normal file
@@ -0,0 +1,2 @@
|
||||
# Test re-load alias for alias
|
||||
alias("testaliasAgain2", "testaliasAgain3")
|
||||
@@ -0,0 +1,2 @@
|
||||
# ERROR: alias using name of exising alias for different record
|
||||
alias("testrec", "testaliasAgain1")
|
||||
@@ -0,0 +1,2 @@
|
||||
# ERROR: alias using name of exising record fails
|
||||
alias("testrecAgain", "testrec")
|
||||
@@ -12,7 +12,7 @@
|
||||
use strict;
|
||||
use Test;
|
||||
|
||||
BEGIN {plan tests => 12}
|
||||
BEGIN {plan tests => 14}
|
||||
|
||||
# Check include/substitute command model
|
||||
ok(msi('-I .. ../t1-template.txt'), slurp('../t1-result.txt'));
|
||||
@@ -56,6 +56,12 @@ my %envs = (TEST_NO => 12, PREFIX => 't');
|
||||
ok(msi('-I. -I.. -S ../t12-substitute.txt'), slurp('../t12-result.txt'));
|
||||
delete @ENV{ keys %envs }; # Not really needed
|
||||
|
||||
# Substitution file, relative path includes
|
||||
ok(msi('-I @TOP@/modules -S ../t13-substitute.txt'), slurp('../t13-result.txt'));
|
||||
|
||||
# Template file, relative path includes
|
||||
ok(msi('-I @TOP@/modules ../t14-template.txt'), slurp('../t14-result.txt'));
|
||||
|
||||
# Test support routines
|
||||
|
||||
sub slurp {
|
||||
|
||||
2
modules/database/test/ioc/dbtemplate/t13-result.txt
Normal file
2
modules/database/test/ioc/dbtemplate/t13-result.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
# comment line
|
||||
a=foo
|
||||
3
modules/database/test/ioc/dbtemplate/t13-substitute.txt
Normal file
3
modules/database/test/ioc/dbtemplate/t13-substitute.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
file database/test/ioc/dbtemplate/t13-template.txt {
|
||||
{ a=foo }
|
||||
}
|
||||
2
modules/database/test/ioc/dbtemplate/t13-template.txt
Normal file
2
modules/database/test/ioc/dbtemplate/t13-template.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
# comment line
|
||||
a=$(a)
|
||||
1
modules/database/test/ioc/dbtemplate/t14-include.txt
Normal file
1
modules/database/test/ioc/dbtemplate/t14-include.txt
Normal file
@@ -0,0 +1 @@
|
||||
I'm a file!
|
||||
5
modules/database/test/ioc/dbtemplate/t14-result.txt
Normal file
5
modules/database/test/ioc/dbtemplate/t14-result.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
This is t14-template.txt
|
||||
|
||||
I'm a file!
|
||||
|
||||
End of t14-template.txt
|
||||
5
modules/database/test/ioc/dbtemplate/t14-template.txt
Normal file
5
modules/database/test/ioc/dbtemplate/t14-template.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
This is t14-template.txt
|
||||
|
||||
include "database/test/ioc/dbtemplate/t14-include.txt"
|
||||
|
||||
End of t14-template.txt
|
||||
@@ -510,6 +510,7 @@ static void rtshellCallFunc(const iocshArgBuf *args)
|
||||
|
||||
if (!cmd) {
|
||||
fprintf(stderr, "ERR: No such command\n");
|
||||
iocshSetError(-1);
|
||||
|
||||
} else {
|
||||
fflush(stdout);
|
||||
@@ -517,6 +518,7 @@ static void rtshellCallFunc(const iocshArgBuf *args)
|
||||
ret = (*cmd->command)(args[1].aval.ac,args[1].aval.av);
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
iocshSetError(ret);
|
||||
if(ret)
|
||||
fprintf(stderr, "ERR: %d\n",ret);
|
||||
}
|
||||
@@ -611,18 +613,27 @@ static void nfsMountCallFunc(const iocshArgBuf *args)
|
||||
}
|
||||
*cp = '/';
|
||||
}
|
||||
nfsMount(args[0].sval, args[1].sval, args[2].sval);
|
||||
iocshSetError(nfsMount(args[0].sval, args[1].sval, args[2].sval));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void zoneset(const char *zone)
|
||||
int zoneset(const char *zone)
|
||||
{
|
||||
if(zone)
|
||||
setenv("TZ", zone, 1);
|
||||
else
|
||||
unsetenv("TZ");
|
||||
int ret;
|
||||
if(zone) {
|
||||
if ((ret = setenv("TZ", zone, 1)) < 0)
|
||||
return ret;
|
||||
}
|
||||
#if defined( __NEWLIB_MINOR__ ) /* Added in newlib 2.2.0 */
|
||||
else if ((ret = unsetenv("TZ")) < 0)
|
||||
return ret;
|
||||
#else
|
||||
else
|
||||
unsetenv("TZ");
|
||||
#endif
|
||||
tzset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const iocshArg zonesetArg0 = {"zone string", iocshArgString};
|
||||
@@ -634,7 +645,7 @@ static const iocshFuncDef zonesetFuncDef = {"zoneset",1,zonesetArgs
|
||||
};
|
||||
static void zonesetCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
zoneset(args[0].sval);
|
||||
iocshSetError(zoneset(args[0].sval));
|
||||
}
|
||||
|
||||
#ifndef RTEMS_LEGACY_STACK
|
||||
@@ -667,6 +678,7 @@ static void setlogmaskCallFunc(const iocshArgBuf *args)
|
||||
return;
|
||||
}
|
||||
printf("Error: unknown log level.\n");
|
||||
iocshSetError(-1);
|
||||
}
|
||||
}
|
||||
static const iocshArg setlogmaskArg0 = {"level name", iocshArgString};
|
||||
|
||||
@@ -483,18 +483,27 @@ static void nfsMountCallFunc(const iocshArgBuf *args)
|
||||
}
|
||||
*cp = '/';
|
||||
}
|
||||
nfsMount(args[0].sval, args[1].sval, args[2].sval);
|
||||
iocshSetError(nfsMount(args[0].sval, args[1].sval, args[2].sval));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void zoneset(const char *zone)
|
||||
int zoneset(const char *zone)
|
||||
{
|
||||
if(zone)
|
||||
setenv("TZ", zone, 1);
|
||||
else
|
||||
unsetenv("TZ");
|
||||
int ret;
|
||||
if(zone) {
|
||||
if ((ret = setenv("TZ", zone, 1)) < 0)
|
||||
return ret;
|
||||
}
|
||||
#if defined( __NEWLIB_MINOR__ ) /* Added in newlib 2.2.0 */
|
||||
else if ((ret = unsetenv("TZ")) < 0)
|
||||
return ret;
|
||||
#else
|
||||
else
|
||||
unsetenv("TZ");
|
||||
#endif
|
||||
tzset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const iocshArg zonesetArg0 = {"zone string", iocshArgString};
|
||||
@@ -502,7 +511,7 @@ static const iocshArg * const zonesetArgs[1] = {&zonesetArg0};
|
||||
static const iocshFuncDef zonesetFuncDef = {"zoneset",1,zonesetArgs};
|
||||
static void zonesetCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
zoneset(args[0].sval);
|
||||
iocshSetError(zoneset(args[0].sval));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -311,13 +311,13 @@ inline bool tsSLIterConst<T>::valid () const
|
||||
template < class T >
|
||||
inline bool tsSLIterConst<T>::operator == ( const tsSLIterConst<T> &rhs ) const
|
||||
{
|
||||
return this->pEntry == rhs.pConstEntry;
|
||||
return this->pEntry == rhs.pEntry;
|
||||
}
|
||||
|
||||
template < class T >
|
||||
inline bool tsSLIterConst<T>::operator != (const tsSLIterConst<T> &rhs) const
|
||||
{
|
||||
return this->pEntry != rhs.pConstEntry;
|
||||
return this->pEntry != rhs.pEntry;
|
||||
}
|
||||
|
||||
template < class T >
|
||||
|
||||
1
modules/libcom/src/env/envDefs.h
vendored
1
modules/libcom/src/env/envDefs.h
vendored
@@ -76,6 +76,7 @@ LIBCOM_API extern const ENV_PARAM EPICS_IOC_LOG_FILE_COMMAND;
|
||||
LIBCOM_API extern const ENV_PARAM IOCSH_PS1;
|
||||
LIBCOM_API extern const ENV_PARAM IOCSH_HISTSIZE;
|
||||
LIBCOM_API extern const ENV_PARAM IOCSH_HISTEDIT_DISABLE;
|
||||
LIBCOM_API extern const ENV_PARAM EPICS_ABORT_ON_ASSERT;
|
||||
LIBCOM_API extern const ENV_PARAM *env_param_list[];
|
||||
|
||||
struct in_addr;
|
||||
|
||||
@@ -1325,7 +1325,7 @@ iocshCmd (const char *cmd)
|
||||
int epicsStdCall
|
||||
iocshLoad(const char *pathname, const char *macros)
|
||||
{
|
||||
if (pathname)
|
||||
if (pathname && !getenv("IOCSH_STARTUP_SCRIPT"))
|
||||
epicsEnvSet("IOCSH_STARTUP_SCRIPT", pathname);
|
||||
return iocshBody(pathname, NULL, macros);
|
||||
}
|
||||
|
||||
@@ -139,10 +139,12 @@ static void epicsEnvSetCallFunc(const iocshArgBuf *args)
|
||||
|
||||
if (name == NULL) {
|
||||
fprintf(stderr, "Missing environment variable name argument.\n");
|
||||
iocshSetError(-1);
|
||||
return;
|
||||
}
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "Missing environment variable value argument.\n");
|
||||
iocshSetError(-1);
|
||||
return;
|
||||
}
|
||||
epicsEnvSet (name, value);
|
||||
@@ -159,6 +161,7 @@ static void epicsEnvUnsetCallFunc(const iocshArgBuf *args)
|
||||
|
||||
if (name == NULL) {
|
||||
fprintf(stderr, "Missing environment variable name argument.\n");
|
||||
iocshSetError(-1);
|
||||
return;
|
||||
}
|
||||
epicsEnvUnset (name);
|
||||
@@ -215,7 +218,7 @@ static const iocshFuncDef iocLogInitFuncDef = {"iocLogInit",0,0,
|
||||
" see 'setIocLogDisable' command\n"};
|
||||
static void iocLogInitCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocLogInit ();
|
||||
iocshSetError(iocLogInit ());
|
||||
}
|
||||
|
||||
/* iocLogDisable */
|
||||
@@ -354,6 +357,7 @@ static void threadCallFunc(const iocshArgBuf *args)
|
||||
tid = epicsThreadGetId (cp);
|
||||
if (!tid) {
|
||||
fprintf(stderr, "\t'%s' is not a known thread name\n", cp);
|
||||
iocshSetError(-1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -429,6 +433,7 @@ static void epicsThreadResumeCallFunc(const iocshArgBuf *args)
|
||||
tid = epicsThreadGetId(cp);
|
||||
if (!tid) {
|
||||
fprintf(stderr, "'%s' is not a valid thread name\n", cp);
|
||||
iocshSetError(-1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -437,12 +442,14 @@ static void epicsThreadResumeCallFunc(const iocshArgBuf *args)
|
||||
epicsThreadGetName(tid, nameBuf, sizeof nameBuf);
|
||||
if (nameBuf[0] == '\0') {
|
||||
fprintf(stderr, "'%s' is not a valid thread id\n", cp);
|
||||
iocshSetError(-1);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
if (!epicsThreadIsSuspended(tid)) {
|
||||
fprintf(stderr, "Thread %s is not suspended\n", cp);
|
||||
iocshSetError(-1);
|
||||
continue;
|
||||
}
|
||||
epicsThreadResume(tid);
|
||||
@@ -458,7 +465,7 @@ static const iocshFuncDef generalTimeReportFuncDef = {"generalTimeReport",1,gene
|
||||
" 1 - Additionally show current time obtained from each provider.\n"};
|
||||
static void generalTimeReportCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
generalTimeReport(args[0].ival);
|
||||
iocshSetError(generalTimeReport(args[0].ival));
|
||||
}
|
||||
|
||||
/* installLastResortEventProvider */
|
||||
@@ -467,7 +474,7 @@ static const iocshFuncDef installLastResortEventProviderFuncDef = {"installLastR
|
||||
"which returns the current time for every event number\n"};
|
||||
static void installLastResortEventProviderCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
installLastResortEventProvider();
|
||||
iocshSetError(installLastResortEventProvider());
|
||||
}
|
||||
|
||||
static iocshVarDef comDefs[] = {
|
||||
|
||||
@@ -24,7 +24,7 @@ LIBCOM_API void * callocMustSucceed(size_t count, size_t size, const char *msg)
|
||||
void * mem = NULL;
|
||||
if (count > 0 && size > 0) {
|
||||
while ((mem = calloc(count, size)) == NULL) {
|
||||
errlogPrintf("%s: callocMustSucceed(%lu, %lu) - calloc failed\n",
|
||||
errlogPrintf("%s: callocMustSucceed(%lu, %lu) - " ERL_ERROR " calloc failed\n",
|
||||
msg, (unsigned long)count, (unsigned long)size);
|
||||
errlogPrintf("Thread %s (%p) suspending.\n",
|
||||
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
|
||||
@@ -40,7 +40,7 @@ LIBCOM_API void * mallocMustSucceed(size_t size, const char *msg)
|
||||
void * mem = NULL;
|
||||
if (size > 0) {
|
||||
while ((mem = malloc(size)) == NULL) {
|
||||
errlogPrintf("%s: mallocMustSucceed(%lu) - malloc failed\n",
|
||||
errlogPrintf("%s: mallocMustSucceed(%lu) - " ERL_ERROR " malloc failed\n",
|
||||
msg, (unsigned long)size);
|
||||
errlogPrintf("Thread %s (%p) suspending.\n",
|
||||
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
|
||||
@@ -59,7 +59,7 @@ LIBCOM_API void cantProceed(const char *msg, ...)
|
||||
errlogVprintf(msg, pvar);
|
||||
va_end(pvar);
|
||||
|
||||
errlogPrintf("Thread %s (%p) can't proceed, suspending.\n",
|
||||
errlogPrintf(ANSI_RED("CRITICAL ERROR") " Thread %s (%p) can't proceed, suspending.\n",
|
||||
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
|
||||
|
||||
epicsStackTrace();
|
||||
|
||||
@@ -56,18 +56,24 @@ LIBCOM_API void cantProceed(
|
||||
* gracefully when memory runs out.
|
||||
*/
|
||||
/** @{ */
|
||||
/** \brief A calloc() that never returns NULL.
|
||||
/** \brief A calloc() which suspends on error.
|
||||
* \param count Number of objects.
|
||||
* \param size Size of each object.
|
||||
* \param errorMessage What this memory is needed for.
|
||||
* \return Pointer to zeroed allocated memory.
|
||||
* \param errorMessage Context added to logged error message
|
||||
* \return Pointer to zeroed allocated memory. Should later be free() d
|
||||
*
|
||||
* Will always return NULL for a zero length allocation.
|
||||
* Will never return NULL otherwise.
|
||||
*/
|
||||
LIBCOM_API void * callocMustSucceed(size_t count, size_t size,
|
||||
const char *errorMessage);
|
||||
/** \brief A malloc() that never returns NULL.
|
||||
/** \brief A malloc() which suspends on error.
|
||||
* \param size Size of block to allocate.
|
||||
* \param errorMessage What this memory is needed for.
|
||||
* \return Pointer to allocated memory.
|
||||
* \param errorMessage Context added to logged error message
|
||||
* \return Pointer to allocated memory. Should later be free() d
|
||||
*
|
||||
* Will always return NULL for a zero length allocation.
|
||||
* Will never return NULL otherwise.
|
||||
*/
|
||||
LIBCOM_API void * mallocMustSucceed(size_t size, const char *errorMessage);
|
||||
/** @} */
|
||||
|
||||
@@ -35,8 +35,6 @@
|
||||
#include "cantProceed.h"
|
||||
#include "epicsExit.h"
|
||||
|
||||
void epicsMutexCleanup(void);
|
||||
|
||||
typedef struct exitNode {
|
||||
ELLNODE node;
|
||||
epicsExitFunc func;
|
||||
@@ -115,8 +113,6 @@ LIBCOM_API void epicsExitCallAtExits(void)
|
||||
epicsExitCallAtExitsPvt ( pep );
|
||||
destroyExitPvt ( pep );
|
||||
}
|
||||
/* Handle specially to avoid circular reference */
|
||||
epicsMutexCleanup();
|
||||
}
|
||||
|
||||
LIBCOM_API void epicsExitCallAtThreadExits(void)
|
||||
|
||||
@@ -26,30 +26,23 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsThread.h"
|
||||
#include "valgrind/valgrind.h"
|
||||
#include "ellLib.h"
|
||||
#include "errlog.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsMutexImpl.h"
|
||||
#include "epicsThread.h"
|
||||
#include "cantProceed.h"
|
||||
|
||||
static epicsThreadOnceId epicsMutexOsiOnce = EPICS_THREAD_ONCE_INIT;
|
||||
static ELLLIST mutexList;
|
||||
static ELLLIST freeList;
|
||||
|
||||
struct epicsMutexParm {
|
||||
ELLNODE node;
|
||||
epicsMutexOSD * id;
|
||||
# ifdef LOG_LAST_OWNER
|
||||
epicsThreadId lastOwner;
|
||||
# endif
|
||||
const char *pFileName;
|
||||
int lineno;
|
||||
};
|
||||
|
||||
static epicsMutexOSD * epicsMutexGlobalLock;
|
||||
static ELLLIST mutexList = ELLLIST_INIT;
|
||||
|
||||
/* Specially initialized to bootstrap initialization.
|
||||
* When supported (posix and !rtems) use statically initiallized mutex.
|
||||
* Otherwise, initialize via epicsMutexOsdSetup().
|
||||
*/
|
||||
struct epicsMutexParm epicsMutexGlobalLock = {ELLNODE_INIT, __FILE__, __LINE__};
|
||||
|
||||
// vxWorks 5.4 gcc fails during compile when I use std::exception
|
||||
using namespace std;
|
||||
@@ -76,176 +69,82 @@ const char * epicsMutex::invalidMutex::what () const throw ()
|
||||
return "epicsMutex::invalidMutex()";
|
||||
}
|
||||
|
||||
static void epicsMutexOsiInit(void *) {
|
||||
ellInit(&mutexList);
|
||||
ellInit(&freeList);
|
||||
VALGRIND_CREATE_MEMPOOL(&freeList, 0, 0);
|
||||
epicsMutexGlobalLock = epicsMutexOsdCreate();
|
||||
}
|
||||
|
||||
epicsMutexId epicsStdCall epicsMutexOsiCreate(
|
||||
const char *pFileName,int lineno)
|
||||
{
|
||||
epicsMutexOSD * id;
|
||||
epicsMutexOsdSetup();
|
||||
|
||||
epicsThreadOnce(&epicsMutexOsiOnce, epicsMutexOsiInit, NULL);
|
||||
epicsMutexId ret = (epicsMutexId)calloc(1, sizeof(*ret));
|
||||
if(ret) {
|
||||
ret->pFileName = pFileName;
|
||||
ret->lineno = lineno;
|
||||
|
||||
if(!epicsMutexOsdPrepare(ret)) {
|
||||
epicsMutexMustLock(&epicsMutexGlobalLock);
|
||||
ellAdd(&mutexList, &ret->node);
|
||||
(void)epicsMutexUnlock(&epicsMutexGlobalLock);
|
||||
|
||||
} else {
|
||||
free(ret);
|
||||
ret = NULL;
|
||||
}
|
||||
|
||||
id = epicsMutexOsdCreate();
|
||||
if(!id) {
|
||||
return 0;
|
||||
}
|
||||
epicsMutexLockStatus lockStat =
|
||||
epicsMutexOsdLock(epicsMutexGlobalLock);
|
||||
assert ( lockStat == epicsMutexLockOK );
|
||||
epicsMutexParm *pmutexNode =
|
||||
reinterpret_cast < epicsMutexParm * > ( ellFirst(&freeList) );
|
||||
if(pmutexNode) {
|
||||
ellDelete(&freeList,&pmutexNode->node);
|
||||
VALGRIND_MEMPOOL_FREE(&freeList, pmutexNode);
|
||||
} else {
|
||||
pmutexNode = static_cast < epicsMutexParm * > ( calloc(1,sizeof(epicsMutexParm)) );
|
||||
}
|
||||
VALGRIND_MEMPOOL_ALLOC(&freeList, pmutexNode, sizeof(epicsMutexParm));
|
||||
pmutexNode->id = id;
|
||||
# ifdef LOG_LAST_OWNER
|
||||
pmutexNode->lastOwner = 0;
|
||||
# endif
|
||||
pmutexNode->pFileName = pFileName;
|
||||
pmutexNode->lineno = lineno;
|
||||
ellAdd(&mutexList,&pmutexNode->node);
|
||||
epicsMutexOsdUnlock(epicsMutexGlobalLock);
|
||||
return(pmutexNode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
epicsMutexId epicsStdCall epicsMutexOsiMustCreate(
|
||||
const char *pFileName,int lineno)
|
||||
{
|
||||
epicsMutexId id = epicsMutexOsiCreate(pFileName,lineno);
|
||||
assert(id);
|
||||
return(id );
|
||||
if(!id) {
|
||||
cantProceed("epicsMutexOsiMustCreate() fails at %s:%d\n",
|
||||
pFileName, lineno);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void epicsStdCall epicsMutexDestroy(epicsMutexId pmutexNode)
|
||||
{
|
||||
epicsMutexLockStatus lockStat =
|
||||
epicsMutexOsdLock(epicsMutexGlobalLock);
|
||||
assert ( lockStat == epicsMutexLockOK );
|
||||
ellDelete(&mutexList,&pmutexNode->node);
|
||||
epicsMutexOsdDestroy(pmutexNode->id);
|
||||
VALGRIND_MEMPOOL_FREE(&freeList, pmutexNode);
|
||||
VALGRIND_MEMPOOL_ALLOC(&freeList, &pmutexNode->node, sizeof(pmutexNode->node));
|
||||
ellAdd(&freeList,&pmutexNode->node);
|
||||
epicsMutexOsdUnlock(epicsMutexGlobalLock);
|
||||
}
|
||||
|
||||
void epicsStdCall epicsMutexUnlock(epicsMutexId pmutexNode)
|
||||
{
|
||||
epicsMutexOsdUnlock(pmutexNode->id);
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsStdCall epicsMutexLock(
|
||||
epicsMutexId pmutexNode)
|
||||
{
|
||||
epicsMutexLockStatus status =
|
||||
epicsMutexOsdLock(pmutexNode->id);
|
||||
# ifdef LOG_LAST_OWNER
|
||||
if ( status == epicsMutexLockOK ) {
|
||||
pmutexNode->lastOwner = epicsThreadGetIdSelf();
|
||||
}
|
||||
# endif
|
||||
return status;
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsStdCall epicsMutexTryLock(
|
||||
epicsMutexId pmutexNode)
|
||||
{
|
||||
epicsMutexLockStatus status =
|
||||
epicsMutexOsdTryLock(pmutexNode->id);
|
||||
# ifdef LOG_LAST_OWNER
|
||||
if ( status == epicsMutexLockOK ) {
|
||||
pmutexNode->lastOwner = epicsThreadGetIdSelf();
|
||||
}
|
||||
# endif
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Empty the freeList.
|
||||
* Called from epicsExit.c, but not via epicsAtExit()
|
||||
* to avoid the possibility of a circular reference.
|
||||
*/
|
||||
extern "C"
|
||||
void epicsMutexCleanup(void)
|
||||
{
|
||||
ELLNODE *cur;
|
||||
epicsMutexLockStatus lockStat =
|
||||
epicsMutexOsdLock(epicsMutexGlobalLock);
|
||||
assert ( lockStat == epicsMutexLockOK );
|
||||
|
||||
while((cur=ellGet(&freeList))!=NULL) {
|
||||
VALGRIND_MEMPOOL_FREE(&freeList, cur);
|
||||
free(cur);
|
||||
if(pmutexNode) {
|
||||
epicsMutexMustLock(&epicsMutexGlobalLock);
|
||||
ellDelete(&mutexList, &pmutexNode->node);
|
||||
(void)epicsMutexUnlock(&epicsMutexGlobalLock);
|
||||
epicsMutexOsdCleanup(pmutexNode);
|
||||
free(pmutexNode);
|
||||
}
|
||||
|
||||
epicsMutexOsdUnlock(epicsMutexGlobalLock);
|
||||
}
|
||||
|
||||
void epicsStdCall epicsMutexShow(
|
||||
epicsMutexId pmutexNode, unsigned int level)
|
||||
{
|
||||
# ifdef LOG_LAST_OWNER
|
||||
char threadName [255];
|
||||
if ( pmutexNode->lastOwner ) {
|
||||
# error currently not safe to fetch name for stale thread
|
||||
epicsThreadGetName ( pmutexNode->lastOwner,
|
||||
threadName, sizeof ( threadName ) );
|
||||
}
|
||||
else {
|
||||
strcpy ( threadName, "<not used>" );
|
||||
}
|
||||
printf("epicsMutexId %p last owner \"%s\" source %s line %d\n",
|
||||
(void *)pmutexNode, threadName,
|
||||
pmutexNode->pFileName, pmutexNode->lineno);
|
||||
# else
|
||||
printf("epicsMutexId %p source %s line %d\n",
|
||||
(void *)pmutexNode, pmutexNode->pFileName,
|
||||
pmutexNode->lineno);
|
||||
# endif
|
||||
printf("epicsMutexId %p source %s line %d\n",
|
||||
(void *)pmutexNode, pmutexNode->pFileName,
|
||||
pmutexNode->lineno);
|
||||
if ( level > 0 ) {
|
||||
epicsMutexOsdShow(pmutexNode->id,level-1);
|
||||
epicsMutexOsdShow(pmutexNode,level-1);
|
||||
}
|
||||
}
|
||||
|
||||
void epicsStdCall epicsMutexShowAll(int onlyLocked,unsigned int level)
|
||||
{
|
||||
epicsMutexParm *pmutexNode;
|
||||
epicsMutexOsdSetup();
|
||||
|
||||
if (epicsMutexOsiOnce == EPICS_THREAD_ONCE_INIT)
|
||||
return;
|
||||
|
||||
printf("ellCount(&mutexList) %d ellCount(&freeList) %d\n",
|
||||
ellCount(&mutexList),ellCount(&freeList));
|
||||
printf("ellCount(&mutexList) %d\n", ellCount(&mutexList));
|
||||
epicsMutexOsdShowAll();
|
||||
epicsMutexLockStatus lockStat =
|
||||
epicsMutexOsdLock(epicsMutexGlobalLock);
|
||||
assert ( lockStat == epicsMutexLockOK );
|
||||
pmutexNode = reinterpret_cast < epicsMutexParm * > ( ellFirst(&mutexList) );
|
||||
while(pmutexNode) {
|
||||
epicsMutexMustLock(&epicsMutexGlobalLock);
|
||||
for(ELLNODE *cur =ellFirst(&mutexList); cur; cur = ellNext(cur)) {
|
||||
epicsMutexParm *lock = CONTAINER(cur, epicsMutexParm, node);
|
||||
if(onlyLocked) {
|
||||
epicsMutexLockStatus status;
|
||||
status = epicsMutexOsdTryLock(pmutexNode->id);
|
||||
if(status==epicsMutexLockOK) {
|
||||
epicsMutexOsdUnlock(pmutexNode->id);
|
||||
pmutexNode =
|
||||
reinterpret_cast < epicsMutexParm * >
|
||||
( ellNext(&pmutexNode->node) );
|
||||
continue;
|
||||
// cycle through to test state
|
||||
if(epicsMutexTryLock(lock)==epicsMutexLockOK) {
|
||||
epicsMutexUnlock(lock);
|
||||
continue; // was not locked, skip
|
||||
}
|
||||
}
|
||||
epicsMutexShow(pmutexNode, level);
|
||||
pmutexNode =
|
||||
reinterpret_cast < epicsMutexParm * > ( ellNext(&pmutexNode->node) );
|
||||
epicsMutexShow(lock, level);
|
||||
}
|
||||
epicsMutexOsdUnlock(epicsMutexGlobalLock);
|
||||
epicsMutexUnlock(&epicsMutexGlobalLock);
|
||||
}
|
||||
|
||||
#if !defined(__GNUC__) || __GNUC__<4 || (__GNUC__==4 && __GNUC_MINOR__<8)
|
||||
|
||||
@@ -247,21 +247,6 @@ LIBCOM_API void epicsStdCall epicsMutexShow(
|
||||
LIBCOM_API void epicsStdCall epicsMutexShowAll(
|
||||
int onlyLocked,unsigned int level);
|
||||
|
||||
/**@privatesection
|
||||
* The following are interfaces to the OS dependent
|
||||
* implementation and should NOT be called directly by
|
||||
* user code.
|
||||
*/
|
||||
struct epicsMutexOSD * epicsMutexOsdCreate(void);
|
||||
void epicsMutexOsdDestroy(struct epicsMutexOSD *);
|
||||
void epicsMutexOsdUnlock(struct epicsMutexOSD *);
|
||||
epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD *);
|
||||
epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD *);
|
||||
void epicsMutexOsdShow(struct epicsMutexOSD *,unsigned int level);
|
||||
#ifdef EPICS_PRIVATE_API
|
||||
void epicsMutexOsdShowAll(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
66
modules/libcom/src/osi/epicsMutexImpl.h
Normal file
66
modules/libcom/src/osi/epicsMutexImpl.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* Copyright (c) 2023 Michael Davidsaver
|
||||
* SPDX-License-Identifier: EPICS
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* Only include from osdMutex.c */
|
||||
|
||||
#ifndef epicsMutexImpl_H
|
||||
#define epicsMutexImpl_H
|
||||
|
||||
#if defined(vxWorks)
|
||||
# include <vxWorks.h>
|
||||
# include <semLib.h>
|
||||
#elif defined(_WIN32)
|
||||
# define VC_EXTRALEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
#elif defined(__rtems__)
|
||||
# include <rtems.h>
|
||||
# include <rtems/score/cpuopts.h>
|
||||
#else
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include "ellLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct epicsMutexParm {
|
||||
/* global list of mutex */
|
||||
ELLNODE node;
|
||||
/* location where mutex was allocated */
|
||||
const char *pFileName;
|
||||
int lineno;
|
||||
#if defined(vxWorks)
|
||||
SEM_ID osd;
|
||||
#elif defined(_WIN32)
|
||||
CRITICAL_SECTION osd;
|
||||
#elif defined(__RTEMS_MAJOR__) && __RTEMS_MAJOR__<5
|
||||
Semaphore_Control *osd;
|
||||
#else
|
||||
pthread_mutex_t osd;
|
||||
#endif
|
||||
};
|
||||
|
||||
void epicsMutexOsdSetup(void);
|
||||
long epicsMutexOsdPrepare(struct epicsMutexParm *);
|
||||
void epicsMutexOsdCleanup(struct epicsMutexParm *);
|
||||
void epicsMutexOsdShow(struct epicsMutexParm *,unsigned int level);
|
||||
void epicsMutexOsdShowAll(void);
|
||||
|
||||
extern struct epicsMutexParm epicsMutexGlobalLock;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C
|
||||
#endif
|
||||
|
||||
#endif // epicsMutexImpl_H
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsThread.h"
|
||||
#include "rtemsNamePvt.h"
|
||||
#include "errlog.h"
|
||||
|
||||
/* #define EPICS_RTEMS_SEMAPHORE_STATS */
|
||||
@@ -47,12 +48,9 @@ epicsEventCreate(epicsEventInitialState initialState)
|
||||
{
|
||||
rtems_status_code sc;
|
||||
rtems_id sid;
|
||||
rtems_interrupt_level level;
|
||||
static char c1 = 'a';
|
||||
static char c2 = 'a';
|
||||
static char c3 = 'a';
|
||||
static uint32_t name;
|
||||
|
||||
sc = rtems_semaphore_create (rtems_build_name ('B', c3, c2, c1),
|
||||
sc = rtems_semaphore_create (next_rtems_name ('B', &name),
|
||||
initialState,
|
||||
RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE |
|
||||
RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL,
|
||||
@@ -62,26 +60,6 @@ epicsEventCreate(epicsEventInitialState initialState)
|
||||
errlogPrintf ("Can't create binary semaphore: %s\n", rtems_status_text (sc));
|
||||
return NULL;
|
||||
}
|
||||
rtems_interrupt_disable (level);
|
||||
if (c1 == 'z') {
|
||||
if (c2 == 'z') {
|
||||
if (c3 == 'z') {
|
||||
c3 = 'a';
|
||||
}
|
||||
else {
|
||||
c3++;
|
||||
}
|
||||
c2 = 'a';
|
||||
}
|
||||
else {
|
||||
c2++;
|
||||
}
|
||||
c1 = 'a';
|
||||
}
|
||||
else {
|
||||
c1++;
|
||||
}
|
||||
rtems_interrupt_enable (level);
|
||||
return (epicsEventId)sid;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <rtems.h>
|
||||
#include <rtems/error.h>
|
||||
|
||||
@@ -26,97 +27,84 @@
|
||||
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsMutexImpl.h"
|
||||
#include "rtemsNamePvt.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#define RTEMS_FAST_MUTEX
|
||||
/* #define EPICS_RTEMS_SEMAPHORE_STATS */
|
||||
/*
|
||||
* Some performance tuning instrumentation
|
||||
*/
|
||||
#ifdef EPICS_RTEMS_SEMAPHORE_STATS
|
||||
unsigned long semMstat[4];
|
||||
#define SEMSTAT(i) semMstat[i]++;
|
||||
#else
|
||||
#define SEMSTAT(i)
|
||||
#endif
|
||||
uint32_t next_rtems_name(char prefix, uint32_t* counter)
|
||||
{
|
||||
uint32_t next;
|
||||
rtems_interrupt_level level;
|
||||
char a, b, c;
|
||||
|
||||
struct epicsMutexOSD *
|
||||
epicsMutexOsdCreate(void)
|
||||
rtems_interrupt_disable (level);
|
||||
next = *counter;
|
||||
*counter = (next+1)%(26u*26u*26u);
|
||||
rtems_interrupt_enable (level);
|
||||
|
||||
a = 'a' + (next % 26u);
|
||||
next /= 26u;
|
||||
b = 'a' + (next % 26u);
|
||||
next /= 26u;
|
||||
c = 'a' + (next % 26u); // modulo should be redundant, but ... paranoia
|
||||
|
||||
return rtems_build_name(prefix, a, b, c);
|
||||
}
|
||||
|
||||
void epicsMutexOsdSetup(void)
|
||||
{
|
||||
// TODO: use RTEMS_SYSINIT_ITEM() ?
|
||||
if(!epicsMutexGlobalLock.osd) {
|
||||
epicsMutexOsdPrepare(&epicsMutexGlobalLock);
|
||||
}
|
||||
}
|
||||
|
||||
long epicsMutexOsdPrepare(struct epicsMutexParm *mutex)
|
||||
{
|
||||
rtems_status_code sc;
|
||||
rtems_id sid;
|
||||
rtems_interrupt_level level;
|
||||
static char c1 = 'a';
|
||||
static char c2 = 'a';
|
||||
static char c3 = 'a';
|
||||
static uint32_t name;
|
||||
|
||||
sc = rtems_semaphore_create (rtems_build_name ('M', c3, c2, c1),
|
||||
sc = rtems_semaphore_create (next_rtems_name ('M', &name),
|
||||
1,
|
||||
RTEMS_PRIORITY|RTEMS_BINARY_SEMAPHORE|RTEMS_INHERIT_PRIORITY|RTEMS_NO_PRIORITY_CEILING|RTEMS_LOCAL,
|
||||
0,
|
||||
&sid);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
errlogPrintf ("Can't create mutex semaphore: %s\n", rtems_status_text (sc));
|
||||
return NULL;
|
||||
return ENOMEM;
|
||||
}
|
||||
rtems_interrupt_disable (level);
|
||||
if (c1 == 'z') {
|
||||
if (c2 == 'z') {
|
||||
if (c3 == 'z') {
|
||||
c3 = 'a';
|
||||
}
|
||||
else {
|
||||
c3++;
|
||||
}
|
||||
c2 = 'a';
|
||||
}
|
||||
else {
|
||||
c2++;
|
||||
}
|
||||
c1 = 'a';
|
||||
}
|
||||
else {
|
||||
c1++;
|
||||
}
|
||||
rtems_interrupt_enable (level);
|
||||
#ifdef RTEMS_FAST_MUTEX
|
||||
{
|
||||
Semaphore_Control *the_semaphore;
|
||||
Objects_Locations location;
|
||||
Objects_Locations location;
|
||||
|
||||
the_semaphore = _Semaphore_Get( sid, &location );
|
||||
_Thread_Enable_dispatch();
|
||||
mutex->osd = _Semaphore_Get( sid, &location );
|
||||
_Thread_Enable_dispatch(); /* _Semaphore_Get() disables */
|
||||
|
||||
return (struct epicsMutexOSD *)the_semaphore;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return (struct epicsMutexOSD *)sid;
|
||||
}
|
||||
|
||||
void epicsMutexOsdDestroy(struct epicsMutexOSD * id)
|
||||
void epicsMutexOsdCleanup(struct epicsMutexParm *mutex)
|
||||
{
|
||||
rtems_status_code sc;
|
||||
rtems_id sid;
|
||||
#ifdef RTEMS_FAST_MUTEX
|
||||
Semaphore_Control *the_semaphore = (Semaphore_Control *)id;
|
||||
Semaphore_Control *the_semaphore = mutex->osd;
|
||||
sid = the_semaphore->Object.id;
|
||||
#else
|
||||
sid = (rtems_id)id;
|
||||
#endif
|
||||
sc = rtems_semaphore_delete (sid);
|
||||
if (sc == RTEMS_RESOURCE_IN_USE) {
|
||||
rtems_semaphore_release (sid);
|
||||
sc = rtems_semaphore_delete (sid);
|
||||
}
|
||||
if (sc != RTEMS_SUCCESSFUL)
|
||||
errlogPrintf ("Can't destroy semaphore %p (%lx): %s\n", id, (unsigned long)sid, rtems_status_text (sc));
|
||||
errlogPrintf ("Can't destroy semaphore %p (%lx): %s\n",
|
||||
mutex, (unsigned long)sid, rtems_status_text (sc));
|
||||
}
|
||||
|
||||
void epicsMutexOsdUnlock(struct epicsMutexOSD * id)
|
||||
void epicsMutexUnlock(struct epicsMutexParm *mutex)
|
||||
{
|
||||
#ifdef RTEMS_FAST_MUTEX
|
||||
Semaphore_Control *the_semaphore = (Semaphore_Control *)id;
|
||||
Semaphore_Control *the_semaphore = mutex->osd;
|
||||
_Thread_Disable_dispatch();
|
||||
_CORE_mutex_Surrender (
|
||||
&the_semaphore->Core_control.mutex,
|
||||
@@ -124,18 +112,13 @@ void epicsMutexOsdUnlock(struct epicsMutexOSD * id)
|
||||
NULL
|
||||
);
|
||||
_Thread_Enable_dispatch();
|
||||
#else
|
||||
epicsEventSignal (id);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * id)
|
||||
epicsMutexLockStatus epicsMutexLock(struct epicsMutexParm *mutex)
|
||||
{
|
||||
#ifdef RTEMS_FAST_MUTEX
|
||||
Semaphore_Control *the_semaphore = (Semaphore_Control *)id;
|
||||
Semaphore_Control *the_semaphore = mutex->osd;
|
||||
ISR_Level level;
|
||||
SEMSTAT(0)
|
||||
_ISR_Disable( level );
|
||||
_CORE_mutex_Seize(
|
||||
&the_semaphore->Core_control.mutex,
|
||||
@@ -148,19 +131,12 @@ epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * id)
|
||||
return epicsMutexLockOK;
|
||||
else
|
||||
return epicsMutexLockError;
|
||||
#else
|
||||
SEMSTAT(0)
|
||||
return((epicsEventWait (id) == epicsEventWaitOK)
|
||||
?epicsMutexLockOK : epicsMutexLockError);
|
||||
#endif
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * id)
|
||||
epicsMutexLockStatus epicsMutexTryLock(struct epicsMutexParm *mutex)
|
||||
{
|
||||
#ifdef RTEMS_FAST_MUTEX
|
||||
Semaphore_Control *the_semaphore = (Semaphore_Control *)id;
|
||||
Semaphore_Control *the_semaphore = mutex->osd;
|
||||
ISR_Level level;
|
||||
SEMSTAT(2)
|
||||
_ISR_Disable( level );
|
||||
_CORE_mutex_Seize(
|
||||
&the_semaphore->Core_control.mutex,
|
||||
@@ -175,25 +151,12 @@ epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * id)
|
||||
return epicsMutexLockTimeout;
|
||||
else
|
||||
return epicsMutexLockError;
|
||||
#else
|
||||
epicsEventWaitStatus status;
|
||||
SEMSTAT(2)
|
||||
status = epicsEventTryWait(id);
|
||||
return((status==epicsEventWaitOK
|
||||
? epicsMutexLockOK
|
||||
: (status==epicsEventWaitTimeout)
|
||||
? epicsMutexLockTimeout
|
||||
: epicsMutexLockError));
|
||||
#endif
|
||||
}
|
||||
|
||||
LIBCOM_API void epicsMutexOsdShow(struct epicsMutexOSD * id,unsigned int level)
|
||||
LIBCOM_API void epicsMutexOsdShow(struct epicsMutexParm *mutex,unsigned int level)
|
||||
{
|
||||
#ifdef RTEMS_FAST_MUTEX
|
||||
Semaphore_Control *the_semaphore = (Semaphore_Control *)id;
|
||||
id = (struct epicsMutexOSD *)the_semaphore->Object.id;
|
||||
#endif
|
||||
epicsEventShow ((epicsEventId)id,level);
|
||||
Semaphore_Control *the_semaphore = mutex->osd;
|
||||
epicsEventShow ((epicsEventId)the_semaphore->Object.id,level);
|
||||
}
|
||||
|
||||
void epicsMutexOsdShowAll(void) {}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "epicsStdio.h"
|
||||
#include "errlog.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsMutexImpl.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsThread.h"
|
||||
#include "cantProceed.h"
|
||||
@@ -59,7 +60,7 @@ struct taskVar {
|
||||
unsigned int threadVariableCapacity;
|
||||
void **threadVariables;
|
||||
};
|
||||
static struct epicsMutexOSD *taskVarMutex;
|
||||
static struct epicsMutexParm taskVarMutex = {ELLNODE_INIT, __FILE__, __LINE__};
|
||||
static struct taskVar *taskVarHead;
|
||||
#define RTEMS_NOTEPAD_TASKVAR 11
|
||||
|
||||
@@ -67,15 +68,7 @@ static struct taskVar *taskVarHead;
|
||||
* Support for `once-only' execution
|
||||
*/
|
||||
static volatile int initialized = 0; /* strictly speaking 'volatile' is not enough here, but it shouldn't hurt */
|
||||
static struct epicsMutexOSD *onceMutex;
|
||||
|
||||
static
|
||||
void epicsMutexOsdMustLock(struct epicsMutexOSD * L)
|
||||
{
|
||||
while(epicsMutexOsdLock(L)!=epicsMutexLockOK) {
|
||||
cantProceed("epicsThreadOnce() mutex error");
|
||||
}
|
||||
}
|
||||
static struct epicsMutexParm onceMutex = {ELLNODE_INIT, __FILE__, __LINE__};
|
||||
|
||||
/*
|
||||
* Just map osi 0 to 99 into RTEMS 199 to 100
|
||||
@@ -161,13 +154,13 @@ epicsThreadGetStackSize (epicsThreadStackSizeClass size)
|
||||
static void
|
||||
taskVarLock (void)
|
||||
{
|
||||
epicsMutexOsdMustLock (taskVarMutex);
|
||||
epicsMutexMustLock (&taskVarMutex);
|
||||
}
|
||||
|
||||
static void
|
||||
taskVarUnlock (void)
|
||||
{
|
||||
epicsMutexOsdUnlock (taskVarMutex);
|
||||
epicsMutexUnlock (&taskVarMutex);
|
||||
}
|
||||
|
||||
static
|
||||
@@ -243,7 +236,7 @@ setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr,
|
||||
v->threadVariables = NULL;
|
||||
v->isRunning = 1;
|
||||
if (joinable) {
|
||||
char c[3];
|
||||
char c[3] = {0,0,0};
|
||||
strncpy(c, v->name, 3);
|
||||
sc = rtems_barrier_create(rtems_build_name('~', c[0], c[1], c[2]),
|
||||
RTEMS_BARRIER_AUTOMATIC_RELEASE | RTEMS_LOCAL,
|
||||
@@ -288,10 +281,8 @@ epicsThreadInit (void)
|
||||
rtems_task_priority old;
|
||||
|
||||
rtems_task_set_priority (RTEMS_SELF, epicsThreadGetOssPriorityValue(99), &old);
|
||||
onceMutex = epicsMutexOsdCreate();
|
||||
taskVarMutex = epicsMutexOsdCreate();
|
||||
if (!onceMutex || !taskVarMutex)
|
||||
cantProceed("epicsThreadInit() can't create global mutexes\n");
|
||||
epicsMutexOsdPrepare(&taskVarMutex);
|
||||
epicsMutexOsdPrepare(&onceMutex);
|
||||
rtems_task_ident (RTEMS_SELF, 0, &tid);
|
||||
if(setThreadInfo (tid, "_main_", NULL, NULL, 0) != RTEMS_SUCCESSFUL)
|
||||
cantProceed("epicsThreadInit() unable to setup _main_");
|
||||
@@ -317,7 +308,7 @@ epicsThreadCreateOpt (
|
||||
unsigned int stackSize;
|
||||
rtems_id tid;
|
||||
rtems_status_code sc;
|
||||
char c[4];
|
||||
char c[4] = {0,0,0,0};
|
||||
|
||||
if (!initialized)
|
||||
epicsThreadInit();
|
||||
@@ -612,26 +603,26 @@ void epicsThreadOnce(epicsThreadOnceId *id, void(*func)(void *), void *arg)
|
||||
#define EPICS_THREAD_ONCE_DONE (epicsThreadId) 1
|
||||
|
||||
if (!initialized) epicsThreadInit();
|
||||
epicsMutexOsdMustLock(onceMutex);
|
||||
epicsMutexMustLock(&onceMutex);
|
||||
if (*id != EPICS_THREAD_ONCE_DONE) {
|
||||
if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */
|
||||
*id = epicsThreadGetIdSelf(); /* mark active */
|
||||
epicsMutexOsdUnlock(onceMutex);
|
||||
epicsMutexUnlock(&onceMutex);
|
||||
func(arg);
|
||||
epicsMutexOsdMustLock(onceMutex);
|
||||
epicsMutexMustLock(&onceMutex);
|
||||
*id = EPICS_THREAD_ONCE_DONE; /* mark done */
|
||||
} else if (*id == epicsThreadGetIdSelf()) {
|
||||
epicsMutexOsdUnlock(onceMutex);
|
||||
epicsMutexUnlock(&onceMutex);
|
||||
cantProceed("Recursive epicsThreadOnce() initialization\n");
|
||||
} else
|
||||
while (*id != EPICS_THREAD_ONCE_DONE) {
|
||||
/* Another thread is in the above func(arg) call. */
|
||||
epicsMutexOsdUnlock(onceMutex);
|
||||
epicsMutexUnlock(&onceMutex);
|
||||
epicsThreadSleep(epicsThreadSleepQuantum());
|
||||
epicsMutexOsdMustLock(onceMutex);
|
||||
epicsMutexMustLock(&onceMutex);
|
||||
}
|
||||
}
|
||||
epicsMutexOsdUnlock(onceMutex);
|
||||
epicsMutexUnlock(&onceMutex);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
20
modules/libcom/src/osi/os/RTEMS-score/rtemsNamePvt.h
Normal file
20
modules/libcom/src/osi/os/RTEMS-score/rtemsNamePvt.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2023 Michael Davidsaver
|
||||
* SPDX-License-Identifier: EPICS
|
||||
* EPICS Base is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef RTEMSNAMEPVT_H
|
||||
#define RTEMSNAMEPVT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Compute rtems_build_name(prefix, A, B, C) where A, B, C are the letters a-z.
|
||||
* eg. "Qaaa"
|
||||
*
|
||||
* 'counter' is incremented atomically during each call.
|
||||
*/
|
||||
uint32_t next_rtems_name(char prefix, uint32_t* counter);
|
||||
|
||||
#endif // RTEMSNAMEPVT_H
|
||||
@@ -20,147 +20,59 @@
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define VC_EXTRALEAN
|
||||
#define STRICT
|
||||
#include <windows.h>
|
||||
#if _WIN32_WINNT < 0x0501
|
||||
# error Minimum supported is Windows XP
|
||||
#endif
|
||||
|
||||
#define EPICS_PRIVATE_API
|
||||
|
||||
#include "libComAPI.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsMutexImpl.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsStdio.h"
|
||||
|
||||
typedef struct epicsMutexOSD {
|
||||
union {
|
||||
HANDLE mutex;
|
||||
CRITICAL_SECTION criticalSection;
|
||||
} os;
|
||||
} epicsMutexOSD;
|
||||
|
||||
static BOOL thisIsNT = FALSE;
|
||||
static LONG weHaveInitialized = 0;
|
||||
|
||||
/*
|
||||
* epicsMutexCreate ()
|
||||
*/
|
||||
epicsMutexOSD * epicsMutexOsdCreate ( void )
|
||||
static epicsThreadOnceId epicsMutexOsdOnce = EPICS_THREAD_ONCE_INIT;
|
||||
static void epicsMutexOsdInit(void* unused)
|
||||
{
|
||||
epicsMutexOSD * pSem;
|
||||
|
||||
if ( ! weHaveInitialized ) {
|
||||
BOOL status;
|
||||
OSVERSIONINFO osInfo;
|
||||
osInfo.dwOSVersionInfoSize = sizeof ( OSVERSIONINFO );
|
||||
status = GetVersionEx ( & osInfo );
|
||||
thisIsNT = status && ( osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT );
|
||||
weHaveInitialized = 1;
|
||||
}
|
||||
|
||||
pSem = malloc ( sizeof (*pSem) );
|
||||
if ( pSem ) {
|
||||
if ( thisIsNT ) {
|
||||
InitializeCriticalSection ( &pSem->os.criticalSection );
|
||||
}
|
||||
else {
|
||||
pSem->os.mutex = CreateMutex ( NULL, FALSE, NULL );
|
||||
if ( pSem->os.mutex == 0 ) {
|
||||
free ( pSem );
|
||||
pSem = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pSem;
|
||||
(void)unused;
|
||||
InitializeCriticalSection(&epicsMutexGlobalLock.osd);
|
||||
}
|
||||
|
||||
/*
|
||||
* epicsMutexOsdDestroy ()
|
||||
*/
|
||||
void epicsMutexOsdDestroy ( epicsMutexOSD * pSem )
|
||||
void epicsMutexOsdSetup()
|
||||
{
|
||||
if ( thisIsNT ) {
|
||||
DeleteCriticalSection ( &pSem->os.criticalSection );
|
||||
}
|
||||
else {
|
||||
CloseHandle ( pSem->os.mutex );
|
||||
}
|
||||
free ( pSem );
|
||||
epicsThreadOnce(&epicsMutexOsdOnce, &epicsMutexOsdInit, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* epicsMutexOsdUnlock ()
|
||||
*/
|
||||
void epicsMutexOsdUnlock ( epicsMutexOSD * pSem )
|
||||
long epicsMutexOsdPrepare(struct epicsMutexParm *mutex)
|
||||
{
|
||||
if ( thisIsNT ) {
|
||||
LeaveCriticalSection ( &pSem->os.criticalSection );
|
||||
}
|
||||
else {
|
||||
BOOL success = ReleaseMutex ( pSem->os.mutex );
|
||||
assert ( success );
|
||||
}
|
||||
InitializeCriticalSection(&mutex->osd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* epicsMutexOsdLock ()
|
||||
*/
|
||||
epicsMutexLockStatus epicsMutexOsdLock ( epicsMutexOSD * pSem )
|
||||
void epicsMutexOsdCleanup(struct epicsMutexParm *mutex)
|
||||
{
|
||||
if ( thisIsNT ) {
|
||||
EnterCriticalSection ( &pSem->os.criticalSection );
|
||||
}
|
||||
else {
|
||||
DWORD status = WaitForSingleObject ( pSem->os.mutex, INFINITE );
|
||||
if ( status != WAIT_OBJECT_0 ) {
|
||||
return epicsMutexLockError;
|
||||
}
|
||||
}
|
||||
DeleteCriticalSection(&mutex->osd);
|
||||
}
|
||||
|
||||
void epicsStdCall epicsMutexUnlock ( struct epicsMutexParm *mutex )
|
||||
{
|
||||
LeaveCriticalSection ( &mutex->osd );
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsStdCall epicsMutexLock ( struct epicsMutexParm *mutex )
|
||||
{
|
||||
EnterCriticalSection ( &mutex->osd );
|
||||
return epicsMutexLockOK;
|
||||
}
|
||||
|
||||
/*
|
||||
* epicsMutexOsdTryLock ()
|
||||
*/
|
||||
epicsMutexLockStatus epicsMutexOsdTryLock ( epicsMutexOSD * pSem )
|
||||
epicsMutexLockStatus epicsStdCall epicsMutexTryLock ( struct epicsMutexParm *mutex )
|
||||
{
|
||||
if ( thisIsNT ) {
|
||||
if ( TryEnterCriticalSection ( &pSem->os.criticalSection ) ) {
|
||||
return epicsMutexLockOK;
|
||||
}
|
||||
else {
|
||||
return epicsMutexLockTimeout;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DWORD status = WaitForSingleObject ( pSem->os.mutex, 0 );
|
||||
if ( status != WAIT_OBJECT_0 ) {
|
||||
if (status == WAIT_TIMEOUT) {
|
||||
return epicsMutexLockTimeout;
|
||||
}
|
||||
else {
|
||||
return epicsMutexLockError;
|
||||
}
|
||||
}
|
||||
}
|
||||
return epicsMutexLockOK;
|
||||
return TryEnterCriticalSection ( &mutex->osd ) ? epicsMutexLockOK : epicsMutexLockTimeout;
|
||||
}
|
||||
|
||||
/*
|
||||
* epicsMutexOsdShow ()
|
||||
*/
|
||||
void epicsMutexOsdShow ( epicsMutexOSD * pSem, unsigned level )
|
||||
void epicsMutexOsdShow ( struct epicsMutexParm *mutex, unsigned level )
|
||||
{
|
||||
if ( thisIsNT ) {
|
||||
printf ("epicsMutex: win32 critical section at %p\n",
|
||||
(void * ) & pSem->os.criticalSection );
|
||||
}
|
||||
else {
|
||||
printf ( "epicsMutex: win32 mutex at %p\n",
|
||||
( void * ) pSem->os.mutex );
|
||||
}
|
||||
(void)level;
|
||||
printf ("epicsMutex: win32 critical section at %p\n",
|
||||
(void * ) & mutex->osd );
|
||||
}
|
||||
|
||||
void epicsMutexOsdShowAll(void) {}
|
||||
|
||||
@@ -20,12 +20,14 @@
|
||||
#include "epicsTime.h"
|
||||
#include "cantProceed.h"
|
||||
#include "epicsStackTrace.h"
|
||||
#include "envDefs.h"
|
||||
|
||||
|
||||
void epicsAssert (const char *pFile, const unsigned line,
|
||||
const char *pExp, const char *pAuthorName)
|
||||
{
|
||||
epicsTimeStamp current;
|
||||
int shouldAbort = 0;
|
||||
|
||||
errlogPrintf("\n\n\n"
|
||||
"A call to 'assert(%s)'\n"
|
||||
@@ -50,6 +52,13 @@ void epicsAssert (const char *pFile, const unsigned line,
|
||||
errlogPrintf("Please E-mail this message to %s or to tech-talk@aps.anl.gov\n",
|
||||
pAuthorName);
|
||||
|
||||
errlogPrintf("Calling epicsThreadSuspendSelf()\n");
|
||||
epicsThreadSuspendSelf ();
|
||||
if (envGetBoolConfigParam(&EPICS_ABORT_ON_ASSERT, &shouldAbort) == 0 && shouldAbort) {
|
||||
errlogPrintf("Calling abort()\n");
|
||||
errlogFlush();
|
||||
abort();
|
||||
}
|
||||
else {
|
||||
errlogPrintf("Calling epicsThreadSuspendSelf()\n");
|
||||
epicsThreadSuspendSelf ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,14 +25,15 @@
|
||||
#include <pthread.h>
|
||||
|
||||
#define EPICS_PRIVATE_API
|
||||
#define epicsStdioStdStreams
|
||||
#define epicsStdioStdPrintfEtc
|
||||
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsMutexImpl.h"
|
||||
#include "osdPosixMutexPriv.h"
|
||||
#include "cantProceed.h"
|
||||
#include "epicsTime.h"
|
||||
#include "errlog.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsAssert.h"
|
||||
|
||||
#define checkStatus(status,message) \
|
||||
if((status)) { \
|
||||
@@ -119,62 +120,62 @@ static int mutexLock(pthread_mutex_t *id)
|
||||
return status;
|
||||
}
|
||||
|
||||
typedef struct epicsMutexOSD {
|
||||
pthread_mutex_t lock;
|
||||
} epicsMutexOSD;
|
||||
/* used if OS does not support statically allocated mutex */
|
||||
static pthread_once_t epicsMutexOsdOnce = PTHREAD_ONCE_INIT;
|
||||
|
||||
epicsMutexOSD * epicsMutexOsdCreate(void) {
|
||||
epicsMutexOSD *pmutex;
|
||||
int status;
|
||||
|
||||
pmutex = calloc(1, sizeof(*pmutex));
|
||||
if(!pmutex)
|
||||
return NULL;
|
||||
|
||||
status = osdPosixMutexInit(&pmutex->lock, PTHREAD_MUTEX_RECURSIVE);
|
||||
if (!status)
|
||||
return pmutex;
|
||||
|
||||
free(pmutex);
|
||||
return NULL;
|
||||
static void epicsMutexOsdInit(void)
|
||||
{
|
||||
int ret = pthread_mutex_init(&epicsMutexGlobalLock.osd, NULL);
|
||||
if(ret) {
|
||||
/* something has gone wrong early. Not much can be done...*/
|
||||
fprintf(stderr, "osdMutex early init failure %d.\n", ret);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void epicsMutexOsdDestroy(struct epicsMutexOSD * pmutex)
|
||||
void epicsMutexOsdSetup()
|
||||
{
|
||||
int status;
|
||||
int ret = pthread_once(&epicsMutexOsdOnce, &epicsMutexOsdInit);
|
||||
if(ret) {
|
||||
/* ditto...*/
|
||||
fprintf(stderr, "osdMutex early once failure %d.\n", ret);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
status = pthread_mutex_destroy(&pmutex->lock);
|
||||
long epicsMutexOsdPrepare(struct epicsMutexParm *pmutex) {
|
||||
return osdPosixMutexInit(&pmutex->osd, PTHREAD_MUTEX_RECURSIVE);
|
||||
}
|
||||
|
||||
void epicsMutexOsdCleanup(struct epicsMutexParm *pmutex)
|
||||
{
|
||||
int status = pthread_mutex_destroy(&pmutex->osd);
|
||||
checkStatus(status, "pthread_mutex_destroy");
|
||||
free(pmutex);
|
||||
}
|
||||
|
||||
void epicsMutexOsdUnlock(struct epicsMutexOSD * pmutex)
|
||||
void epicsMutexUnlock(struct epicsMutexParm * pmutex)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = pthread_mutex_unlock(&pmutex->lock);
|
||||
int status = pthread_mutex_unlock(&pmutex->osd);
|
||||
checkStatus(status, "pthread_mutex_unlock epicsMutexOsdUnlock");
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * pmutex)
|
||||
epicsMutexLockStatus epicsMutexLock(struct epicsMutexParm * pmutex)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = mutexLock(&pmutex->lock);
|
||||
int status = mutexLock(&pmutex->osd);
|
||||
if (status == EINVAL) return epicsMutexLockError;
|
||||
if(status) {
|
||||
errlogMessage("epicsMutex pthread_mutex_lock failed: error epicsMutexOsdLock\n");
|
||||
errlogMessage("epicsMutex pthread_mutex_lock failed: error epicsMutexLock\n");
|
||||
return epicsMutexLockError;
|
||||
}
|
||||
return epicsMutexLockOK;
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * pmutex)
|
||||
epicsMutexLockStatus epicsMutexTryLock(struct epicsMutexParm * pmutex)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (!pmutex) return epicsMutexLockError;
|
||||
status = pthread_mutex_trylock(&pmutex->lock);
|
||||
status = pthread_mutex_trylock(&pmutex->osd);
|
||||
if (status == EINVAL) return epicsMutexLockError;
|
||||
if (status == EBUSY) return epicsMutexLockTimeout;
|
||||
if(status) {
|
||||
@@ -184,12 +185,13 @@ epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * pmutex)
|
||||
return epicsMutexLockOK;
|
||||
}
|
||||
|
||||
void epicsMutexOsdShow(struct epicsMutexOSD * pmutex, unsigned int level)
|
||||
void epicsMutexOsdShow(struct epicsMutexParm * pmutex, unsigned int level)
|
||||
{
|
||||
(void)level;
|
||||
/* GLIBC w/ NTPL is passing the &lock.__data.__lock as the first argument (UADDR)
|
||||
* of the futex() syscall. __lock is at offset 0 of the enclosing structures.
|
||||
*/
|
||||
printf(" pthread_mutex_t* uaddr=%p\n", &pmutex->lock);
|
||||
epicsStdoutPrintf(" pthread_mutex_t* uaddr=%p\n", &pmutex->osd);
|
||||
}
|
||||
|
||||
void epicsMutexOsdShowAll(void)
|
||||
@@ -198,11 +200,11 @@ void epicsMutexOsdShowAll(void)
|
||||
int proto = -1;
|
||||
int ret = pthread_mutexattr_getprotocol(&globalAttrRecursive, &proto);
|
||||
if(ret) {
|
||||
printf("PI maybe not enabled: %d\n", ret);
|
||||
epicsStdoutPrintf("PI maybe not enabled: %d\n", ret);
|
||||
} else {
|
||||
printf("PI is%s enabled\n", proto==PTHREAD_PRIO_INHERIT ? "" : " not");
|
||||
epicsStdoutPrintf("PI is%s enabled\n", proto==PTHREAD_PRIO_INHERIT ? "" : " not");
|
||||
}
|
||||
#else
|
||||
printf("PI not supported\n");
|
||||
epicsStdoutPrintf("PI not supported\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <vxWorks.h>
|
||||
#include <semLib.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <objLib.h>
|
||||
#include <sysLib.h>
|
||||
@@ -23,30 +24,47 @@ int sysClkRateGet(void);
|
||||
#define EPICS_PRIVATE_API
|
||||
|
||||
#include "epicsMutex.h"
|
||||
|
||||
struct epicsMutexOSD * epicsMutexOsdCreate(void)
|
||||
#include "epicsMutexImpl.h"
|
||||
|
||||
void epicsMutexOsdSetup(void)
|
||||
{
|
||||
return((struct epicsMutexOSD *)
|
||||
semMCreate(SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY));
|
||||
if(!epicsMutexGlobalLock.osd) {
|
||||
epicsMutexOsdPrepare(&epicsMutexGlobalLock);
|
||||
}
|
||||
}
|
||||
|
||||
void epicsMutexOsdDestroy(struct epicsMutexOSD * id)
|
||||
long epicsMutexOsdPrepare(struct epicsMutexParm *mutex)
|
||||
{
|
||||
semDelete((SEM_ID)id);
|
||||
mutex->osd = semMCreate(SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY);
|
||||
return mutex->osd ? 0 : ENOMEM;
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * id)
|
||||
void epicsMutexOsdCleanup(struct epicsMutexParm *mutex)
|
||||
{
|
||||
int status;
|
||||
status = semTake((SEM_ID)id,NO_WAIT);
|
||||
semDelete(mutex->osd);
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsMutexLock(struct epicsMutexParm *mutex)
|
||||
{
|
||||
return semTake(mutex->osd,WAIT_FOREVER)==OK ? epicsMutexLockOK : epicsMutexLockError;
|
||||
}
|
||||
|
||||
epicsMutexLockStatus epicsMutexTryLock(struct epicsMutexParm *mutex)
|
||||
{
|
||||
int status = semTake(mutex->osd,NO_WAIT);
|
||||
if(status==OK) return(epicsMutexLockOK);
|
||||
if(errno==S_objLib_OBJ_UNAVAILABLE) return(epicsMutexLockTimeout);
|
||||
return(epicsMutexLockError);
|
||||
}
|
||||
|
||||
void epicsMutexOsdShow(struct epicsMutexOSD * id,unsigned int level)
|
||||
void epicsMutexUnlock(struct epicsMutexParm *mutex)
|
||||
{
|
||||
semShow((SEM_ID)id,level);
|
||||
semGive(mutex->osd);
|
||||
}
|
||||
|
||||
void epicsMutexOsdShow(struct epicsMutexParm *mutex,unsigned int level)
|
||||
{
|
||||
semShow(mutex->osd,level);
|
||||
}
|
||||
|
||||
void epicsMutexOsdShowAll(void) {}
|
||||
|
||||
@@ -13,13 +13,3 @@
|
||||
|
||||
#include <vxWorks.h>
|
||||
#include <semLib.h>
|
||||
|
||||
/* If the macro is replaced by inline it is necessary to say
|
||||
static __inline__
|
||||
but then a warning message appears everywhere osdMutex.h is included
|
||||
*/
|
||||
|
||||
#define epicsMutexOsdUnlock(ID) semGive((SEM_ID)(ID))
|
||||
|
||||
#define epicsMutexOsdLock(ID) \
|
||||
(semTake((SEM_ID)(ID),WAIT_FOREVER)==OK ? epicsMutexLockOK : epicsMutexLockError)
|
||||
|
||||
Submodule modules/normativeTypes updated: 7a2d264f2c...1250a3c236
Submodule modules/pvAccess updated: f1268adb8e...96061ca1cc
Submodule modules/pvData updated: 144f0228cc...0ef8a36172
Submodule modules/pvDatabase updated: f207e512d6...8cf550ff57
Submodule modules/pva2pva updated: 949b3f63c2...3a08da445b
Submodule modules/pvaClient updated: 8ed07fef96...a34876e36a
Reference in New Issue
Block a user