Compare commits

...

36 Commits

Author SHA1 Message Date
Andrew Johnson
86154953f5 Final commit for 7.0.9
Some checks failed
Check EditorConfig / editorconfig (push) Failing after 2s
Base / Cross linux-aarch64 (push) Failing after 1s
Base / Cross linux-arm gnueabi (push) Failing after 2s
Base / Cross linux-arm gnueabihf (push) Failing after 1s
Base / Fedora-33 (push) Failing after 2s
Base / Fedora-latest (push) Failing after 1s
CodeQL / Analyze (cpp) (push) Failing after 2s
Base / Ub-20 gcc + RT-5.1 uC5282 (push) Has been cancelled
Base / Ub-20 gcc + RT-5.1 xilinx_zynq_a9_qemu (push) Has been cancelled
Base / Win2019 mingw (push) Has been cancelled
Base / Ub-20 gcc + MinGW (push) Has been cancelled
Base / Ub-20 gcc unsigned char (push) Has been cancelled
Base / Ub-20 gcc C++11, static (push) Has been cancelled
Base / Ub-20 gcc + MinGW, static (push) Has been cancelled
Base / Ub-22 gcc-12 c++20 Werror (push) Has been cancelled
Base / Win2019 MSC-19 (push) Has been cancelled
Base / Win2019 MSC-19, debug (push) Has been cancelled
Base / Win2019 MSC-19, static (push) Has been cancelled
Base / Ub-20 clang C++11 (push) Has been cancelled
Base / MacOS clang (push) Has been cancelled
Base / Ub-20 clang (push) Has been cancelled
Base / Ub-20 gcc + RT-5.1 beatnik (push) Has been cancelled
Base / Ub-20 gcc + RT-4.10 (push) Has been cancelled
Base / Ub-20 gcc + RT-4.9 (push) Has been cancelled
Base / Ub-20 gcc + RT-5.1 pc686 (push) Has been cancelled
2025-02-21 17:31:33 -06:00
Andrew Johnson
07572ab025 Update submodules to released versions 2025-02-21 15:47:00 -06:00
Andrew Johnson
0733beae50 Fix for PR #597 breakage of VS-2012/2010 builds 2025-02-20 10:27:41 -06:00
Andrew Johnson
a3d8531008 Revert PR #589
* Revert "Release Notes for PYTHON=python3"
  commit 2612b47c3f.

* Revert "Remove Python build dependency when LINKER_USE_RPATH=ORIGIN"
  commit f4c474eb77.
