Compare commits

..

134 Commits

Author SHA1 Message Date
Andrew Johnson
841ef37820 Update version numbers for Base and internal modules
Clear EPICS_DEV_SNAPSHOT flag for tagging.
2019-07-31 12:11:22 -05:00
Andrew Johnson
f550f278ad Update version numbers in documentation to 7.0.2 2019-07-30 18:09:33 -05:00
Andrew Johnson
1706da98cc Update submodules to tagged versions 2019-07-30 14:36:46 -05:00
Andrew Johnson
e8be04bc43 Update pvDatabase submodule 2019-07-25 18:14:12 -05:00
Andrew Johnson
dee526f2f6 Merge msi fix from 3.15 branch into 7.0 2019-07-25 18:11:26 -05:00
Andrew Johnson
59cffd51ae Merge 3.15 branch into 7.0 2019-07-25 18:10:40 -05:00
Andrew Johnson
9b5034f307 Restore errlogFlush() call to msi.cpp 2019-07-25 14:35:41 -05:00
Michael Davidsaver
e2881fb37e More release notes
Forgot to stage...
2019-07-24 18:42:02 -07:00
Michael Davidsaver
afd9697290 update PVA modules 2019-07-24 17:45:08 -07:00
Michael Davidsaver
9988be0906 update release notes 2019-07-24 16:44:31 -07:00
Andrew Johnson
a6812c5869 Revert "More msi.plt retries for Jenkins builds on Windows"
This reverts commit a69bd833fc.
2019-07-17 16:19:37 -05:00
Michael Davidsaver
b89494a840 Revert "Testing msi: Add retries if necessary"
This reverts commit 29c069db3d.

# Conflicts:
#	src/ioc/dbtemplate/test/msi.plt
2019-07-17 11:20:03 -07:00
Freddie Akeroyd
771ad6a442 msi: Flush stdout on program exit
On WIN32 if the reopen()ed stdout is not closed it can occasionally
result in missing lines in the output file
2019-07-17 10:45:09 -07:00
Michael Davidsaver
d214d81794 CROSS_COMPILER_RUNTEST_ARCHS for non-RTEMS 2019-07-07 16:50:02 -07:00
Michael Davidsaver
c70b1a9a29 libcom: detect uclibc support for backtrace() 2019-07-06 13:43:58 -07:00
Andrew Johnson
c9c4eea0f8 Now fix the non-windows systems 2019-06-27 15:21:41 -05:00
Andrew Johnson
78ce2dac05 Don't use / in Windows program paths
Fixing Windows msi.t script failures...
2019-06-27 13:28:56 -05:00
Andrew Johnson
b2285bb8aa Redirect msi's STDERR to /dev/null (NUL on Windows) during tests
Also moves the environment variable handling code out to the only
test that needs it and simplifies it; some Perl versions were giving
warnings from splitting an undef value.
2019-06-27 12:01:42 -05:00
Michael Davidsaver
59cb5ba6a0 Merge remote-tracking branch 'konrad/clean-up-msi' into 3.15
* konrad/clean-up-msi:
  Add test for macro expansion in file names
  Reduce scope of some variables
  Convert substitutionName to std::string
  Use bool for more clarity
  Remove unneeded errlogFlush()
  Manage psubFile with new/delete
  Convert copy to std::string
  Use std::list for patternList
  Convert patternNode.var to std::string
  Convert fullname to std::string
  Convert inputFile.filename to std::string
  Simplify inputConstruct()
  Use std::list for pathList
  Use std::list for inputFileList
  Constructor for struct inputData
  Simplify catMacroReplacements()
  Constructor for struct subInfo
  Add some const keywords
  Convert MSI to C++
  Test expansion of empty patterns with MSI
2019-06-26 14:55:40 -07:00
Michael Davidsaver
c8e5deca76 dbEvent tweak Queue size 2019-06-26 09:40:41 -07:00
Michael Davidsaver
5b09bb6e71 Set file+line for epicsMutex
Makes the output of epicsMutexShowAll more useful wrt. libca.
2019-06-26 09:40:41 -07:00
Michael Davidsaver
ede52d96b8 posix show mapping of epicsMutexId and uaddr passed to futex()
On Linux w/ GLIBC+NTPL useful to establish mapping between
args to futux syscall and epicsMutexId (and file+line of creation).
2019-06-26 09:40:41 -07:00
Michael Davidsaver
ceccafe823 mbbo/mbbiDirect number of bits as precision
Not really useful w/ RSRV/CA, but can be used by QSRV/PVA
to provide number of bits as meta-data.
2019-06-26 09:39:58 -07:00
Michael Davidsaver
f955199805 epicsTimerTest loosen test threshold 2019-06-26 09:38:48 -07:00
Michael Davidsaver
2af0c10470 debug epicsTimerTest 2019-06-26 09:38:48 -07:00
Michael Davidsaver
89f0f1338a ioc/db: consolidate callback state tracking
combine cbCtl and callbackIsInit.
use atomic ops to avoid false positive data races.
2019-06-26 09:38:48 -07:00
Michael Davidsaver
6ce8dfec01 libCom: timerQueueActive atomic exitFlag
quiet false positive data race
2019-06-26 09:38:48 -07:00
Michael Davidsaver
c1ab30142a sysconf /0 paranoia
Seems unlikely, but avoid any possibility to divide by zero.
2019-06-26 08:59:35 -07:00
Michael Davidsaver
2db2f1a53f Merge remote-tracking branch 'konrad/fix-compiler-warnings' into 3.15
* konrad/fix-compiler-warnings:
  epicsTime: rely on implicit copy constructor
  iocLogServer: check return values
  Fix potential buffer overflow in iocLogServer
  Fix weird use of strncpy
2019-06-24 18:21:32 -07:00
Michael Davidsaver
b811d3402f Merge remote-tracking branch 'konrad/dont-nuke-global-cac-thread-id-in-exit-handler' into 3.15
* konrad/dont-nuke-global-cac-thread-id-in-exit-handler:
  Remove cacExitHandler
2019-06-24 18:12:58 -07:00
Michael Davidsaver
a64cb4a877 update PVA modules 2019-06-17 17:48:41 -07:00
Andrew Johnson
e75a72ef76 VxWorks: Mark undetected underflow parse test as ToDo 2019-06-07 17:22:40 -05:00
Andrew Johnson
33e099a51b Fix VxWorks osiSockOptMcastLoop_t => char
Repairs osiSockTest
2019-06-07 17:21:18 -05:00
Andrew Johnson
345cfcffa8 Fix sync filter example in dbd.pod 2019-06-04 12:53:41 -05:00
Michael Davidsaver
655c5bed92 osiClockTime Darwin doesn't use.
Darwin/osdTime.cpp unconditionally provides osdTimeGetCurrent()
and never calls ClockTime_Init().
2019-05-20 15:32:57 -07:00
Michael Davidsaver
2c99c36ef9 Merge remote-tracking branch 'lp-Com/timeopt' into 7.0
* lp-Com/timeopt:
  !gtMoreThanDefault -> useOsdGetCurrent
  generalTime short circuit current time
2019-05-20 13:49:10 -07:00
Andrew Johnson
71d4d3ecbd epicsErrlogTest.c improvements
Instrument testEqInt() macro with caller's source line-number.
Added another epicsEvent to wait for the listeners to run.
Not guaranteed to fix all issues, but should be more reliable.
2019-05-09 15:03:56 -05:00
Andrew Johnson
86138fdd23 Fix make-tar.sh to work on MacOS too
The BSD version of tar doesn't support -a, and
requires -T instead of --files-from.
2019-05-09 11:51:26 -05:00
Ralph Lange
8c993405b4 doc/ca: clarify variable size array subscription
(cherry-picked from branch 3.15)
2019-05-09 17:32:05 +02:00
Ralph Lange
96259b7bdc doc/ca: clarify variable size array subscription 2019-05-09 17:28:36 +02:00
Michael Davidsaver
bf8ff4e5f1 !gtMoreThanDefault -> useOsdGetCurrent 2019-05-08 19:05:58 -07:00
Martin Konrad
25576c316a Remove cacExitHandler
We need to ensure the allocated resources stick around until
the last instance using them doesn't need them anymore. Sharing
a raw pointer by exporting it as a global variable is not
compatible with cleaning up at all. For now we remove the
clean-up code.

Note: This fixes a segfault during IOC shutdown when using both
pyDevSup and pyepics. See the discussion on
https://epics.anl.gov/tech-talk/2019/msg00778.php for details.
2019-05-06 19:50:01 -04:00
Andrew Johnson
2d3de1916b Don't clear caClientCallbackThreadId in CA's exit handler 2019-05-06 12:22:34 -05:00
Ralph Lange
fe34c6f35e appveyor-ci: ANL Make install 4.1 -> 4.2.1 2019-05-03 15:34:37 +02:00
Ralph Lange
52cdfe0a44 appveyor-ci: use pre-installed AppVeyor MinGW 2019-05-03 15:34:01 +02:00
Ralph Lange
16ddcaeaed appveyor-ci: ANL Make install 4.1 -> 4.2.1 2019-05-03 15:30:59 +02:00
Ralph Lange
297206e247 appveyor-ci: use pre-installed AppVeyor MinGW 2019-05-03 15:29:07 +02:00
Ralph Lange
3110322f8c appveyor-ci: use choco MinGW 5.3.0 to work around build problem
(fixes lp:1827225)
2019-05-02 12:09:09 +02:00
Ralph Lange
63bf8a8219 appveyor-ci: use choco MinGW 5.3.0 to work around build problem
(fixes lp:1827225)
2019-05-02 11:39:04 +02:00
Ralph Lange
ec7193d0be appveyor-ci: exclude some cygwin builds (broken compiler)
appveyor-ci: remove slack, add email and GitHub notifications
(cherry-picked from branch 7.0)
2019-04-30 15:53:50 +02:00
Ralph Lange
17dea964a6 appveyor-ci: remove slack, add email and GitHub notifications 2019-04-30 15:51:28 +02:00
Ralph Lange
12283ce25a appveyor-ci: exclude some cygwin builds (broken compiler) 2019-04-30 15:26:35 +02:00
Andrew Johnson
f486dba478 Add some epics-controls publishing instructions, incomplete 2019-04-26 13:53:24 -05:00
Martin Konrad
1f95d0db12 epicsTime: rely on implicit copy constructor
When a custom copy constructor is defined the assignment operator
also needs to be defined explicitly. For this simple class the
implicit copy ctor/assignment operator are sufficient, though. This
fixes a warning emitted by GCC9.
2019-04-25 11:00:39 -04:00
Martin Konrad
e02c5c3026 iocLogServer: check return values
No serious issues here just fixing some warnings.
2019-04-24 22:25:31 -04:00
Martin Konrad
8e9d75ad71 Fix potential buffer overflow in iocLogServer 2019-04-24 22:25:31 -04:00
Martin Konrad
630663caa8 Fix weird use of strncpy
Note: The old code was correct. This change just gets rid of
a bunch of warnings.
2019-04-24 22:25:31 -04:00
Andrew Johnson
7a84a1abba Update Base version numbers for next patch release 2019-04-23 16:23:20 -05:00
Andrew Johnson
a9dbd4dd39 Update libcom and base version numbers, no -DEV 2019-04-23 16:07:43 -05:00
Michael Davidsaver
ccf34ae3d0 update PVD and PVA 2019-04-17 14:17:37 -07:00
Andrew Johnson
f559c36a6f Update tagged modules: pvDatabase 4.4.2, pvaClient 4.5.0 2019-04-15 11:54:13 -05:00
Andrew Johnson
8217fa86fe Reimplement show-makefiles to display duplicates 2019-04-12 18:14:57 -05:00
Andrew Johnson
f0e118da91 Adjust warning message in RULES_BUILD to suggest cause 2019-04-12 18:13:55 -05:00
Andrew Johnson
5a55aa17d9 Update pvData and pvDatabase modules
Not updating pva2pva, the next 7.0.2.2 release should
keep the previous tagged version
2019-04-10 16:45:35 -05:00
Andrew Johnson
f79f2cf8fa Release Notes for commits since 7.0.2.1 2019-04-10 16:44:21 -05:00
Andrew Johnson
f0f7a1ef06 Move rules from modules/Makefile to new RULES_MODULES file 2019-04-10 16:16:12 -05:00
Andrew Johnson
1412abadf9 Move cvsclean and depclean to RULES_COMMON 2019-04-10 14:13:29 -05:00
Andrew Johnson
3a0371894b Checklist tweaks for 7.0.2.2 2019-04-10 13:24:09 -05:00
Andrew Johnson
e92a9ae426 Make submodule builds more generic
Adds PARENT_MODULE to detect when building a submodule.
RULES_TOP uses that to disable uninstall rules and abort instead of
deleting the parent's configure/ directory.
2019-04-10 12:13:33 -05:00
Andrew Johnson
3a5fb898d6 Move rules for cvsclean and depclean to RULES_DIRS
Fix FIND_TOOLS for submodules when EPICS_BASE not built
Add 'make help' for newer test targets
2019-04-08 17:50:19 -05:00
Ralph Lange
2a14647eec Change -DEV to be based on next patch release number 2019-03-28 16:43:50 -07:00
Michael Davidsaver
597393a8ee libCom: drop CLOCK_MONOTONIC_RAW
Turns out this is ~10x slower to query than CLOCK_MONOTONIC
2019-03-28 09:59:24 -07:00
Andrew Johnson
bad8b25e4e Prevent submodules from cleaning the EPICS_BASE/configure directory 2019-03-20 16:12:14 -05:00
Andrew Johnson
7fe8373c32 Changes after creating the 7.0.2.1 tag 2019-03-20 15:28:37 -05:00
Andrew Johnson
72be690fec Set Base version to 7.0.2.1 (final) 2019-03-20 15:24:51 -05:00
Andrew Johnson
a5b3157ec1 Update version numbers for libcom and database 2019-03-20 15:18:05 -05:00
Andrew Johnson
9a062cd6a1 Update submodules 2019-03-20 15:16:55 -05:00
Andrew Johnson
592b935146 Document the macOS -flat_namespace flag change 2019-03-20 13:46:47 -05:00
Andrew Johnson
638391249d Update EPICS_TIMEZONE settings in CONFIG_SITE_ENV
Deleted 2018, added 2023.
2019-03-20 13:44:02 -05:00
Michael Davidsaver
cde682f7ba nonEpicsThreadPriorityTest is Linux only 2019-03-18 16:08:20 -07:00
Michael Davidsaver
89da4130fc make-tar.sh allow HEAD for testing 2019-03-18 13:49:25 -07:00
Michael Davidsaver
1d2637a04e update submodules 2019-03-18 13:26:47 -07:00
Michael Davidsaver
a2123db9fb Merge branch 'fix_1816841_only' into 7.0
* fix_1816841_only:
  Another hack to deal with RTEMS which is POSIX but still different
  stripped-down fix for 1816841 only
2019-03-17 18:43:34 -07:00
till straumann
7f55bb0386 Another hack to deal with RTEMS which is POSIX but still different 2019-03-14 11:47:52 -07:00
till straumann
511a541f31 stripped-down fix for 1816841 only 2019-03-14 10:36:55 -07:00
Michael Davidsaver
394c39da51 update release notes 2019-03-13 18:17:57 -07:00
Michael Davidsaver
632d1f45c8 Merge branch 'putf-pact' into 7.0
* putf-pact:
  RPRO/PUTF log instead of assert()
  RPRO/PUTF test all three link types
  RPRO/PUTF rename
  RPRO/PUTF also handle self link case
  fix RPRO/PUTF regression
  test for RPRO/PUTF regression
  dbRec2Pvt()
  Remove Warning: 'blah.PUTF' found true with PACT false
  dbNotify set PUTF
2019-03-13 18:04:18 -07:00
Michael Davidsaver
0f75e0aa7f RPRO/PUTF log instead of assert() 2019-03-13 17:57:41 -07:00
Michael Davidsaver
c93ec231a2 update PVD and PVA 2019-03-11 19:45:37 -07:00
Michael Davidsaver
d1149a0ba9 iocsh epicsMutexShowAll accepts 2 arguments 2019-03-11 17:53:51 -07:00
Andrew Johnson
8c3c5a9731 Restore the -flat_namespace linker flag on macOS
Latest versions of Apple XCode require it.
2019-03-11 16:00:54 -05:00
Michael Davidsaver
95cb81c286 generalTime short circuit current time
epicsTimeGetCurrent() is called frequently.
Unless more than the default provider is registered,
locking and going through the timeProvider list each
time is a waste.

Instead, short circuit to directly call the default
time current time provider unless a another has
been registered.
2019-03-10 16:36:54 -07:00
Andrew Johnson
44510f2fb2 Merge 3.15 branch into 7.0 2019-03-07 12:56:57 -06:00
Andrew Johnson
933733465e Improve timeout diagnostics from netget.plt 2019-02-13 16:02:16 -06:00
Michael Davidsaver
688f32cff0 RPRO/PUTF test all three link types
Shouldn't be any difference, but check IN_LINK
and FWD_LINK to be sure.
2019-02-06 16:07:40 -08:00
Michael Davidsaver
d3feb1e2f9 RPRO/PUTF rename 2019-02-04 16:37:20 -08:00
Michael Davidsaver
62c11c22c9 RPRO/PUTF also handle self link case 2019-02-02 15:34:59 -08:00
Andrew Johnson
736075daf6 Document macOS Mojave fix 2019-02-01 16:49:11 -06:00
Andrew Johnson
9ef3b77348 Fix ca/client/perl/Makefile for macOS Mojave
... in which Apple moved the Perl headers into XCode.
This should also make the build a little more forgiving on other
architectures that have incomplete Perl installations; it gives up
trying to build the Perl bindings with a warning if perl.h is missing.
2019-02-01 16:14:45 -06:00
Andrew Johnson
d15c8093ec Extract dbEntryToAddr() from dbChannelCreate() & dbNameToAddr() 2019-02-01 15:26:35 -06:00
Andrew Johnson
0211698b69 Move dbAccess prototypes to the correct header file 2019-02-01 12:02:41 -06:00
Michael Davidsaver
e860617389 fix RPRO/PUTF regression 2019-01-31 20:16:09 -08:00
Michael Davidsaver
e918994704 test for RPRO/PUTF regression 2019-01-31 20:16:06 -08:00
Michael Davidsaver
5eb7da4595 dbRec2Pvt() 2019-01-31 20:16:03 -08:00
Andrew Johnson
2206934ae2 Fix MacOS build warning
The MacOS ld linker complains if a -L option points to
a directory that doesn't exist, so create that directory
before running the linker. Might be unnecessary, but...
2019-01-21 16:08:29 -06:00
Andrew Johnson
318fc96912 dbStatic: Removed old DCT_ macros and routines 2019-01-15 16:21:32 -06:00
Andrew Johnson
a58cc37a5e Fix dbhcr before iocInit 2019-01-15 16:03:17 -06:00
Andrew Johnson
b5e041b991 Update Release Notes version number to 7.0.3 2019-01-15 16:01:55 -06:00
Andrew Johnson
5e1bad2b34 dbStatic parser: Reject empty object names 2019-01-10 14:45:18 -06:00
Andrew Johnson
0ae50485cf Fix int64inRecord::get_units() 2019-01-08 14:42:43 -06:00
Martin Konrad
a9606dbf6e Add test for macro expansion in file names 2019-01-08 09:34:55 -05:00
Martin Konrad
409ee26fae Reduce scope of some variables 2019-01-08 09:34:55 -05:00
Martin Konrad
9a4787155c Convert substitutionName to std::string 2019-01-08 09:34:55 -05:00
Martin Konrad
84dba0d2b7 Use bool for more clarity 2019-01-08 09:34:55 -05:00
Martin Konrad
87f6c3dec9 Remove unneeded errlogFlush() 2019-01-08 09:34:55 -05:00
Martin Konrad
940814becf Manage psubFile with new/delete 2019-01-08 09:34:55 -05:00
Martin Konrad
68a1a529b2 Convert copy to std::string 2019-01-08 09:34:55 -05:00
Martin Konrad
ce38caf41b Use std::list for patternList
This improves type safety and readability.
2019-01-08 09:34:55 -05:00
Martin Konrad
f03f10e664 Convert patternNode.var to std::string 2019-01-08 09:34:55 -05:00
Martin Konrad
b4f4fb853d Convert fullname to std::string 2019-01-08 09:34:55 -05:00
Martin Konrad
b518ebe85b Convert inputFile.filename to std::string 2019-01-08 09:34:55 -05:00
Martin Konrad
e461d782f4 Simplify inputConstruct() 2019-01-08 09:34:55 -05:00
Martin Konrad
ef2a381e92 Use std::list for pathList
This improves type safety and readability.
2019-01-08 09:34:55 -05:00
Martin Konrad
265d4962a4 Use std::list for inputFileList
This improves type safety and readability.
2019-01-08 09:34:55 -05:00
Martin Konrad
db9267bbd5 Constructor for struct inputData 2019-01-08 09:34:55 -05:00
Martin Konrad
1cf3fa9ba9 Simplify catMacroReplacements()
This fixes lp:1810946 and lp:1810949.
2019-01-08 09:34:40 -05:00
Martin Konrad
3e8b0028dc Constructor for struct subInfo 2019-01-08 09:22:52 -05:00
Martin Konrad
d8f18c27f4 Add some const keywords 2019-01-08 09:22:52 -05:00
Martin Konrad
801c01b9b6 Convert MSI to C++ 2019-01-08 09:22:52 -05:00
Martin Konrad
0c1874bbfe Test expansion of empty patterns with MSI
This was subject to a regression in 3.15.6. See lp:1810946.
2019-01-08 09:22:40 -05:00
Michael Davidsaver
bc7ee94e2c Remove Warning: 'blah.PUTF' found true with PACT false 2019-01-03 20:34:12 -08:00
Michael Davidsaver
3fb10b6d59 dbNotify set PUTF 2019-01-03 20:34:12 -08:00
Andrew Johnson
a8fdf2efeb realclean rule must delete all RELEASE.<host>.local files 2018-12-18 15:43:18 -06:00
Andrew Johnson
c9eda3ca48 Reset snapshot to -DEV after tagging 2018-12-17 17:42:57 -06:00
94 changed files with 1490 additions and 845 deletions

