Compare commits

...

83 Commits

Author SHA1 Message Date
d6481e190f update spec file for EPICS 7.0.9
Some checks failed
Check EditorConfig / editorconfig (push) Failing after 1s
Base / Cross linux-aarch64 (push) Failing after 2s
Base / Cross linux-arm gnueabi (push) Failing after 1s
Base / Cross linux-arm gnueabihf (push) Failing after 2s
Base / Fedora-33 (push) Failing after 1s
Base / Fedora-latest (push) Failing after 2s
Base / Win2019 clang-cl (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
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 / Win2022 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
2025-03-05 11:21:20 +01:00
2e77bc8cc1 improve atExit debugging 2025-03-05 11:21:19 +01:00
ea7459084e Merge remote-tracking branch 'github/noreturn' into PSI-7.0 2025-03-04 17:55:32 +01:00
ddc49e9f79 Merge remote-tracking branch 'github/epicsExportAddress' into PSI-7.0 2025-03-04 17:55:15 +01:00
a3a99e4d14 build mingw for testing 2025-03-04 17:53:22 +01:00
12144fe267 Merge remote-tracking branch 'github/RuntestArchs' into PSI-7.0 2025-03-04 17:49:04 +01:00
c5315d95b8 no longer need extern "C" { } around epicsExport macros 2025-03-04 17:45:04 +01:00
74d231b04f decorate functions that do not return 2025-03-04 14:41:05 +01:00
23521e0a08 Merge remote-tracking branch 'github/fdManager_using_poll' into PSI-7.0 2025-03-03 17:20:13 +01:00
55791ef470 Merge branch 7.0.9 into PSI-7.0 2025-03-03 17:19:23 +01:00
7b2fb669ec Merge remote-tracking branch 'xiaoqiang/7.0' into PSI-7.0 2025-03-03 17:14:20 +01:00
312a602952 support CROSS_COMPILER_RUNTESTS_ARCHS other than RTEMS 2025-03-03 14:11:45 +01:00
Andrew Johnson
7bd3e7aa2e Update version numbers and submodules after release 2025-02-21 17:45:00 -06:00
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
bc27476554 document fdManager change 2025-02-10 08:50:15 +01:00
9c1334ae15 silence Codacy warning 2025-02-10 08:47:59 +01:00
Michael Davidsaver
9481deacb0 fdManagerTest: quiet codacy warnings 2025-02-10 08:47:59 +01:00
8f1a3888c6 cleanup coding style in fdManager 2025-02-10 08:47:59 +01:00
c9183b5241 fdManager: filter poll flags for Window's sake 2025-02-10 08:47:59 +01:00
c3f57ee818 make fdManagerPrivate::fdReg volatile to avoid codacy warning 2025-02-10 08:47:59 +01:00
27f4261dfb use smart pointers in fdManager 2025-02-10 08:47:59 +01:00
5eb9997791 fix codacy warning: make fdManagerPrivate constructor explicit 2025-02-10 08:47:59 +01:00
bfc2f832ec fdManager uses poll() on Darwin too 2025-02-10 08:47:59 +01:00
f09b235fce Keep implementation details of fdManager out of header file 2025-02-10 08:47:59 +01:00
ece031c88b fdManager use std::vector 2025-02-10 08:47:59 +01:00
cbbbd67843 fdManager uses poll() on Windows and RTEMS too
RTEMS needs to use the "new" network stack
Windows has poll since Vista
Don't use poll on cygwin: it emulates poll() using select().
2025-02-10 08:47:59 +01:00
57c0295024 fdManager changed to use poll()
The implementation using select() limits file desciptors to FD_SETSIZE,
typically 1024 on Linux. This number is too low for some applications,
for example for the CA gateway.
Therefore, Linux builds use poll() instead.
2025-02-10 08:47:59 +01:00
Michael Davidsaver
8f77e941c7 add fdManager test 2025-02-10 08:47:59 +01:00
Michael Davidsaver
c76395abc6 remove fdmgrTest
Not a unittest, and not functional.
2025-02-10 08:47:59 +01: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
Xiaoqiang Wang
ccbe608c4a pvDataCPP with a different clang-cl workaround 2023-06-19 09:47:03 +02:00
Xiaoqiang Wang
c68d5a8dc3 direct include of CONFIG.msvcCommon 2023-05-26 08:33:10 +02:00
Xiaoqiang Wang
cfa5ad3195 rename LLVM_PREFIX/SUFFIX to MSVC_PREFIX/SUFFIX 2023-05-25 19:26:05 +02:00
Xiaoqiang Wang
d7a0edf121 direct include of CONFIG.msvcCommon 2023-05-25 19:18:36 +02:00
Xiaoqiang Wang
61fa360461 update ci-scripts module 2023-05-24 22:14:39 +02:00
Xiaoqiang Wang
01fc9f83a9 use windows-2022 to use MinGW 11
MinGW 8 on windows-2019 cannot compile pvData module
2023-05-24 21:27:53 +02:00
Xiaoqiang Wang
ccc730332a Revert "debug: only build clang windows"
This reverts commit b948c03675.
2023-05-24 16:45:08 +02:00
Xiaoqiang Wang
b948c03675 debug: only build clang windows 2023-05-24 13:19:55 +02:00
Xiaoqiang Wang
538a3d2f09 update ci-scripts module 2023-05-24 13:16:44 +02:00
Xiaoqiang Wang
3724420dc6 fail fast for testing purpose 2023-05-24 12:38:35 +02:00
Xiaoqiang Wang
6b1d30408f update ci-scripts module 2023-05-24 12:37:50 +02:00
Xiaoqiang Wang
b615232788 update ci-scripts module 2023-05-24 12:15:40 +02:00
Xiaoqiang Wang
763760c58b add clang windows build 2023-05-24 12:04:11 +02:00
Xiaoqiang Wang
f0d98b9b9a use forked .ci module with clang windows support 2023-05-24 12:03:46 +02:00
Xiaoqiang Wang
60239498a1 use forked pvData module with clang-cl fixes 2023-05-23 21:22:30 +02:00
Xiaoqiang Wang
166157dcbf allow clang windows as host arch 2023-05-23 18:23:17 +02:00
Xiaoqiang Wang
29da5d67e1 add win32-x86-clang and windows-x64-clang targets 2023-05-13 21:37:29 +02:00
Xiaoqiang Wang
8e8fb81062 explicit include SAL header to help clang-cl compiler 2023-05-13 21:34:55 +02:00
Xiaoqiang Wang
448bde1798 split out msvc common settings 2023-05-13 21:34:18 +02:00
97 changed files with 1947 additions and 786 deletions

View File

@@ -45,8 +45,9 @@ jobs:
CI_CROSS_TARGETS: ${{ matrix.cross }}
EXTRA: ${{ matrix.extra }}
TEST: ${{ matrix.test }}
CHOCO: llvm
strategy:
fail-fast: false
fail-fast: true
matrix:
# Job names also name artifacts, character limitations apply
include:
@@ -170,10 +171,16 @@ jobs:
configuration: debug
name: "Win2019 MSC-19, debug"
- os: windows-2019
- os: windows-2022
cmp: gcc
configuration: default
name: "Win2019 mingw"
name: "Win2022 mingw"
- os: windows-2019
cmp: clang+vs2019
configuration: default
name: "Win2019 clang-cl"
choco: ["llvm"]
# Cross builds

280
configure/CONFIG.msvcCommon Normal file
View File

@@ -0,0 +1,280 @@
# MSVC/clang-cl compiler defaults
CMPLR_CLASS = msvc
OPT_WHOLE_PROGRAM = YES
#-------------------------------------------------------
WINLINK = link
RCCMD = $(MSVC_PREFIX)rc$(MSVC_SUFFIX) -nologo -l 0x409 $(INCLUDES) -fo $@ $<
ARCMD = $(MSVC_PREFIX)lib$(MSVC_SUFFIX) -nologo -verbose -out:$@ $(LIB_OPT_LDFLAGS) $(LIBRARY_LD_OBJS)
#
# Configure OS vendor C compiler
CC = cl
# OS vendor c preprocessor
CPP = $(CC) -nologo -C -E
# Configure OS vendor C++ compiler
#
# -EHsc - generate code for exceptions
# -GR - generate code for run time type identification
#
CCC = $(CC) -EHsc -GR
# Override CONFIG.gnuCommon settings for cross builds.
GNU = NO
HDEPENDS_METHOD = MKMF
# Compiler flags for C files (C++ is below)
#
# -W<d> display warnings at level d
# -W4 is for maximum (lint type) warnings
# -W3 is for production quality warnings
# -W2 displays significant warnings
# -W1 is the default and shows severe warnings only
# -w<d><n> Set warning C<n> to be shown at level <d>
WARN_CFLAGS_YES = -W3
WARN_CFLAGS_NO = -W1
#
# -Ox maximum optimizations
# -GL whole program optimization
# -Oy- re-enable creation of frame pointers
OPT_CFLAGS_YES_YES = -Ox -GL -Oy-
OPT_CFLAGS_YES_NO = -Ox -Oy-
OPT_CFLAGS_YES = $(OPT_CFLAGS_YES_$(OPT_WHOLE_PROGRAM))
#
# -Z7 generate C7 compatible debugging information (inside .obj)
# -RTCsu enable run-time error checks
OPT_CFLAGS_NO = -Z7 -RTCsu
# specify object file name and location
OBJ_CFLAG = -Fo
#
# the following options are required when
# vis c++ compiles the code (and includes
# the header files)
#
# -MT static multithreaded C RTL
# -MTd static multithreaded C RTL (debug version)
# -MD multithreaded C RTL in DLL
# -MDd multithreaded C RTL in DLL (debug version)
BUILD_DLL_CFLAGS_NO =
BUILD_DLL_CFLAGS_YES = -DEPICS_BUILD_DLL
BUILD_DLL_CFLAGS = $(BUILD_DLL_CFLAGS_$(SHARED_LIBRARIES))
VISC_CFLAGS_DEBUG_NO = d
VISC_CFLAGS_DEBUG_YES =
VISC_CFLAGS_DEBUG = $(VISC_CFLAGS_DEBUG_$(HOST_OPT))
STATIC_CFLAGS_YES= -MT$(VISC_CFLAGS_DEBUG) $(BUILD_DLL_CFLAGS)
STATIC_CFLAGS_NO= -MD$(VISC_CFLAGS_DEBUG) $(BUILD_DLL_CFLAGS) -DEPICS_CALL_DLL
# Other compiler flags, used for CPP, C and C++
#
# -FC - Show absolute path of source file in diagnostics
# -D__STDC__=0 gives us both:
# 1) define STDC for code (pretend ANSI conformance)
# 2) set it to 0 to use MS C "extensions" (open for _open etc.)
# because MS uses: if __STDC__ ... disable many nice things
#
CODE_CPPFLAGS += -nologo -FC -D__STDC__=0
CODE_CPPFLAGS += -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE
# Compiler flags for C++ files
#
# -W<n> disable warnings from levels > n
# -w<n><m> set warning m to level n
# -w44355 "'this' used in the base initializer list"
# -w44344 "behavior change: use of explicit template arguments results in ..."
# -w44251 "class needs to have dll-interface to be used by clients of ..."
WARN_CXXFLAGS_YES = -W3 -w44355 -w44344 -w44251
WARN_CXXFLAGS_NO = -W1
# Silence tr1 namespace deprecation warnings
WARN_CXXFLAGS += -D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING
#
# -Ox maximum optimizations
# -GL whole program optimization
# -Oy- re-enable creation of frame pointers
OPT_CXXFLAGS_YES_YES = -Ox -GL -Oy-
OPT_CXXFLAGS_YES_NO = -Ox -Oy-
OPT_CXXFLAGS_YES = $(OPT_CXXFLAGS_YES_$(OPT_WHOLE_PROGRAM))
#
# -Z7 generate C7 compatible debugging information (inside .obj)
# -RTCsu enable run-time error checks
OPT_CXXFLAGS_NO = -RTCsu -Z7
# specify object file name and location
OBJ_CXXFLAG = -Fo
#
# the following options are required when
# vis c++ compiles the code (and includes
# the header files)
#
# -MT static multithreaded C RTL
# -MTd static multithreaded C RTL (debug version)
# -MD multithreaded C RTL in DLL
# -MDd multithreaded C RTL in DLL (debug version)
STATIC_CXXFLAGS_YES= -MT$(VISC_CFLAGS_DEBUG) $(BUILD_DLL_CFLAGS)
STATIC_CXXFLAGS_NO= -MD$(VISC_CFLAGS_DEBUG) $(BUILD_DLL_CFLAGS) -DEPICS_CALL_DLL
STATIC_LDLIBS_YES=ws2_32.lib advapi32.lib user32.lib kernel32.lib winmm.lib dbghelp.lib
STATIC_LDLIBS_NO=
STATIC_LDFLAGS=
RANLIB=
# add -profile here to run the ms profiler
# -LTCG whole program optimization
# -incremental:no full linking
# -fixed:no generate relocatable code
# -version:<major>.<minor> - only 2 components allowed, 0-65535 each
# -debug generate debugging info
LINK_OPT_FLAGS_WHOLE_YES = -LTCG
LINK_OPT_FLAGS_YES = $(LINK_OPT_FLAGS_WHOLE_$(OPT_WHOLE_PROGRAM))
LINK_OPT_FLAGS_YES += -incremental:no -opt:ref
LINK_OPT_FLAGS_YES += -release $(PROD_VERSION:%=-version:%)
LINK_OPT_FLAGS_NO = -debug -incremental:no -fixed:no
OPT_LDFLAGS = $(LINK_OPT_FLAGS_$(HOST_OPT))
LIB_OPT_FLAGS_YES = $(LINK_OPT_FLAGS_WHOLE_$(OPT_WHOLE_PROGRAM))
LIB_OPT_LDFLAGS = $(LIB_OPT_FLAGS_$(HOST_OPT))
ARCH_DEP_CFLAGS=
SHRLIB_CFLAGS=
OS_CLASS=WIN32
POSIX=NO
# ifdef WIN32 looks better that ifeq ($(OS_CLASS),WIN32) ??
WIN32=1
EXE=.exe
OBJ=.obj
RES=.res
# MS Visual C++ doesn't recognize *.cc as a C++ source file,
# so C++ compiles get the flag -TP
COMPILER_CXXFLAGS = -TP
# Operating system flags
OP_SYS_CFLAGS =
OP_SYS_CXXFLAGS = $(COMPILER_CXXFLAGS)
# Files and flags needed to link DLLs (used in RULES_BUILD)
WIN32_DLLFLAGS = -subsystem:windows -dll $(OPT_LDFLAGS) \
$(USR_LDFLAGS) $(CMD_LDFLAGS) $(TARGET_LDFLAGS) $(LIB_LDFLAGS)
# Specify dll .def file only if it exists
DLL_DEF_FLAG = $(addprefix -def:,$(wildcard ../$(addsuffix .def,$*)))
# A WIN32 dll has three parts:
# x.dll: the real dll (SHRLIBNAME)
# x.lib: what you link to progs that use the dll (DLLSTUB_LIBNAME)
# x.exp: what you need to build the dll (in no variable)
LINK.shrlib = $(WINLINK) -nologo $(WIN32_DLLFLAGS) -out:$@ \
-implib:$(@:%$(SHRLIB_SUFFIX)=%$(LIB_SUFFIX)) \
$(DLL_DEF_FLAG) $(LIBRARY_LD_OBJS) $(LIBRARY_LD_RESS) $(SHRLIB_LDLIBS)
# Adjust names of libraries to build
SHRLIB_SUFFIX_BASE = .dll
SHRLIB_SUFFIX = $(SHRLIB_SUFFIX_BASE)
SHRLIBNAME_YES = $(BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX))
LOADABLE_SHRLIBNAME = $(LOADABLE_BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX))
TESTSHRLIBNAME_YES = $(TESTBUILD_LIBRARY:%=%$(SHRLIB_SUFFIX_BASE))
# When SHARED_LIBRARIES is YES we are building a DLL shared library.
# When SHARED_LIBRARIES is NO we are building an object library
DLLSTUB_SUFFIX = .lib
DLLSTUB_LIBNAME_YES = $(BUILD_LIBRARY:%=%.lib)
DLLSTUB_LIBNAME = $(DLLSTUB_LIBNAME_$(SHARED_LIBRARIES))
TESTDLLSTUB_LIBNAME_YES = $(TESTBUILD_LIBRARY:%=%.lib)
TESTDLLSTUB_LIBNAME = $(TESTDLLSTUB_LIBNAME_$(SHARED_LIBRARIES))
LIB_PREFIX=
LIB_SUFFIX=.lib
LIBNAME_NO = $(BUILD_LIBRARY:%=%.lib)
LIBNAME = $(LIBNAME_$(SHARED_LIBRARIES))
TESTLIBNAME_NO = $(TESTBUILD_LIBRARY:%=%.lib)
TESTLIBNAME = $(TESTLIBNAME_$(SHARED_LIBRARIES))
# dll install location
INSTALL_SHRLIB = $(INSTALL_BIN)
#--------------------------------------------------
# Products dependancy definitions
PROD_DEPLIBS = $(foreach lib, $(PROD_LIBS) $(USR_LIBS), \
$(firstword $(wildcard \
$(addsuffix /$(DLLSTUB_PREFIX)$(lib)$(DLLSTUB_SUFFIX), \
$($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \
$(addsuffix /$(SHRLIB_PREFIX)$(lib)*$(SHRLIB_SUFFIX_BASE)*, \
$($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \
$(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \
$($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \
) $(addsuffix /$(BUILDLIB_PREFIX)$(lib)$(BUILDLIB_SUFFIX), \
$(if $(filter $(lib),$(TESTLIBRARY)),.,$(INSTALL_LIB)))))
PROD_LDLIBS += $($*_DEPLIBS) $(PROD_DEPLIBS)
PROD_LDLIBS += $(addsuffix .lib, \
$($*_SYS_LIBS) $(PROD_SYS_LIBS) $(USR_SYS_LIBS))
LDLIBS_STATIC_YES = LDLIBS
LDLIBS_SHARED_NO = LDLIBS
PROD_LDLIBS += $(STATIC_LDLIBS) \
$($(firstword $(LDLIBS_STATIC_$(STATIC_BUILD)) \
$(LDLIBS_SHARED_$(SHARED_LIBRARIES))))
#--------------------------------------------------
# Libraries dependancy definitions
# libs that we need to link the DLL with
# (it isnt necessary to rebuild the dll if these change)
SHRLIB_DEPLIBS = $(foreach lib, $(LIB_LIBS) $(USR_LIBS), \
$(firstword $(wildcard \
$(addsuffix /$(DLLSTUB_PREFIX)$(lib)$(DLLSTUB_SUFFIX), \
$($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \
$(addsuffix /$(SHRLIB_PREFIX)$(lib)*$(SHRLIB_SUFFIX_BASE)*, \
$($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \
$(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \
$($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \
) $(addsuffix /$(BUILDLIB_PREFIX)$(lib)$(BUILDLIB_SUFFIX), \
$(if $(filter $(lib),$(TESTLIBRARY)),.,$(INSTALL_LIB)))))
SHRLIB_LDLIBS += $($*_DLL_DEPLIBS) $($*_DEPLIBS) $(SHRLIB_DEPLIBS)
SHRLIB_LDLIBS += $(addsuffix .lib, \
$($*_SYS_DLL_LIBS) \
$($*_SYS_LIBS) $(LIB_SYS_LIBS) $(USR_SYS_LIBS) )
#--------------------------------------------------
# Linker definition
LINK.cpp = $(WINLINK) -nologo $(STATIC_LDFLAGS) $(LDFLAGS) $(PROD_LDFLAGS) \
-out:$@ $(PROD_LD_OBJS) $(PROD_LD_RESS) $(PROD_LDLIBS)
#--------------------------------------------------
# UseManifestTool.pl checks MS Visual c++ compiler version number to
# decide whether or not to use the Manifest Tool command to embed the
# linker created .manifest file into a library or product target.
# useManifestTool.pl returns 0(don't use) or 1(use).
#
MT.exe = $(MSVC_PREFIX)mt$(MSVC_SUFFIX) -nologo -manifest $@.manifest
MT_DLL_COMMAND1 = $(MT.exe) "-outputresource:$@;\#2"
MT_EXE_COMMAND_YES =
MT_EXE_COMMAND_NO = $(MT.exe) "-outputresource:$@;\#1"
MT_EXE_COMMAND1 = $(MT_EXE_COMMAND_$(STATIC_BUILD))
MT_DLL_COMMAND = $(MT_DLL_COMMAND$(shell $(PERL) $(TOOLS)/useManifestTool.pl))
MT_EXE_COMMAND = $(MT_EXE_COMMAND$(shell $(PERL) $(TOOLS)/useManifestTool.pl))

View File

@@ -48,11 +48,11 @@ 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 = 1
# Immediately after an official release the EPICS_PATCH_LEVEL is incremented
# and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions)
@@ -71,6 +71,3 @@ endif
EPICS_SHORT_VERSION=$(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION)$(EPICS_PATCH_VSTRING)
EPICS_VERSION_NUMBER=$(EPICS_SHORT_VERSION)$(EPICS_DEV_SNAPSHOT)$(EPICS_SITE_VSTRING)
EPICS_VERSION_STRING="EPICS Version $(EPICS_VERSION_NUMBER)"
# Provide this in case anyone is still using the old name
COMMIT_DATE="-no-date-"

View File

@@ -2,7 +2,7 @@
EPICS_CA_MAJOR_VERSION = 4
EPICS_CA_MINOR_VERSION = 14
EPICS_CA_MAINTENANCE_VERSION = 4
EPICS_CA_MAINTENANCE_VERSION = 6
# Development flag, set to zero for release versions

View File

@@ -1,7 +1,7 @@
# Version number for the database APIs and shared library
EPICS_DATABASE_MAJOR_VERSION = 3
EPICS_DATABASE_MINOR_VERSION = 23
EPICS_DATABASE_MINOR_VERSION = 24
EPICS_DATABASE_MAINTENANCE_VERSION = 1
# Development flag, set to zero for release versions

View File

@@ -1,7 +1,7 @@
# Version number for the libcom APIs and shared library
EPICS_LIBCOM_MAJOR_VERSION = 3
EPICS_LIBCOM_MINOR_VERSION = 23
EPICS_LIBCOM_MINOR_VERSION = 24
EPICS_LIBCOM_MAINTENANCE_VERSION = 1
# Development flag, set to zero for release versions

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,18 @@
# CONFIG.Common.win32-x86-clang
#
# Definitions for win32-x86-clang target build
# Override these definitions in CONFIG_SITE.Common.win32-x86-clang
#-------------------------------------------------------
#Include definitions common to msvc compiler
include $(CONFIG)/CONFIG.msvcCommon
VALID_BUILDS = Ioc Host Command
# Override CONFIG.msvcCommon settings:
MSVC_PREFIX = llvm-
CC = clang-cl$(MSVC_SUFFIX) --target=i686-pc-windows-msvc
WINLINK = lld-link$(MSVC_SUFFIX)
# clang-cl does not support /GL option
OPT_WHOLE_PROGRAM = NO

View File

@@ -0,0 +1,18 @@
# CONFIG.Common.windows-x64-clang
#
# Definitions for windows-x64-clang target builds
# Sites may override these definitions in CONFIG_SITE.Common.windows-x64-clang
#-------------------------------------------------------
#Include definitions common to msvc compiler
include $(CONFIG)/CONFIG.msvcCommon
VALID_BUILDS = Ioc Host Command
# Override CONFIG.msvcCommon settings:
MSVC_PREFIX = llvm-
CC = clang-cl$(MSVC_SUFFIX) --target=x86_64-pc-windows-msvc
WINLINK = lld-link$(MSVC_SUFFIX)
# clang-cl does not support /GL option
OPT_WHOLE_PROGRAM = NO

View File

@@ -8,3 +8,4 @@ export WINEPREFIX = $(HOME)/.wine-$(EPICS_HOST_ARCH)
export WINEDEBUG=fixme-all
export WINEDLLOVERRIDES="mscoree,mshtml="
export WINE = wine64
export WINEPATH = $(realpath $(INSTALL_BIN))

View File

@@ -0,0 +1 @@
include $(CONFIG)/os/CONFIG.linux-x86_64.win32-x86-mingw

View File

@@ -0,0 +1 @@
include $(CONFIG)/os/CONFIG.linux-x86_64.windows-x64-mingw

View File

@@ -1,3 +1,9 @@
#CONFIG.$(EPICS_HOST_ARCH).Common is required by build system
#Include definitions common to linux hosts
include $(CONFIG)/os/CONFIG.linux-x86_64.Common
# Windows 32 bit cross builds using mingw
# (broken on RHEL8)
CROSS_COMPILER_TARGET_ARCHS += win32-x86-mingw
CROSS_COMPILER_RUNTEST_ARHCS += win32-x86-mingw

View File

@@ -0,0 +1 @@
include $(CONFIG)/os/CONFIG.linux-x86_64.win32-x86-mingw

View File

@@ -0,0 +1 @@
include $(CONFIG)/os/CONFIG.linux-x86_64.windows-x64-mingw

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

@@ -0,0 +1,9 @@
# CONFIG.win32-x86-clang.Common
#
# Definitions for win32-x86-clang host arch
# Override these definitions in CONFIG_SITE.win32-x86-clang.Common
#-------------------------------------------------------
#Include definitions common to win32-x86 hosts
include $(CONFIG)/os/CONFIG.win32-x86.Common

View File

@@ -4,286 +4,7 @@
# Override these definitions in CONFIG_SITE.win32-x86.win32-x86
#-------------------------------------------------------
# Win32 valid build types and include directory suffixes
# Include common msvc compiler definitions
include $(CONFIG)/CONFIG.msvcCommon
VALID_BUILDS = Host Ioc Command
CMPLR_CLASS = msvc
OPT_WHOLE_PROGRAM = YES
#-------------------------------------------------------
WINLINK = link
RCCMD = rc -nologo -l 0x409 $(INCLUDES) -fo $@ $<
ARCMD = lib -nologo -verbose -out:$@ $(LIB_OPT_LDFLAGS) $(LIBRARY_LD_OBJS)
#
# Configure OS vendor C compiler
CC = cl
# Override CONFIG.gnuCommon settings for cross builds.
GNU = NO
HDEPENDS_METHOD = MKMF
# Compiler flags for C files (C++ is below)
#
# -W<d> display warnings at level d
# -W4 is for maximum (lint type) warnings
# -W3 is for production quality warnings
# -W2 displays significant warnings
# -W1 is the default and shows severe warnings only
# -w<d><n> Set warning C<n> to be shown at level <d>
WARN_CFLAGS_YES = -W3
WARN_CFLAGS_NO = -W1
#
# -Ox maximum optimizations
# -GL whole program optimization
# -Oy- re-enable creation of frame pointers
OPT_CFLAGS_YES_YES = -Ox -GL -Oy-
OPT_CFLAGS_YES_NO = -Ox -Oy-
OPT_CFLAGS_YES = $(OPT_CFLAGS_YES_$(OPT_WHOLE_PROGRAM))
#
# -Z7 generate C7 compatible debugging information (inside .obj)
# -RTCsu enable run-time error checks
OPT_CFLAGS_NO = -Z7 -RTCsu
# specify object file name and location
OBJ_CFLAG = -Fo
#
# the following options are required when
# vis c++ compiles the code (and includes
# the header files)
#
# -MT static multithreaded C RTL
# -MTd static multithreaded C RTL (debug version)
# -MD multithreaded C RTL in DLL
# -MDd multithreaded C RTL in DLL (debug version)
BUILD_DLL_CFLAGS_NO =
BUILD_DLL_CFLAGS_YES = -DEPICS_BUILD_DLL
BUILD_DLL_CFLAGS = $(BUILD_DLL_CFLAGS_$(SHARED_LIBRARIES))
VISC_CFLAGS_DEBUG_NO = d
VISC_CFLAGS_DEBUG_YES =
VISC_CFLAGS_DEBUG = $(VISC_CFLAGS_DEBUG_$(HOST_OPT))
STATIC_CFLAGS_YES= -MT$(VISC_CFLAGS_DEBUG) $(BUILD_DLL_CFLAGS)
STATIC_CFLAGS_NO= -MD$(VISC_CFLAGS_DEBUG) $(BUILD_DLL_CFLAGS) -DEPICS_CALL_DLL
# OS vendor c preprocessor
CPP = cl -nologo -C -E
# Configure OS vendor C++ compiler
#
# -EHsc - generate code for exceptions
# -GR - generate code for run time type identification
#
CCC = cl -EHsc -GR
# Other compiler flags, used for CPP, C and C++
#
# -FC - Show absolute path of source file in diagnostics
# -D__STDC__=0 gives us both:
# 1) define STDC for code (pretend ANSI conformance)
# 2) set it to 0 to use MS C "extensions" (open for _open etc.)
# because MS uses: if __STDC__ ... disable many nice things
#
CODE_CPPFLAGS += -nologo -FC -D__STDC__=0
CODE_CPPFLAGS += -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE
# Compiler flags for C++ files
#
# -W<n> disable warnings from levels > n
# -w<n><m> set warning m to level n
# -w44355 "'this' used in the base initializer list"
# -w44344 "behavior change: use of explicit template arguments results in ..."
# -w44251 "class needs to have dll-interface to be used by clients of ..."
WARN_CXXFLAGS_YES = -W3 -w44355 -w44344 -w44251
WARN_CXXFLAGS_NO = -W1
# Silence tr1 namespace deprecation warnings
WARN_CXXFLAGS += -D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING
#
# -Ox maximum optimizations
# -GL whole program optimization
# -Oy- re-enable creation of frame pointers
OPT_CXXFLAGS_YES_YES = -Ox -GL -Oy-
OPT_CXXFLAGS_YES_NO = -Ox -Oy-
OPT_CXXFLAGS_YES = $(OPT_CXXFLAGS_YES_$(OPT_WHOLE_PROGRAM))
#
# -Z7 generate C7 compatible debugging information (inside .obj)
# -RTCsu enable run-time error checks
OPT_CXXFLAGS_NO = -RTCsu -Z7
# specify object file name and location
OBJ_CXXFLAG = -Fo
#
# the following options are required when
# vis c++ compiles the code (and includes
# the header files)
#
# -MT static multithreaded C RTL
# -MTd static multithreaded C RTL (debug version)
# -MD multithreaded C RTL in DLL
# -MDd multithreaded C RTL in DLL (debug version)
STATIC_CXXFLAGS_YES= -MT$(VISC_CFLAGS_DEBUG) $(BUILD_DLL_CFLAGS)
STATIC_CXXFLAGS_NO= -MD$(VISC_CFLAGS_DEBUG) $(BUILD_DLL_CFLAGS) -DEPICS_CALL_DLL
STATIC_LDLIBS_YES=ws2_32.lib advapi32.lib user32.lib kernel32.lib winmm.lib dbghelp.lib
STATIC_LDLIBS_NO=
STATIC_LDFLAGS=
RANLIB=
# add -profile here to run the ms profiler
# -LTCG whole program optimization
# -incremental:no full linking
# -fixed:no generate relocatable code
# -version:<major>.<minor> - only 2 components allowed, 0-65535 each
# -debug generate debugging info
LINK_OPT_FLAGS_WHOLE_YES = -LTCG
LINK_OPT_FLAGS_YES = $(LINK_OPT_FLAGS_WHOLE_$(OPT_WHOLE_PROGRAM))
LINK_OPT_FLAGS_YES += -incremental:no -opt:ref
LINK_OPT_FLAGS_YES += -release $(PROD_VERSION:%=-version:%)
LINK_OPT_FLAGS_NO = -debug -incremental:no -fixed:no
OPT_LDFLAGS = $(LINK_OPT_FLAGS_$(HOST_OPT))
LIB_OPT_FLAGS_YES = $(LINK_OPT_FLAGS_WHOLE_$(OPT_WHOLE_PROGRAM))
LIB_OPT_LDFLAGS = $(LIB_OPT_FLAGS_$(HOST_OPT))
ARCH_DEP_CFLAGS=
SHRLIB_CFLAGS=
OS_CLASS=WIN32
POSIX=NO
# ifdef WIN32 looks better that ifeq ($(OS_CLASS),WIN32) ??
WIN32=1
EXE=.exe
OBJ=.obj
RES=.res
# MS Visual C++ doesn't recognize *.cc as a C++ source file,
# so C++ compiles get the flag -TP
COMPILER_CXXFLAGS = -TP
# Operating system flags
OP_SYS_CFLAGS =
OP_SYS_CXXFLAGS = $(COMPILER_CXXFLAGS)
# Files and flags needed to link DLLs (used in RULES_BUILD)
WIN32_DLLFLAGS = -subsystem:windows -dll $(OPT_LDFLAGS) \
$(USR_LDFLAGS) $(CMD_LDFLAGS) $(TARGET_LDFLAGS) $(LIB_LDFLAGS)
# Specify dll .def file only if it exists
DLL_DEF_FLAG = $(addprefix -def:,$(wildcard ../$(addsuffix .def,$*)))
# A WIN32 dll has three parts:
# x.dll: the real dll (SHRLIBNAME)
# x.lib: what you link to progs that use the dll (DLLSTUB_LIBNAME)
# x.exp: what you need to build the dll (in no variable)
LINK.shrlib = $(WINLINK) -nologo $(WIN32_DLLFLAGS) -out:$@ \
-implib:$(@:%$(SHRLIB_SUFFIX)=%$(LIB_SUFFIX)) \
$(DLL_DEF_FLAG) $(LIBRARY_LD_OBJS) $(LIBRARY_LD_RESS) $(SHRLIB_LDLIBS)
# Adjust names of libraries to build
SHRLIB_SUFFIX_BASE = .dll
SHRLIB_SUFFIX = $(SHRLIB_SUFFIX_BASE)
SHRLIBNAME_YES = $(BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX))
LOADABLE_SHRLIBNAME = $(LOADABLE_BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX))
TESTSHRLIBNAME_YES = $(TESTBUILD_LIBRARY:%=%$(SHRLIB_SUFFIX_BASE))
# When SHARED_LIBRARIES is YES we are building a DLL shared library.
# When SHARED_LIBRARIES is NO we are building an object library
DLLSTUB_SUFFIX = .lib
DLLSTUB_LIBNAME_YES = $(BUILD_LIBRARY:%=%.lib)
DLLSTUB_LIBNAME = $(DLLSTUB_LIBNAME_$(SHARED_LIBRARIES))
TESTDLLSTUB_LIBNAME_YES = $(TESTBUILD_LIBRARY:%=%.lib)
TESTDLLSTUB_LIBNAME = $(TESTDLLSTUB_LIBNAME_$(SHARED_LIBRARIES))
LIB_PREFIX=
LIB_SUFFIX=.lib
LIBNAME_NO = $(BUILD_LIBRARY:%=%.lib)
LIBNAME = $(LIBNAME_$(SHARED_LIBRARIES))
TESTLIBNAME_NO = $(TESTBUILD_LIBRARY:%=%.lib)
TESTLIBNAME = $(TESTLIBNAME_$(SHARED_LIBRARIES))
# dll install location
INSTALL_SHRLIB = $(INSTALL_BIN)
#--------------------------------------------------
# Products dependancy definitions
PROD_DEPLIBS = $(foreach lib, $(PROD_LIBS) $(USR_LIBS), \
$(firstword $(wildcard \
$(addsuffix /$(DLLSTUB_PREFIX)$(lib)$(DLLSTUB_SUFFIX), \
$($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \
$(addsuffix /$(SHRLIB_PREFIX)$(lib)*$(SHRLIB_SUFFIX_BASE)*, \
$($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \
$(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \
$($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \
) $(addsuffix /$(BUILDLIB_PREFIX)$(lib)$(BUILDLIB_SUFFIX), \
$(if $(filter $(lib),$(TESTLIBRARY)),.,$(INSTALL_LIB)))))
PROD_LDLIBS += $($*_DEPLIBS) $(PROD_DEPLIBS)
PROD_LDLIBS += $(addsuffix .lib, \
$($*_SYS_LIBS) $(PROD_SYS_LIBS) $(USR_SYS_LIBS))
LDLIBS_STATIC_YES = LDLIBS
LDLIBS_SHARED_NO = LDLIBS
PROD_LDLIBS += $(STATIC_LDLIBS) \
$($(firstword $(LDLIBS_STATIC_$(STATIC_BUILD)) \
$(LDLIBS_SHARED_$(SHARED_LIBRARIES))))
#--------------------------------------------------
# Libraries dependancy definitions
# libs that we need to link the DLL with
# (it isnt necessary to rebuild the dll if these change)
SHRLIB_DEPLIBS = $(foreach lib, $(LIB_LIBS) $(USR_LIBS), \
$(firstword $(wildcard \
$(addsuffix /$(DLLSTUB_PREFIX)$(lib)$(DLLSTUB_SUFFIX), \
$($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \
$(addsuffix /$(SHRLIB_PREFIX)$(lib)*$(SHRLIB_SUFFIX_BASE)*, \
$($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \
$(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \
$($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \
) $(addsuffix /$(BUILDLIB_PREFIX)$(lib)$(BUILDLIB_SUFFIX), \
$(if $(filter $(lib),$(TESTLIBRARY)),.,$(INSTALL_LIB)))))
SHRLIB_LDLIBS += $($*_DLL_DEPLIBS) $($*_DEPLIBS) $(SHRLIB_DEPLIBS)
SHRLIB_LDLIBS += $(addsuffix .lib, \
$($*_SYS_DLL_LIBS) \
$($*_SYS_LIBS) $(LIB_SYS_LIBS) $(USR_SYS_LIBS) )
#--------------------------------------------------
# Linker definition
LINK.cpp = $(WINLINK) -nologo $(STATIC_LDFLAGS) $(LDFLAGS) $(PROD_LDFLAGS) \
-out:$@ $(PROD_LD_OBJS) $(PROD_LD_RESS) $(PROD_LDLIBS)
#--------------------------------------------------
# UseManifestTool.pl checks MS Visual c++ compiler version number to
# decide whether or not to use the Manifest Tool command to embed the
# linker created .manifest file into a library or product target.
# useManifestTool.pl returns 0(don't use) or 1(use).
#
MT.exe = mt.exe -nologo -manifest $@.manifest
MT_DLL_COMMAND1 = $(MT.exe) "-outputresource:$@;\#2"
MT_EXE_COMMAND_YES =
MT_EXE_COMMAND_NO = $(MT.exe) "-outputresource:$@;\#1"
MT_EXE_COMMAND1 = $(MT_EXE_COMMAND_$(STATIC_BUILD))
MT_DLL_COMMAND = $(MT_DLL_COMMAND$(shell $(PERL) $(TOOLS)/useManifestTool.pl))
MT_EXE_COMMAND = $(MT_EXE_COMMAND$(shell $(PERL) $(TOOLS)/useManifestTool.pl))

View File

@@ -0,0 +1,9 @@
# CONFIG.windows-x64-clang.Common
#
# Definitions for windows-x64-clang host arch
# Override these definitions in CONFIG_SITE.windows-x64-clang.Common
#-------------------------------------------------------
#Include definitions common to windows-x64 hosts
include $(CONFIG)/os/CONFIG.windows-x64.Common

View File

@@ -4,9 +4,10 @@
# Override these definitions in CONFIG_SITE.windows-x64.windows-x64
#-------------------------------------------------------
#Include definitions common to win32-x86 builds
include $(CONFIG)/os/CONFIG.win32-x86.win32-x86
-include $(CONFIG)/os/CONFIG_SITE.win32-x86.win32-x86
# Include common msvc compiler definitions
include $(CONFIG)/CONFIG.msvcCommon
VALID_BUILDS = Host Ioc Command
OPT_LDFLAGS += -MACHINE:X64
# -MACHINE:X64

View File

@@ -0,0 +1 @@
include $(CONFIG)/os/CONFIG_SITE.linux-x86_64.win32-x86-mingw

View File

@@ -0,0 +1 @@
include $(CONFIG)/os/CONFIG_SITE.linux-x86_64.windows-x64-mingw

View File

@@ -0,0 +1 @@
include $(CONFIG)/os/CONFIG_SITE.linux-x86_64.win32-x86-mingw

View File

@@ -0,0 +1 @@
include $(CONFIG)/os/CONFIG_SITE.linux-x86_64.windows-x64-mingw

View File

@@ -13,6 +13,11 @@ GNU_HOST_ARCH_64=x86_64
# Windows cross builds using Wine
CROSS_COMPILER_TARGET_ARCHS += windows-x64
CROSS_COMPILER_RUNTEST_ARCHS += windows-x64
# Windows cross builds using mingw
CROSS_COMPILER_TARGET_ARCHS += windows-x64-mingw
CROSS_COMPILER_RUNTEST_ARCHS += windows-x64-mingw
# IOxOS IFC1211
#CROSS_COMPILER_TARGET_ARCHS += fslqoriq20-e6500_64

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,154 @@ 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)
__This version of EPICS has not been released yet.__
**This version of EPICS has not been released yet.**
## Changes made on the 7.0 branch since 7.0.9
## Changes made on the 7.0 branch since 7.0.8.1
__Add new items below here__
### DBE_PROPERTY event rate changed
=======
### epicsExport simplifications
Updating property fields now only post DBE_PROPERTY events if the
`epicsExportAddress()`, `epicsExportRegistrar()` and `epicsRegisterFunction()`
no loger require to be wrapped in `extern "C" { }` in C++ code.
### Build system `$(PYTHON)` default changed
## EPICS Release 7.0.9
### Core documentation published at ReadTheDocs
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.
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.
### fdManager file descriptor limit removed
In order to support file descriptors above 1023, fdManager now uses
poll() instead of select() on all architectures that support it
(Linux, MacOS, Windows, newer RTEMS).
### Post monitors from compress record when it's reset
Writing into a compress record's `RES` field now posts a monitor event instead
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 +393,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 +515,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 +687,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 +791,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 +810,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 +828,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 +842,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 +1040,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 +1110,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 +1243,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 +1360,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 +1454,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 +1464,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 +1698,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 +2277,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

@@ -1,11 +1,11 @@
# Always make sure EpicsVersion.Version-Release matches the git tag!
%define EpicsVersion 7.0.8
%define EpicsVersion 7.0.9
Name: epics-base-%{EpicsVersion}
Summary: EPICS Base %{EpicsVersion}
Version: 2
Release: 4%{?dist}
Version: 1
Release: 0%{?dist}
License: EPICS Open License
Group: Development/Languages
URL: https://git.psi.ch/epics_base/base-7.0

View File

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

View File

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

View File

@@ -286,7 +286,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,9 +226,10 @@ 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;
}
@@ -378,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

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

@@ -14,6 +14,7 @@ SRC_DIRS += $(IOCDIR)/dbtemplate
PROD_CMD += 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

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

View File

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

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

View File

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

View File

@@ -227,6 +227,29 @@ TARGET_SRCS += dbHeaderTest.cpp
TARGETS += dbHeaderTestxx$(OBJ)
TARGET_SRCS += dbHeaderTestxx.cpp
TARGETS += $(COMMON_DIR)/epicsExportTestIoc.dbd
DBDDEPENDS_FILES += epicsExportTestIoc.dbd$(DEP)
epicsExportTestIoc_DBD += base.dbd
epicsExportTestIoc_DBD += epicsExportTest.dbd
TESTLIBRARY += epicsExportTestLib
epicsExportTestLib_SRCS += epicsExportTest.c
epicsExportTestLib_LIBS += dbCore Com
TESTPROD_HOST += epicsExportTest
TESTS += epicsExportTest
TARGETS += epicsExportTest$(OBJ)
epicsExportTest_SRCS += epicsExportTestMain.c
epicsExportTest_SRCS += epicsExportTestIoc_registerRecordDeviceDriver.cpp
epicsExportTest_LIBS = epicsExportTestLib
TESTLIBRARY += epicsExportTestxxLib
epicsExportTestxxLib_SRCS += epicsExportTestxx.cpp
epicsExportTestxxLib_LIBS += dbCore Com
TESTPROD_HOST += epicsExportTestxx
TESTS += epicsExportTestxx
TARGETS += epicsExportTestxx$(OBJ)
epicsExportTestxx_SRCS += epicsExportTestMain.c
epicsExportTestxx_SRCS += epicsExportTestIoc_registerRecordDeviceDriver.cpp
epicsExportTestxx_LIBS = epicsExportTestxxLib
ifeq ($(T_A),$(EPICS_HOST_ARCH))
# Host-only tests of softIoc/softIocPVA, caget and pvget (if present)
# Unfortunately hangs too often on CI systems:
@@ -251,8 +274,8 @@ TESTSPEC_RTEMS = recordTestHarness.boot; epicsRunRecordTests
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
TESTPROD_RTEMS = $(TESTPROD_HOST)
TESTSCRIPTS_RTEMS += $(TESTS:%=%.t)
TESTPROD += $(TESTPROD_HOST)
TESTSCRIPTS += $(TESTS:%=%.t)
endif
include $(TOP)/configure/RULES

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,74 @@
/*************************************************************************\
* Copyright (c) 2025 Dirk Zimoch
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* Compile and link test for epicsExport.h
*
*/
#include <epicsUnitTest.h>
#include <dbUnitTest.h>
#include <testMain.h>
#include <dbAccess.h>
#include <iocsh.h>
#include <epicsExport.h>
void epicsExportTestIoc_registerRecordDeviceDriver(struct dbBase *);
MAIN(epicsExportTest)
{
const iocshVarDef *var_i1, *var_i2;
testPlan(26);
testdbPrepare();
testdbReadDatabase("epicsExportTestIoc.dbd", 0, 0);
epicsExportTestIoc_registerRecordDeviceDriver(pdbbase);
testDiag("Testing if dsets and functions are found");
testdbReadDatabase("epicsExportTest.db", 0, 0);
testIocInitOk();
testDiag("Testing if dsets work correctly");
testdbGetFieldEqual("li1", DBF_LONG, -1);
testdbGetFieldEqual("li2", DBF_LONG, -2);
testdbPutFieldOk("li1.PROC", DBF_LONG, 1);
testdbPutFieldOk("li2.PROC", DBF_LONG, 1);
testdbGetFieldEqual("li1", DBF_LONG, 5);
testdbGetFieldEqual("li2", DBF_LONG, 5);
testDiag("Testing if functions work correctly");
testdbGetFieldEqual("asub1.A", DBF_LONG, 1);
testdbGetFieldEqual("asub1.B", DBF_LONG, -2);
testdbGetFieldEqual("asub2.A", DBF_LONG, 3);
testdbGetFieldEqual("asub2.B", DBF_LONG, -4);
testdbPutFieldOk("asub1.PROC", DBF_LONG, 1);
testdbPutFieldOk("asub2.PROC", DBF_LONG, 1);
testdbGetFieldEqual("asub1.A", DBF_LONG, 1);
testdbGetFieldEqual("asub1.B", DBF_LONG, 2);
testdbGetFieldEqual("asub2.A", DBF_LONG, 3);
testdbGetFieldEqual("asub2.B", DBF_LONG, 4);
testDiag("Testing if variable access works");
var_i1 = iocshFindVariable("i1");
var_i2 = iocshFindVariable("i2");
if (var_i1 && var_i2 && var_i1->type == iocshArgInt && var_i2->type == iocshArgInt) {
int *pi1 = (int*)(var_i1->pval);
int *pi2 = (int*)(var_i2->pval);
testOk(*pi1==2, "Variable i1 counted registrars: %d == 2", *pi1);
testOk(*pi2==2, "Variable i2 counted registrars: %d == 2", *pi2);
testDiag("Testing if variables are accessible from iocsh");
testOk(iocshCmd("var i1,4") == 0, "Setting i1 = 4 in iocsh");
testOk(iocshCmd("var i2,5") == 0, "Setting i2 = 5 in iocsh");
testOk(*pi1==4, "Variable i1: %d == 4", *pi1);
testOk(*pi2==5, "Variable i2: %d == 5", *pi2);
} else {
testFail("Cannot access variables i1, i2");
}
testIocShutdownOk();
testdbCleanup();
return testDone();
}

View File

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

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

@@ -77,6 +77,7 @@ 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_MUTEX_USE_PRIORITY_INHERITANCE;
LIBCOM_API extern const ENV_PARAM EPICS_ABORT_ON_ASSERT;
LIBCOM_API extern const ENV_PARAM *env_param_list[];
struct in_addr;

View File

@@ -19,39 +19,117 @@
// 1) This library is not thread safe
//
#include <algorithm>
#define instantiateRecourceLib
#include "epicsAssert.h"
#include "epicsThread.h"
#include "fdManager.h"
#include "locationException.h"
using std :: max;
#if !defined(FDMGR_USE_POLL) && !defined(FDMGR_USE_SELECT)
#if defined(__linux__) || defined(darwin) || _WIN32_WINNT >= 0x600 || (defined(__rtems__) && !defined(RTEMS_LEGACY_STACK))
#define FDMGR_USE_POLL
#else
#define FDMGR_USE_SELECT
#endif
#endif
#ifdef FDMGR_USE_POLL
#include <vector>
#if !defined(_WIN32)
#include <poll.h>
#endif
static const short PollEvents[] = { // must match fdRegType
POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR,
POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR,
POLLPRI };
#if defined(_WIN32)
#define poll WSAPoll
// Filter out PollEvents that Windows does not accept in events (only returns in revents)
#define WIN_POLLEVENT_FILTER(ev) static_cast<short>((ev) & (POLLIN | POLLOUT))
#else
// Linux, MacOS and RTEMS don't care
#define WIN_POLLEVENT_FILTER(ev) (ev)
#endif
#endif
#ifdef FDMGR_USE_SELECT
#include <algorithm>
#endif
struct fdManagerPrivate {
tsDLList<fdReg> regList;
tsDLList<fdReg> activeList;
resTable<fdReg, fdRegId> fdTbl;
const double sleepQuantum;
epics::auto_ptr<epicsTimerQueuePassive> pTimerQueue;
bool processInProg;
#ifdef FDMGR_USE_POLL
std::vector<struct pollfd> pollfds;
#endif
#ifdef FDMGR_USE_SELECT
fd_set fdSets[fdrNEnums];
SOCKET maxFD;
#endif
//
// Set to fdreg when in call back
// and nill otherwise
//
volatile fdReg* pCBReg;
fdManager& owner;
explicit fdManagerPrivate(fdManager& owner);
void lazyInitTimerQueue();
};
fdManagerPrivate::fdManagerPrivate(fdManager& owner) :
sleepQuantum(epicsThreadSleepQuantum()),
processInProg(false),
pCBReg(NULL), owner(owner)
{}
inline void fdManagerPrivate::lazyInitTimerQueue()
{
if (!pTimerQueue.get()) {
pTimerQueue.reset(&epicsTimerQueuePassive::create(owner));
}
}
epicsTimer& fdManager::createTimer()
{
priv->lazyInitTimerQueue();
return priv->pTimerQueue->createTimer();
}
fdManager fileDescriptorManager;
const unsigned mSecPerSec = 1000u;
const unsigned uSecPerSec = 1000u * mSecPerSec;
static const unsigned mSecPerSec = 1000u;
#ifdef FDMGR_USE_SELECT
static const unsigned uSecPerSec = 1000u * mSecPerSec;
#endif
//
// fdManager::fdManager()
//
// hopefully its a reasonable guess that select() and epicsThreadSleep()
// hopefully its a reasonable guess that poll()/select() and epicsThreadSleep()
// will have the same sleep quantum
//
LIBCOM_API fdManager::fdManager () :
sleepQuantum ( epicsThreadSleepQuantum () ),
fdSetsPtr ( new fd_set [fdrNEnums] ),
pTimerQueue ( 0 ), maxFD ( 0 ), processInProg ( false ),
pCBReg ( 0 )
LIBCOM_API fdManager::fdManager() :
priv(new fdManagerPrivate(*this))
{
int status = osiSockAttach ();
assert (status);
int status = osiSockAttach();
assert(status);
for ( size_t i = 0u; i < fdrNEnums; i++ ) {
FD_ZERO ( &fdSetsPtr[i] );
#ifdef FDMGR_USE_SELECT
priv->maxFD = 0;
for (size_t i = 0u; i < fdrNEnums; i++) {
FD_ZERO(&priv->fdSets[i]);
}
#endif
}
//
@@ -59,82 +137,138 @@ LIBCOM_API fdManager::fdManager () :
//
LIBCOM_API fdManager::~fdManager()
{
fdReg *pReg;
fdReg* pReg;
while ( (pReg = this->regList.get()) ) {
while ((pReg = priv->regList.get())) {
pReg->state = fdReg::limbo;
pReg->destroy();
}
while ( (pReg = this->activeList.get()) ) {
while ((pReg = priv->activeList.get())) {
pReg->state = fdReg::limbo;
pReg->destroy();
}
delete this->pTimerQueue;
delete [] this->fdSetsPtr;
osiSockRelease();
}
//
// fdManager::process()
//
LIBCOM_API void fdManager::process (double delay)
LIBCOM_API void fdManager::process(double delay)
{
this->lazyInitTimerQueue ();
priv->lazyInitTimerQueue();
//
// no recursion
//
if (this->processInProg) {
if (priv->processInProg)
return;
}
this->processInProg = true;
priv->processInProg = true;
//
// One shot at expired timers prior to going into
// select. This allows zero delay timers to arm
// poll/select. This allows zero delay timers to arm
// fd writes. We will never process the timer queue
// more than once here so that fd activity get serviced
// in a reasonable length of time.
//
double minDelay = this->pTimerQueue->process(epicsTime::getCurrent());
double minDelay = priv->pTimerQueue->process(epicsTime::getCurrent());
if ( minDelay >= delay ) {
if (minDelay >= delay) {
minDelay = delay;
}
bool ioPending = false;
tsDLIter < fdReg > iter = this->regList.firstIter ();
while ( iter.valid () ) {
FD_SET(iter->getFD(), &this->fdSetsPtr[iter->getType()]);
ioPending = true;
#ifdef FDMGR_USE_POLL
priv->pollfds.clear();
#endif
int ioPending = 0;
tsDLIter<fdReg> iter = priv->regList.firstIter();
while (iter.valid()) {
++ioPending;
#ifdef FDMGR_USE_POLL
#if __cplusplus >= 201100L
priv->pollfds.emplace_back(pollfd{
.fd = iter->getFD(),
.events = WIN_POLLEVENT_FILTER(PollEvents[iter->getType()])
});
#else
struct pollfd pollfd;
pollfd.fd = iter->getFD();
pollfd.events = WIN_POLLEVENT_FILTER(PollEvents[iter->getType()]);
pollfd.revents = 0;
priv->pollfds.push_back(pollfd);
#endif
#endif
#ifdef FDMGR_USE_SELECT
FD_SET(iter->getFD(), &priv->fdSets[iter->getType()]);
#endif
++iter;
}
if ( ioPending ) {
if (ioPending) {
#ifdef FDMGR_USE_POLL
if (minDelay * mSecPerSec > INT_MAX)
minDelay = INT_MAX / mSecPerSec;
int status = poll(&priv->pollfds[0], // ancient C++ has no vector.data()
ioPending, static_cast<int>(minDelay * mSecPerSec));
int i = 0;
#endif
#ifdef FDMGR_USE_SELECT
struct timeval tv;
tv.tv_sec = static_cast<time_t> ( minDelay );
tv.tv_usec = static_cast<long> ( (minDelay-tv.tv_sec) * uSecPerSec );
tv.tv_sec = static_cast<time_t>(minDelay);
tv.tv_usec = static_cast<long>((minDelay-tv.tv_sec) * uSecPerSec);
fd_set * pReadSet = & this->fdSetsPtr[fdrRead];
fd_set * pWriteSet = & this->fdSetsPtr[fdrWrite];
fd_set * pExceptSet = & this->fdSetsPtr[fdrException];
int status = select (this->maxFD, pReadSet, pWriteSet, pExceptSet, &tv);
int status = select(priv->maxFD,
&priv->fdSets[fdrRead],
&priv->fdSets[fdrWrite],
&priv->fdSets[fdrException], &tv);
#endif
this->pTimerQueue->process(epicsTime::getCurrent());
priv->pTimerQueue->process(epicsTime::getCurrent());
if ( status > 0 ) {
if (status > 0) {
//
// Look for activity
//
iter=this->regList.firstIter ();
while ( iter.valid () && status > 0 ) {
tsDLIter < fdReg > tmp = iter;
iter = priv->regList.firstIter();
while (iter.valid() && status > 0) {
tsDLIter<fdReg> tmp = iter;
tmp++;
if (FD_ISSET(iter->getFD(), &this->fdSetsPtr[iter->getType()])) {
FD_CLR(iter->getFD(), &this->fdSetsPtr[iter->getType()]);
this->regList.remove(*iter);
this->activeList.add(*iter);
#ifdef FDMGR_USE_POLL
// In a single threaded application, nothing should have
// changed the order of regList and pollfds by now.
// But just in case...
int isave = i;
while (priv->pollfds[i].fd != iter->getFD() ||
priv->pollfds[i].events != WIN_POLLEVENT_FILTER(PollEvents[iter->getType()]))
{
errlogPrintf("fdManager: skipping (removed?) pollfd %d (expected %d)\n", priv->pollfds[i].fd, iter->getFD());
i++; // skip pollfd of removed items
if (i >= ioPending) { // skip unknown (inserted?) items
errlogPrintf("fdManager: skipping (inserted?) item %d\n", iter->getFD());
iter = tmp;
tmp++;
if (!iter.valid()) break;
i = isave;
}
}
if (i >= ioPending) break; // any unhandled item stays in regList for next time
if (priv->pollfds[i++].revents & PollEvents[iter->getType()]) {
#endif
#ifdef FDMGR_USE_SELECT
if (FD_ISSET(iter->getFD(), &priv->fdSets[iter->getType()])) {
FD_CLR(iter->getFD(), &priv->fdSets[iter->getType()]);
#endif
priv->regList.remove(*iter);
priv->activeList.add(*iter);
iter->state = fdReg::active;
status--;
}
@@ -145,8 +279,8 @@ LIBCOM_API void fdManager::process (double delay)
// I am careful to prevent problems if they access the
// above list while in a "callBack()" routine
//
fdReg * pReg;
while ( (pReg = this->activeList.get()) ) {
fdReg* pReg;
while ((pReg = priv->activeList.get())) {
pReg->state = fdReg::limbo;
//
@@ -154,45 +288,53 @@ LIBCOM_API void fdManager::process (double delay)
// can detect if it was deleted
// during the call back
//
this->pCBReg = pReg;
priv->pCBReg = pReg;
pReg->callBack();
if (this->pCBReg != NULL) {
if (priv->pCBReg != NULL) {
//
// check only after we see that it is non-null so
// that we don't trigger bounds-checker dangling pointer
// error
//
assert (this->pCBReg==pReg);
this->pCBReg = 0;
assert(priv->pCBReg == pReg);
priv->pCBReg = NULL;
if (pReg->onceOnly) {
pReg->destroy();
}
else {
this->regList.add(*pReg);
priv->regList.add(*pReg);
pReg->state = fdReg::pending;
}
}
}
}
else if ( status < 0 ) {
else if (status < 0) {
int errnoCpy = SOCKERRNO;
#ifdef FDMGR_USE_SELECT
// don't depend on flags being properly set if
// an error is returned from select
for ( size_t i = 0u; i < fdrNEnums; i++ ) {
FD_ZERO ( &fdSetsPtr[i] );
for (size_t i = 0u; i < fdrNEnums; i++) {
FD_ZERO(&priv->fdSets[i]);
}
#endif
//
// print a message if its an unexpected error
// print a message if it's an unexpected error
//
if ( errnoCpy != SOCK_EINTR ) {
if (errnoCpy != SOCK_EINTR) {
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
fprintf ( stderr,
"fdManager: select failed because \"%s\"\n",
sockErrBuf );
epicsSocketConvertErrnoToString(
sockErrBuf, sizeof(sockErrBuf));
errlogPrintf("fdManager: "
#ifdef FDMGR_USE_POLL
"poll()"
#endif
#ifdef FDMGR_USE_SELECT
"select()"
#endif
" failed because \"%s\"\n",
sockErrBuf);
}
}
}
@@ -203,9 +345,9 @@ LIBCOM_API void fdManager::process (double delay)
* of select()
*/
epicsThreadSleep(minDelay);
this->pTimerQueue->process(epicsTime::getCurrent());
priv->pTimerQueue->process(epicsTime::getCurrent());
}
this->processInProg = false;
priv->processInProg = false;
}
//
@@ -222,7 +364,7 @@ void fdReg::destroy()
//
fdReg::~fdReg()
{
this->manager.removeReg(*this);
manager.removeReg(*this);
}
//
@@ -230,57 +372,62 @@ fdReg::~fdReg()
//
void fdReg::show(unsigned level) const
{
printf ("fdReg at %p\n", (void *) this);
if (level>1u) {
printf ("\tstate = %d, onceOnly = %d\n",
this->state, this->onceOnly);
printf("fdReg at %p\n", this);
if (level > 1u) {
printf("\tstate = %d, onceOnly = %d\n",
state, onceOnly);
}
this->fdRegId::show(level);
fdRegId::show(level);
}
//
// fdRegId::show()
//
void fdRegId::show ( unsigned level ) const
void fdRegId::show(unsigned level) const
{
printf ( "fdRegId at %p\n",
static_cast <const void *> ( this ) );
if ( level > 1u ) {
printf ( "\tfd = %d, type = %d\n",
int(this->fd), this->type );
printf("fdRegId at %p\n", this);
if (level > 1u) {
printf("\tfd = %"
#if defined(_WIN32)
"I"
#endif
"d, type = %d\n",
fd, type);
}
}
//
// fdManager::installReg ()
// fdManager::installReg()
//
void fdManager::installReg (fdReg &reg)
void fdManager::installReg(fdReg &reg)
{
this->maxFD = max ( this->maxFD, reg.getFD()+1 );
// Most applications will find that its important to push here to
#ifdef FDMGR_USE_SELECT
priv->maxFD = std::max(priv->maxFD, reg.getFD()+1);
#endif
// Most applications will find that it's important to push here to
// the front of the list so that transient writes get executed
// first allowing incoming read protocol to find that outgoing
// buffer space is newly available.
this->regList.push ( reg );
priv->regList.push(reg);
reg.state = fdReg::pending;
int status = this->fdTbl.add ( reg );
if ( status != 0 ) {
throwWithLocation ( fdInterestSubscriptionAlreadyExits () );
int status = priv->fdTbl.add(reg);
if (status != 0) {
throwWithLocation(fdInterestSubscriptionAlreadyExits());
}
// errlogPrintf("fdManager::adding fd %d\n", reg.getFD());
}
//
// fdManager::removeReg ()
// fdManager::removeReg()
//
void fdManager::removeReg (fdReg &regIn)
void fdManager::removeReg(fdReg &regIn)
{
fdReg *pItemFound;
fdReg* pItemFound;
pItemFound = this->fdTbl.remove (regIn);
if (pItemFound!=&regIn) {
fprintf(stderr,
"fdManager::removeReg() bad fd registration object\n");
pItemFound = priv->fdTbl.remove(regIn);
if (pItemFound != &regIn) {
errlogPrintf("fdManager::removeReg() bad fd registration object\n");
return;
}
@@ -288,16 +435,16 @@ void fdManager::removeReg (fdReg &regIn)
// signal fdManager that the fdReg was deleted
// during the call back
//
if (this->pCBReg == &regIn) {
this->pCBReg = 0;
if (priv->pCBReg == &regIn) {
priv->pCBReg = NULL;
}
switch (regIn.state) {
case fdReg::active:
this->activeList.remove (regIn);
priv->activeList.remove(regIn);
break;
case fdReg::pending:
this->regList.remove (regIn);
priv->regList.remove(regIn);
break;
case fdReg::limbo:
break;
@@ -309,49 +456,55 @@ void fdManager::removeReg (fdReg &regIn)
}
regIn.state = fdReg::limbo;
FD_CLR(regIn.getFD(), &this->fdSetsPtr[regIn.getType()]);
#ifdef FDMGR_USE_SELECT
FD_CLR(regIn.getFD(), &priv->fdSets[regIn.getType()]);
#endif
// errlogPrintf("fdManager::removing fd %d\n", regIn.getFD());
}
//
// fdManager::reschedule ()
// fdManager::reschedule()
// NOOP - this only runs single threaded, and therefore they can only
// add a new timer from places that will always end up in a reschedule
//
void fdManager::reschedule ()
void fdManager::reschedule()
{
}
double fdManager::quantum ()
double fdManager::quantum()
{
return this->sleepQuantum;
return priv->sleepQuantum;
}
//
// lookUpFD()
//
LIBCOM_API fdReg *fdManager::lookUpFD (const SOCKET fd, const fdRegType type)
LIBCOM_API fdReg* fdManager::lookUpFD(const SOCKET fd, const fdRegType type)
{
if (fd<0) {
if (fd < 0) {
return NULL;
}
fdRegId id (fd,type);
return this->fdTbl.lookup(id);
fdRegId id(fd,type);
return priv->fdTbl.lookup(id);
}
//
// fdReg::fdReg()
//
fdReg::fdReg (const SOCKET fdIn, const fdRegType typIn,
fdReg::fdReg(const SOCKET fdIn, const fdRegType typIn,
const bool onceOnlyIn, fdManager &managerIn) :
fdRegId (fdIn,typIn), state (limbo),
onceOnly (onceOnlyIn), manager (managerIn)
fdRegId(fdIn,typIn), state(limbo),
onceOnly(onceOnlyIn), manager(managerIn)
{
#ifdef FDMGR_USE_SELECT
if (!FD_IN_FDSET(fdIn)) {
fprintf (stderr, "%s: fd > FD_SETSIZE ignored\n",
errlogPrintf("%s: fd > FD_SETSIZE ignored\n",
__FILE__);
return;
}
this->manager.installReg (*this);
#endif
manager.installReg(*this);
}
template class resTable<fdReg, fdRegId>;

View File

@@ -19,6 +19,16 @@
#ifndef fdManagerH_included
#define fdManagerH_included
#include <memory>
namespace epics {
#if __cplusplus>=201103L
template<typename T>
using auto_ptr = std::unique_ptr<T>;
#else
using std::auto_ptr;
#endif
}
#include "libComAPI.h" // reset share lib defines
#include "tsDLList.h"
#include "resourceLib.h"
@@ -37,27 +47,27 @@ class LIBCOM_API fdRegId
{
public:
fdRegId (const SOCKET fdIn, const fdRegType typeIn) :
fdRegId(const SOCKET fdIn, const fdRegType typeIn) :
fd(fdIn), type(typeIn) {}
SOCKET getFD () const
SOCKET getFD() const
{
return this->fd;
return fd;
}
fdRegType getType () const
fdRegType getType() const
{
return this->type;
return type;
}
bool operator == (const fdRegId &idIn) const
bool operator == (const fdRegId& idIn) const
{
return this->fd == idIn.fd && this->type==idIn.type;
return fd == idIn.fd && type == idIn.type;
}
resTableIndex hash () const;
resTableIndex hash() const;
virtual void show (unsigned level) const;
virtual void show(unsigned level) const;
virtual ~fdRegId() {}
private:
@@ -70,43 +80,31 @@ private:
//
// file descriptor manager
//
class fdManager : public epicsTimerQueueNotify {
class LIBCOM_API fdManager : public epicsTimerQueueNotify {
public:
//
// exceptions
//
class fdInterestSubscriptionAlreadyExits {};
LIBCOM_API fdManager ();
LIBCOM_API virtual ~fdManager ();
LIBCOM_API void process ( double delay ); // delay parameter is in seconds
fdManager();
virtual ~fdManager();
void process(double delay); // delay parameter is in seconds
// returns NULL if the fd is unknown
LIBCOM_API class fdReg *lookUpFD (const SOCKET fd, const fdRegType type);
class fdReg* lookUpFD(const SOCKET fd, const fdRegType type);
epicsTimer & createTimer ();
epicsTimer& createTimer();
private:
tsDLList < fdReg > regList;
tsDLList < fdReg > activeList;
resTable < fdReg, fdRegId > fdTbl;
const double sleepQuantum;
fd_set * fdSetsPtr;
epicsTimerQueuePassive * pTimerQueue;
SOCKET maxFD;
bool processInProg;
//
// Set to fdreg when in call back
// and nill otherwise
//
fdReg * pCBReg;
void reschedule ();
double quantum ();
void installReg (fdReg &reg);
void removeReg (fdReg &reg);
void lazyInitTimerQueue ();
fdManager ( const fdManager & );
fdManager & operator = ( const fdManager & );
epics::auto_ptr <struct fdManagerPrivate> priv;
void reschedule();
double quantum();
void installReg(fdReg& reg);
void removeReg(fdReg& reg);
fdManager(const fdManager&);
fdManager& operator = (const fdManager&);
friend class fdReg;
};
@@ -126,11 +124,11 @@ class LIBCOM_API fdReg :
public:
fdReg (const SOCKET fdIn, const fdRegType type,
const bool onceOnly=false, fdManager &manager = fileDescriptorManager);
virtual ~fdReg ();
fdReg(const SOCKET fdIn, const fdRegType type,
const bool onceOnly=false, fdManager& manager = fileDescriptorManager);
virtual ~fdReg();
virtual void show (unsigned level) const;
virtual void show(unsigned level) const;
//
// Called by the file descriptor manager:
@@ -141,7 +139,7 @@ public:
//
// fdReg::destroy() does a "delete this"
//
virtual void destroy ();
virtual void destroy();
private:
enum state {active, pending, limbo};
@@ -153,32 +151,32 @@ private:
// lifetime of a fdReg object if the constructor
// specified "onceOnly"
//
virtual void callBack ()=0;
virtual void callBack() = 0;
unsigned char state; // state enums go here
unsigned char onceOnly;
fdManager &manager;
fdManager& manager;
fdReg ( const fdReg & );
fdReg & operator = ( const fdReg & );
fdReg(const fdReg&);
fdReg& operator = (const fdReg&);
};
//
// fdRegId::hash()
//
inline resTableIndex fdRegId::hash () const
inline resTableIndex fdRegId::hash() const
{
const unsigned fdManagerHashTableMinIndexBits = 8;
const unsigned fdManagerHashTableMaxIndexBits = sizeof(SOCKET)*CHAR_BIT;
const unsigned fdManagerHashTableMaxIndexBits = sizeof(SOCKET) * CHAR_BIT;
resTableIndex hashid;
hashid = integerHash ( fdManagerHashTableMinIndexBits,
fdManagerHashTableMaxIndexBits, this->fd );
hashid = integerHash(fdManagerHashTableMinIndexBits,
fdManagerHashTableMaxIndexBits, fd);
//
// also evenly distribute based on the type of fdRegType
//
hashid ^= this->type;
hashid ^= type;
//
// the result here is always masked to the
@@ -187,18 +185,5 @@ inline resTableIndex fdRegId::hash () const
return hashid;
}
inline void fdManager::lazyInitTimerQueue ()
{
if ( ! this->pTimerQueue ) {
this->pTimerQueue = & epicsTimerQueuePassive::create ( *this );
}
}
inline epicsTimer & fdManager::createTimer ()
{
this->lazyInitTimerQueue ();
return this->pTimerQueue->createTimer ();
}
#endif // fdManagerH_included

View File

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

View File

@@ -1329,7 +1329,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

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

@@ -96,6 +96,8 @@ static void epicsExitCallAtExitsPvt(exitPvt *pep)
ellDelete ( & pep->list, & pexitNode->node );
free ( pexitNode );
}
if (atExitDebug)
fprintf(stderr, "atExit done.\n");
}
LIBCOM_API void epicsExitCallAtExits(void)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -48,7 +48,13 @@
* Enable format-string checking if compiler supports it (if msvc is 2015 or newer)
*/
#if _MSC_VER >= 1900
# include <sal.h>
# define EPICS_PRINTF_FMT(a) _Printf_format_string_ a
#endif
/*
* No return marker
*/
#define EPICS_NORETURN __declspec(noreturn)
#endif /* ifndef compilerSpecific_h */

View File

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

View File

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

View File

@@ -48,7 +48,6 @@ epicsEventCreate(epicsEventInitialState initialState)
{
rtems_status_code sc;
rtems_id sid;
rtems_interrupt_level level;
static uint32_t name;
sc = rtems_semaphore_create (next_rtems_name ('B', &name),

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

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

View File

@@ -209,6 +209,11 @@ aslibtest_SRCS += aslibtest.c
testHarness_SRCS += aslibtest.c
TESTS += aslibtest
TESTPROD_HOST += fdManagerTest
fdManagerTest_SRCS += fdManagerTest.cpp
testHarness_SRCS += fdManagerTest.cpp
TESTS += fdManagerTest
# Perl module tests:
TESTS += macLib
@@ -295,7 +300,7 @@ TESTSPEC_RTEMS = libComTestHarness.boot; epicsRunLibComTests
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
TESTPROD = $(TESTPROD_HOST)
TESTPROD += $(TESTPROD_HOST)
TESTSCRIPTS += $(filter-out epicsUnitTestTest.t, $(TESTS:%=%.t))
endif
@@ -315,11 +320,6 @@ TESTPROD_HOST += buckTest
buckTest_SRCS += buckTest.c
testHarness_SRCS += buckTest.c
#TESTPROD_HOST += fdmgrTest
fdmgrTest_SRCS += fdmgrTest.c
fdmgrTest_LIBS += ca
# FIXME: program never exits.
TESTPROD_HOST += epicsAtomicPerform
epicsAtomicPerform_SRCS += epicsAtomicPerform.cpp
testHarness_SRCS += epicsAtomicPerform.cpp

View File

@@ -54,6 +54,7 @@ int initHookTest(void);
int ipAddrToAsciiTest(void);
int macDefExpandTest(void);
int macLibTest(void);
int fdManagerTest(void);
int osiSockTest(void);
int ringBytesTest(void);
int ringPointerTest(void);
@@ -110,6 +111,7 @@ void epicsRunLibComTests(void)
runTest(ipAddrToAsciiTest);
runTest(macDefExpandTest);
runTest(macLibTest);
runTest(fdManagerTest);
runTest(osiSockTest);
runTest(ringBytesTest);
runTest(ringPointerTest);

View File

@@ -0,0 +1,316 @@
/*************************************************************************\
* Copyright (c) 2025 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.
\*************************************************************************/
#include <algorithm>
#include <string.h>
#include <osiSock.h>
#include <fdManager.h>
#include <epicsTime.h>
#include <epicsAtomic.h>
#include <epicsThread.h>
#include <epicsUnitTest.h>
#include <testMain.h>
#if __cplusplus<201103L
# define final
# define override
#endif
namespace {
void set_non_blocking(SOCKET sd)
{
osiSockIoctl_t yes = true;
int status = socket_ioctl ( sd,
FIONBIO, & yes);
if(status)
testFail("set_non_blocking fails : %d", SOCKERRNO);
}
// RAII for epicsTimer
struct ScopedTimer {
epicsTimer& timer;
explicit
ScopedTimer(epicsTimer& t) :timer(t) {}
~ScopedTimer() { timer.destroy(); }
};
struct ScopedFDReg {
fdReg * const reg;
explicit
ScopedFDReg(fdReg* reg) :reg(reg) {}
~ScopedFDReg() { reg->destroy(); }
};
// RAII for socket
struct Socket {
SOCKET sd;
Socket() :sd(INVALID_SOCKET) {}
explicit
Socket(SOCKET sd) :sd(sd) {}
Socket(int af, int type)
:sd(epicsSocketCreate(af, type, 0))
{
if(sd==INVALID_SOCKET)
testAbort("failed to allocate socket %d %d", af, type);
}
private:
Socket(const Socket&);
Socket& operator=(const Socket&);
public:
~Socket() {
if(sd!=INVALID_SOCKET)
epicsSocketDestroy(sd);
}
void swap(Socket& o) {
std::swap(sd, o.sd);
}
osiSockAddr bind()
{
osiSockAddr addr = {0};
addr.ia.sin_family = AF_INET;
addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if(::bind(sd, &addr.sa, sizeof(addr)))
testAbort("Unable to bind lo : %d", SOCKERRNO);
osiSockAddr ret;
osiSocklen_t addrlen = sizeof(ret);
if(getsockname(sd, &ret.sa, &addrlen))
testAbort("Unable to getsockname : %d", SOCKERRNO);
(void)addrlen;
if(listen(sd, 1))
testAbort("Unable to listen : %d", SOCKERRNO);
return ret;
}
};
struct DoConnect final : public epicsThreadRunable {
const SOCKET sd;
osiSockAddr to;
DoConnect(SOCKET sd, const osiSockAddr& to)
:sd(sd)
,to(to)
{}
void run() override final {
int err = connect(sd, &to.sa, sizeof(to));
testOk(err==0, "connect() %d %d", err, SOCKERRNO);
}
};
struct DoAccept final : public epicsThreadRunable {
const SOCKET sd;
Socket peer;
osiSockAddr peer_addr;
explicit
DoAccept(SOCKET sd) :sd(sd) {}
void run() override final {
osiSocklen_t len(sizeof(peer_addr));
Socket temp(accept(sd, &peer_addr.sa, &len));
if(temp.sd==INVALID_SOCKET)
testFail("accept() -> %d", SOCKERRNO);
temp.swap(peer);
}
};
struct DoRead final : public epicsThreadRunable {
const SOCKET sd;
char* buf;
unsigned buflen;
int n;
DoRead(SOCKET sd, char* buf, unsigned buflen): sd(sd), buf(buf), buflen(buflen), n(0) {}
void run() override final {
n = recv(sd, buf, buflen, 0);
if(n<0)
testFail("read() -> %d, %d", n, SOCKERRNO);
}
};
struct DoWriteAll final : public epicsThreadRunable {
const SOCKET sd;
const char* buf;
unsigned buflen;
DoWriteAll(SOCKET sd, const char* buf, unsigned buflen): sd(sd), buf(buf), buflen(buflen) {}
void run() override final {
unsigned nsent = 0;
while(nsent<buflen) {
int n = send(sd, nsent+buf, buflen-nsent, 0);
if(n<0) {
testFail("send() -> %d, %d", n, SOCKERRNO);
break;
}
nsent += n;
}
}
};
struct Expire final : public epicsTimerNotify {
bool expired;
Expire() :expired(false) {}
virtual ~Expire() {}
virtual expireStatus expire(const epicsTime &) override final
{
if(!expired) {
expired = true;
testPass("expired");
} else {
testFail("re-expired?");
}
return noRestart;
}
};
struct Event final : public fdReg {
int* ready;
Event(fdManager& mgr, fdRegType evt, SOCKET sd, int* ready)
:fdReg(sd, evt, false, mgr)
,ready(ready)
{}
virtual ~Event() {}
virtual void callBack() override final {
epics::atomic::set(*ready, 1);
}
};
struct OneShot final : public fdReg {
int *mask;
OneShot(fdManager& mgr, fdRegType evt, SOCKET sd, int *mask)
:fdReg(sd, evt, true, mgr)
,mask(mask)
{}
virtual ~OneShot() {
epics::atomic::add(*mask, 2);
}
virtual void callBack() override final {
epics::atomic::add(*mask, 1);
}
};
void testEmpty()
{
fdManager empty;
empty.process(0.1); // ca-gateway always passes 0.01
testPass("Did nothing");
}
void testOnlyTimer()
{
fdManager mgr;
Expire trig, never;
ScopedTimer trig_timer(mgr.createTimer()),
never_timer(mgr.createTimer());
epicsTime now(epicsTime::getCurrent());
trig_timer.timer.start(trig, now+0.1);
never_timer.timer.start(never, now+9999999.0);
mgr.process(0.2);
testOk1(trig.expired);
testOk1(!never.expired);
}
void testSockIO()
{
fdManager mgr;
Socket listener(AF_INET, SOCK_STREAM);
set_non_blocking(listener.sd);
osiSockAddr servAddr(listener.bind());
Socket client(AF_INET, SOCK_STREAM);
Socket server;
// listen() / connect()
{
int readable = 0;
Event evt(mgr, fdrRead, listener.sd, &readable);
DoConnect conn(client.sd, servAddr);
epicsThread connector(conn, "connect", 0);
connector.start();
mgr.process(5.0);
testOk1(readable);
DoAccept acc(listener.sd);
acc.run();
server.swap(acc.peer);
}
set_non_blocking(server.sd);
// writeable
{
int mask = 0;
new OneShot(mgr, fdrWrite, server.sd, &mask);
mgr.process(5.0);
testOk(mask==3, "OneShot event mask %x", mask);
}
// read
{
const char msg[] = "testing";
int readable = 0;
Event evt(mgr, fdrRead, server.sd, &readable);
DoWriteAll op(client.sd, msg, sizeof(msg)-1);
epicsThread writer(op, "writer", 0);
writer.start();
mgr.process(5.0);
testOk1(readable);
char buf[sizeof(msg)] = "";
DoRead(server.sd, buf, sizeof(buf)-1).run();
buf[sizeof(buf)-1] = '\0';
testOk(strcmp(msg, buf)==0, "%s == %s", msg, buf);
}
// timer while unreadable
{
int readable = 0;
Event evt(mgr, fdrRead, server.sd, &readable);
Expire tmo;
ScopedTimer timer(mgr.createTimer());
timer.timer.start(tmo, epicsTime::getCurrent()); // immediate
mgr.process(1.0);
testOk1(!readable);
testOk1(tmo.expired);
}
// notification on close()
{
int readable = 0;
Event evt(mgr, fdrRead, server.sd, &readable);
shutdown(client.sd, SHUT_RDWR);
//Socket().swap(client);
mgr.process(1.0);
testOk1(readable);
}
}
} // namespace
MAIN(fdManagerTest)
{
testPlan(13);
osiSockAttach();
testEmpty();
testOnlyTimer();
testSockIO();
osiSockRelease();
return testDone();
}

View File

@@ -1,139 +0,0 @@
/*************************************************************************\
* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include <stdio.h>
#include <math.h>
#include "fdmgr.h"
#include "epicsTime.h"
#include "epicsAssert.h"
#include "cadef.h"
#define verify(exp) ((exp) ? (void)0 : \
epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor))
static const unsigned uSecPerSec = 1000000;
typedef struct cbStructCreateDestroyFD {
fdctx *pfdm;
int trig;
} cbStructCreateDestroyFD;
void fdHandler (void *pArg)
{
cbStructCreateDestroyFD *pCBFD = (cbStructCreateDestroyFD *) pArg;
printf ("triggered\n");
pCBFD->trig = 1;
}
void fdCreateDestroyHandler (void *pArg, int fd, int open)
{
cbStructCreateDestroyFD *pCBFD = (cbStructCreateDestroyFD *) pArg;
int status;
if (open) {
printf ("new fd = %d\n", fd);
status = fdmgr_add_callback (pCBFD->pfdm, fd, fdi_read, fdHandler, pArg);
verify (status==0);
}
else {
printf ("terminated fd = %d\n", fd);
status = fdmgr_clear_callback (pCBFD->pfdm, fd, fdi_read);
verify (status==0);
}
}
typedef struct cbStuctTimer {
epicsTimeStamp time;
int done;
} cbStruct;
void alarmCB (void *parg)
{
cbStruct *pCBS = (cbStruct *) parg;
epicsTimeGetCurrent (&pCBS->time);
pCBS->done = 1;
}
void testTimer (fdctx *pfdm, double delay)
{
int status;
fdmgrAlarmId aid;
struct timeval tmo;
epicsTimeStamp begin;
cbStruct cbs;
double measuredDelay;
double measuredError;
epicsTimeGetCurrent (&begin);
cbs.done = 0;
tmo.tv_sec = (time_t) delay;
tmo.tv_usec = (unsigned long) ((delay - tmo.tv_sec) * uSecPerSec);
aid = fdmgr_add_timeout (pfdm, &tmo, alarmCB, &cbs);
verify (aid!=fdmgrNoAlarm);
while (!cbs.done) {
tmo.tv_sec = (time_t) delay;
tmo.tv_usec = (unsigned long) ((delay - tmo.tv_sec) * uSecPerSec);
status = fdmgr_pend_event (pfdm, &tmo);
verify (status==0);
}
measuredDelay = epicsTimeDiffInSeconds (&cbs.time, &begin);
measuredError = fabs (measuredDelay-delay);
printf ("measured delay for %lf sec was off by %lf sec (%lf %%)\n",
delay, measuredError, 100.0*measuredError/delay);
}
int main (int argc, char **argv)
{
int status;
fdctx *pfdm;
cbStructCreateDestroyFD cbsfd;
struct timeval tmo;
chid chan;
pfdm = fdmgr_init ();
verify (pfdm);
SEVCHK (ca_task_initialize(), NULL);
cbsfd.pfdm = pfdm;
SEVCHK (ca_add_fd_registration (fdCreateDestroyHandler, &cbsfd), NULL);
/*
* timer test
*/
testTimer (pfdm, 0.001);
testTimer (pfdm, 0.01);
testTimer (pfdm, 0.1);
testTimer (pfdm, 1.0);
if (argc==2) {
SEVCHK(ca_search (argv[1], &chan), NULL);
}
while (1) {
tmo.tv_sec = 0;
tmo.tv_usec = 100000;
cbsfd.trig = 0;
status = fdmgr_pend_event (pfdm, &tmo);
verify (status==0);
ca_poll ();
}
status = fdmgr_delete (pfdm);
verify (status==0);
printf ( "Test Complete\n" );
return 0;
}