2025-02-19 14:10:16 -06:00
Michael Davidsaver
7a6e11cae0 make casStatsFetch() safe-ish when RSRV not initialized
Maybe too early.  Or maybe RSRV disabled via dbServer.
2025-02-18 07:30:51 -08:00
Michael Davidsaver
7384351181 clarify cantProceed() message 2025-02-18 07:30:51 -08:00
Michael Davidsaver
890cbc2c0d doc 2025-02-18 07:30:51 -08:00
Andrew Johnson
2612b47c3f Release Notes for PYTHON=python3 2025-02-17 22:47:27 -06:00
Andrew Johnson
f4c474eb77 Remove Python build dependency when LINKER_USE_RPATH=ORIGIN
This change converts the makeRPath.py script to Perl.
Also changes the PYTHON default to run `python3`
2025-02-17 22:47:27 -06:00
Andrew Johnson
1735a821db Update pvDatabase 2025-02-11 17:44:51 -06:00
Michael Davidsaver
1d19ba4cc2 doc and log for *MustSucceed()
Replace the "never returns NULL." statement which is
manifestly not true.
2025-02-07 09:09:22 -08:00
JJL772
144f9756ea Add iocshSetError in a bunch of places 2025-02-05 10:16:16 -06:00
DW
48eed22f3b check IOCSH_STARTUP_SCRIPT before set 2025-02-05 10:03:35 -06:00
Érico Nogueira
8ac2c87156 Post monitors from compress record when resetting it
This way clients receive updates with an empty array after writing into
the RES field.
2025-02-05 09:59:18 -06:00
Simon Rose
0d2ffcd97f Typo fix on arch name (#593) 2025-02-05 09:47:24 -06:00
Andrew Johnson
c8eccfcb8f Edit Release messages for English & Markdown 2025-02-03 11:36:37 -06:00
Michael Davidsaver
6fb40b02fe update RELEASE_NOTES 2025-02-02 20:16:38 -08:00
Andrew Johnson
dad0ee9c89 Revert "Limit _FORTIFY_SOURCE <= 2"
This reverts commit 5fe563bed8.
2025-02-02 19:59:39 -08:00
Timo Korhonen
065fe7cab6 Fix internal link 2025-01-28 15:05:55 -08:00
Michael Davidsaver
e4ad4becde rationalize osdMutex
Avoids split allocation.
Eliminates special case free-list.

win32: eliminate pre-XP
rtems-score: eliminate non-fast
2024-12-29 16:35:26 -08:00
Michael Davidsaver
1cd141c540 dbLoadTemplate: error propagation and more context on .db syntax error 2024-12-29 16:35:26 -08:00
72026a27a0 allow to load the same alias multiple times (unless dbRecordsOnceOnly is set) 2024-12-29 16:35:25 -08:00
Simon Rose
9fb820b46e Add tests for relative include paths 2024-12-29 16:34:10 -08:00
Jeremy Lorelli
721e9cc3a7 Add ABORT_ON_ASSERT flag to CONFIG_SITE
This flag causes EPICS to call abort() on assertion failures rather than
suspend the executing thread. With the epicsThreadSuspendSelf() behavior,
an IOC can end up in a difficult to detect error state where one or more
threads has essentially crashed due to an assertion failure.

This also matches the C behavior of assert(3)
2024-12-29 16:34:10 -08:00
Simon Rose
0186836449 Reading a file from the command-line should not use include paths 2024-12-29 16:34:10 -08:00
Simon Rose
b90ab7de13 Improve relative path check for msi
For posix-ish systems, we previously checked that a path was relative
by simpy checking if it included a '/' character. This meant that you
could not, for example, do
```
$ cat foo.substitutions
file rel/to/bar.template {
}

$ msi -I /some/path foo.substitutions
```
where our template file is located at `/some/path/rel/to/bar.template`.

Note that relateive paths work differently on Windows, so we carve out
an exception there.
2024-12-29 16:34:10 -08:00
Jeremy Lorelli
333be085c0 Fix compile error in tsSLList.h 2024-12-28 12:40:27 -08:00
Ralph Lange
169948967f Merge pull request #578 from ralphlange/doc-udfs
Add documentation for the UDFS field
2024-12-27 15:10:43 +01:00
Ralph Lange
5a11954c51 Add documentation for the UDFS field 2024-12-26 18:44:23 +01:00
Jure Varlec
9f8a8b9c1f Update RELEASE_NOTES with AMSG propagation 2024-12-11 09:38:02 -06:00
Jure Varlec
d0cf47cd6f Propagate AMSG through MSS links
MS and MSI links do not propagate STAT and therefore do not propagate
AMSG, either. CA, CP and CPP links also do not propagate AMSG, but the
reason is technical: the message is not available over Channel Access.
2024-12-11 09:38:02 -06:00
Simon Rose
f4aee8e6b7 Add debug build configuration for os x mX arch 2024-12-11 09:33:24 -06:00
dac620a708 loop safe wrapper for dbGet added 2024-12-11 09:31:49 -06:00
4ee766b6b1 correct mask for finding field in linkGrp
It worked before because the get_xxx functions are never called for
the links (bit0 = 1), but checking both bits looks cleaner.
2024-12-11 09:31:49 -06:00
5143258011 fix support for link 0 2024-12-11 09:31:49 -06:00
Michael Davidsaver
ead8b7e82b doc getIocState() 2024-12-09 17:00:03 -08:00
71 changed files with 1007 additions and 696 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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

View 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

View File

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

View File

@@ -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 &lt; 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -207,7 +207,7 @@ static const iocshFuncDef dbPvdTableSizeFuncDef = {
};
static void dbPvdTableSizeCallFunc(const iocshArgBuf *args)
{
dbPvdTableSize(args[0].ival);
iocshSetError(dbPvdTableSize(args[0].ival));
}
/* dbReportDeviceConfig */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -384,6 +384,7 @@ static long special(DBADDR *paddr, int after)
if (special_type == SPC_RESET) {
reset(prec);
monitor(prec);
return 0;
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,4 @@
# Test re-load alias in record
record(x, "testrecAgain") {
alias("testaliasAgain1")
}

View File

@@ -0,0 +1,2 @@
# Test re-load alias for record
alias("testrecAgain", "testaliasAgain2")

View File

@@ -0,0 +1,2 @@
# Test re-load alias for alias
alias("testaliasAgain2", "testaliasAgain3")

View File

@@ -0,0 +1,2 @@
# ERROR: alias using name of exising alias for different record
alias("testrec", "testaliasAgain1")

View File

@@ -0,0 +1,2 @@
# ERROR: alias using name of exising record fails
alias("testrecAgain", "testrec")

View File

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

View File

@@ -0,0 +1,2 @@
# comment line
a=foo

View File

@@ -0,0 +1,3 @@
file database/test/ioc/dbtemplate/t13-template.txt {
{ a=foo }
}

View File

@@ -0,0 +1,2 @@
# comment line
a=$(a)

View File

@@ -0,0 +1 @@
I'm a file!

View File

@@ -0,0 +1,5 @@
This is t14-template.txt
I'm a file!
End of t14-template.txt

View File

@@ -0,0 +1,5 @@
This is t14-template.txt
include "database/test/ioc/dbtemplate/t14-include.txt"
End of t14-template.txt

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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[] = {

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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