View File

@@ -48,6 +48,7 @@ environment:
TOOLCHAIN: 2017
- TOOLCHAIN: cygwin
- TOOLCHAIN: mingw
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
# Platform: architecture
platform:
@@ -60,6 +61,10 @@ matrix:
# VS Express installs don't have the 64 bit compiler
- platform: x64
TOOLCHAIN: 10.0
# Cygwin static-debug has compiler problems
- configuration: static-debug
TOOLCHAIN: cygwin
#---------------------------------#
# building & testing #
@@ -81,6 +86,9 @@ test_script:
notifications:
- provider: Slack
incoming_webhook:
secure: RYOm3FIUYeZGjWKaeTVKwq+C3fzK54AKwbmAoECED45mex3lN+8HmrC845a6mg9xPUJ/ND51RopWVaKDD9/UzaM0SO195RQLKqUTIUafiuM=
- provider: Email
to:
- core-talk@aps.anl.gov
on_build_success: false
- provider: GitHubPullRequest

View File

@@ -42,13 +42,13 @@ if "%TOOLCHAIN%"=="mingw" (
set "MAKE=mingw32-make"
if "%OS%"=="64BIT" (
set "EPICS_HOST_ARCH=windows-x64-mingw"
set "INCLUDE=C:\tools\mingw64\include;%INCLUDE%"
set "PATH=C:\tools\mingw64\bin;%PATH%"
set "INCLUDE=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\include;%INCLUDE%"
set "PATH=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;%PATH%"
echo [INFO] MinGW Toolchain 64bit
) else (
set "EPICS_HOST_ARCH=win32-x86-mingw"
set "INCLUDE=C:\tools\mingw32\include;%INCLUDE%"
set "PATH=C:\tools\mingw32\bin;%PATH%"
set "INCLUDE=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\include;%INCLUDE%"
set "PATH=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin;%PATH%"
echo [INFO] MinGW Toolchain 32bit
)
echo [INFO] Compiler Version

View File

@@ -55,16 +55,9 @@ if "%TOOLCHAIN%"=="mingw" (
) || (
echo [INFO] EPICS set up for optimized build
)
if "%OS%"=="64BIT" (
echo [INFO] Installing MinGW 64bit
cinst mingw || cinst mingw
) else (
echo [INFO] Installing MinGW 32bit
cinst mingw --x86 || cinst mingw --x86
)
)
echo [INFO] Installing Make 4.1
curl -fsS --retry 3 -o C:\tools\make-4.1.zip https://epics.anl.gov/download/tools/make-4.1-win64.zip
echo [INFO] Installing Make 4.2.1 from ANL web site
curl -fsS --retry 3 -o C:\tools\make-4.2.1.zip https://epics.anl.gov/download/tools/make-4.2.1-win64.zip
cd \tools
"C:\Program Files\7-Zip\7z" e make-4.1.zip
"C:\Program Files\7-Zip\7z" e make-4.2.1.zip

View File

@@ -16,27 +16,53 @@ PREFIX="$3"
if ! [ "$TOPREV" ]
then
cat <<EOF >&2
usage: $0 [rev] [outfile.tar.gz] [prefix/]
usage: $0 <rev> [<outfile> [<prefix>]]
<rev> may be any git revision spec. (tag, branch, or commit id).
Output file may be .tar.gz, .tar.bz2, or any extension supported by "tar -a".
If output file name is omitted, "base-<rev>.tar.gz" will be used.
If <prefix> is omitted, the default prefix is "base-<rev>/".
If provided, <outfile> must end with ".tar", ".tar.gz" or ".tar.bz2".
If <outfile> is omitted, "base-<rev>.tar.gz" will be used.
If provided, <prefix> must end with "/". If <prefix> is omitted,
the default is "base-<rev>/".
EOF
exit 1
fi
[ "$FINALTAR" ] || FINALTAR="base-$TOPREV.tar.gz"
[ "$PREFIX" ] || PREFIX="base-$TOPREV/"
case "$FINALTAR" in
"")
TAROPT=-z
FINALTAR="base-$TOPREV.tar.gz"
;;
*.tar)
TAROPT=""
;;
*.tar.gz)
TAROPT=-z
;;
*.tar.bz2)
TAROPT=-j
;;
*)
die "outfile must end with '.tar.gz' or '.tar.bz2'"
;;
esac
case "$PREFIX" in
*/) ;;
*) die "Prefix must end with '/'";;
"")
PREFIX="base-$TOPREV/"
;;
*/)
;;
*)
die "Prefix must end with '/'"
;;
esac
# Check for both <tag> and R<tag>
if ! [ `git tag -l $TOPREV` ]
if [ "$TOPREV" = "HEAD" ]
then
true
elif ! [ `git tag -l $TOPREV` ]
then
if [ `git tag -l R$TOPREV` ]
then
@@ -94,13 +120,11 @@ then
fi
# Use the filtered list to build the final tar
# The -a option chooses compression automatically based on output file name.
tar -C "$TDIR"/tar --files-from="$TDIR"/list.2 -caf "$FINALTAR"
tar -c $TAROPT -C "$TDIR"/tar -T "$TDIR"/list.2 -f "$FINALTAR"
echo "Wrote $FINALTAR"
tar -taf "$FINALTAR" > "$TDIR"/list.3
tar -t $TAROPT -f "$FINALTAR" > "$TDIR"/list.3
# make sure we haven't picked up anything extra
if ! diff -u "$TDIR"/list.2 "$TDIR"/list.3

View File

@@ -30,10 +30,11 @@ endif # BASE_TOP
#---------------------------------------------------------------
# Where to find the installed build tools
# Windows does not like commands with relative paths starting ../
# but the Perl scripts in TOP/src/tools are OK
# so TOOLS must be an absolute path, although Perl scripts are OK.
# FIND_TOOL is for scripts run before the build reaches src/tools.
TOOLS = $(abspath $(EPICS_BASE_HOST_BIN))
FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) $(TOP)/src/tools/$(1)))
FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) $(EPICS_BASE)/src/tools/$(1)))
#---------------------------------------------------------------
# EPICS Base build tools and tool flags
@@ -54,3 +55,8 @@ INSTALL_LIBRARY = $(INSTALL)
# tools for making header dependencies and variable replacement
MKMF = $(PERL) $(TOOLS)/mkmf.pl
REPLACEVAR = $(PERL) $(TOOLS)/replaceVAR.pl
#---------------------------------------------------------------
# tools for cleaning out unwanted files
CVSCLEAN = $(call FIND_TOOL,cvsclean.pl)
DEPCLEAN = $(call FIND_TOOL,depclean.pl)

View File

@@ -48,13 +48,15 @@ EPICS_VERSION = 7
EPICS_REVISION = 0
# EPICS_MODIFICATION must be a number >=0 and <256
EPICS_MODIFICATION = 2
EPICS_MODIFICATION = 3
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
# Not included if zero
# Not included in the official EPICS version number if zero
EPICS_PATCH_LEVEL = 0
# This will end in -DEV between official releases
# Between official releases, the EPICS_PATCH_LEVEL gets incremented
# and a -DEV suffix is added (similar to the Maven -SNAPSHOT versions)
EPICS_DEV_SNAPSHOT=
#EPICS_DEV_SNAPSHOT=-DEV
#EPICS_DEV_SNAPSHOT=-pre1
#EPICS_DEV_SNAPSHOT=-pre1-DEV
@@ -64,7 +66,6 @@ EPICS_PATCH_LEVEL = 0
#EPICS_DEV_SNAPSHOT=-rc1-DEV
#EPICS_DEV_SNAPSHOT=-rc2
#EPICS_DEV_SNAPSHOT=-rc2-DEV
EPICS_DEV_SNAPSHOT=
# No changes should be needed below here

View File

@@ -34,14 +34,9 @@
# The future dates below assume the rules don't get changed;
# see http://www.timeanddate.com/time/dst/2018.html to check.
#
# DST for 2018 US: Mar 11 - Nov 04
# EU: Mar 25 - Oct 28
EPICS_TIMEZONE = CUS::360:031102:110402
#EPICS_TIMEZONE = MET::-60:032502:102803
#
# DST for 2019 US: Mar 10 - Nov 03
# EU: Mar 31 - Oct 27
#EPICS_TIMEZONE = CUS::360:031002:110302
EPICS_TIMEZONE = CUS::360:031002:110302
#EPICS_TIMEZONE = MET::-60:033102:102703
#
# DST for 2020 US: Mar 08 - Nov 01
@@ -58,6 +53,11 @@ EPICS_TIMEZONE = CUS::360:031102:110402
# EU: Mar 27 - Oct 30
#EPICS_TIMEZONE = CUS::360:031302:110602
#EPICS_TIMEZONE = MET::-60:032702:103003
#
# DST for 2023 US: Mar 13 - Nov 06
# EU: Mar 27 - Oct 30
#EPICS_TIMEZONE = CUS::360:031202:110502
#EPICS_TIMEZONE = MET::-60:032602:102903
# EPICS_TS_NTP_INET
# NTP time server ip address for VxWorks and RTEMS.

View File

@@ -202,9 +202,9 @@ endif
$(TESTPRODNAME) $(PRODNAME): $(PRODUCT_OBJS) $(PROD_RESS) $(PROD_DEPLIBS)
$(TESTPRODNAME) $(PRODNAME): %$(EXE):
$(TESTPRODNAME) $(PRODNAME): %$(EXE): | $(INSTALL_LIB)
@$(RM) $@
$(DEBUGCMD) $(LINK.cpp)
$(LINK.cpp)
$(MT_EXE_COMMAND)
%_ctdt$(OBJ) : %_ctdt.c
@@ -305,6 +305,10 @@ $(LOADABLE_SHRLIBNAME): $(LOADABLE_SHRLIB_PREFIX)%$(LOADABLE_SHRLIB_SUFFIX):
$(LINK.shrlib)
$(MT_DLL_COMMAND)
$(LIBNAME) $(SHRLIBNAME) $(LOADABLE_SHRLIBNAME): | $(INSTALL_LIB)
$(INSTALL_LIB):
@$(MKDIR) $@
#---------------------------------------------------------------
# C++ munching for VxWorks
@@ -467,9 +471,11 @@ $(INSTALL_TCLLIB)/%: ../%
@$(INSTALL) -d -m $(BIN_PERMISSIONS) $< $(INSTALL_TCLLIB)
endif
ifneq ($(TCLINDEX),)
$(INSTALL_TCLLIB)/$(TCLINDEX): $(INSTALL_TCLLIBS)
$(ECHO) "Updating $@"
$(ECHO) eval auto_mkindex $(INSTALL_TCLLIB) "$(TCLLIBNAME)" | tclsh
endif
$(INSTALL_LOADABLE_SHRLIBS): $(INSTALL_SHRLIB)/%: %
$(ECHO) "Installing loadable shared library $@"
@@ -547,6 +553,6 @@ include $(CONFIG)/RULES_EXPAND
include $(CONFIG)/RULES_COMMON
else
$(warning RULES_BUILD included more than once. \
Use 'make show-makefiles' to work out why.)
$(warning Warning: Base configure/RULES_BUILD file included more than once. \
Does configure/RELEASE have multiple pointers to $(EPICS_BASE)?)
endif # BASE_RULES_BUILD

View File

@@ -8,16 +8,18 @@
# These rules show the set of Makefiles, config files and
# rules files loaded by GNUmake.
# Protect against filenames containing colons (Windows)
SAFE_MAKEFILES = $(subst :,__colon__,$(MAKEFILE_LIST))
SHOW_MAKEFILES = $(SAFE_MAKEFILES:%=show-makefile.%)
show-makefiles: $(SHOW_MAKEFILES)
show-makefiles::
@echo
@echo Makefiles read:
define SHOW_MAKEFILE_template
show-makefiles::
@echo " $(1)"
endef
$(foreach file,$(MAKEFILE_LIST), \
$(eval $(call SHOW_MAKEFILE_template,$(file))))
# The sort prevents warnings about duplicate targets:
$(sort $(SHOW_MAKEFILES)): show-makefile.%:
@echo " $(subst __colon__,:,$(@:show-makefile.%=%))"
.PHONY: show-makefiles
.PHONY: show-makefiles show-makefile.%
# These rules support printing a Makefile variable values.
# Many variables are only set inside an O.<arch> build directory.
@@ -30,6 +32,17 @@ PRINT.%:
.PHONY: PRINT PRINT.%
# Clean rules for recursively deleting editor backup files
# and dependency (.d) files from CWD and below.
cvsclean:
$(PERL) $(CVSCLEAN)
depclean:
$(PERL) $(DEPCLEAN)
.PHONY: cvsclean depclean
# User specific rules
#
-include $(HOME)/configure/RULES_USER

View File

@@ -86,10 +86,11 @@ $(DIRS) $(dirActionTargets) $(dirArchTargets) $(dirActionArchTargets) :
$(ARCHS) $(ACTIONS) $(actionArchTargets) :%: \
$(foreach dir, $(DIRS), $(dir)$(DIVIDER)%)
.PHONY : $(DIRS) all host rebuild
.PHONY : $(ARCHS) $(ACTIONS)
.PHONY : $(dirActionTargets) $(dirArchTargets)
.PHONY : $(dirActionArchTargets)
.PHONY : $(actionArchTargets)
.PHONY: $(DIRS) all host rebuild
.PHONY: $(ARCHS) $(ACTIONS)
.PHONY: $(dirActionTargets) $(dirArchTargets)
.PHONY: $(dirActionArchTargets)
.PHONY: $(actionArchTargets)
include $(CONFIG)/RULES_COMMON

47
configure/RULES_MODULES Normal file
View File

@@ -0,0 +1,47 @@
#*************************************************************************
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
# Support modules can use these rules to build submodules too.
#
# The requirements to do so are:
# 1. Create a file CONFIG_SITE.local in the same directory as the
# Makefile, which defines these variables (the last one is empty):
# PARENT_MODULE - The name submodules call their parent
# INSTALL_LOCATION := $($(PARENT_MODULE))
# CONFIG_INSTALLS =
# 2. The Makefile must set TOP and include $(TOP)/configure/CONFIG and
# CONFIG_SITE.local
# 3. Submodules are added to the SUBMODULES variable in the Makefile
# 4. Dependencies between submodules must be set using
# <name>_DEPEND_DIRS = <prerequisites>
# 5. The Makefile must end by including $(TOP)/configure/RULES_MODULES
# 6. Submodules must have a configure/RELEASE file that contains
# -include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
# 7. Submodules must have a configure/CONFIG_SITE file that contains
# -include $(TOP)/../CONFIG_SITE.local
# Add checked-out submodules to DIRS
DIRS += $(subst /Makefile,,$(wildcard $(addsuffix /Makefile, $(SUBMODULES))))
include $(CONFIG)/RULES_DIRS
INSTALL_LOCATION_ABS := $(abspath $(INSTALL_LOCATION))
RELEASE_LOCAL := RELEASE.$(EPICS_HOST_ARCH).local
# Ensure that RELEASE.<host>.local exists before doing anything else
all host $(DIRS) $(ARCHS) $(ACTIONS) $(dirActionTargets) $(dirArchTargets) \
$(dirActionArchTargets) $(actionArchTargets): | $(RELEASE_LOCAL)
# Convenience target
RELEASE.host: $(RELEASE_LOCAL)
$(RELEASE_LOCAL): Makefile CONFIG_SITE.local
$(ECHO) Creating $@ with
$(ECHO) " $(PARENT_MODULE) = $(INSTALL_LOCATION_ABS)"
@echo $(PARENT_MODULE) = $(INSTALL_LOCATION_ABS)> $@
realclean:
$(RM) $(wildcard RELEASE.*.local)
.PHONY: RELEASE.host realclean

View File

@@ -9,24 +9,30 @@
include $(CONFIG)/RULES_DIRS
# Disable most top rules when installing into a parent's tree, indicated
# by PARENT_MODULE being set in the modules/CONFIG_SITE.local file and
# INSTALL_LOCATION pointing to the the same place as in the parent.
ifeq ($(origin PARENT_MODULE),file)
ifeq ($(INSTALL_LOCATION),$($(PARENT_MODULE)))
DISABLE_TOP_RULES=YES
endif
endif
ifndef DISABLE_TOP_RULES
#
# Rules for a regular application top directory
#
distclean: realclean cvsclean realuninstall
CVSCLEAN = $(call FIND_TOOL,cvsclean.pl)
cvsclean:
$(PERL) $(CVSCLEAN)
DEPCLEAN = $(call FIND_TOOL,depclean.pl)
depclean:
$(PERL) $(DEPCLEAN)
realuninstall: uninstallDirs
$(RMDIR) $(INSTALL_LOCATION_BIN)
$(RMDIR) $(INSTALL_LOCATION_LIB)
UNINSTALL_DIRS += $(INSTALL_DBD) $(INSTALL_INCLUDE) $(INSTALL_DOC) \
$(INSTALL_HTML) $(INSTALL_TEMPLATES) $(INSTALL_DB) $(DIRECTORY_TARGETS)
UNINSTALL_DIRS += $(INSTALL_DB) $(INSTALL_DBD) $(INSTALL_DOC) $(INSTALL_HTML)
UNINSTALL_DIRS += $(INSTALL_INCLUDE) $(INSTALL_TEMPLATES) $(DIRECTORY_TARGETS)
ifneq ($(INSTALL_LOCATION),$(TOP))
UNINSTALL_DIRS += $(INSTALL_CONFIG)
UNINSTALL_DIRS += $(INSTALL_CONFIG)
endif
uninstallDirs:
$(RMDIR) $(UNINSTALL_DIRS)
@@ -40,6 +46,8 @@ uninstall$(DIVIDER)%:
$(RMDIR) $(INSTALL_LOCATION_BIN)/$(archPart)
$(RMDIR) $(INSTALL_LOCATION_LIB)/$(archPart)
# Remove the bin and lib directories if they have no sub-directories
#
cleandirs:
@$(NOP)
ifeq ($(wildcard $(INSTALL_LOCATION_BIN)/*),)
@@ -49,6 +57,16 @@ ifeq ($(wildcard $(INSTALL_LOCATION_LIB)/*),)
$(RMDIR) $(INSTALL_LOCATION_LIB)
endif
else
#
# Using a disabled rule aborts
#
cleandirs distclean uninstall realuninstall archuninstall:
$(error Target '$@' not available in a submodule)
endif # DISABLE_TOP_RULES
help:
@echo "Usage: gnumake [options] [target] ..."
@@ -64,33 +82,39 @@ help:
@echo " Cannot be used within an O.<arch> dir"
@echo " rebuild - Same as clean install"
@echo " archclean - Removes O.<arch> dirs but not O.Common dir"
@echo " runtests - Run self-tests, summarize results"
@echo " depclean - Removes .d files from all O.<arch> dirs."
@echo " cvsclean - Removes backup files etc. from all dirs below"
@echo " runtests - Run self-tests, summarize results immediately"
@echo " tapfiles - Run self-tests, save to O.<arch>/*.tap files"
@echo " test-results - Summarize all O.<arch>/*.tap files"
@echo " clean-tests - Removes all O.<arch>/*.tap files"
@echo "\"Partial\" build targets supported by Makefiles:"
@echo " host - Builds and installs $(EPICS_HOST_ARCH) only."
@echo " inc$(DIVIDER)<arch> - Installs <arch> only header files."
@echo " build$(DIVIDER)<arch> - Builds and installs <arch> only."
@echo " install$(DIVIDER)<arch> - Builds and installs <arch> only."
@echo " clean$(DIVIDER)<arch> - Cleans <arch> binaries in O.<arch> dirs only."
@echo " uninstall$(DIVIDER)<arch> - Remove bin & lib directories for <arch> only."
@echo "Targets supported by top level Makefile:"
ifndef DISABLE_TOP_RULES
@echo " archuninstall - Remove bin & lib directories created by this hostarch."
@echo " uninstall$(DIVIDER)<arch> - Remove bin & lib directories for <arch> only."
@echo " uninstall - Remove install directories created by this hostarch."
@echo " realuninstall - Removes ALL install dirs"
@echo " distclean - Same as realclean cvsclean realuninstall."
@echo " depclean - Removes all .d files from O.<arch> directories."
@echo " cvsclean - Removes cvs .#* files in all dirs of directory tree"
endif
@echo " help - Prints this list of valid make targets "
@echo "Indiv. object targets are supported by O.<arch> level Makefile .e.g"
@echo "Object targets are supported by the O.<arch> level Makefile .e.g"
@echo " xxxRecord.o"
.PHONY: cleandirs distclean cvsclean depclean
.PHONY: cleandirs distclean uninstall help
.PHONY: realuninstall archuninstall uninstallDirs
.PHONY: uninstall help
# Include <top>/cfg/TOP_RULES* files from tops defined in RELEASE* files
#
RELEASE_CFG_TOP_RULES = $(foreach top, $(RELEASE_TOPS), \
$(wildcard $($(top))/cfg/TOP_RULES*))
ifneq ($(RELEASE_CFG_TOP_RULES),)
include $(RELEASE_CFG_TOP_RULES)
ifndef DISABLE_TOP_RULES
# Include <top>/cfg/TOP_RULES* files from tops defined in RELEASE* files
#
RELEASE_CFG_TOP_RULES = $(foreach top, $(RELEASE_TOPS), \
$(wildcard $($(top))/cfg/TOP_RULES*))
ifneq ($(RELEASE_CFG_TOP_RULES),)
include $(RELEASE_CFG_TOP_RULES)
endif
endif

View File

@@ -65,7 +65,7 @@ GNU = NO
#
# Darwin shared libraries
#
SHRLIB_LDFLAGS = -dynamiclib -undefined dynamic_lookup \
SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined dynamic_lookup \
-install_name $(shell $(FULLPATHNAME) $(INSTALL_LIB))/$@ \
$(addprefix -compatibility_version , $(SHRLIB_VERSION)) \
$(addprefix -current_version , $(SHRLIB_VERSION))

View File

@@ -4,17 +4,17 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Known Problems in EPICS 7.0.2</title>
<title>Known Problems in EPICS 7.0.3</title>
</head>
<body>
<h1 style="text-align: center">EPICS 7.0.2: Known Problems</h1>
<h1 style="text-align: center">EPICS 7.0.3: Known Problems</h1>
<p>Any patch files linked below should be applied at the root of the
base-7.0.2 tree. Download them, then use the GNU Patch program as
base-7.0.3 tree. Download them, then use the GNU Patch program as
follows:</p>
<blockquote><pre>% <b>cd <i>/path/to/</i>base-7.0.2</b>
<blockquote><pre>% <b>cd <i>/path/to/</i>base-7.0.3</b>
% <b>patch -p1 &lt; <i>/path/to/</i>file.patch</b></pre></blockquote>
<p>The following problems were known by the developers at the time of this

View File

@@ -1,24 +1,24 @@
Installation Instructions
EPICS Base Release 7.0.2
EPICS Base Release 7.0.3
--------------------------------------------------------------------------
Table of Contents
* What is EPICS base?
* What is new in this release?
* Copyright
* Supported platforms
* Supported compilers
* Software requirements
* Host system storage requirements
* Documentation
* Directory Structure
* Build related components
* Building EPICS base (Unix and Win32)
* Example application and extension
* Multiple host platforms
* What is EPICS base?
* What is new in this release?
* Copyright
* Supported platforms
* Supported compilers
* Software requirements
* Host system storage requirements
* Documentation
* Directory Structure
* Build related components
* Building EPICS base (Unix and Win32)
* Example application and extension
* Multiple host platforms
--------------------------------------------------------------------------

View File

@@ -9,7 +9,7 @@
<BODY>
<CENTER>
<H1>Installation Instructions</H1>
<H2>EPICS Base Release 7.0.2</H2><BR>
<H2>EPICS Base Release 7.0.3</H2><BR>
</CENTER>
<HR>
<H3> Table of Contents</H3>

View File

@@ -20,8 +20,7 @@ release.</p>
which should also be read to understand what has changed since an earlier
release.</p>
<h1 align="center">EPICS Release 7.0.2</h1>
<h1 align="center">EPICS Release 7.0.3</h1>
<!-- Insert new items immediately below this template ...
@@ -31,6 +30,124 @@ release.</p>
-->
<h3>epicsTimeGetCurrent() optimization</h3>
<p>Add a fast path to epicsTimeGetCurrent() and related calls
in the common case where only the default OS current time provider is
registered. This path does not take the global mutex guarding
the time providers list, potentially reducing lock contention.</p>
<h3>dbEvent tweak Queue size</h3>
<p>The size of the queue used by dbEvent to push monitor updates
has been slightly increased based on DBR_TIME_DOUBLE to better fill
an ethernet frame. This may result in slightly fewer, but larger
frames being sent.</p>
<h3>mbbo/mbbiDirect number of bits as precision</h3>
<p>Report NOBT as &quot;precision&quot; through the dbAccess API.
This is not accessible through CA, but is planned to be used through
QSRV.</p>
<h1 align="center">EPICS Release 7.0.2.2</h1>
<h3>Build System changes</h3>
<ul>
<li>The GNUmake build targets <tt>cvsclean</tt> and <tt>depclean</tt> are now
available from any directory; previously they were only available from
application top directories.</li>
<li>The approach that EPICS Base uses for building submodules inside the parent
module looks useful for support modules too. The rules for building submodules
have been modified and extracted into a new <tt>RULES_MODULES</tt> file, so a
support module will be able to use them too without having to copy them into its
own <tt>modules/Makefile</tt>. There are some specific requirements that support
modules and their submodules must follow, which are described as comments in the
new <tt>base/configure/RULES_MODULES</tt> file itself.</li>
</ul>
<h3>EPICS_BASE_VERSION Update Policy change</h3>
<p>In the past, a build of EPICS using sources checked out from the repository
branch between official releases would have shown the version number of the
previous release, followed by a <q>-DEV</q> suffix, for example
<q>7.0.2.1-DEV</q>.</p>
<p>The policy that controls when the number gets updated has been changed, and
now immediately after a release has been tagged the version number will be
updated to the next patch release version, plus the <q>-DEV</q> suffix as
before. Thus following <q>7.0.2.2</q> the version number will show as
<q>7.0.2.3-DEV</q>. This does not require the next official release to be
numbered <q>7.0.2.3</q> though, it could become <q>7.0.3</q> or even
<q>7.1.0</q> if the changes incorporated into it are more substantial than bug
fixes.</p>
<h3>Drop CLOCK_MONOTONIC_RAW from posix/osdMonotonic.c</h3>
<p>Turns out this is ~10x slower to query than CLOCK_MONOTONIC.</p>
<h1 align="center">EPICS Release 7.0.2.1</h1>
<h3>Linking shared libraries on macOS</h3>
<p>The linker flag <tt>-flat_namespace</tt> has been restored for creating
shared libraries, although not for loadable libraries (bundles). This was
required for building using the latest versions of Apple XCode.</p>
<h3>Fix DB_LINK loop breaking</h3>
<p>A regression was introduced in 7.0.2 which caused record chains with loops to
be incorrectly broken. Processing should be skipped when a DB_LINK with Process
Passive (PP) closes a loop to a synchronous record.</p>
<p>Instead in 7.0.2 the targeted record would be processed if processing began
with a remote action (or some other caller of <tt>dbPutField()</tt>). This would
result in the loop running a second time. The loop would be broken on the second
iteration.</p>
<p><a href="https://bugs.launchpad.net/epics-base/+bug/1809570">See lp:
#1809570</a></p>
<h3>Old dbStaticLib APIs removed</h3>
<p>Support for some obsolete dbStaticLib Database Configuration Tool (DCT) APIs
was removed some time ago, but vestiges of them still remained. The following
routines and macros and have now finally been removed:</p>
<ul>
<li>int dbGetFieldType(DBENTRY *pdbentry)</li>
<li>int dbGetLinkType(DBENTRY *pdbentry)</li>
<li>DCT_STRING</li>
<li>DCT_INTEGER</li>
<li>DCT_REAL</li>
<li>DCT_MENU</li>
<li>DCT_MENUFORM</li>
<li>DCT_INLINK</li>
<li>DCT_OUTLINK</li>
<li>DCT_FWDLINK</li>
<li>DCT_NOACCESS</li>
<li>DCT_LINK_CONSTANT</li>
<li>DCT_LINK_FORM</li>
<li>DCT_LINK_PV</li>
</ul>
<h3>Fix for <tt>dbhcr</tt> before <tt>iocInit</tt></h3>
<p>The <tt>dbhcr</tt> command used to work before <tt>iocInit</tt> as well as
afterwards. It displays all records that have hardware addresses (VME_IO,
CAMAC_IO, GPIB_IO, INST_IO etc.) but stopped working if run before iocInit due
to the rewrite of the link address parser code in dbStaticLib. This release
fixes that issue, although in some cases the output may be slightly different
than it used to be.</p>
<h1 align="center">EPICS Release 7.0.2</h1>
<h3>Launchpad Bugs</h3>
<p>The list of tracked bugs fixed in this release can be found on the
@@ -920,6 +1037,24 @@ of its CALLBACK objects.</p>
<!-- Insert inherited items immediately below here ... -->
<h3>Cleaning up with Multiple CA contexts in a Process</h3>
<p>Bruno Martins reported a problem with the CA client library at shutdown in a
process that uses multiple CA client contexts. The first context that triggers
the CA client exit handler prevents any others from being able to clean up
because it resets the ID of an internal epicsThreadPrivate variable which is
shared by all clients. This action has been removed from the client library,
which makes cleanup of clients like this possible.</p>
<h3>Perl CA bindings fixed for macOS Mojave</h3>
<p>Apple removed some Perl header files from macOS Mojave that were available
in their SDK, requiring a change to the include paths used when compiling the
CA bindings. The new version should build on new and older macOS versions, and
these changes may also help other targets that have an incomplete installation
of Perl (the build will continue after printing a warning that the Perl CA
bindings could not be built).</p>
<h3>Routine <tt>epicsTempName()</tt> removed from libCom</h3>
<p>This routine was a simple wrapper around the C89 function <tt>tmpnam()</tt>

View File

@@ -37,6 +37,8 @@ that should be performed when creating production releases of EPICS Base.</p>
<h3>The Release Process</h3>
<h4>Full Process</h4>
<p>The version released on the Feature Freeze date is designated the first
pre-release, <tt>-pre1</tt>. The first release candidate <tt>-rc1</tt> is the
first version that has undergone testing by the developers and has shown no
@@ -49,6 +51,13 @@ release candidate has been available for 2 weeks without any new problems being
reported or major changes having to be committed, the final release can be
made.</p>
<h4>Short Process for Patch Releases</h4>
<p>The Patch Release date and its scope are agreed upon a few weeks ahead of the
release. If no blocking issues are raised, the release is made by the Release
Manager on or as soon as possible after that date, following the steps below
starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
<h3>Roles</h3>
<p>The following roles are used below:</p>
@@ -64,6 +73,7 @@ made.</p>
<dd>Responsible for the EPICS website</dd>
</dl>
<form>
<table border="1" width="100%">
<tbody>
<tr>
@@ -116,80 +126,81 @@ made.</p>
<tr>
<td>&nbsp;</td>
<td>Website Manager</td>
<td>Create a release milestone on Launchpad if necessary and set the
expected release date. Note that pre-release and release-candidate
versions do not appear on Launchpad, only the final release.</td>
<td>Create a release milestone on Launchpad. If a target release date is
known set "Date Targeted" to the expected release date. Note that
pre-release and release-candidate versions should not get Launchpad
milestones, only the final release.</td>
</tr>
<tr>
<th colspan="3">Creating pre-release and release-candidate versions</th>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<!-- Submodules... -->
<td>Edit and commit changes to the EPICS version number file
configure/CONFIG_BASE_VERSION.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Tag the module in Git, using these tag conventions:
<ul>
<li>
<tt>R7.0.2-pre<i>n</i></tt>
<tt>R7.0.3-pre<i>n</i></tt>
&mdash; pre-release tag
</li>
<li>
<tt>R7.0.2-rc<i>n</i></tt>
<tt>R7.0.3-rc<i>n</i></tt>
&mdash; release candidate tag
</li>
</ul>
<blockquote><tt>
cd base-7.0<br />
git tag -m 'ANJ: Tagged for 7.0.2-rc1' R7.0.2-rc1
git tag -m 'ANJ: Tagged for 7.0.3-rc1' R7.0.3-rc1
</tt></blockquote>
Note that submodules must <em>not</em> be tagged with the version used
for the top-level, they each have their own separate version numbers
that are only tagged at the final release.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Export the tagged version into a tarfile. The <tt>make-tar.sh</tt>
script generates a gzipped tarfile directly from the tag, excluding the
files and directories that are only used for continuous integration:
<blockquote><tt>
cd base-7.0<br />
./.tools/make-tar.sh R7.0.2-rc1 base-7.0.2-rc1.tar.gz base-7.0.2-rc1/
./.tools/make-tar.sh R7.0.3-rc1 base-7.0.3-rc1.tar.gz base-7.0.3-rc1/
</tt></blockquote>
Create a GPG signature file of the tarfile as follows:
<blockquote><tt>
gpg --armor --sign --detach-sig base-7.0.2-rc1.tar.gz
gpg --armor --sign --detach-sig base-7.0.3-rc1.tar.gz
</tt></blockquote>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Test the tarfile by extracting its contents and building it on at
least one supported platform.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Website Manager</td>
<td>Copy the tarfile and its signature to the Base download area of the
website and add the new files to the website Base download index
page.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Website Manager</td>
<td>Create or update a website subdirectory to hold the release
documentation, and copy in selected files from the base/documentation
and base/html directories of the tarfile.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Website Manager</td>
<td>Create or modify the webpage for the new release with links to the
release documents and tar file. Pre-release and release-candidate
@@ -249,10 +260,11 @@ made.</p>
</tr>
<tr>
<th colspan="3">Release Approval</th>
<th colspan="3" id="ReleaseApproval">
Release Approval</th>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Obtain a positive <q>Ok to release</q> from all platform developers
once a release candidate version has gone for 2 weeks without any major
@@ -262,7 +274,7 @@ made.</p>
<th colspan="3">Creating the final release version</th>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>
<p>For each external submodule, check if the module's release version
@@ -274,7 +286,7 @@ made.</p>
commit.</p></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Edit and commit changes to the EPICS Base version number file and
the embedded module version files:
@@ -286,33 +298,33 @@ made.</p>
</ul></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Tag the epics-base module in Git:
<blockquote><tt>
cd base-7.0<br />
git tag -m 'ANJ: Tagged for 7.0.2' R7.0.2</i>
git tag -m 'ANJ: Tagged for 7.0.3' R7.0.3</i>
</tt></blockquote>
Don't push the new tag to the Launchpad repository yet.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Export the tagged version into a tarfile. The <tt>make-tar.sh</tt>
script generates a gzipped tarfile directly from the tag, excluding the
files and directories that are only used for continuous integration:
<blockquote><tt>
cd base-7.0<br />
./.tools/make-tar.sh R7.0.2 base-7.0.2.tar.gz base-7.0.2/
./.tools/make-tar.sh R7.0.3 base-7.0.3.tar.gz base-7.0.3/
</tt></blockquote>
Create a GPG signature file of the tarfile as follows:
<blockquote><tt>
gpg --armor --sign --detach-sig base-7.0.2.tar.gz
gpg --armor --sign --detach-sig base-7.0.3.tar.gz
</tt></blockquote>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Test the tar file by extracting its contents and building it on at
least one supported platform. When this succeeds the new git tag can be
@@ -323,63 +335,109 @@ made.</p>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Edit and commit changes to the EPICS Base version number file and
the embedded module version files:
<ul>
<li>configure/CONFIG_BASE_VERSION </li>
<li>modules/libcom/configure/CONFIG_LIBCOM_VERSION</li>
<li>modules/ca/configure/CONFIG_CA_VERSION</li>
<li>modules/database/configure/CONFIG_DATABASE_VERSION</li>
</ul>
Version numbers should be set to the next expected patch release,
with a "-DEV" tag added (where applicable).
</td>
</tr>
<tr>
<th colspan="3">Publish to epics.anl.gov</th>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Copy the tarfile and its signature to the Base download area of the
website.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>Release Manager</td>
<td>Find all Launchpad bug reports with the status Fix Committed which
have been fixed in this release and mark them Fix Released.</td>
</tr>
<tr>
<th colspan="3">Publish and Announce it</th>
</tr>
<tr>
<td>&nbsp;</td>
<td>Website Manager</td>
<td>Upload the tar file and its <tt>.asc</tt> signature file to the
Launchpad milestone for this release version.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Website Manager</td>
<td>Update the website subdirectory that holds the release
documentation, and copy in the files from the base/documentation
directory of the tarfile.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Website Manager</td>
<td>Update the webpage for the new release with links to the release
documents and tar file.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Website Manager</td>
<td>Add the new release tar file to the website Base download index
page.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Website Manager</td>
<td>Link to the release webpage from other relevent areas of the
website - update front page and sidebars.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox"></td>
<td>Website Manager</td>
<td>Add an entry to the website News page, linking to the new version
webpage.</td>
</tr>
<tr>
<td>&nbsp;</td>
<th colspan="3">Publish to epics-controls</th>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Website Manager</td>
<td>Upload the tar file and its <tt>.asc</tt> signature file to the
epics-controls web-server [ToDo: ssh-key, location]</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Website Manager</td>
<td>Follow instructions on
<a href="https://epics-controls.org/resources-and-support/documents/epics-website-documentation/adding-a-page-for-a-new-release/">
Add a page for a new release</a> to create a new release webpage (not
required for a patch release though, just edit the existing page).</td>
</tr>
<tr>
<th colspan="3">Publish to Launchpad</th>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Website Manager</td>
<td>Go to the Launchpad milestone for this release. Click the Create
release button and add the release date. Put a URL for the release page
in the Release notes box, and click the Create release button. Upload
the tar file and its <tt>.asc</tt> signature file to the new Launchpad
release page.</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Find all Launchpad bug reports with the status Fix Committed which
have been fixed in this release and mark them Fix Released.</td>
</tr>
<tr>
<th colspan="3">Make Announcement</th>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Announce the release on the tech-talk mailing list.</td>
</tr>
</tbody>
</table>
</form>
</body>
</html>

View File

@@ -3,8 +3,11 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
# When building submodules, this should always be true:
INSTALL_LOCATION = $(EPICS_BASE)
# The name our submodules know us by:
PARENT_MODULE = EPICS_BASE
# Stop submodules from installing their configuration files:
# When building submodules, this should always be true:
INSTALL_LOCATION := $($(PARENT_MODULE))
# Stop submodules installing their configure/ files into our area
CONFIG_INSTALLS =

View File

@@ -5,6 +5,7 @@
TOP = ..
include $(TOP)/configure/CONFIG
include CONFIG_SITE.local
# Submodules for bundle build
SUBMODULES += libcom
@@ -39,20 +40,4 @@ example_DEPEND_DIRS = pva2pva pvaClient
# Allow sites to add extra submodules
-include Makefile.local
# Add only checked-out submodules to DIRS
DIRS += $(subst /Makefile,,$(wildcard $(addsuffix /Makefile, $(SUBMODULES))))
include $(TOP)/configure/RULES_DIRS
INSTALL_LOCATION_ABS := $(abspath $(INSTALL_LOCATION))
RELEASE_LOCAL := RELEASE.$(EPICS_HOST_ARCH).local
# Ensure that RELEASE.<host>.local exists before doing anything else
all host $(DIRS) $(ARCHS) $(ACTIONS) $(dirActionTargets) $(dirArchTargets) \
$(dirActionArchTargets) $(actionArchTargets): | $(RELEASE_LOCAL)
$(RELEASE_LOCAL):
$(ECHO) Creating $@, EPICS_BASE = $(INSTALL_LOCATION_ABS)
@echo EPICS_BASE = $(INSTALL_LOCATION_ABS)> $@
realclean:
$(RM) $(RELEASE_LOCAL)
include $(TOP)/configure/RULES_MODULES

View File

@@ -1,4 +1,4 @@
EPICS_CA_MAJOR_VERSION = 4
EPICS_CA_MINOR_VERSION = 13
EPICS_CA_MAINTENANCE_VERSION = 3
EPICS_CA_MAINTENANCE_VERSION = 4
EPICS_CA_DEVELOPMENT_FLAG = 0

View File

@@ -3143,7 +3143,8 @@ indicating the current state of the channel.</p>
<dl>
<dt><code>COUNT</code></dt>
<dd>The element count to be read from the specified channel. A count of
zero means use the current element count from the server.</dd>
zero means use the current element count from the server, effectively
resulting in a variable size array subscription.</dd>
</dl>
<dl>
<dt><code>CHID</code></dt>

View File

@@ -45,20 +45,12 @@ static epicsThreadOnceId cacOnce = EPICS_THREAD_ONCE_INIT;
const unsigned ca_client_context :: flushBlockThreshold = 0x58000;
extern "C" void cacExitHandler ( void *)
{
epicsThreadPrivateDelete ( caClientCallbackThreadId );
caClientCallbackThreadId = 0;
delete ca_client_context::pDefaultServiceInstallMutex;
}
// runs once only for each process
extern "C" void cacOnceFunc ( void * )
{
caClientCallbackThreadId = epicsThreadPrivateCreate ();
assert ( caClientCallbackThreadId );
ca_client_context::pDefaultServiceInstallMutex = newEpicsMutex;
epicsAtExit ( cacExitHandler,0 );
}
extern epicsThreadPrivateId caClientContextId;
@@ -67,6 +59,8 @@ cacService * ca_client_context::pDefaultService = 0;
epicsMutex * ca_client_context::pDefaultServiceInstallMutex;
ca_client_context::ca_client_context ( bool enablePreemptiveCallback ) :
mutex(__FILE__, __LINE__),
cbMutex(__FILE__, __LINE__),
createdByThread ( epicsThreadGetIdSelf () ),
ca_exception_func ( 0 ), ca_exception_arg ( 0 ),
pVPrintfFunc ( errlogVprintf ), fdRegFunc ( 0 ), fdRegArg ( 0 ),

View File

@@ -289,7 +289,6 @@ private:
};
extern "C" void cacOnceFunc ( void * );
extern "C" void cacExitHandler ( void *);
struct ca_client_context : public cacContextNotify
{
@@ -429,7 +428,6 @@ private:
ca_client_context & operator = ( const ca_client_context & );
friend void cacOnceFunc ( void * );
friend void cacExitHandler ( void *);
static cacService * pDefaultService;
static epicsMutex * pDefaultServiceInstallMutex;
static const unsigned flushBlockThreshold;

View File

@@ -37,6 +37,7 @@ repeaterSubscribeTimer::repeaterSubscribeTimer (
epicsMutex & cbMutexIn, cacContextNotify & ctxNotifyIn ) :
timer ( queueIn.createTimer () ), iiu ( iiuIn ),
cbMutex ( cbMutexIn ),ctxNotify ( ctxNotifyIn ),
stateMutex(__FILE__, __LINE__),
attempts ( 0 ), registered ( false ), once ( false )
{
}

View File

@@ -8,6 +8,18 @@
TOP = ../..
include $(TOP)/configure/CONFIG
ifdef T_A
PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version)
PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname)
PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME)
PERL_ARCHLIB := $(shell $(PERL) ../perlConfig.pl archlib)
PERL_h = $(PERL_ARCHLIB)/CORE/perl.h
EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils
PERLBIN := $(shell $(PERL) ../perlConfig.pl bin)
XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp))
# Special settings for Darwin:
ifeq ($(OS_CLASS),Darwin)
# Use hdepends command (not GNU compiler flags)
@@ -18,22 +30,23 @@ ifeq ($(OS_CLASS),Darwin)
# Perl loadable libraries on Darwin have funny names
LOADABLE_SHRLIB_PREFIX =
LOADABLE_SHRLIB_SUFFIX = .$(shell $(PERL) ../perlConfig.pl dlext)
ifeq ($(wildcard $(PERL_h)),)
# Perl's headers moved in Mojave
SDK_PATH := $(shell xcodebuild -version -sdk macosx Path)
PERL_ARCHLIB := $(SDK_PATH)/$(PERL_ARCHLIB)
endif
endif
ifdef T_A
PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version)
PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname)
PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME)
EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils
PERLBIN := $(shell $(PERL) ../perlConfig.pl bin)
XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp))
ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!)
ifeq ($(strip $(XSUBPP)),)
$(warning Perl's xsubpp program was not found.)
$(warning The Perl CA module will not be built.)
else
ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!)
ifeq ($(wildcard $(PERL_h)),)
$(warning Perl's C header files were not found.)
$(warning The Perl CA module will not be built.)
else
ifeq ($(findstring $(OS_CLASS),WIN32 cygwin32),) # Doesn't build on WIN32
LOADABLE_LIBRARY_HOST = Cap5
@@ -50,10 +63,11 @@ endif
endif
endif
endif
endif
Cap5_SRCS = Cap5.xs
Cap5_LIBS = ca Com
Cap5_INCLUDES = -I$(shell $(PERL) ../perlConfig.pl archlib)/CORE
Cap5_INCLUDES = -I$(PERL_ARCHLIB)/CORE
Cap5_CFLAGS = $(shell $(PERL) ../perlConfig.pl ccflags)
CLEANS += Cap5.c

View File

@@ -1,4 +1,4 @@
EPICS_DATABASE_MAJOR_VERSION = 3
EPICS_DATABASE_MINOR_VERSION = 17
EPICS_DATABASE_MAINTENANCE_VERSION = 2
EPICS_DATABASE_MAINTENANCE_VERSION = 4
EPICS_DATABASE_DEVELOPMENT_FLAG = 0

View File

@@ -55,7 +55,7 @@ typedef struct cbQueueSet {
epicsRingPointerId queue;
int queueOverflow;
int queueOverflows;
int shutdown;
int shutdown; // use atomic
int threadsConfigured;
int threadsRunning;
} cbQueueSet;
@@ -71,12 +71,15 @@ epicsExportAddress(int,callbackParallelThreadsDefault);
/* Timer for Delayed Requests */
static epicsTimerQueueId timerQueue;
/* Shutdown handling */
enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
static volatile enum ctl cbCtl;
static epicsEventId startStopEvent;
enum cbState_t {
cbInit, /* before callbackInit() and after callbackCleanup() */
cbRun, /* after callbackInit() and before callbackStop() */
cbStop, /* after callbackStop() and before callbackCleanup() */
};
static int callbackIsInit;
static int cbState; // holdscbState_t, use atomic ops
static epicsEventId startStopEvent;
/* Static data */
static char *threadNamePrefix[NUM_CALLBACK_PRIORITIES] = {
@@ -96,7 +99,7 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2};
int callbackSetQueueSize(int size)
{
if (callbackIsInit) {
if (epicsAtomicGetIntT(&cbState)!=cbInit) {
fprintf(stderr, "Callback system already initialized\n");
return -1;
}
@@ -107,7 +110,7 @@ int callbackSetQueueSize(int size)
int callbackQueueStatus(const int reset, callbackQueueStats *result)
{
int ret;
if (!callbackIsInit) return -1;
if (epicsAtomicGetIntT(&cbState)==cbInit) return -1;
if (result) {
int prio;
result->size = callbackQueueSize;
@@ -151,7 +154,7 @@ void callbackQueueShow(const int reset)
int callbackParallelThreads(int count, const char *prio)
{
if (callbackIsInit) {
if (epicsAtomicGetIntT(&cbState)!=cbInit) {
fprintf(stderr, "Callback system already initialized\n");
return -1;
}
@@ -207,7 +210,7 @@ static void callbackTask(void *arg)
taskwdInsert(0, NULL, NULL);
epicsEventSignal(startStopEvent);
while(!mySet->shutdown) {
while(!epicsAtomicGetIntT(&mySet->shutdown)) {
void *ptr;
if (epicsRingPointerIsEmpty(mySet->queue))
epicsEventMustWait(mySet->semWakeUp);
@@ -230,11 +233,10 @@ void callbackStop(void)
{
int i;
if (cbCtl == ctlExit) return;
cbCtl = ctlExit;
if (epicsAtomicCmpAndSwapIntT(&cbState, cbRun, cbStop)!=cbRun) return;
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
callbackQueue[i].shutdown = 1;
epicsAtomicSetIntT(&callbackQueue[i].shutdown, 1);
epicsEventSignal(callbackQueue[i].semWakeUp);
}
@@ -252,6 +254,10 @@ void callbackCleanup(void)
{
int i;
if(epicsAtomicCmpAndSwapIntT(&cbState, cbStop, cbInit)!=cbStop) {
fprintf(stderr, "callbackCleanup() but not stopped\n");
}
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
cbQueueSet *mySet = &callbackQueue[i];
@@ -261,7 +267,6 @@ void callbackCleanup(void)
}
epicsTimerQueueRelease(timerQueue);
callbackIsInit = 0;
memset(callbackQueue, 0, sizeof(callbackQueue));
}
@@ -271,15 +276,14 @@ void callbackInit(void)
int j;
char threadName[32];
if (callbackIsInit) {
errlogMessage("Warning: callbackInit called again before callbackCleanup\n");
if (epicsAtomicCmpAndSwapIntT(&cbState, cbInit, cbRun)!=cbInit) {
fprintf(stderr, "Warning: callbackInit called again before callbackCleanup\n");
return;
}
callbackIsInit = 1;
if(!startStopEvent)
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
cbCtl = ctlRun;
timerQueue = epicsTimerQueueAllocate(0, epicsThreadPriorityScanHigh);
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {

View File

@@ -601,6 +601,31 @@ all_done:
return status;
}
long dbEntryToAddr(const DBENTRY *pdbentry, DBADDR *paddr)
{
dbFldDes *pflddes = pdbentry->pflddes;
short dbfType = pflddes->field_type;
paddr->precord = pdbentry->precnode->precord;
paddr->pfield = pdbentry->pfield;
paddr->pfldDes = pflddes;
paddr->no_elements = 1;
paddr->field_type = dbfType;
paddr->field_size = pflddes->size;
paddr->special = pflddes->special;
paddr->dbr_field_type = mapDBFToDBR[dbfType];
if (paddr->special == SPC_DBADDR) {
const rset *prset = dbGetRset(paddr);
/* Let record type modify paddr */
if (prset && prset->cvt_dbaddr) {
return prset->cvt_dbaddr(paddr);
}
}
return 0;
}
/*
* Fill out a database structure (*paddr) for
* a record given by the name "pname."
@@ -611,9 +636,7 @@ all_done:
long dbNameToAddr(const char *pname, DBADDR *paddr)
{
DBENTRY dbEntry;
dbFldDes *pflddes;
long status = 0;
short dbfType;
if (!pname || !*pname || !pdbbase)
return S_db_notFound;
@@ -628,46 +651,28 @@ long dbNameToAddr(const char *pname, DBADDR *paddr)
status = dbGetAttributePart(&dbEntry, &pname);
if (status) goto finish;
pflddes = dbEntry.pflddes;
dbfType = pflddes->field_type;
paddr->precord = dbEntry.precnode->precord;
paddr->pfield = dbEntry.pfield;
paddr->pfldDes = pflddes;
paddr->no_elements = 1;
paddr->field_type = dbfType;
paddr->field_size = pflddes->size;
paddr->special = pflddes->special;
paddr->dbr_field_type = mapDBFToDBR[dbfType];
if (paddr->special == SPC_DBADDR) {
rset *prset = dbGetRset(paddr);
/* Let record type modify paddr */
if (prset && prset->cvt_dbaddr) {
status = prset->cvt_dbaddr(paddr);
if (status)
goto finish;
dbfType = paddr->field_type;
}
}
status = dbEntryToAddr(&dbEntry, paddr);
if (status) goto finish;
/* Handle field modifiers */
if (*pname++ == '$') {
short dbfType = paddr->field_type;
/* Some field types can be accessed as char arrays */
if (dbfType == DBF_STRING) {
paddr->no_elements = paddr->field_size;
paddr->field_type = DBF_CHAR;
paddr->field_size = 1;
paddr->dbr_field_type = DBR_CHAR;
} else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
}
else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
/* Clients see a char array, but keep original dbfType */
paddr->no_elements = PVLINK_STRINGSZ;
paddr->field_size = 1;
paddr->dbr_field_type = DBR_CHAR;
} else {
}
else {
status = S_dbLib_fieldNotFound;
goto finish;
}
}
@@ -679,7 +684,7 @@ finish:
void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry)
{
struct dbCommon *prec = paddr->precord;
dbCommonPvt *ppvt = CONTAINER(prec, dbCommonPvt, common);
dbCommonPvt *ppvt = dbRec2Pvt(prec);
memset((char *)pdbentry,'\0',sizeof(DBENTRY));
@@ -693,7 +698,7 @@ void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry)
void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry)
{
dbCommonPvt *ppvt = CONTAINER(prec, dbCommonPvt, common);
dbCommonPvt *ppvt = dbRec2Pvt(prec);
memset((char *)pdbentry,'\0',sizeof(DBENTRY));

View File

@@ -204,6 +204,8 @@ struct dbr_alDouble {DBRalDouble};
#define S_db_notInit (M_dbAccess|67) /*Not initialized*/
#define S_db_bufFull (M_dbAccess|68) /*Buffer full*/
struct dbEntry;
epicsShareFunc long dbPutSpecial(struct dbAddr *paddr,int pass);
epicsShareFunc rset * dbGetRset(const struct dbAddr *paddr);
epicsShareFunc long dbPutAttribute(
@@ -213,8 +215,29 @@ epicsShareFunc int dbGetFieldIndex(const struct dbAddr *paddr);
epicsShareFunc long dbScanPassive(
struct dbCommon *pfrom,struct dbCommon *pto);
epicsShareFunc long dbProcess(struct dbCommon *precord);
epicsShareFunc long dbNameToAddr(
const char *pname,struct dbAddr *);
epicsShareFunc long dbNameToAddr(const char *pname, struct dbAddr *paddr);
/** Initialize DBADDR from a dbEntry
* Also handles SPC_DBADDR processing. This is really an internal
* routine for use by dbNameToAddr() and dbChannelCreate().
*/
epicsShareFunc long dbEntryToAddr(const struct dbEntry *pdbentry,
struct dbAddr *paddr);
/** Initialize DBENTRY from a valid dbAddr*
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
* and finally dbFollowAlias().
*/
epicsShareFunc void dbInitEntryFromAddr(struct dbAddr *paddr,
struct dbEntry *pdbentry);
/** Initialize DBENTRY from a valid record (dbCommon*)
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
* and finally dbFollowAlias() when no field is specified.
*/
epicsShareFunc void dbInitEntryFromRecord(struct dbCommon *prec,
struct dbEntry *pdbentry);
epicsShareFunc devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp);
epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset);
epicsShareFunc long dbGetField(

View File

@@ -473,9 +473,7 @@ dbChannel * dbChannelCreate(const char *name)
dbChannel *chan = NULL;
char *cname;
dbAddr *paddr;
dbFldDes *pflddes;
long status;
short dbfType;
if (!name || !*name || !pdbbase)
return NULL;
@@ -498,32 +496,14 @@ dbChannel * dbChannelCreate(const char *name)
ellInit(&chan->post_chain);
paddr = &chan->addr;
pflddes = dbEntry.pflddes;
dbfType = pflddes->field_type;
paddr->precord = dbEntry.precnode->precord;
paddr->pfield = dbEntry.pfield;
paddr->pfldDes = pflddes;
paddr->no_elements = 1;
paddr->field_type = dbfType;
paddr->field_size = pflddes->size;
paddr->special = pflddes->special;
paddr->dbr_field_type = mapDBFToDBR[dbfType];
if (paddr->special == SPC_DBADDR) {
rset *prset = dbGetRset(paddr);
/* Let record type modify paddr */
if (prset && prset->cvt_dbaddr) {
status = prset->cvt_dbaddr(paddr);
if (status)
goto finish;
dbfType = paddr->field_type;
}
}
status = dbEntryToAddr(&dbEntry, paddr);
if (status)
goto finish;
/* Handle field modifiers */
if (*pname) {
short dbfType = paddr->field_type;
if (*pname == '$') {
/* Some field types can be accessed as char arrays */
if (dbfType == DBF_STRING) {
@@ -531,12 +511,14 @@ dbChannel * dbChannelCreate(const char *name)
paddr->field_type = DBF_CHAR;
paddr->field_size = 1;
paddr->dbr_field_type = DBR_CHAR;
} else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
}
else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
/* Clients see a char array, but keep original dbfType */
paddr->no_elements = PVLINK_STRINGSZ;
paddr->field_size = 1;
paddr->dbr_field_type = DBR_CHAR;
} else {
}
else {
status = S_dbLib_fieldNotFound;
goto finish;
}

View File

@@ -1,14 +1,27 @@
#ifndef DBCOMMONPVT_H
#define DBCOMMONPVT_H
#include <compilerDependencies.h>
#include <dbDefs.h>
#include "dbCommon.h"
struct epicsThreadOSD;
/** Base internal additional information for every record
*/
typedef struct dbCommonPvt {
struct dbRecordNode *recnode;
/* Thread which is currently processing this record */
struct epicsThreadOSD* procThread;
struct dbCommon common;
} dbCommonPvt;
static EPICS_ALWAYS_INLINE
dbCommonPvt* dbRec2Pvt(struct dbCommon *prec)
{
return CONTAINER(prec, dbCommonPvt, common);
}
#endif // DBCOMMONPVT_H

View File

@@ -56,7 +56,7 @@
#include "dbAddr.h"
#include "dbBase.h"
#include "dbBkpt.h"
#include "dbCommon.h"
#include "dbCommonPvt.h"
#include "dbConvertFast.h"
#include "dbConvert.h"
#include "db_field_log.h"
@@ -386,8 +386,11 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst)
{
char context[40] = "";
int trace = dbAccessDebugPUTF && *dbLockSetAddrTrace(psrc);
int claim_src = dbRec2Pvt(psrc)->procThread==NULL;
int claim_dst = psrc!=pdst && dbRec2Pvt(pdst)->procThread==NULL;
long status;
epicsUInt8 pact = psrc->pact;
epicsThreadId self = epicsThreadGetIdSelf();
psrc->pact = TRUE;
@@ -406,14 +409,11 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst)
printf("%s: '%s' -> '%s' with PUTF=%u\n",
context, psrc->name, pdst->name, psrc->putf);
if (pdst->putf)
errlogPrintf("Warning: '%s.PUTF' found true with PACT false\n",
pdst->name);
pdst->putf = psrc->putf;
}
else if (psrc->putf) {
/* The dst record is busy (awaiting async reprocessing) and
else if (psrc->putf && claim_dst) {
/* The dst record is busy (awaiting async reprocessing),
* not being processed recursively by us, and
* we were originally triggered by a call to dbPutField(),
* so we mark the dst record for reprocessing once the async
* completion is over.
@@ -426,17 +426,43 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst)
pdst->rpro = TRUE;
}
else {
/* The dst record is busy, but we weren't triggered by a call
* to dbPutField(). Do nothing.
/* The dst record is busy, but either is being processed recursively,
* or wasn't triggered by a call to dbPutField(). Do nothing.
*/
if (trace)
printf("%s: '%s' -> Active '%s', done\n",
context, psrc->name, pdst->name);
}
if(claim_src) {
dbRec2Pvt(psrc)->procThread = self;
}
if(claim_dst) {
dbRec2Pvt(pdst)->procThread = self;
}
if(dbRec2Pvt(psrc)->procThread!=self ||
dbRec2Pvt(pdst)->procThread!=self) {
errlogPrintf("Logic Error: processTarget 1 from %p, %s(%p) -> %s(%p)\n",
self, psrc->name, dbRec2Pvt(psrc), pdst->name, dbRec2Pvt(pdst));
}
status = dbProcess(pdst);
psrc->pact = pact;
if(dbRec2Pvt(psrc)->procThread!=self ||
dbRec2Pvt(pdst)->procThread!=self) {
errlogPrintf("Logic Error: processTarget 2 from %p, %s(%p) -> %s(%p)\n",
self, psrc->name, dbRec2Pvt(psrc), pdst->name, dbRec2Pvt(pdst));
}
if(claim_src) {
dbRec2Pvt(psrc)->procThread = NULL;
}
if(claim_dst) {
dbRec2Pvt(pdst)->procThread = NULL;
}
return status;
}

View File

@@ -49,7 +49,13 @@
#include "link.h"
#include "special.h"
#define EVENTSPERQUE 32
/* Queue size based on Ethernet MTU of 1500 bytes.
* Assume <=66 bytes of ethernet+IP+TCP overhead
* and 40 byte CA messages (DBF_TIME_DOUBLE).
*
* (1500-66)/40 -> 35
*/
#define EVENTSPERQUE 36
#define EVENTENTRIES 4 /* the number of que entries for each event */
#define EVENTQUESIZE (EVENTENTRIES * EVENTSPERQUE)
#define EVENTQEMPTY ((struct evSubscrip *)NULL)

View File

@@ -86,12 +86,6 @@ typedef struct notifyGlobal {
static notifyGlobal *pnotifyGlobal = 0;
/*Local routines*/
static void notifyInit(processNotify *ppn);
static void notifyCleanup(processNotify *ppn);
static void restartCheck(processNotifyRecord *ppnr);
static void callDone(dbCommon *precord,processNotify *ppn);
static void processNotifyCommon(processNotify *ppn,dbCommon *precord);
static void notifyCallback(CALLBACK *pcallback);
#define ellSafeAdd(list,listnode) \
@@ -210,7 +204,7 @@ static void callDone(dbCommon *precord, processNotify *ppn)
return;
}
static void processNotifyCommon(processNotify *ppn,dbCommon *precord)
static void processNotifyCommon(processNotify *ppn, dbCommon *precord, int first)
{
notifyPvt *pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;
int didPut = 0;
@@ -256,6 +250,9 @@ static void processNotifyCommon(processNotify *ppn,dbCommon *precord)
doProcess = 1;
if (doProcess) {
if (first) {
precord->putf = TRUE;
}
ppn->wasProcessed = 1;
precord->ppn = ppn;
ellSafeAdd(&pnotifyPvt->waitList, &precord->ppnr->waitNode);
@@ -298,7 +295,7 @@ static void notifyCallback(CALLBACK *pcallback)
return;
}
if(pnotifyPvt->state == notifyRestartCallbackRequested) {
processNotifyCommon(ppn, precord);
processNotifyCommon(ppn, precord, 0);
return;
}
/* All done. Clean up and call userCallback */
@@ -382,7 +379,7 @@ void dbProcessNotify(processNotify *ppn)
precord->ppnr->precord = precord;
ellInit(&precord->ppnr->restartList);
}
processNotifyCommon(ppn, precord);
processNotifyCommon(ppn, precord, 1);
}
void dbNotifyCancel(processNotify *ppn)
@@ -582,7 +579,7 @@ static void tpnThread(void *pvt)
processNotify *ppn = (processNotify *) ptpnInfo->ppn;
dbProcessNotify(ppn);
epicsEventWait(ptpnInfo->callbackDone);
epicsEventMustWait(ptpnInfo->callbackDone);
dbNotifyCancel(ppn);
epicsEventDestroy(ptpnInfo->callbackDone);
dbChannelDelete(ppn->chan);

View File

@@ -434,6 +434,10 @@ static void dbMenuHead(char *name)
dbMenu *pdbMenu;
GPHENTRY *pgphentry;
if (!*name) {
yyerrorAbort("dbMenuHead: Menu name can't be empty");
return;
}
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->menuList);
if(pgphentry) {
duplicate = TRUE;
@@ -447,6 +451,10 @@ static void dbMenuHead(char *name)
static void dbMenuChoice(char *name,char *value)
{
if (!*name) {
yyerror("dbMenuChoice: Menu choice name can't be empty");
return;
}
if(duplicate) return;
allocTemp(epicsStrDup(name));
allocTemp(epicsStrDup(value));
@@ -494,6 +502,10 @@ static void dbRecordtypeHead(char *name)
dbRecordType *pdbRecordType;
GPHENTRY *pgphentry;
if (!*name) {
yyerrorAbort("dbRecordtypeHead: Recordtype name can't be empty");
return;
}
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->recordTypeList);
if(pgphentry) {
duplicate = TRUE;
@@ -512,6 +524,10 @@ static void dbRecordtypeFieldHead(char *name,char *type)
dbFldDes *pdbFldDes;
int i;
if (!*name) {
yyerrorAbort("dbRecordtypeFieldHead: Field name can't be empty");
return;
}
if(duplicate) return;
pdbFldDes = dbCalloc(1,sizeof(dbFldDes));
allocTemp(pdbFldDes);
@@ -580,7 +596,7 @@ static void dbRecordtypeFieldItem(char *name,char *value)
if(sscanf(value,"%hd",&pdbFldDes->special)==1) {
return;
}
yyerror("Illegal special value.");
yyerror("Illegal 'special' value.");
return;
}
if(strcmp(name,"pp")==0) {
@@ -589,13 +605,13 @@ static void dbRecordtypeFieldItem(char *name,char *value)
} else if((strcmp(value,"NO")==0) || (strcmp(value,"FALSE")==0)) {
pdbFldDes->process_passive = FALSE;
} else {
yyerror("Illegal value. Must be NO or YES");
yyerror("Illegal 'pp' value, must be YES/NO/TRUE/FALSE");
}
return;
}
if(strcmp(name,"interest")==0) {
if(sscanf(value,"%hd",&pdbFldDes->interest)!=1)
yyerror("Illegal value. Must be integer");
yyerror("Illegal 'interest' value, must be integer");
return;
}
if(strcmp(name,"base")==0) {
@@ -604,13 +620,13 @@ static void dbRecordtypeFieldItem(char *name,char *value)
} else if(strcmp(value,"HEX")==0) {
pdbFldDes->base = CT_HEX;
} else {
yyerror("Illegal value. Must be CT_DECIMAL or CT_HEX");
yyerror("Illegal 'base' value, must be DECIMAL/HEX");
}
return;
}
if(strcmp(name,"size")==0) {
if(sscanf(value,"%hd",&pdbFldDes->size)!=1)
yyerror("Illegal value. Must be integer");
yyerror("Illegal 'size' value, must be integer");
return;
}
if(strcmp(name,"extra")==0) {
@@ -802,6 +818,10 @@ static void dbDriver(char *name)
drvSup *pdrvSup;
GPHENTRY *pgphentry;
if (!*name) {
yyerrorAbort("dbDriver: Driver name can't be empty");
return;
}
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->drvList);
if(pgphentry) {
return;
@@ -841,6 +861,10 @@ static void dbRegistrar(char *name)
dbText *ptext;
GPHENTRY *pgphentry;
if (!*name) {
yyerrorAbort("dbRegistrar: Registrar name can't be empty");
return;
}
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->registrarList);
if(pgphentry) {
return;
@@ -860,6 +884,10 @@ static void dbFunction(char *name)
dbText *ptext;
GPHENTRY *pgphentry;
if (!*name) {
yyerrorAbort("dbFunction: Function name can't be empty");
return;
}
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->functionList);
if(pgphentry) {
return;
@@ -879,6 +907,10 @@ static void dbVariable(char *name, char *type)
dbVariableDef *pvar;
GPHENTRY *pgphentry;
if (!*name) {
yyerrorAbort("dbVariable: Variable name can't be empty");
return;
}
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->variableList);
if(pgphentry) {
return;
@@ -899,6 +931,10 @@ static void dbBreakHead(char *name)
brkTable *pbrkTable;
GPHENTRY *pgphentry;
if (!*name) {
yyerrorAbort("dbBreakHead: Breaktable name can't be empty");
return;
}
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->bptList);
if(pgphentry) {
duplicate = TRUE;
@@ -1001,6 +1037,10 @@ static void dbRecordHead(char *recordType, char *name, int visible)
DBENTRY *pdbentry;
long status;
if (!*name) {
yyerrorAbort("dbRecordHead: Record name can't be empty");
return;
}
badch = strpbrk(name, " \"'.$");
if (badch) {
epicsPrintf("Bad character '%c' in record name \"%s\"\n",
@@ -1108,6 +1148,10 @@ static void dbRecordInfo(char *name, char *value)
tempListNode *ptempListNode;
long status;
if (!*name) {
yyerrorAbort("dbRecordInfo: Info item name can't be empty");
return;
}
if (duplicate) return;
ptempListNode = (tempListNode *)ellFirst(&tempList);
pdbentry = ptempListNode->item;
@@ -1132,6 +1176,10 @@ static void dbRecordAlias(char *name)
tempListNode *ptempListNode;
long status;
if (!*name) {
yyerrorAbort("dbRecordAlias: Alias name can't be empty");
return;
}
if (duplicate) return;
ptempListNode = (tempListNode *)ellFirst(&tempList);
pdbentry = ptempListNode->item;
@@ -1149,6 +1197,10 @@ static void dbAlias(char *name, char *alias)
DBENTRY dbEntry;
DBENTRY *pdbEntry = &dbEntry;
if (!*alias) {
yyerrorAbort("dbAlias: Alias name can't be empty");
return;
}
dbInitEntry(pdbbase, pdbEntry);
if (dbFindRecord(pdbEntry, name)) {
epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n",

View File

@@ -79,16 +79,6 @@ epicsShareDef maplinkType pamaplinkType[LINK_NTYPES] = {
{"VXI_IO",VXI_IO}
};
static int mapDBFtoDCT[DBF_NOACCESS+1] = {
DCT_STRING,
DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,
DCT_REAL,DCT_REAL,
DCT_INTEGER,
DCT_MENU,
DCT_MENUFORM,
DCT_INLINK,DCT_OUTLINK,DCT_FWDLINK,
DCT_NOACCESS};
/*forward references for private routines*/
static void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...)
EPICS_PRINTF_STYLE(2,3);
@@ -166,38 +156,6 @@ void dbFreePath(DBBASE *pdbbase)
}
static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess)
{
char message[200];
char *pmessage=&message[0];
dbRecordNode *precnode = pdbentry->precnode;
dbFldDes *pflddes = pdbentry->pflddes;
char *pname = NULL;
*pmessage=0;
if(pdbentry->precordType) pname = pdbentry->precordType->name;
if(pname) {
strcat(pmessage,"RecordType:");
strcat(pmessage,pname);
}
if(precnode){
if (dbIsAlias(pdbentry))
strcat(pmessage," Record Alias:");
else
strcat(pmessage," Record:");
strcat(pmessage,(char *)precnode->precord);
}
if(pflddes) {
char *pstr=pflddes->name;
strcat(pmessage," Field:");
strcat(pmessage,pstr);
}
strcat(pmessage,"\n");
strcat(pmessage,mess);
errMessage(status,pmessage);
}
static void zeroDbentry(DBENTRY *pdbentry)
{
/*NOTE that pdbbase and message MUST NOT be set to NULL*/
@@ -1398,19 +1356,6 @@ long dbNextField(DBENTRY *pdbentry,int dctonly)
}
}
int dbGetFieldType(DBENTRY *pdbentry)
{
dbFldDes *pflddes = pdbentry->pflddes;
long status;
if(!pflddes){
status = S_dbLib_flddesNotFound;
entryErrMessage(pdbentry,status,"dbGetFieldType");
return(status);
}
return(mapDBFtoDCT[pflddes->field_type]);
}
int dbGetNFields(DBENTRY *pdbentry,int dctonly)
{
dbRecordType *precordType = pdbentry->precordType;
@@ -3216,48 +3161,21 @@ int dbGetNLinks(DBENTRY *pdbentry)
return((int)precordType->no_links);
}
long dbGetLinkField(DBENTRY *pdbentry,int index)
long dbGetLinkField(DBENTRY *pdbentry, int index)
{
dbRecordType *precordType = pdbentry->precordType;
dbFldDes *pflddes;
dbRecordType *precordType = pdbentry->precordType;
dbFldDes *pflddes;
if (!precordType)
return S_dbLib_recordTypeNotFound;
if (index < 0 || index >= precordType->no_links)
return S_dbLib_badLink;
if(!precordType) return(S_dbLib_recordTypeNotFound);
if(index<0 || index>=precordType->no_links) return(S_dbLib_badLink);
pdbentry->indfield = precordType->link_ind[index];
pdbentry->pflddes = pflddes = precordType->papFldDes[pdbentry->indfield];
dbGetFieldAddress(pdbentry);
return(0);
}
int dbGetLinkType(DBENTRY *pdbentry)
{
dbFldDes *pflddes;
DBLINK *plink;
int field_type;
dbGetFieldAddress(pdbentry);
pflddes = pdbentry->pflddes;
if(!pflddes) return(-1);
plink = (DBLINK *)pdbentry->pfield;
if(!plink) return(-1);
field_type = pflddes->field_type;
switch (field_type) {
case DBF_INLINK:
case DBF_OUTLINK:
case DBF_FWDLINK:
switch(plink->type) {
case CONSTANT:
return(DCT_LINK_CONSTANT);
case PV_LINK:
case PN_LINK:
case DB_LINK:
case CA_LINK:
return(DCT_LINK_PV);
default:
return(DCT_LINK_FORM);
}
}
return(-1);
return 0;
}
void dbDumpPath(DBBASE *pdbbase)
@@ -3556,68 +3474,110 @@ void dbDumpBreaktable(DBBASE *pdbbase,const char *name)
return;
}
static char *bus[VXI_IO+1] = {"","","VME","CAMAC","AB",
"GPIB","BITBUS","","","","","","INST","BBGPIB","VXI"};
void dbReportDeviceConfig(dbBase *pdbbase,FILE *report)
static char *bus[LINK_NTYPES] = {
"", /* CONSTANT */
NULL, /* PV_LINK */
"VME",
"CAMAC",
"AB",
"GPIB",
"BITBUS",
NULL, /* MACRO_LINK */
NULL, /* JSON_LINK */
NULL, /* PN_LINK */
NULL, /* DB_LINK */
NULL, /* CA_LINK */
"INST",
"BBGPIB",
"VXI"
};
void dbReportDeviceConfig(dbBase *pdbbase, FILE *report)
{
DBENTRY dbentry;
DBENTRY *pdbentry=&dbentry;
long status;
char linkValue[messagesize];
char dtypValue[50];
char cvtValue[40];
int ilink,nlinks;
struct link *plink;
int linkType;
FILE *stream = (report==0) ? stdout : report;
DBENTRY dbentry, *pdbentry = &dbentry;
long status;
FILE *stream = report ? report : stdout;
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return;
if (!pdbbase) {
fprintf(stderr, "dbReportDeviceConfig: pdbbase not specified\n");
return;
}
dbInitEntry(pdbbase,pdbentry);
status = dbFirstRecordType(pdbentry);
while(!status) {
status = dbFirstRecord(pdbentry);
while(!status) {
nlinks = dbGetNLinks(pdbentry);
for(ilink=0; ilink<nlinks; ilink++) {
status = dbGetLinkField(pdbentry,ilink);
if(status || dbGetLinkType(pdbentry)!=DCT_LINK_FORM) continue;
plink = pdbentry->pfield;
linkType = plink->type;
if(bus[linkType][0]==0) continue;
strncpy(linkValue, dbGetString(pdbentry), NELEMENTS(linkValue)-1);
linkValue[NELEMENTS(linkValue)-1] = '\0';
status = dbFindField(pdbentry,"DTYP");
if(status) break;
strcpy(dtypValue,dbGetString(pdbentry));
status = dbFindField(pdbentry,"LINR");
if(status) {
cvtValue[0] = 0;
} else {
if(strcmp(dbGetString(pdbentry),"LINEAR")!=0) {
cvtValue[0] = 0;
} else {
strcpy(cvtValue,"cvt(");
status = dbFindField(pdbentry,"EGUL");
if(!status) strcat(cvtValue,dbGetString(pdbentry));
status = dbFindField(pdbentry,"EGUF");
if(!status) {
strcat(cvtValue,",");
strcat(cvtValue,dbGetString(pdbentry));
}
strcat(cvtValue,")");
}
}
fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n",
bus[linkType],linkValue,dtypValue,
dbGetRecordName(pdbentry),cvtValue);
break;
}
status = dbNextRecord(pdbentry);
}
status = dbNextRecordType(pdbentry);
while (!status) {
const int nlinks = dbGetNLinks(pdbentry);
status = dbFirstRecord(pdbentry);
while (!status) {
int ilink;
for (ilink=0; ilink<nlinks; ilink++) {
char linkValue[messagesize];
char dtypValue[50];
char cvtValue[40];
struct link *plink;
int linkType;
status = dbGetLinkField(pdbentry, ilink);
if (status)
continue;
plink = pdbentry->pfield;
linkType = plink->type;
if (plink->text) { /* Not yet parsed */
dbLinkInfo linfo;
if (dbParseLink(plink->text, pdbentry->pflddes->field_type, &linfo))
continue;
linkType = linfo.ltype;
if (linkType && bus[linkType])
strncpy(linkValue, plink->text, messagesize-1);
dbFreeLinkInfo(&linfo);
}
else {
strncpy(linkValue, dbGetString(pdbentry), messagesize-1);
}
if (!linkType || !bus[linkType])
continue;
linkValue[messagesize-1] = '\0';
status = dbFindField(pdbentry, "DTYP");
if (status)
break; /* Next record type */
strcpy(dtypValue, dbGetString(pdbentry));
status = dbFindField(pdbentry, "LINR");
if (status) {
cvtValue[0] = 0;
}
else {
if (strcmp(dbGetString(pdbentry), "LINEAR") != 0) {
cvtValue[0] = 0;
}
else {
strcpy(cvtValue,"cvt(");
status = dbFindField(pdbentry, "EGUL");
if (!status)
strcat(cvtValue, dbGetString(pdbentry));
status = dbFindField(pdbentry, "EGUF");
if (!status) {
strcat(cvtValue, ",");
strcat(cvtValue, dbGetString(pdbentry));
}
strcat(cvtValue, ")");
}
}
fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n",
bus[linkType], linkValue, dtypValue,
dbGetRecordName(pdbentry), cvtValue);
break;
}
status = dbNextRecord(pdbentry);
}
status = dbNextRecordType(pdbentry);
}
dbFinishEntry(pdbentry);
finishOutstream(stream);

View File

@@ -28,25 +28,9 @@
extern "C" {
#endif
/*Field types as seen by static database access clients*/
#define DCT_STRING 0
#define DCT_INTEGER 1
#define DCT_REAL 2
#define DCT_MENU 3
#define DCT_MENUFORM 4
#define DCT_INLINK 5
#define DCT_OUTLINK 6
#define DCT_FWDLINK 7
#define DCT_NOACCESS 8
/*Link types as seen by static database access clients*/
#define DCT_LINK_CONSTANT 0
#define DCT_LINK_FORM 1
#define DCT_LINK_PV 2
typedef dbBase DBBASE;
typedef struct{
typedef struct dbEntry {
DBBASE *pdbbase;
dbRecordType *precordType;
dbFldDes *pflddes;
@@ -57,9 +41,6 @@ typedef struct{
short indfield;
} DBENTRY;
struct dbAddr;
struct dbCommon;
/* Static database access routines*/
epicsShareFunc DBBASE * dbAllocBase(void);
epicsShareFunc void dbFreeBase(DBBASE *pdbbase);
@@ -68,18 +49,6 @@ epicsShareFunc void dbFreeEntry(DBENTRY *pdbentry);
epicsShareFunc void dbInitEntry(DBBASE *pdbbase,
DBENTRY *pdbentry);
/** Initialize DBENTRY from a valid dbAddr*.
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
* and finally dbFollowAlias()
*/
epicsShareFunc void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry);
/** Initialize DBENTRY from a valid record (dbCommon*).
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
* and finally dbFollowAlias() when no field is specified.
*/
epicsShareFunc void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry);
epicsShareFunc void dbFinishEntry(DBENTRY *pdbentry);
epicsShareFunc DBENTRY * dbCopyEntry(DBENTRY *pdbentry);
epicsShareFunc void dbCopyEntryContents(DBENTRY *pfrom,
@@ -139,7 +108,6 @@ epicsShareFunc long dbGetAttributePart(DBENTRY *pdbentry,
epicsShareFunc long dbFirstField(DBENTRY *pdbentry, int dctonly);
epicsShareFunc long dbNextField(DBENTRY *pdbentry, int dctonly);
epicsShareFunc int dbGetFieldType(DBENTRY *pdbentry);
epicsShareFunc int dbGetNFields(DBENTRY *pdbentry, int dctonly);
epicsShareFunc char * dbGetFieldName(DBENTRY *pdbentry);
epicsShareFunc char * dbGetDefault(DBENTRY *pdbentry);
@@ -231,7 +199,6 @@ epicsShareFunc linkSup * dbFindLinkSup(dbBase *pdbbase,
epicsShareFunc int dbGetNLinks(DBENTRY *pdbentry);
epicsShareFunc long dbGetLinkField(DBENTRY *pdbentry, int index);
epicsShareFunc int dbGetLinkType(DBENTRY *pdbentry);
/* Dump routines */
epicsShareFunc void dbDumpPath(DBBASE *pdbbase);

View File

@@ -179,7 +179,7 @@ long dbFreeRecord(DBENTRY *pdbentry)
if(!pdbRecordType) return(S_dbLib_recordTypeNotFound);
if(!precnode) return(S_dbLib_recNotFound);
if(!precnode->precord) return(S_dbLib_recNotFound);
free(CONTAINER(precnode->precord, dbCommonPvt, common));
free(dbRec2Pvt(precnode->precord));
precnode->precord = NULL;
return(0);
}

View File

@@ -13,7 +13,7 @@ SRC_DIRS += $(IOCDIR)/dbtemplate
PROD_HOST += msi
msi_SRCS = msi.c
msi_SRCS = msi.cpp
msi_LIBS += Com
HTMLS += msi.html

View File

@@ -9,6 +9,9 @@
/* msi - macro substitutions and include */
#include <string>
#include <list>
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
@@ -18,7 +21,6 @@
#include <dbDefs.h>
#include <macLib.h>
#include <ellLib.h>
#include <errlog.h>
#include <epicsString.h>
#include <osiFileName.h>
@@ -56,32 +58,35 @@ int din = 0;
typedef struct inputData inputData;
static void inputConstruct(inputData **ppvt);
static void inputDestruct(inputData *pvt);
static void inputAddPath(inputData *pvt, char *pval);
static void inputBegin(inputData *pvt, char *fileName);
static char *inputNextLine(inputData *pvt);
static void inputNewIncludeFile(inputData *pvt, char *name);
static void inputErrPrint(inputData *pvt);
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 char *inputNextLine(inputData * const pvt);
static void inputNewIncludeFile(inputData * const pvt, const char * const name);
static void inputErrPrint(const inputData * const pvt);
/* Module to read the substitution file */
typedef struct subInfo subInfo;
static void substituteOpen(subInfo **ppvt, char *substitutionName);
static void substituteDestruct(subInfo *pvt);
static int substituteGetNextSet(subInfo *pvt, char **filename);
static int substituteGetGlobalSet(subInfo *pvt);
static char *substituteGetReplacements(subInfo *pvt);
static char *substituteGetGlobalReplacements(subInfo *pvt);
static void substituteOpen(subInfo **ppvt, const std::string& substitutionName);
static void substituteDestruct(subInfo * const pvt);
static bool substituteGetNextSet(subInfo * const pvt, char **filename);
static bool substituteGetGlobalSet(subInfo * const pvt);
static const char *substituteGetReplacements(subInfo * const pvt);
static const char *substituteGetGlobalReplacements(subInfo * const pvt);
/* Forward references to local routines */
static void usageExit(int status);
static void abortExit(int status);
static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval);
static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName);
static void usageExit(const int status);
static void abortExit(const int status);
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);
/*Global variables */
static int opt_V = 0;
static int opt_D = 0;
static bool opt_D = false;
static char *outFile = 0;
static int numDeps = 0, depHashes[MAX_DEPS];
@@ -92,23 +97,21 @@ int main(int argc,char **argv)
inputData *inputPvt;
MAC_HANDLE *macPvt;
char *pval;
int narg;
char *substitutionName = 0;
std::string substitutionName;
char *templateName = 0;
int i;
int localScope = 1;
bool localScope = true;
inputConstruct(&inputPvt);
macCreateHandle(&macPvt, 0);
while ((argc > 1) && (argv[1][0] == '-')) {
narg = (strlen(argv[1]) == 2) ? 2 : 1;
int narg = (strlen(argv[1]) == 2) ? 2 : 1;
pval = (narg == 1) ? (argv[1] + 2) : argv[2];
if (strncmp(argv[1], "-I", 2) == 0) {
inputAddPath(inputPvt, pval);
}
else if (strcmp(argv[1], "-D") == 0) {
opt_D = 1;
opt_D = true;
narg = 1; /* no argument for this option */
}
else if(strncmp(argv[1], "-o", 2) == 0) {
@@ -118,14 +121,14 @@ int main(int argc,char **argv)
addMacroReplacements(macPvt, pval);
}
else if(strncmp(argv[1], "-S", 2) == 0) {
substitutionName = epicsStrDup(pval);
substitutionName = pval;
}
else if (strcmp(argv[1], "-V") == 0) {
opt_V = 1;
narg = 1; /* no argument for this option */
}
else if (strcmp(argv[1], "-g") == 0) {
localScope = 0;
localScope = false;
narg = 1; /* no argument for this option */
}
else if (strcmp(argv[1], "-h") == 0) {
@@ -137,7 +140,7 @@ int main(int argc,char **argv)
}
argc -= narg;
for (i = 1; i < argc; i++)
for (int i = 1; i < argc; i++)
argv[i] = argv[i + narg];
}
@@ -165,24 +168,24 @@ int main(int argc,char **argv)
if (argc == 2)
templateName = epicsStrDup(argv[1]);
if (!substitutionName) {
if (substitutionName.empty()) {
STEP("Single template+substitutions file");
makeSubstitutions(inputPvt, macPvt, templateName);
}
else {
subInfo *substitutePvt;
char *filename = 0;
int isGlobal, isFile;
bool isGlobal, isFile;
STEPS("Substitutions from file", substitutionName);
STEPS("Substitutions from file", substitutionName.c_str());
substituteOpen(&substitutePvt, substitutionName);
do {
isGlobal = substituteGetGlobalSet(substitutePvt);
if (isGlobal) {
STEP("Handling global macros");
pval = substituteGetGlobalReplacements(substitutePvt);
if (pval)
addMacroReplacements(macPvt, pval);
const char *macStr = substituteGetGlobalReplacements(substitutePvt);
if (macStr)
addMacroReplacements(macPvt, macStr);
}
else if ((isFile = substituteGetNextSet(substitutePvt, &filename))) {
if (templateName)
@@ -193,11 +196,12 @@ int main(int argc,char **argv)
}
STEPS("Handling template file", filename);
while ((pval = substituteGetReplacements(substitutePvt))) {
const char *macStr;
while ((macStr = substituteGetReplacements(substitutePvt))) {
if (localScope)
macPushScope(macPvt);
addMacroReplacements(macPvt, pval);
addMacroReplacements(macPvt, macStr);
makeSubstitutions(inputPvt, macPvt, filename);
if (localScope)
@@ -207,18 +211,18 @@ int main(int argc,char **argv)
} while (isGlobal || isFile);
substituteDestruct(substitutePvt);
}
errlogFlush();
macDeleteHandle(macPvt);
errlogFlush(); // macLib calls errlogPrintf()
inputDestruct(inputPvt);
if (opt_D) {
printf("\n");
}
fflush(stdout);
free(templateName);
free(substitutionName);
return opt_V & 2;
}
void usageExit(int status)
void usageExit(const int status)
{
fprintf(stderr,
"Usage: msi [options] [template]\n"
@@ -236,7 +240,7 @@ void usageExit(int status)
exit(status);
}
void abortExit(int status)
void abortExit(const int status)
{
if (outFile) {
fclose(stdout);
@@ -245,7 +249,8 @@ void abortExit(int status)
exit(status);
}
static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval)
static void addMacroReplacements(MAC_HANDLE * const macPvt,
const char * const pval)
{
char **pairs;
long status;
@@ -268,7 +273,9 @@ static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval)
typedef enum {cmdInclude,cmdSubstitute} cmdType;
static const char *cmdNames[] = {"include","substitute"};
static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName)
static void makeSubstitutions(inputData * const inputPvt,
MAC_HANDLE * const macPvt,
const char * const templateName)
{
char *input;
static char buffer[MAX_BUFFER_SIZE];
@@ -292,7 +299,6 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem
if (command) {
char *pstart;
char *pend;
char *copy;
int cmdind=-1;
int i;
@@ -325,16 +331,15 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem
/*skip quote and any trailing blanks*/
while (*++p == ' ') ;
if (*p != '\n' && *p != 0) goto endcmd;
copy = calloc(pend-pstart + 1, sizeof(char));
strncpy(copy, pstart, pend-pstart);
std::string copy = std::string(pstart, pend);
switch(cmdind) {
case cmdInclude:
inputNewIncludeFile(inputPvt,copy);
inputNewIncludeFile(inputPvt, copy.c_str());
break;
case cmdSubstitute:
addMacroReplacements(macPvt,copy);
addMacroReplacements(macPvt, copy.c_str());
break;
default:
@@ -342,7 +347,6 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem
inputErrPrint(inputPvt);
abortExit(1);
}
free(copy);
expand = 0;
}
@@ -361,94 +365,72 @@ endcmd:
}
typedef struct inputFile {
ELLNODE node;
char *filename;
std::string filename;
FILE *fp;
int lineNum;
} inputFile;
typedef struct pathNode {
ELLNODE node;
char *directory;
} pathNode;
struct inputData {
ELLLIST inputFileList;
ELLLIST pathList;
std::list<inputFile> inputFileList;
std::list<std::string> pathList;
char inputBuffer[MAX_BUFFER_SIZE];
inputData() { memset(inputBuffer, 0, sizeof(inputBuffer) * sizeof(inputBuffer[0])); };
};
static void inputOpenFile(inputData *pinputData, char *filename);
static void inputOpenFile(inputData *pinputData, const char * const filename);
static void inputCloseFile(inputData *pinputData);
static void inputCloseAllFiles(inputData *pinputData);
static void inputConstruct(inputData **ppvt)
{
inputData *pinputData;
pinputData = calloc(1, sizeof(inputData));
ellInit(&pinputData->inputFileList);
ellInit(&pinputData->pathList);
*ppvt = pinputData;
*ppvt = new inputData;
}
static void inputDestruct(inputData *pinputData)
static void inputDestruct(inputData * const pinputData)
{
pathNode *ppathNode;
inputCloseAllFiles(pinputData);
while ((ppathNode = (pathNode *) ellFirst(&pinputData->pathList))) {
ellDelete(&pinputData->pathList, &ppathNode->node);
free(ppathNode->directory);
free(ppathNode);
}
free(pinputData);
delete(pinputData);
}
static void inputAddPath(inputData *pinputData, char *path)
static void inputAddPath(inputData * const pinputData, const char * const path)
{
ELLLIST *ppathList = &pinputData->pathList;
pathNode *ppathNode;
const char *pcolon;
const char *pdir;
size_t len;
int emptyName;
const char sep = *OSI_PATH_LIST_SEPARATOR;
ENTER;
pdir = path;
/*an empty name at beginning, middle, or end means current directory*/
while (pdir && *pdir) {
emptyName = ((*pdir == sep) ? 1 : 0);
bool emptyName = (*pdir == sep);
if (emptyName) ++pdir;
ppathNode = (pathNode *) calloc(1, sizeof(pathNode));
ellAdd(ppathList, &ppathNode->node);
std::string directory;
if (!emptyName) {
pcolon = strchr(pdir, sep);
len = (pcolon ? (pcolon - pdir) : strlen(pdir));
if (len > 0) {
ppathNode->directory = (char *) calloc(len + 1, sizeof(char));
strncpy(ppathNode->directory, pdir, len);
directory = std::string(pdir, len);
pdir = pcolon;
/*unless at end skip past first colon*/
if (pdir && *(pdir + 1) != 0) ++pdir;
}
else { /*must have been trailing : */
emptyName = 1;
emptyName = true;
}
}
if (emptyName) {
ppathNode->directory = (char *) calloc(2, sizeof(char));
strcpy(ppathNode->directory, ".");
directory = ".";
}
pinputData->pathList.push_back(directory);
}
EXIT;
}
static void inputBegin(inputData *pinputData, char *fileName)
static void inputBegin(inputData * const pinputData, const char * const fileName)
{
ENTER;
inputCloseAllFiles(pinputData);
@@ -456,16 +438,16 @@ static void inputBegin(inputData *pinputData, char *fileName)
EXIT;
}
static char *inputNextLine(inputData *pinputData)
static char *inputNextLine(inputData * const pinputData)
{
inputFile *pinputFile;
char *pline;
std::list<inputFile>& inFileList = pinputData->inputFileList;
ENTER;
while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) {
pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, pinputFile->fp);
while (!inFileList.empty()) {
inputFile& inFile = inFileList.front();
char *pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, inFile.fp);
if (pline) {
++pinputFile->lineNum;
++inFile.lineNum;
EXITS(pline);
return pline;
}
@@ -475,32 +457,31 @@ static char *inputNextLine(inputData *pinputData)
return 0;
}
static void inputNewIncludeFile(inputData *pinputData, char *name)
static void inputNewIncludeFile(inputData * const pinputData,
const char * const name)
{
ENTER;
inputOpenFile(pinputData,name);
EXIT;
}
static void inputErrPrint(inputData *pinputData)
static void inputErrPrint(const inputData *const pinputData)
{
inputFile *pinputFile;
ENTER;
fprintf(stderr, "input: '%s' at ", pinputData->inputBuffer);
pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList);
while (pinputFile) {
fprintf(stderr, "line %d of ", pinputFile->lineNum);
const std::list<inputFile>& inFileList = pinputData->inputFileList;
std::list<inputFile>::const_iterator inFileIt = inFileList.begin();
while (inFileIt != inFileList.end()) {
fprintf(stderr, "line %d of ", inFileIt->lineNum);
if (pinputFile->filename) {
fprintf(stderr, " file %s\n", pinputFile->filename);
if (!inFileIt->filename.empty()) {
fprintf(stderr, " file %s\n", inFileIt->filename.c_str());
}
else {
fprintf(stderr, "stdin:\n");
}
pinputFile = (inputFile *) ellNext(&pinputFile->node);
if (pinputFile) {
if (++inFileIt != inFileList.end()) {
fprintf(stderr, " included from ");
}
else {
@@ -511,12 +492,11 @@ static void inputErrPrint(inputData *pinputData)
EXIT;
}
static void inputOpenFile(inputData *pinputData,char *filename)
static void inputOpenFile(inputData *pinputData, const char * const filename)
{
ELLLIST *ppathList = &pinputData->pathList;
pathNode *ppathNode = 0;
inputFile *pinputFile;
char *fullname = 0;
std::list<std::string>& pathList = pinputData->pathList;
std::list<std::string>::iterator pathIt = pathList.end();
std::string fullname;
FILE *fp = 0;
ENTER;
@@ -524,24 +504,19 @@ static void inputOpenFile(inputData *pinputData,char *filename)
STEP("Using stdin");
fp = stdin;
}
else if ((ellCount(ppathList) == 0) || strchr(filename, '/')){
else if (pathList.empty() || strchr(filename, '/')){
STEPS("Opening ", filename);
fp = fopen(filename, "r");
}
else {
ppathNode = (pathNode *) ellFirst(ppathList);
while (ppathNode) {
fullname = calloc(strlen(filename) + strlen(ppathNode->directory) + 2,
sizeof(char));
strcpy(fullname, ppathNode->directory);
strcat(fullname, "/");
strcat(fullname, filename);
pathIt = pathList.begin();
while(pathIt != pathList.end()) {
fullname = *pathIt + "/" + filename;
STEPS("Trying", filename);
fp = fopen(fullname, "r");
fp = fopen(fullname.c_str(), "r");
if (fp)
break;
free(fullname);
ppathNode = (pathNode *) ellNext(&ppathNode->node);
++pathIt;
}
}
@@ -552,20 +527,20 @@ static void inputOpenFile(inputData *pinputData,char *filename)
}
STEP("File opened");
pinputFile = calloc(1, sizeof(inputFile));
inputFile inFile = inputFile();
if (ppathNode) {
pinputFile->filename = fullname;
if (pathIt != pathList.end()) {
inFile.filename = fullname;
}
else if (filename) {
pinputFile->filename = epicsStrDup(filename);
inFile.filename = filename;
}
else {
pinputFile->filename = epicsStrDup("stdin");
inFile.filename = "stdin";
}
if (opt_D) {
int hash = epicsStrHash(pinputFile->filename, 12345);
int hash = epicsStrHash(inFile.filename.c_str(), 12345);
int i = 0;
int match = 0;
@@ -578,7 +553,7 @@ static void inputOpenFile(inputData *pinputData,char *filename)
if (!match) {
const char *wrap = numDeps ? " \\\n" : "";
printf("%s %s", wrap, pinputFile->filename);
printf("%s %s", wrap, inFile.filename.c_str());
if (numDeps < MAX_DEPS) {
depHashes[numDeps++] = hash;
}
@@ -589,33 +564,29 @@ static void inputOpenFile(inputData *pinputData,char *filename)
}
}
pinputFile->fp = fp;
ellInsert(&pinputData->inputFileList, 0, &pinputFile->node);
inFile.fp = fp;
pinputData->inputFileList.push_front(inFile);
EXIT;
}
static void inputCloseFile(inputData *pinputData)
{
inputFile *pinputFile;
std::list<inputFile>& inFileList = pinputData->inputFileList;
ENTER;
pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList);
if (pinputFile) {
ellDelete(&pinputData->inputFileList, &pinputFile->node);
if (fclose(pinputFile->fp))
fprintf(stderr, "msi: Can't close input file '%s'\n", pinputFile->filename);
free(pinputFile->filename);
free(pinputFile);
if(!inFileList.empty()) {
inputFile& inFile = inFileList.front();
if (fclose(inFile.fp))
fprintf(stderr, "msi: Can't close input file '%s'\n", inFile.filename.c_str());
inFileList.erase(inFileList.begin());
}
EXIT;
}
static void inputCloseAllFiles(inputData *pinputData)
{
inputFile *pinputFile;
ENTER;
while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) {
const std::list<inputFile>& inFileList = pinputData->inputFileList;
while(!inFileList.empty()) {
inputCloseFile(pinputData);
}
EXIT;
@@ -627,7 +598,7 @@ typedef enum {
} tokenType;
typedef struct subFile {
char *substitutionName;
std::string substitutionName;
FILE *fp;
int lineNum;
char inputBuffer[MAX_BUFFER_SIZE];
@@ -636,25 +607,20 @@ typedef struct subFile {
char string[MAX_BUFFER_SIZE];
} subFile;
typedef struct patternNode {
ELLNODE node;
char *var;
} patternNode;
struct subInfo {
subFile *psubFile;
int isFile;
bool isFile;
char *filename;
int isPattern;
ELLLIST patternList;
size_t size;
size_t curLength;
char *macroReplacements;
bool isPattern;
std::list<std::string> patternList;
std::string macroReplacements;
subInfo() : psubFile(NULL), isFile(false), filename(NULL),
isPattern(false) {};
};
static char *subGetNextLine(subFile *psubFile);
static tokenType subGetNextToken(subFile *psubFile);
static void subFileErrPrint(subFile *psubFile,char * message);
static void subFileErrPrint(subFile *psubFile, const char * message);
static void freeSubFile(subInfo *psubInfo);
static void freePattern(subInfo *psubInfo);
static void catMacroReplacements(subInfo *psubInfo,const char *value);
@@ -668,7 +634,7 @@ void freeSubFile(subInfo *psubInfo)
if (fclose(psubFile->fp))
fprintf(stderr, "msi: Can't close substitution file\n");
}
free(psubFile);
delete(psubFile);
free(psubInfo->filename);
psubInfo->psubFile = 0;
EXIT;
@@ -676,43 +642,36 @@ void freeSubFile(subInfo *psubInfo)
void freePattern(subInfo *psubInfo)
{
patternNode *ppatternNode;
ENTER;
while ((ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList))) {
ellDelete(&psubInfo->patternList, &ppatternNode->node);
free(ppatternNode->var);
free(ppatternNode);
}
psubInfo->isPattern = 0;
psubInfo->patternList.clear();
psubInfo->isPattern = false;
EXIT;
}
static void substituteDestruct(subInfo *psubInfo)
static void substituteDestruct(subInfo * const psubInfo)
{
ENTER;
freeSubFile(psubInfo);
freePattern(psubInfo);
free(psubInfo);
delete(psubInfo);
EXIT;
}
static void substituteOpen(subInfo **ppvt, char *substitutionName)
static void substituteOpen(subInfo **ppvt, const std::string& substitutionName)
{
subInfo *psubInfo;
subFile *psubFile;
FILE *fp;
ENTER;
psubInfo = calloc(1, sizeof(subInfo));
psubInfo = new subInfo;
*ppvt = psubInfo;
psubFile = calloc(1, sizeof(subFile));
psubFile = new subFile;
psubInfo->psubFile = psubFile;
ellInit(&psubInfo->patternList);
fp = fopen(substitutionName, "r");
fp = fopen(substitutionName.c_str(), "r");
if (!fp) {
fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName);
fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName.c_str());
abortExit(1);
}
@@ -725,7 +684,7 @@ static void substituteOpen(subInfo **ppvt, char *substitutionName)
EXIT;
}
static int substituteGetGlobalSet(subInfo *psubInfo)
static bool substituteGetGlobalSet(subInfo * const psubInfo)
{
subFile *psubFile = psubInfo->psubFile;
@@ -737,17 +696,16 @@ static int substituteGetGlobalSet(subInfo *psubInfo)
strcmp(psubFile->string, "global") == 0) {
subGetNextToken(psubFile);
EXITD(1);
return 1;
return true;
}
EXITD(0);
return 0;
return false;
}
static int substituteGetNextSet(subInfo *psubInfo,char **filename)
static bool substituteGetNextSet(subInfo * const psubInfo,char **filename)
{
subFile *psubFile = psubInfo->psubFile;
patternNode *ppatternNode;
ENTER;
*filename = 0;
@@ -756,7 +714,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
if (psubFile->token == tokenEOF) {
EXITD(0);
return 0;
return false;
}
if (psubFile->token == tokenString &&
@@ -764,7 +722,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
size_t len;
STEP("Parsed 'file'");
psubInfo->isFile = 1;
psubInfo->isFile = true;
if (subGetNextToken(psubFile) != tokenString) {
subFileErrPrint(psubFile, "Parse error, expecting a filename");
abortExit(1);
@@ -799,7 +757,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
if (psubFile->token == tokenLBrace) {
EXITD(1);
return 1;
return true;
}
if (psubFile->token == tokenRBrace) {
@@ -815,7 +773,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
STEP("Parsed 'pattern'");
freePattern(psubInfo);
psubInfo->isPattern = 1;
psubInfo->isPattern = true;
while (subGetNextToken(psubFile) == tokenSeparator);
@@ -825,15 +783,13 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
}
STEP("Parsed '{'");
while (1) {
while (true) {
while (subGetNextToken(psubFile) == tokenSeparator);
if (psubFile->token != tokenString)
break;
ppatternNode = calloc(1, sizeof(patternNode));
ellAdd(&psubInfo->patternList, &ppatternNode->node);
ppatternNode->var = epicsStrDup(psubFile->string);
psubInfo->patternList.push_back(psubFile->string);
}
if (psubFile->token != tokenRBrace) {
@@ -843,23 +799,21 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
subGetNextToken(psubFile);
EXITD(1);
return 1;
return true;
}
static char *substituteGetGlobalReplacements(subInfo *psubInfo)
static const char *substituteGetGlobalReplacements(subInfo * const psubInfo)
{
subFile *psubFile = psubInfo->psubFile;
ENTER;
if (psubInfo->macroReplacements)
psubInfo->macroReplacements[0] = 0;
psubInfo->curLength = 0;
psubInfo->macroReplacements.clear();
while (psubFile->token == tokenSeparator)
subGetNextToken(psubFile);
if (psubFile->token == tokenRBrace && psubInfo->isFile) {
psubInfo->isFile = 0;
psubInfo->isFile = false;
free(psubInfo->filename);
psubInfo->filename = 0;
freePattern(psubInfo);
@@ -877,12 +831,12 @@ static char *substituteGetGlobalReplacements(subInfo *psubInfo)
return 0;
}
while (1) {
while (true) {
switch(subGetNextToken(psubFile)) {
case tokenRBrace:
subGetNextToken(psubFile);
EXITS(psubInfo->macroReplacements);
return psubInfo->macroReplacements;
EXITS(psubInfo->macroReplacements.c_str());
return psubInfo->macroReplacements.c_str();
case tokenSeparator:
catMacroReplacements(psubInfo, ",");
@@ -902,21 +856,18 @@ static char *substituteGetGlobalReplacements(subInfo *psubInfo)
}
}
static char *substituteGetReplacements(subInfo *psubInfo)
static const char *substituteGetReplacements(subInfo * const psubInfo)
{
subFile *psubFile = psubInfo->psubFile;
patternNode *ppatternNode;
ENTER;
if (psubInfo->macroReplacements)
psubInfo->macroReplacements[0] = 0;
psubInfo->curLength = 0;
psubInfo->macroReplacements.clear();
while (psubFile->token == tokenSeparator)
subGetNextToken(psubFile);
if (psubFile->token==tokenRBrace && psubInfo->isFile) {
psubInfo->isFile = 0;
psubInfo->isFile = false;
free(psubInfo->filename);
psubInfo->filename = 0;
freePattern(psubInfo);
@@ -936,15 +887,16 @@ static char *substituteGetReplacements(subInfo *psubInfo)
}
if (psubInfo->isPattern) {
int gotFirstPattern = 0;
bool gotFirstPattern = false;
while (subGetNextToken(psubFile) == tokenSeparator);
ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList);
while (1) {
std::list<std::string>& patternList = psubInfo->patternList;
std::list<std::string>::iterator patternIt = patternList.begin();
while (true) {
if (psubFile->token == tokenRBrace) {
subGetNextToken(psubFile);
EXITS(psubInfo->macroReplacements);
return psubInfo->macroReplacements;
EXITS(psubInfo->macroReplacements.c_str());
return psubInfo->macroReplacements.c_str();
}
if (psubFile->token != tokenString) {
@@ -954,13 +906,13 @@ static char *substituteGetReplacements(subInfo *psubInfo)
if (gotFirstPattern)
catMacroReplacements(psubInfo, ",");
gotFirstPattern = 1;
gotFirstPattern = true;
if (ppatternNode) {
catMacroReplacements(psubInfo, ppatternNode->var);
if (patternIt != patternList.end()) {
catMacroReplacements(psubInfo, patternIt->c_str());
catMacroReplacements(psubInfo, "=");
catMacroReplacements(psubInfo, psubFile->string);
ppatternNode = (patternNode *) ellNext(&ppatternNode->node);
++patternIt;
}
else {
subFileErrPrint(psubFile, "Warning, too many values given");
@@ -969,12 +921,12 @@ static char *substituteGetReplacements(subInfo *psubInfo)
while (subGetNextToken(psubFile) == tokenSeparator);
}
}
else while(1) {
else while(true) {
switch(subGetNextToken(psubFile)) {
case tokenRBrace:
subGetNextToken(psubFile);
EXITS(psubInfo->macroReplacements);
return psubInfo->macroReplacements;
EXITS(psubInfo->macroReplacements.c_str());
return psubInfo->macroReplacements.c_str();
case tokenSeparator:
catMacroReplacements(psubInfo, ",");
@@ -1017,11 +969,12 @@ static char *subGetNextLine(subFile *psubFile)
return &psubFile->inputBuffer[0];
}
static void subFileErrPrint(subFile *psubFile,char * message)
static void subFileErrPrint(subFile *psubFile, const char * message)
{
fprintf(stderr, "msi: %s\n",message);
fprintf(stderr, " in substitution file '%s' at line %d:\n %s",
psubFile->substitutionName, psubFile->lineNum, psubFile->inputBuffer);
psubFile->substitutionName.c_str(), psubFile->lineNum,
psubFile->inputBuffer);
}
@@ -1107,32 +1060,8 @@ done:
static void catMacroReplacements(subInfo *psubInfo, const char *value)
{
size_t len = strlen(value);
ENTER;
if (psubInfo->size <= (psubInfo->curLength + len)) {
size_t newsize = psubInfo->size + MAX_BUFFER_SIZE;
char *newbuf;
STEP("Enlarging buffer");
if (newsize <= psubInfo->curLength + len)
newsize = psubInfo->curLength + len + 1;
newbuf = calloc(1, newsize);
if (!newbuf) {
fprintf(stderr, "calloc failed for size %lu\n",
(unsigned long) newsize);
abortExit(1);
}
if (psubInfo->macroReplacements) {
memcpy(newbuf, psubInfo->macroReplacements, psubInfo->curLength);
free(psubInfo->macroReplacements);
}
psubInfo->size = newsize;
psubInfo->macroReplacements = newbuf;
}
STEPS("Appending", value);
strcat(psubInfo->macroReplacements, value);
psubInfo->curLength += len;
psubInfo->macroReplacements += value;
EXIT;
}

View File

@@ -241,7 +241,7 @@ Assuming there is a system state called "blue", that is being controlled by
some other facility such as a timing system, updates could be restricted to
periods only when "blue" is true by using
Hal$ camonitor 'test:channel' 'test:channel.{"while":"blue"}'
Hal$ camonitor 'test:channel' 'test:channel.{"sync":{"while":"blue"}}'
...
=cut

View File

@@ -348,9 +348,9 @@ static long get_enum_strs(const DBADDR *paddr,struct dbr_enumStrs *pes)
/*SETTING no_str=0 breaks channel access clients*/
pes->no_str = 2;
memset(pes->strs,'\0',sizeof(pes->strs));
strncpy(pes->strs[0],prec->znam,sizeof(prec->znam));
strncpy(pes->strs[0],prec->znam,sizeof(pes->strs[0]));
if(*prec->znam!=0) pes->no_str=1;
strncpy(pes->strs[1],prec->onam,sizeof(prec->onam));
strncpy(pes->strs[1],prec->onam,sizeof(pes->strs[1]));
if(*prec->onam!=0) pes->no_str=2;
return(0);
}

View File

@@ -183,17 +183,16 @@ static long special(DBADDR *paddr, int after)
#define indexof(field) int64inRecord##field
static long get_units(DBADDR *paddr,char *units)
static long get_units(DBADDR *paddr, char *units)
{
int64inRecord *prec=(int64inRecord *)paddr->precord;
int64inRecord *prec = (int64inRecord *) paddr->precord;
if(paddr->pfldDes->field_type == DBF_LONG) {
strncpy(units,prec->egu,DB_UNITS_SIZE);
if (paddr->pfldDes->field_type == DBF_INT64) {
strncpy(units, prec->egu, DB_UNITS_SIZE);
}
return(0);
return 0;
}
static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
{
int64inRecord *prec=(int64inRecord *)paddr->precord;
@@ -255,7 +254,7 @@ static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
static void checkAlarms(int64inRecord *prec, epicsTimeStamp *timeLast)
{
enum {
enum {
range_Lolo = 1,
range_Low,
range_Normal,

View File

@@ -51,7 +51,7 @@ static long special(DBADDR *, int);
#define get_array_info NULL
#define put_array_info NULL
#define get_units NULL
#define get_precision NULL
static long get_precision(const DBADDR *, long *);
#define get_enum_str NULL
#define get_enum_strs NULL
#define put_enum_str NULL
@@ -204,6 +204,16 @@ static long special(DBADDR *paddr, int after)
}
}
static long get_precision(const DBADDR *paddr,long *precision)
{
mbbiDirectRecord *prec=(mbbiDirectRecord *)paddr->precord;
if(dbGetFieldIndex(paddr)==mbbiDirectRecordVAL)
*precision = prec->nobt;
else
recGblGetPrec(paddr,precision);
return 0;
}
static void monitor(mbbiDirectRecord *prec)
{
epicsUInt16 events = recGblResetAlarms(prec);

View File

@@ -51,7 +51,7 @@ static long special(DBADDR *, int);
#define get_array_info NULL
#define put_array_info NULL
#define get_units NULL
#define get_precision NULL
static long get_precision(const DBADDR *, long *);
#define get_enum_str NULL
#define get_enum_strs NULL
#define put_enum_str NULL
@@ -313,6 +313,16 @@ static long special(DBADDR *paddr, int after)
return 0;
}
static long get_precision(const DBADDR *paddr,long *precision)
{
mbboDirectRecord *prec=(mbboDirectRecord *)paddr->precord;
if(dbGetFieldIndex(paddr)==mbboDirectRecordVAL)
*precision = prec->nobt;
else
recGblGetPrec(paddr,precision);
return 0;
}
static void monitor(mbboDirectRecord *prec)
{
epicsUInt16 events = recGblResetAlarms(prec);

View File

@@ -99,7 +99,7 @@ static void monitor(stateRecord *prec)
monitor_mask = recGblResetAlarms(prec);
if(strncmp(prec->oval,prec->val,sizeof(prec->val))) {
db_post_events(prec,&(prec->val[0]),monitor_mask|DBE_VALUE|DBE_LOG);
strncpy(prec->oval,prec->val,sizeof(prec->val));
strncpy(prec->oval,prec->val,sizeof(prec->oval));
}
return;
}

View File

@@ -121,8 +121,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
if (status)
return status;
}
strncpy(prec->oval, prec->val, sizeof(prec->val));
strncpy(prec->oval, prec->val, sizeof(prec->oval));
return 0;
}
@@ -183,7 +182,7 @@ static void monitor(stringinRecord *prec)
if (strncmp(prec->oval, prec->val, sizeof(prec->val))) {
monitor_mask |= DBE_VALUE | DBE_LOG;
strncpy(prec->oval, prec->val, sizeof(prec->val));
strncpy(prec->oval, prec->val, sizeof(prec->oval));
}
if (prec->mpst == stringinPOST_Always)

View File

@@ -126,8 +126,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
if(status)
return status;
}
strncpy(prec->oval, prec->val, sizeof(prec->val));
strncpy(prec->oval, prec->val, sizeof(prec->oval));
return 0;
}
@@ -215,7 +214,7 @@ static void monitor(stringoutRecord *prec)
if (strncmp(prec->oval, prec->val, sizeof(prec->val))) {
monitor_mask |= DBE_VALUE | DBE_LOG;
strncpy(prec->oval, prec->val, sizeof(prec->val));
strncpy(prec->oval, prec->val, sizeof(prec->oval));
}
if (prec->mpst == stringoutPOST_Always)

View File

@@ -212,7 +212,7 @@ sub _getline {
my $line = readline $self->{stdout};
if (defined $line) {
chomp $line;
$line =~ s/[\r\n]+ $//x; # chomp broken on Windows?
printf "#%d >> %s\n", $self->{pid}, $line if $self->{debug};
}
elsif (eof($self->{stdout})) {

View File

@@ -11,7 +11,7 @@
use strict;
use Test;
BEGIN {plan tests => 9}
BEGIN {plan tests => 12}
# Check include/substitute command model
ok(msi('-I .. ../t1-template.txt'), slurp('../t1-result.txt'));
@@ -33,16 +33,9 @@ ok(msi('-S../t6-substitute.txt ../t6-template.txt'), slurp('../t6-result.txt'));
# Output option -o and verbose option -V
my $out = 't7-output.txt';
my $count = 5; # Try up to 5 times...
my $result;
do {
unlink $out;
msi("-I.. -V -o $out ../t1-template.txt");
$result = slurp($out);
print "# msi output file empty, retrying\n"
if $result eq '';
} while ($result eq '') && (--$count > 0);
ok($result, slurp('../t7-result.txt'));
unlink $out;
msi("-I.. -V -o $out ../t1-template.txt");
ok(slurp($out), slurp('../t7-result.txt'));
# Dependency generation, include/substitute model
ok(msi('-I.. -D -o t8.txt ../t1-template.txt'), slurp('../t8-result.txt'));
@@ -50,6 +43,17 @@ ok(msi('-I.. -D -o t8.txt ../t1-template.txt'), slurp('../t8-result.txt'));
# Dependency generation, dbLoadTemplate format
ok(msi('-I.. -D -ot9.txt -S ../t2-substitution.txt'), slurp('../t9-result.txt'));
# Substitution file, variable format, with 0 variable definitions
ok(msi('-I. -I.. -S ../t10-substitute.txt'), slurp('../t10-result.txt'));
# Substitution file, pattern format, with 0 pattern definitions
ok(msi('-I. -I.. -S ../t11-substitute.txt'), slurp('../t11-result.txt'));
# Substitution file, environment variable macros in template filename
my %envs = (TEST_NO => 12, PREFIX => 't');
@ENV{ keys %envs } = values %envs;
ok(msi('-I. -I.. -S ../t12-substitute.txt'), slurp('../t12-result.txt'));
delete @ENV{ keys %envs }; # Not really needed
# Test support routines
@@ -63,21 +67,8 @@ sub slurp {
sub msi {
my ($args) = @_;
my $nul = $^O eq 'MSWin32' ? 'NUL' : '/dev/null';
my $msi = '@TOP@/bin/@ARCH@/msi';
$msi .= '.exe' if ($^O eq 'MSWin32') || ($^O eq 'cygwin');
my $result;
if ($args =~ m/-o / && $args !~ m/-D/) {
# An empty result is expected
$result = `$msi $args`;
}
else {
# Try up to 5 times, sometimes msi fails on Windows
my $count = 5;
do {
$result = `$msi $args`;
print "# result of '$msi $args' empty, retrying\n"
if $result eq '';
} while ($result eq '') && (--$count > 0);
}
return $result;
$msi =~ tr(/)(\\) if $^O eq 'MSWin32';
return `$msi $args 2>$nul`;
}

View File

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

View File

@@ -0,0 +1,8 @@
file t10-template.txt {
{}
}
global { a=gbl }
file t10-template.txt {
{}
}

View File

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

View File

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

View File

@@ -0,0 +1,10 @@
file t11-template.txt {
pattern {}
{}
}
global { a=gbl }
file t11-template.txt {
pattern {}
{}
}

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
file $(PREFIX)$(TEST_NO)-template.txt {
{ a=foo }
}

View File

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

View File

@@ -9,6 +9,7 @@
#include <testMain.h>
#include <dbUnitTest.h>
#include <errlog.h>
#include <dbCommon.h>
#include <dbAccess.h>
#include <epicsEvent.h>
@@ -16,6 +17,7 @@
#include <iocsh.h>
#include "registryFunction.h"
#include <subRecord.h>
#include <dbScan.h>
epicsEventId done;
static int waitFor;
@@ -28,11 +30,17 @@ long doneSubr(subRecord *prec)
return 0;
}
static
void dummydone(void *usr, struct dbCommon* prec)
{
epicsEventMustTrigger(done);
}
void asyncproctest_registerRecordDeviceDriver(struct dbBase *);
MAIN(asyncproctest)
{
testPlan(21);
testPlan(27);
done = epicsEventMustCreate(epicsEventEmpty);
@@ -99,6 +107,40 @@ MAIN(asyncproctest)
testdbGetFieldEqual("chain3", DBF_LONG, 7);
testdbGetFieldEqual("chain3.A", DBF_LONG, 2);
testDiag("===== Chain 4 ======");
{
dbCommon *dummy=testdbRecordPtr("chain4_dummy");
testdbPutFieldOk("chain4_pos.PROC", DBF_LONG, 0);
/* sync once queue to wait for any queued RPRO */
scanOnceCallback(dummy, dummydone, NULL);
if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK)
testAbort("Processing timed out");
testdbGetFieldEqual("chain4_pos", DBF_SHORT, 1);
testdbGetFieldEqual("chain4_rel", DBF_SHORT, 1);
testdbGetFieldEqual("chain4_lim", DBF_SHORT, 1);
}
testDiag("===== Chain 5 ======");
{
dbCommon *dummy=testdbRecordPtr("chain4_dummy");
testdbPutFieldOk("chain5_cnt.PROC", DBF_LONG, 0);
/* sync once queue to wait for any queued RPRO */
scanOnceCallback(dummy, dummydone, NULL);
if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK)
testAbort("Processing timed out");
testdbGetFieldEqual("chain5_cnt", DBF_SHORT, 1);
}
testIocShutdownOk();
testdbCleanup();

View File

@@ -53,3 +53,41 @@ record(sub, "done3") {
field(FLNK, "chain3")
field(TPRO, "$(TPRO=)")
}
# loop breaking regression
# should _not_ RPRO
record(calcout,"chain4_pos") {
field(CALC, "E:=E+1;E")
field(OUT,"chain4_rel.A PP")
field(TPRO, "$(TPRO=)")
}
record(calc,"chain4_rel") {
field(CALC, "E:=E+1;E")
field(FLNK,"chain4_lim")
field(TPRO, "$(TPRO=)")
}
record(calc,"chain4_lim") {
field(CALC, "E:=E+1;E")
field(INPA,"chain4_pos PP")
field(INPB,"chain4_pos.HIGH PP")
field(INPC,"chain4_pos.LOW PP")
field(FLNK,"chain4_pos")
field(TPRO, "$(TPRO=)")
}
record(bo, "chain4_dummy") {
field(TPRO, "$(TPRO=)")
}
# loop breaking regression part 2
# selft link should _not_ RPRO
record(calcout,"chain5_cnt") {
field(CALC, "E:=E+1;E")
field(INPA,"chain5_cnt.A PP")
field(OUT,"chain5_cnt.A PP")
field(FLNK,"chain5_cnt")
field(TPRO, "$(TPRO=)")
}

View File

@@ -14,6 +14,7 @@ $ENV{HARNESS_ACTIVE} = 1 if scalar @ARGV && shift eq '-tap';
$ENV{EPICS_CA_AUTO_ADDR_LIST} = 'NO';
$ENV{EPICS_CA_ADDR_LIST} = 'localhost';
$ENV{EPICS_CA_SERVER_PORT} = 55064;
$ENV{EPICS_CAS_BEACON_PORT} = 55065;
$ENV{EPICS_CAS_INTF_ADDR_LIST} = 'localhost';
$ENV{EPICS_PVA_AUTO_ADDR_LIST} = 'NO';
@@ -29,13 +30,32 @@ my $prefix = "test-$$";
my $ioc = EPICS::IOC->new();
#$ioc->debug(1);
$SIG{__DIE__} = $SIG{INT} = $SIG{QUIT} = $SIG{ALRM} = sub {
$ioc->kill
if ref($ioc) eq 'EPICS::IOC' && $ioc->started;
$SIG{__DIE__} = $SIG{INT} = $SIG{QUIT} = sub {
$ioc->kill;
BAIL_OUT('Caught signal');
};
alarm 30;
# Watchdog utilities
sub kill_bail {
my $doing = shift;
return sub {
$ioc->kill;
BAIL_OUT("Timeout $doing");
}
}
sub watchdog (&$$) {
my ($do, $timeout, $abort) = @_;
$SIG{ALRM} = $abort;
alarm $timeout;
&$do;
alarm 0;
}
# Start the IOC
my $softIoc = "$bin/softIocPVA$exe";
$softIoc = "$bin/softIoc$exe"
@@ -43,42 +63,78 @@ $softIoc = "$bin/softIoc$exe"
BAIL_OUT("Can't find a softIoc executable")
unless -x $softIoc;
$ioc->start($softIoc, '-x', $prefix);
$ioc->cmd; # Wait for command prompt
watchdog {
$ioc->start($softIoc, '-x', $prefix);
$ioc->cmd; # Wait for command prompt
} 10, kill_bail('starting softIoc');
# Get Base Version number from PV
my $pv = "$prefix:BaseVersion";
my @pvs = $ioc->dbl('stringin');
grep(m/$pv/, @pvs)
or BAIL_OUT('No BaseVersion record found');
watchdog {
my @pvs = $ioc->dbl('stringin');
grep(m/^ $pv $/x, @pvs)
or BAIL_OUT('No BaseVersion record found');
} 10, kill_bail('running dbl');
my $version = $ioc->dbgf("$pv");
my $version;
watchdog {
$version = $ioc->dbgf("$pv");
} 10, kill_bail('getting BaseVersion');
like($version, qr/^ \d+ \. \d+ \. \d+ /x,
"Got BaseVersion '$version' from iocsh");
note("CA server configuration:\n",
map(" $_\n", $ioc->cmd('casr', 1)));
my $caget = "$bin/caget$exe";
# Channel Access
SKIP: {
skip "caget not available", 1 unless -x $caget;
my $caVersion = `$caget -w5 $pv`;
like($caVersion, qr/$pv \s+ \Q$version\E/x,
'Got same BaseVersion from caget');
my $caget = "$bin/caget$exe";
skip "caget not available", 1
unless -x $caget;
# CA Server Diagnostics
watchdog {
note("CA server configuration:\n",
map(" $_\n", $ioc->cmd('casr', 1)));
} 10, kill_bail('running casr');
# CA Client test
watchdog {
my $caVersion = `$caget -w5 $pv`;
like($caVersion, qr/^ $pv \s+ \Q$version\E $/x,
'Got same BaseVersion from caget');
} 10, kill_bail('doing caget');
}
my $pvget = "$bin/pvget$exe";
# PV Access
SKIP: {
my $pvget = "$bin/pvget$exe";
skip "softIocPVA not available", 1
if $softIoc eq "$bin/softIoc$exe";
note("PVA server configuration:\n",
map(" $_\n", $ioc->cmd('pvasr')));
# PVA Server Diagnostics
watchdog {
note("PVA server configuration:\n",
map(" $_\n", $ioc->cmd('pvasr')));
} 10, kill_bail('running pvasr');
skip "pvget not available", 1
unless -x $pvget;
my $pvaVersion = `$pvget -w5 $pv`;
like($pvaVersion, qr/$pv \s .* \Q$version\E/x,
'Got same BaseVersion from pvget');
# PVA Client test
watchdog {
my $pvaVersion = `$pvget -w5 $pv`;
like($pvaVersion, qr/^ $pv \s .* \Q$version\E \s* $/x,
'Got same BaseVersion from pvget');
} 10, kill_bail('doing pvget');
}
$ioc->kill;

View File

@@ -1,4 +1,4 @@
EPICS_LIBCOM_MAJOR_VERSION = 3
EPICS_LIBCOM_MINOR_VERSION = 17
EPICS_LIBCOM_MAINTENANCE_VERSION = 2
EPICS_LIBCOM_MAINTENANCE_VERSION = 5
EPICS_LIBCOM_DEVELOPMENT_FLAG = 0

View File

@@ -319,7 +319,7 @@ static const iocshArg epicsMutexShowAllArg1 = { "level",iocshArgInt};
static const iocshArg * const epicsMutexShowAllArgs[2] =
{&epicsMutexShowAllArg0,&epicsMutexShowAllArg1};
static const iocshFuncDef epicsMutexShowAllFuncDef =
{"epicsMutexShowAll",1,epicsMutexShowAllArgs};
{"epicsMutexShowAll",2,epicsMutexShowAllArgs};
static void epicsMutexShowAllCallFunc(const iocshArgBuf *args)
{
epicsMutexShowAll(args[0].ival,args[1].ival);

View File

@@ -37,7 +37,7 @@
static unsigned short ioc_log_port;
static long ioc_log_file_limit;
static char ioc_log_file_name[256];
static char ioc_log_file_name[512];
static char ioc_log_file_command[256];
@@ -866,7 +866,12 @@ static int setupSIGHUP(struct ioc_log_server *pserver)
*/
static void sighupHandler(int signo)
{
(void) write(sighupPipe[1], "SIGHUP\n", 7);
const char msg[] = "SIGHUP\n";
const ssize_t bytesWritten = write(sighupPipe[1], msg, sizeof(msg));
if (bytesWritten != sizeof(msg)) {
fprintf(stderr, "iocLogServer: failed to write to SIGHUP pipe because "
"`%s'\n", strerror(errno));
}
}
@@ -884,7 +889,10 @@ static void serviceSighupRequest(void *pParam)
/*
* Read and discard message from pipe.
*/
(void) read(sighupPipe[0], buff, sizeof buff);
if (read(sighupPipe[0], buff, sizeof buff) <= 0) {
fprintf(stderr, "iocLogServer: failed to read from SIGHUP pipe because "
"`%s'\n", strerror(errno));
};
/*
* Determine new log file name.

View File

@@ -171,6 +171,7 @@ ipAddrToAsciiEngine & ipAddrToAsciiEngine::allocate ()
}
ipAddrToAsciiGlobal::ipAddrToAsciiGlobal () :
mutex(__FILE__, __LINE__),
thread ( *this, "ipToAsciiProxy",
epicsThreadGetStackSize(epicsThreadStackBig),
epicsThreadPriorityLow ),

View File

@@ -78,6 +78,11 @@ static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
static const char * const tsfmt = "%Y-%m-%d %H:%M:%S.%09f";
/* defined in osiClockTime.c or osdTime.cpp */
int osdTimeGetCurrent ( epicsTimeStamp *pDest );
/* cleared if/when gtPvt.timeProviders contains more than the default osdTimeGetCurrent() */
static int useOsdGetCurrent = 1;
/* Implementation */
static void generalTime_InitOnce(void *dummy)
@@ -103,6 +108,9 @@ int generalTimeGetExceptPriority(epicsTimeStamp *pDest, int *pPrio, int ignore)
gtProvider *ptp;
int status = S_time_noProvider;
if(useOsdGetCurrent)
return osdTimeGetCurrent(pDest);
generalTime_Init();
IFDEBUG(2)
@@ -148,6 +156,9 @@ int epicsShareAPI epicsTimeGetCurrent(epicsTimeStamp *pDest)
int status = S_time_noProvider;
epicsTimeStamp ts;
if(useOsdGetCurrent)
return osdTimeGetCurrent(pDest);
generalTime_Init();
IFDEBUG(20)
@@ -370,6 +381,11 @@ static void insertProvider(gtProvider *ptp, ELLLIST *plist, epicsMutexId lock)
ellAdd(plist, &ptp->node);
}
/* Check to see if we have more than just the OS default time source */
if(plist==&gtPvt.timeProviders && (ellCount(plist)!=1 || ptp->get.Time!=&osdTimeGetCurrent)) {
useOsdGetCurrent = 0;
}
epicsMutexUnlock(lock);
}

View File

@@ -205,9 +205,6 @@ epicsTime::epicsTime (const epicsTimeStamp &ts)
epicsTime::epicsTime () :
secPastEpoch(0u), nSec(0u) {}
epicsTime::epicsTime (const epicsTime &t) :
secPastEpoch (t.secPastEpoch), nSec (t.nSec) {}
epicsTime epicsTime::getCurrent ()
{
epicsTimeStamp current;

View File

@@ -82,7 +82,6 @@ public:
class formatProblemWithStructTM {};
epicsTime ();
epicsTime ( const epicsTime & t );
static epicsTime getEvent ( const epicsTimeEvent & );
static epicsTime getCurrent ();

View File

@@ -26,7 +26,7 @@
static clock_serv_t host_clock;
extern "C" {
static int osdTimeGetCurrent (epicsTimeStamp *pDest)
int osdTimeGetCurrent (epicsTimeStamp *pDest)
{
mach_timespec_t mts;
struct timespec ts;

View File

@@ -47,7 +47,8 @@
extern "C" void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName );
static int osdTimeGetCurrent ( epicsTimeStamp *pDest );
extern "C"
int osdTimeGetCurrent ( epicsTimeStamp *pDest );
// for mingw
#if !defined ( MAXLONGLONG )
@@ -116,7 +117,7 @@ static int done = timeRegister();
//
// osdTimeGetCurrent ()
//
static int osdTimeGetCurrent ( epicsTimeStamp *pDest )
int osdTimeGetCurrent ( epicsTimeStamp *pDest )
{
assert ( pCurrentTime );

View File

@@ -7,12 +7,30 @@
* Author: Till Straumann <strauman@slac.stanford.edu>, 2011, 2014
*/
// pull in libc feature test macros
#include <stdlib.h>
// execinfo.h may not be present if uclibc is configured to omit backtrace()
#if !defined(__UCLIBC_MAJOR__) || defined(__UCLIBC_HAS_EXECINFO__)
# define HAS_EXECINFO 1
#else
# define HAS_EXECINFO 0
#endif
#if HAS_EXECINFO
#include <execinfo.h>
#endif
#define epicsExportSharedSymbols
#include "epicsStackTracePvt.h"
int epicsBackTrace(void **buf, int buf_sz)
{
#if HAS_EXECINFO
return backtrace(buf, buf_sz);
#else
return -1;
#endif
}

View File

@@ -18,9 +18,6 @@ void osdMonotonicInit(void)
{
unsigned i;
clockid_t ids[] = {
#ifdef CLOCK_MONOTONIC_RAW
CLOCK_MONOTONIC_RAW, /* Linux specific */
#endif
#ifdef CLOCK_HIGHRES
CLOCK_HIGHRES, /* solaris specific */
#endif

View File

@@ -153,6 +153,10 @@ epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * pmutex)
void epicsMutexOsdShow(struct epicsMutexOSD * pmutex, unsigned int level)
{
/* GLIBC w/ NTPL is passing the &lock.__data.__lock as the first argument (UADDR)
* of the futex() syscall. __lock is at offset 0 of the enclosing structures.
*/
printf(" pthread_mutex_t* uaddr=%p\n", &pmutex->lock);
}
#else /*defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 */

View File

@@ -567,14 +567,13 @@ static epicsThreadOSD *createImplicit(void)
pthreadInfo->osiPriority = 0;
#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0
{
struct sched_param param;
int policy;
if(pthread_getschedparam(tid,&policy,&param) == 0)
pthreadInfo->osiPriority =
(param.sched_priority - pcommonAttr->minPriority) * 100.0 /
if(pthread_getschedparam(tid,&pthreadInfo->schedPolicy,&pthreadInfo->schedParam) == 0) {
if ( pcommonAttr->usePolicy && pthreadInfo->schedPolicy == pcommonAttr->schedPolicy ) {
pthreadInfo->osiPriority =
(pthreadInfo->schedParam.sched_priority - pcommonAttr->minPriority) * 100.0 /
(pcommonAttr->maxPriority - pcommonAttr->minPriority + 1);
}
}
}
#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */
status = pthread_setspecific(getpthreadInfo,(void *)pthreadInfo);
@@ -907,7 +906,7 @@ epicsShareFunc double epicsShareAPI epicsThreadSleepQuantum ()
{
double hz;
hz = sysconf ( _SC_CLK_TCK );
if(hz<0)
if(hz<=0)
return 0.0;
return 1.0 / hz;
}

View File

@@ -32,7 +32,7 @@
LAST_RESORT_PRIORITY, osdTimeGetCurrent)
extern "C" {
static int osdTimeGetCurrent (epicsTimeStamp *pDest)
int osdTimeGetCurrent (epicsTimeStamp *pDest)
{
struct timeval tv;
struct timezone tz;

View File

@@ -65,7 +65,7 @@ typedef int SOCKET;
#define socket_ioctl(A,B,C) ioctl(A,B,(int)C)
typedef int osiSockIoctl_t;
typedef int osiSocklen_t;
typedef int osiSockOptMcastLoop_t;
typedef char osiSockOptMcastLoop_t;
typedef char osiSockOptMcastTTL_t;
#define FD_IN_FDSET(FD) ((FD)<FD_SETSIZE&&(FD)>=0)

View File

@@ -40,7 +40,7 @@ static struct {
static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
#if defined(CLOCK_REALTIME) && !defined(_WIN32)
#if defined(CLOCK_REALTIME) && !defined(_WIN32) && !defined(__APPLE__)
/* This code is not used on systems without Posix CLOCK_REALTIME,
* but the only way to detect that is from the OS headers, so the
* Makefile can't exclude compiling this file on those systems.
@@ -48,7 +48,7 @@ static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
/* Forward references */
static int ClockTimeGetCurrent(epicsTimeStamp *pDest);
int osdTimeGetCurrent(epicsTimeStamp *pDest);
#if defined(vxWorks) || defined(__rtems__)
static void ClockTimeSync(void *dummy);
@@ -89,7 +89,7 @@ static void ClockTime_InitOnce(void *pfirst)
/* Register as a time provider */
generalTimeRegisterCurrentProvider("OS Clock", LAST_RESORT_PRIORITY,
ClockTimeGetCurrent);
osdTimeGetCurrent);
}
void ClockTime_Init(int synchronize)
@@ -125,7 +125,7 @@ void ClockTime_Init(int synchronize)
else {
/* No synchronization thread */
if (firstTime)
ClockTimeGetCurrent(&ClockTimePvt.startTime);
osdTimeGetCurrent(&ClockTimePvt.startTime);
}
}
}
@@ -191,7 +191,7 @@ static void ClockTimeSync(void *dummy)
/* Time Provider Routine */
static int ClockTimeGetCurrent(epicsTimeStamp *pDest)
int osdTimeGetCurrent(epicsTimeStamp *pDest)
{
struct timespec clockNow;

View File

@@ -147,7 +147,7 @@ private:
epicsThread thread;
const double sleepQuantum;
bool okToShare;
bool exitFlag;
int exitFlag; // use atomic ops
bool terminateFlag;
void run ();
void reschedule ();

View File

@@ -25,6 +25,7 @@ const double timerQueue :: exceptMsgMinPeriod = 60.0 * 5.0; // seconds
epicsTimerQueue::~epicsTimerQueue () {}
timerQueue::timerQueue ( epicsTimerQueueNotify & notifyIn ) :
mutex(__FILE__, __LINE__),
notify ( notifyIn ),
pExpireTmr ( 0 ),
processThread ( 0 ),

View File

@@ -15,6 +15,7 @@
#include <stdio.h>
#define epicsExportSharedSymbols
#include "epicsAtomic.h"
#include "timerPrivate.h"
#include "errlog.h"
@@ -46,7 +47,7 @@ timerQueueActive ::
_refMgr ( refMgr ), queue ( *this ), thread ( *this, "timerQueue",
epicsThreadGetStackSize ( epicsThreadStackMedium ), priority ),
sleepQuantum ( epicsThreadSleepQuantum() ), okToShare ( okToShareIn ),
exitFlag ( false ), terminateFlag ( false )
exitFlag ( 0 ), terminateFlag ( false )
{
}
@@ -59,7 +60,7 @@ timerQueueActive::~timerQueueActive ()
{
this->terminateFlag = true;
this->rescheduleEvent.signal ();
while ( ! this->exitFlag ) {
while ( ! epics::atomic::get(this->exitFlag) ) {
this->exitEvent.wait ( 1.0 );
}
// in case other threads are waiting here also
@@ -87,7 +88,7 @@ void timerQueueActive :: _printLastChanceExceptionMessage (
void timerQueueActive :: run ()
{
this->exitFlag = false;
epics::atomic::set(this->exitFlag, 0);
while ( ! this->terminateFlag ) {
try {
double delay = this->queue.process ( epicsTime::getCurrent() );
@@ -105,7 +106,7 @@ void timerQueueActive :: run ()
epicsThreadSleep ( 10.0 );
}
}
this->exitFlag = true;
epics::atomic::set(this->exitFlag, 1);
this->exitEvent.signal (); // no access to queue after exitEvent signal
}
@@ -143,7 +144,7 @@ void timerQueueActive::show ( unsigned int level ) const
printf ( "exit event\n" );
this->exitEvent.show ( level - 1u );
printf ( "exitFlag = %c, terminateFlag = %c\n",
this->exitFlag ? 'T' : 'F',
epics::atomic::get(this->exitFlag) ? 'T' : 'F',
this->terminateFlag ? 'T' : 'F' );
}
}

View File

@@ -20,6 +20,7 @@
#include "timerPrivate.h"
timerQueueActiveMgr::timerQueueActiveMgr ()
:mutex(__FILE__, __LINE__)
{
}

View File

@@ -255,8 +255,8 @@ TESTSPEC_RTEMS = libComTestHarness.boot; epicsRunLibComTests
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
TESTPROD_RTEMS = $(TESTPROD_HOST)
TESTSCRIPTS_RTEMS += $(filter-out epicsUnitTestTest.t, $(TESTS:%=%.t))
TESTPROD = $(TESTPROD_HOST)
TESTSCRIPTS += $(filter-out epicsUnitTestTest.t, $(TESTS:%=%.t))
endif
@@ -288,4 +288,15 @@ TESTPROD_HOST += cvtFastPerform
cvtFastPerform_SRCS += cvtFastPerform.cpp
testHarness_SRCS += cvtFastPerform.cpp
ifeq ($(OS_CLASS),Linux)
ifeq ($(USE_POSIX_THREAD_PRIORITY_SCHEDULING),YES)
TESTPROD_HOST += nonEpicsThreadPriorityTest
nonEpicsThreadPriorityTest_SRCS += nonEpicsThreadPriorityTest.cpp
nonEpicsThreadPriorityTest_SYS_LIBS += $(POSIX_LDLIBS:-l%=%)
testHarness_SRCS += nonEpicsThreadPriorityTest.cpp
epicsRunLibComTests_CFLAGS += -DHAVE_PTHREAD_PRIORITY_SCHEDULING
TESTS += nonEpicsThreadPriorityTest
endif
endif
include $(TOP)/configure/RULES

View File

@@ -78,6 +78,7 @@ typedef struct {
size_t checkLen;
epicsEventId jammer;
int jam;
epicsEventId done;
} clientPvt;
static void testLogPrefix(void);
@@ -102,12 +103,13 @@ static const char prefixexpectedmsg[] = "A message without prefix"
static char prefixmsgbuffer[1024];
static
void testEqInt_(int lhs, int rhs, const char *LHS, const char *RHS)
static void
testEqInt_(int line, int lhs, int rhs, const char *LHS, const char *RHS)
{
testOk(lhs==rhs, "%s (%d) == %s (%d)", LHS, lhs, RHS, rhs);
testOk(lhs==rhs, "%d: %s (%d) == %s (%d)", line, LHS, lhs, RHS, rhs);
}
#define testEqInt(L, R) testEqInt_(L, R, #L, #R);
#define testEqInt(L, R) testEqInt_(__LINE__, L, R, #L, #R);
static
void logClient(void* raw, const char* msg)
{
@@ -161,6 +163,7 @@ void logClient(void* raw, const char* msg)
}
pvt->count++;
epicsEventSignal(pvt->done);
}
MAIN(epicsErrlogTest)
@@ -169,7 +172,7 @@ MAIN(epicsErrlogTest)
char msg[256];
clientPvt pvt, pvt2;
testPlan(32);
testPlan(40);
strcpy(msg, truncmsg);
@@ -188,7 +191,9 @@ MAIN(epicsErrlogTest)
pvt2.jam = 0;
pvt.jammer = epicsEventMustCreate(epicsEventEmpty);
pvt.done = epicsEventMustCreate(epicsEventEmpty);
pvt2.jammer = epicsEventMustCreate(epicsEventEmpty);
pvt2.done = epicsEventMustCreate(epicsEventEmpty);
testDiag("Check listener registration");
@@ -200,6 +205,7 @@ MAIN(epicsErrlogTest)
errlogPrintfNoConsole("%s", pvt.expect);
errlogFlush();
epicsEventMustWait(pvt.done);
testEqInt(pvt.count, 1);
errlogAddListener(&logClient, &pvt2);
@@ -210,7 +216,10 @@ MAIN(epicsErrlogTest)
errlogPrintfNoConsole("%s", pvt.expect);
errlogFlush();
epicsEventMustWait(pvt.done);
testEqInt(pvt.count, 2);
epicsEventMustWait(pvt2.done);
testEqInt(pvt2.count, 1);
/* Removes the first listener */
@@ -223,6 +232,10 @@ MAIN(epicsErrlogTest)
errlogPrintfNoConsole("%s", pvt2.expect);
errlogFlush();
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
"%d: Listener 1 didn't run", __LINE__);
testOk(epicsEventTryWait(pvt2.done) == epicsEventOK,
"%d: Listener 2 ran", __LINE__);
testEqInt(pvt.count, 2);
testEqInt(pvt2.count, 2);
@@ -234,6 +247,10 @@ MAIN(epicsErrlogTest)
errlogPrintfNoConsole("Something different");
errlogFlush();
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
"%d: Listener 1 didn't run", __LINE__);
testOk(epicsEventTryWait(pvt2.done) == epicsEventWaitTimeout,
"%d: Listener 2 didn't run", __LINE__);
testEqInt(pvt.count, 2);
testEqInt(pvt2.count, 2);
@@ -248,6 +265,7 @@ MAIN(epicsErrlogTest)
errlogPrintfNoConsole("%s", longmsg);
errlogFlush();
epicsEventMustWait(pvt.done);
testEqInt(pvt.count, 3);
pvt.expect = NULL;
@@ -259,13 +277,15 @@ MAIN(epicsErrlogTest)
pvt.jam = 1;
errlogPrintfNoConsole("%s", longmsg);
epicsThreadSleep(0.1);
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
"%d: Listener 1 didn't run", __LINE__);
testEqInt(pvt.count, 3);
epicsEventSignal(pvt.jammer);
errlogFlush();
epicsEventMustWait(pvt.done);
testEqInt(pvt.count, 4);
testDiag("Find buffer capacity (%u theoretical)",LOGBUFSIZE);
@@ -303,6 +323,9 @@ MAIN(epicsErrlogTest)
errlogFlush();
}
testOk(epicsEventTryWait(pvt.done) == epicsEventOK,
"%d: Listener 1 ran", __LINE__);
testDiag("Checking buffer use after partial flush");
/* Use the numbers from the largest block size above */
@@ -317,16 +340,18 @@ MAIN(epicsErrlogTest)
for (i = 0; i < N; i++) {
errlogPrintfNoConsole("%s", msg);
}
epicsThreadSleep(0.1); /* should really be a second Event */
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
"%d: Listener 1 didn't run", __LINE__);
testEqInt(pvt.count, 0);
/* Extract the first 2 messages, 2*(sizeof(msgNode) + 128) bytes */
pvt.jam = -2;
epicsEventSignal(pvt.jammer);
epicsThreadSleep(0.1);
epicsThreadSleep(0.5);
testDiag("Drained %u messages", pvt.count);
epicsEventMustWait(pvt.done);
testEqInt(pvt.count, 2);
/* The buffer has space for 1 more message: sizeof(msgNode) + 256 bytes */
@@ -335,12 +360,15 @@ MAIN(epicsErrlogTest)
testDiag("Overflow the buffer");
errlogPrintfNoConsole("%s", msg);
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
"%d: Listener 1 didn't run", __LINE__);
testEqInt(pvt.count, 2);
epicsEventSignal(pvt.jammer); /* Empty */
errlogFlush();
testDiag("Logged %u messages", pvt.count);
epicsEventMustWait(pvt.done);
testEqInt(pvt.count, N+1);
/* Clean up */

View File

@@ -381,8 +381,14 @@ MAIN(epicsStdlibTest)
testOk(epicsParseFloat("1e-40", &f, NULL) == S_stdlib_underflow,
"Float '1e-40' => underflow");
#ifdef vxWorks
testTodoBegin("Not detected on VxWorks");
#endif
testOk(epicsParseDouble("1e-330", &d, NULL) == S_stdlib_underflow,
"Double '1e-330' => underflow");
#ifdef vxWorks
testTodoEnd();
#endif
testOk(epicsScanFloat("1e30", &f) && fabs(f - 1e30) < 1e24,
"Float '1e30'");

View File

@@ -108,11 +108,11 @@ inline double delayVerify::delay () const
double delayVerify::checkError () const
{
const double messageThresh = 2.0; // percent
const double messageThresh = 5.0; // percent
double actualDelay = this->expireStamp - this->beginStamp;
double measuredError = actualDelay - this->expectedDelay;
double percentError = 100.0 * fabs ( measuredError ) / this->expectedDelay;
if ( ! testOk1 ( percentError < messageThresh ) ) {
if ( ! testOk ( percentError < messageThresh, "%f < %f", percentError, messageThresh ) ) {
testDiag ( "delay = %f s, error = %f s (%.1f %%)",
this->expectedDelay, measuredError, percentError );
}

View File

@@ -0,0 +1,100 @@
#include <epicsThread.h>
#include "epicsUnitTest.h"
#include "testMain.h"
#include <stdio.h>
#include <string.h>
#ifdef __rtems__
/* RTEMS is posix but currently does not use the pthread API */
MAIN(nonEpicsThreadPriorityTest)
{
testPlan(1);
testSkip(1, "Platform does not use pthread API");
return testDone();
}
#else
static void *nonEpicsTestFunc(void *arg)
{
unsigned int pri;
// epicsThreadGetIdSelf() creates an EPICS context
// verify that the priority computed by epics context
// is OK
pri = epicsThreadGetPriority( epicsThreadGetIdSelf() );
if ( ! testOk( 0 == pri, "'createImplicit' assigned correct priority (%d) to non-EPICS thread", pri) ) {
return 0;
}
return (void*)1;
}
static void testFunc(void *arg)
{
epicsEventId ev = (epicsEventId)arg;
int policy;
struct sched_param param;
int status;
pthread_t tid;
void *rval;
pthread_attr_t attr;
status = pthread_getschedparam(pthread_self(), &policy,&param);
if ( status ) {
testSkip(1, "pthread_getschedparam failed");
goto done;
}
if ( SCHED_FIFO != policy ) {
testSkip(1, "nonEpicsThreadPriorityTest must be executed with privileges to use SCHED_FIFO");
goto done;
}
if ( pthread_attr_init( &attr ) ) {
testSkip(1, "pthread_attr_init failed");
goto done;
}
if ( pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ) ) {
testSkip(1, "pthread_attr_setinheritsched failed");
goto done;
}
if ( pthread_attr_setschedpolicy ( &attr, SCHED_OTHER ) ) {
testSkip(1, "pthread_attr_setschedpolicy failed");
goto done;
}
param.sched_priority = 0;
if ( pthread_attr_setschedparam ( &attr, &param ) ) {
testSkip(1, "pthread_attr_setschedparam failed");
goto done;
}
if ( pthread_create( &tid, &attr, nonEpicsTestFunc, 0 ) ) {
testSkip(1, "pthread_create failed");
goto done;
}
if ( pthread_join( tid, &rval ) ) {
testSkip(1, "pthread_join failed");
goto done;
}
done:
epicsEventSignal( ev );
}
MAIN(nonEpicsThreadPriorityTest)
{
testPlan(2);
epicsEventId testComplete = epicsEventMustCreate(epicsEventEmpty);
epicsThreadMustCreate("nonEpicsThreadPriorityTest", epicsThreadPriorityLow,
epicsThreadGetStackSize(epicsThreadStackMedium),
testFunc, testComplete);
epicsEventWaitStatus status = epicsEventWait(testComplete);
testOk(status == epicsEventWaitOK,
"epicsEventWait returned %d", status);
return testDone();
}
#endif