Compare commits

..

322 Commits

Author SHA1 Message Date
Andrew Johnson
82042e270e CONFIG_BASE_VERSION for R3.16.0.1 2016-03-03 17:36:43 -06:00
Andrew Johnson
32c3f8a44f Merged reformatted Release Notes from 3.15, to revno 12735 2016-03-03 17:32:51 -06:00
Andrew Johnson
fc4f010972 Documentation updates for release 2016-03-03 17:19:50 -06:00
Andrew Johnson
bd2ada3f8c Reformat Release Notes slightly 2016-03-03 16:02:46 -06:00
Andrew Johnson
3d7e95b88a Debug print value of IF flags in default/osdNetIntf.c 2016-03-03 13:39:48 -06:00
Andrew Johnson
8fea0117ae Clean up build rules, remove CapFast & dbst support 2016-03-02 14:20:53 -06:00
Andrew Johnson
8392106f59 Merged revno 12734 from 3.15 branch 2016-03-02 10:44:40 -06:00
Andrew Johnson
9502841b55 Silence unused variable warning 2016-03-02 10:41:34 -06:00
Michael Davidsaver
19680d7869 libCom/test: fixup epicsAtomicTest
Add testBasic to check basic op symantics.
Add anon namespace to hide symbols other than MAIN(epicsAtomicTest).
Replace using namespace with using for individual functions.
For RTEMS skip the tests using threads as the use of
RTEMS_NO_TIMESLICE prevents them from being meaningful
2016-03-01 10:57:26 -05:00
Michael Davidsaver
4a0ef34771 edit RELEASE_NOTES.html 2016-02-29 21:08:25 -05:00
Michael Davidsaver
5d42e60745 rec/test: asTest fixup rtems test harness
Since this test has device support it must appear in a
DLL for windows dynamic builds.
However, the rRDD function is in the executable,
and not accessible here.  So use iocsh.
For rtems/vxworks the test harness clears
iocsh registrations, so iocsh can't work here.
2016-02-29 20:43:40 -05:00
Michael Davidsaver
e01d4b14d6 rec/test: add missing TESTFILES 2016-02-29 20:37:25 -05:00
Andrew Johnson
791fa1310a Merge fixes from 3.15 branch, to revno 12733 2016-02-29 18:15:28 -06:00
Andrew Johnson
b4404c8266 Clean up Release Notes merge 2016-02-29 17:53:59 -06:00
Andrew Johnson
f73f3332fa Merge RTEMS NTP fix from 3.14 branch, to revno 12615 2016-02-29 17:49:08 -06:00
Andrew Johnson
3f4c6abb31 Fix for RTEMS NTP sync issue 2016-02-29 17:07:03 -06:00
Andrew Johnson
64cd49410e Make epicsMutex intialization use epicsThreadOnceId
Includes Michael's patch to replace calls to the epicsMutex API
from inside the libCom/osi/os/RTEMS/osdThread.c code.

Fixes lp:1542539
2016-02-29 16:22:43 -06:00
Andrew Johnson
0f6f833e91 Merged link-support branch 2016-02-29 13:48:18 -06:00
Andrew Johnson
c08df82898 Merged compress branch 2016-02-29 13:24:16 -06:00
Andrew Johnson
f0c56bcbba Merged 3.15 changes, to revno 12731 2016-02-26 18:16:26 -06:00
Andrew Johnson
76c422c5fd Merge Cygwin fix from 3.14, fix in rsrv too 2016-02-26 17:10:35 -06:00
Andrew Johnson
fee62836cd Use OS __CYGWIN__ macro, deprecate our CYGWIN32 2016-02-26 16:27:13 -06:00
Michael Davidsaver
2c48bcc26b dbChannelShow reporting detail level changed 2016-02-26 16:07:47 -05:00
Andrew Johnson
a3ab342833 Fix Release Notes for last commit 2016-02-26 14:22:00 -06:00
Andrew Johnson
3304470323 Merged the rsrvbindiface branch 2016-02-26 14:15:22 -06:00
Andrew Johnson
4f2f910d09 Added extern "C" wrappers to clean up some Solaris warnings 2016-02-26 13:50:39 -06:00
Andrew Johnson
217f515985 Merged fix-dbGet-arrays branch 2016-02-26 12:29:20 -06:00
Andrew Johnson
ebc59a4223 Add epicsAtomicTest to epicsRunLibComTests 2016-02-26 12:27:33 -06:00
Andrew Johnson
58285501de Revert, test programs may be included in harness without being run 2016-02-26 12:25:59 -06:00
Andrew Johnson
fa07bc3efe Added build files for cygwin to cross-compile linux-arm targets
Slightly strange combination and not actually tested, but could
be useful for some developers -- some FPGA tool-sets come with a
Cygwin cross-build environment for their target devices.
2016-02-26 11:19:37 -06:00
Michael Davidsaver
bfa8e324f7 remove dbChArrTest from testHarness
dbChArrTest calls iocInit() and so can't
be in the shared test harness
2016-02-25 21:10:42 -05:00
Michael Davidsaver
51a69862e7 ioc/db: single return for dbGet()
Single return path after call to prset->get_array_info
to ensure that paddr->pfield is restored.
2016-02-25 20:59:10 -05:00
Michael Davidsaver
4b8c11aeef memset transposed arguments 2016-02-25 20:08:44 -05:00
Andrew Johnson
62efa2e96a Fix the dbGet() array with field-log bug.
This also fixes a problem where the array is used in circular buffer
mode and the request has an offset, but the array is not actually
full at the time; to copy the data out of the array the convert()
routine needs to know the capacity of the array to know when to wrap
around, but we were passing it the current length instead.
2016-02-25 17:50:33 -06:00
Andrew Johnson
c7bcb09540 Add tests for dbGet() with db_field_log
Regression testing for a bug where an array field with a sub-array
channel filter can read past the end of the db_field_log array,
because in dbGet() the call to prset->get_array_info() occurs even
when pfl is supposed to be overriding the record's array.
2016-02-25 17:44:45 -06:00
Andrew Johnson
5ee778b0c2 Merged changes from Base-3.14, to revno 12613 2016-02-25 14:13:40 -06:00
Michael Davidsaver
7b3a040b34 rsrv: use SOCKERRNO
Must use OSI SOCKERRNO for correct handling
of errors w/ winsock
2016-02-25 11:32:56 -05:00
Andrew Johnson
e721be4ff5 Merged the posix-mlockall branch 2016-02-25 10:20:56 -06:00
Michael Davidsaver
88b47c021c cygwin inherits winsock broadcast behavior 2016-02-25 11:10:28 -05:00
Michael Davidsaver
d0a316f7ca win32: include ws2tcpip.h from osdSock.h
Needed to get some additional definitions
including IP_ADD_MEMBERSHIP
2016-02-25 11:02:09 -05:00
Ralph Lange
ba307ba6e7 rsrv: populate the server list - also on Windows builds 2016-02-25 09:36:31 +01:00
Andrew Johnson
d943f6966f Minor updates to the extensions/configure template 2016-02-24 17:39:29 -06:00
Andrew Johnson
76d7ce1f76 Fix MSVC (de)-optimizer issue 2016-02-24 15:06:59 -06:00
Andrew Johnson
66b0acbe3e Report UDP client status on Windows 2016-02-24 11:07:33 -06:00
Ralph Lange
822c6815c5 pcas: fix compilation on MSC Windows 2016-02-24 11:24:17 +01:00
Andrew Johnson
684ae7b35c Merged the fix-calc-bit-manipulation branch 2016-02-23 16:21:38 -06:00
Andrew Johnson
e18eca80db Changes from patch in lp:1539791 2016-02-23 15:43:26 -06:00
Andrew Johnson
b45622ac5e Test each bitwise cast individually for overflow 2016-02-23 12:42:00 -06:00
Andrew Johnson
89e6fdbca0 Fixed conversion overflows in tests
Minor tidying-up, added comments about casting for bitwise operations.
2016-02-19 15:57:44 -06:00
Ralph Lange
599e6635fb libCom/calc: fix bit31 bit manipulations 2016-02-18 15:19:01 +01:00
Ralph Lange
4241b4e6cb libCom/test: add calc tests for bit31 operators (lp:1514520) 2016-02-18 15:17:52 +01:00
Ralph Lange
9eedf0581e libCom/calc: Use epics(U)Int32 for all integer calculations
Adapted backport of 3.15 revision 12308
2016-02-18 14:44:43 +01:00
Michael Davidsaver
ffb23a3480 pcas: don't send beacons w/ connect()d socket
use sendto() and leave the origin field to 0.0.0.0
so it will be filled in by the receiving repeater
2016-02-16 16:28:04 -05:00
Michael Davidsaver
2be59e985d Avoid race in linkRetarget
Add dbCaSync() to avoid a race between a call to dbCaGetField()
and the link becoming connected.
2016-02-16 15:07:40 -05:00
Michael Davidsaver
b4fd19e7db libCom/test: remove test_debug from epicsStackTraceTest
No one will look at this output unless the test fails,
then more output is better.
2016-02-15 17:09:44 -05:00
Michael Davidsaver
969b1030b2 RTEMS: show PWD and nfsMount() args on start 2016-02-15 12:29:22 -05:00
Michael Davidsaver
84880e876d dbUnitTest: show PWD when database load fails 2016-02-15 12:29:22 -05:00
Michael Davidsaver
45a741ac6f libCom/RTEMS: ensure lookup of localhost on RTEMS
create /etc/hosts if it doesn't already exist
2016-02-15 12:29:06 -05:00
Michael Davidsaver
0575d3764f libCom/test: fixup RTEMS tests to run clean 2016-02-15 12:29:06 -05:00
Michael Davidsaver
48da96cce5 libCom/test: epicsMMIOTest missing test spec 2016-02-15 12:29:06 -05:00
Michael Davidsaver
18d863b918 db/test: missing/wrong .db file names 2016-02-15 12:06:10 -05:00
Andrew Johnson
61e61aab24 Use MS' strto[u]ll() if present 2016-02-12 15:15:19 -06:00
Andrew Johnson
d3e046a466 Document [s:e] and [s] filter usage 2016-02-12 14:20:33 -06:00
Andrew Johnson
f528f347cd Increase testMonitorWait() timeout 2016-02-12 11:33:54 -06:00
Andrew Johnson
ffed8910c0 Merged Michael's dbcadynamic branch 2016-02-12 10:22:47 -06:00
Andrew Johnson
99d331e50a Print timestamp with bad UDP msg errors 2016-02-11 14:36:48 -06:00
Andrew Johnson
0fb923186a Merged dbentrybuf branch 2016-02-10 14:27:38 -06:00
Andrew Johnson
ce500bc98c More indentation and other trivial cleanup 2016-02-10 14:03:52 -06:00
Andrew Johnson
6376ae8b1a Fixed some issues with indentation 2016-02-09 18:31:17 -06:00
Andrew Johnson
445c0ada8c Fix Windows problems from Valgrind merge. 2016-02-09 14:10:51 -06:00
Andrew Johnson
87759d1c1d Merged Michael's valgrind branch 2016-02-08 18:01:59 -06:00
Andrew Johnson
9d95528eba Show UDP name server status in casr 2+ output 2016-02-08 16:34:37 -06:00
Michael Davidsaver
95a1998e0b update release notes 2016-02-04 19:09:44 -05:00
Michael Davidsaver
5769db3862 rsrv: "casr 1" shows ignore list 2016-02-04 18:57:02 -05:00
Michael Davidsaver
00a74fbb4e libCom: fix debug print formats in osiSockDiscoverBroadcastAddresses() 2016-02-04 18:45:18 -05:00
Michael Davidsaver
03fde89dd4 rsrv: EPICS_CAS_IGNORE_ADDR_LIST 2016-02-04 18:44:33 -05:00
Michael Davidsaver
8421b46398 update release notes 2016-02-04 17:59:08 -05:00
Michael Davidsaver
650aaea203 ca: update CAref.html with changes to beacon address list
Note that server beacon address list will no longer
consider client address list as a fallback.
2016-02-04 17:56:31 -05:00
Michael Davidsaver
fcfe7ed312 pcas: no fallbacks for beacon address list
Don't mix client and server configuration
by defaulting the server beacon list to use
the client address list.
2016-02-04 17:39:28 -05:00
Andrew Johnson
f2c4b2c81e Add config for linux cross-compile to windows-x64-mingw 2016-02-04 15:57:23 -06:00
Michael Davidsaver
167dea3735 rsrv: decouple some client/server config
the automatic beacon address list should follow
the server interface address list, and not be
effected by any client config.

Also, don't populate the interface address list
from EPICS_CAS_BEACON_ADDR_LIST as this will
almost never contain local interface addresses.
2016-01-29 08:33:41 -05:00
Andrew Johnson
1c1eb030a9 Improve casr() output 2016-01-27 17:59:38 -06:00
Andrew Johnson
43cf5af621 dbQuietMacroWarnings control 2016-01-26 12:28:00 -06:00
Michael Davidsaver
235e4bd835 rsrv: more beacon address fixups
should really be based on *server* config
as beacons should be sent to clients attempting
to connect to the server.

By default send beacons to bcast addresses
from the interface address list.
2016-01-23 16:15:17 -05:00
Michael Davidsaver
601d6d9fe8 rsrv: oops
improper rebase
2016-01-23 13:53:44 -05:00
Michael Davidsaver
b961b25151 rsrv: fix wildcard check ordering
filter out mcast addresses before wildcard uniqueness check.
2016-01-23 13:53:44 -05:00
Michael Davidsaver
eb9246e4c9 rsrv: beaconCounter not updated 2016-01-21 17:14:23 -05:00
Andrew Johnson
d729006908 Fix build for vxWorks 5.5.2 (again) 2016-01-21 12:27:01 -06:00
Michael Davidsaver
0aeba281be update release notes 2016-01-21 13:05:56 -05:00
Michael Davidsaver
04fee51795 ipAddrToAsciiAsync.: don't try to join the daemon thread
This isn't really necessary and may unnecessarily delay
things (~10 sec. on Linux) if a query to an unreachable
DNS server is in progress.
2016-01-21 13:00:50 -05:00
Michael Davidsaver
cba449d53b pcas: listen for mcast search request 2016-01-21 12:17:02 -05:00
Michael Davidsaver
cdffcbfbae update release notes 2016-01-21 12:17:02 -05:00
Michael Davidsaver
ba5ea5da93 don't use EPICS_CA_ADDR_LIST for server interface list
keep existing behavior, and server binds wildcard
when only client address list is specified.
2016-01-21 12:17:02 -05:00
Michael Davidsaver
c884175a86 rsrv: loopback mcast beacons 2016-01-21 12:17:02 -05:00
Michael Davidsaver
a4e9dcff00 rsrv: fixup beacon address list handling
should be based on client interface list,
not server.
2016-01-21 12:17:02 -05:00
Michael Davidsaver
c9323da5e3 minor
fix typo
expand error message
skip redundant (and misleading) print of beacon address list
2016-01-21 12:17:02 -05:00
Andrew Johnson
ffe441d032 Fix build for vxWorks 5.5.2 2016-01-21 10:34:32 -06:00
Michael Davidsaver
6143520680 update release notes 2016-01-13 21:46:00 -05:00
Michael Davidsaver
f814398d77 std/rec/test: add compressTest
test circular buffer mode for FIFO and LIFO
2016-01-13 20:58:58 -05:00
Michael Davidsaver
d2e4fcbbac compressRecord: LIFO doesn't zero pad
Change handling of OFF so that reading a partially
populated buffer doesn't pad with zero.

Simplify put_value() and get_array_info()
2016-01-13 20:58:58 -05:00
Michael Davidsaver
4cca4673ca compress: copy some text from RRM wiki
Still a lot remaining.  No idea what to do about the images.
2016-01-13 20:58:58 -05:00
Michael Davidsaver
840da801fb std/filters: arr wrap on capacity not length 2016-01-13 20:58:58 -05:00
Michael Davidsaver
e75f44100e dbAccess: dbGet wrap at capacity count not valid count
Change the usage of rset::get_array_info in dbGet()
in the case that offset>0 and no_elements<max_elements.
Presently this results in an early wrap which corrupts
the last valid element in the result buffer
in the case that offset==1 and no_element==max_elements-1.

Should have no effect in other situations.
2016-01-13 20:58:58 -05:00
Ralph Lange
45c8a173f3 libCom/osi: fix timespec definition for MinGW 2016-01-13 17:28:23 +01:00
Michael Davidsaver
e227ae3590 update asTest
test dbGetString()
and fix cleanup order
2016-01-12 11:37:01 -05:00
Michael Davidsaver
ecedd9c362 Move dbGetStringNum() to dbStaticLib.c
use getpMessage() and keep it private
2016-01-12 11:36:58 -05:00
Michael Davidsaver
4e7b185977 update release notes 2016-01-12 10:13:10 -05:00
Michael Davidsaver
b74ecff2fc dbCa: set putnelements for scalar case
ensure that putnelements is kept in sync. w/ pputNative
2016-01-12 09:38:28 -05:00
Michael Davidsaver
1255e75828 db/test: missing coverage for dbGetLink w/ larger element count 2016-01-12 09:32:47 -05:00
Michael Davidsaver
d6eea14fd0 dbCa: dbCaGet/PutLink dynamic size
dbCaGetLink return actual number of elements w/o zero padding.
dbCaPutLink write only requested number of elements, w/o padding
2016-01-11 23:01:26 -05:00
Michael Davidsaver
98504d1cdc libca: enable mcast loopback 2016-01-11 20:59:08 -05:00
Michael Davidsaver
568ece9c27 rsrv: multicast name lookup 2016-01-11 20:59:08 -05:00
Michael Davidsaver
f876bdb42c rsrv: don't leak sockets when building tcp set 2016-01-11 20:59:08 -05:00
Michael Davidsaver
605074a580 rsrv: additional fallback for interface list 2016-01-11 20:59:08 -05:00
Michael Davidsaver
64da181e6f expand beacon address list when binding to wildcard 2016-01-11 20:59:08 -05:00
Michael Davidsaver
823613fb4f libca: addAddrToChannelAccessAddressList report success 2016-01-11 20:59:08 -05:00
Michael Davidsaver
f837add8c4 envDefs: add envGetBoolConfigParam 2016-01-11 20:59:08 -05:00
Michael Davidsaver
15307c4db6 rsrv: redo initialization to support bind multiple interfaces.
consolidate most setup tasks in rsrv_init(),
which now spawns threads directly.
For each interface create 3-4 sockets,

* TCP listener
* UDP receiver (unicast)
* UDP receiver (broadcast optional)
* UDP beacon sender
2016-01-11 20:59:07 -05:00
Michael Davidsaver
8d5815ac95 rsrv: move some from req_server to rsrv_init 2016-01-11 20:59:07 -05:00
Michael Davidsaver
25d7d46e08 rsrv: no lazy init of BUCKET 2016-01-11 20:59:07 -05:00
Michael Davidsaver
5f31d9d2b1 rsrv: unglobalize IOC_sock 2016-01-11 20:59:07 -05:00
Michael Davidsaver
bb67c9db2f rsrv: UDP listener on more than one iface 2016-01-11 20:59:07 -05:00
Michael Davidsaver
cb965611ce rsrv: casr show multiple UDP clients 2016-01-11 20:59:07 -05:00
Michael Davidsaver
e805abe971 libCom/osi: osiSockDiscoverBroadcastAddresses() finds 127.255.255.255
For the default implementation of osiSockDiscoverBroadcastAddresses()
allow the loopback interface to have IFF_BROADCAST set (usually isn't)
and on Linux assume that the loopback broadcast address is usable
even if IFF_BROADCAST isn't set.
2016-01-11 20:59:07 -05:00
Michael Davidsaver
c80f71e294 rsrv: more renaming 2016-01-11 20:59:07 -05:00
Michael Davidsaver
692bbafffe rsrv: placeholder for cast_server thread shutdown 2016-01-11 20:59:07 -05:00
Michael Davidsaver
fd863738be rename prsrv_cast_client in cast_server() 2016-01-11 20:59:07 -05:00
Michael Davidsaver
2052062324 remove global prsrv_cast_client 2016-01-11 20:59:07 -05:00
Michael Davidsaver
5d74216017 rsrv: unnecessary use of global 2016-01-11 20:59:07 -05:00
Michael Davidsaver
991ff308e0 rsrv: when binding to a specific interface also bind to iface bcast
An oddness of BSD sockets (not winsock) is that binding to
INADDR_ANY will receive unicast and broadcast, but binding to
a specific interface address receives only unicast.  The trick
is to bind a second socket to the interface broadcast address,
which will then receive only broadcasts.
2016-01-11 20:59:07 -05:00
Andrew Johnson
9779d6987c Merged Michael's dbunittestmonitor branch 2016-01-07 13:22:43 -06:00
Michael Davidsaver
b7c2815c21 linkRetargetLinkTest set number of tests 2016-01-07 14:08:51 -05:00
Michael Davidsaver
77a5f0db77 epicsMMIODef.h use static inline
'static inline' is necessary for consistent behavior with C and C++.
Plain 'inline' in C can result in multiply defined symbol
errors when linking.
2016-01-05 21:31:51 -05:00
Bjorklund, Eric A
8d6469957e vxWorks: missing extern "C" around sysPci* prototypes 2016-01-05 21:16:04 -05:00
Andrew Johnson
ef90d2d3c7 Merged Michael's tzwin32 branch 2016-01-05 17:17:58 -06:00
Michael Davidsaver
29d2b7f6a2 RTEMS: readline missing } 2015-12-28 16:36:58 -05:00
Michael Davidsaver
ef39f658cd dbStaticLib: dbGetString() fix spaces in link modifiers 2015-12-21 19:42:31 -05:00
Michael Davidsaver
e298fb4c27 dbStatic: avoid write past end of DBENTRY::message
Prevent long link strings (and maybe others)
from overflowing the fixed length string
buffer associated with each DBENTRY.
2015-12-21 18:56:39 -05:00
Michael Davidsaver
d94840ae6c std/filter: plugins cleanup freeList 2015-12-21 13:44:23 -05:00
Michael Davidsaver
c1f742e741 libCom: valgrind track taskwd freelist 2015-12-21 13:44:23 -05:00
Michael Davidsaver
6f0814108a libCom: all valgrind hooks to epicsMutex free-list 2015-12-21 13:44:23 -05:00
Michael Davidsaver
770d9ab313 freeListLib: instrument with valgrind 2015-12-21 13:44:23 -05:00
Michael Davidsaver
7e7cca6164 dbmf: instrument with valgrind 2015-12-21 13:44:23 -05:00
Michael Davidsaver
03bdf4effa include valgrind.h 2015-12-21 13:44:23 -05:00
Michael Davidsaver
3258927bab freeListLib: cleanup indent
all whitespace
2015-12-21 13:44:23 -05:00
Michael Davidsaver
a8e285067d dbmf: cleanup indent
whitespace only
2015-12-21 13:44:23 -05:00
Michael Davidsaver
2d9c529f5e osdTime: win32 use C89 localtime/gmtime
as recommended by Freddie Akeroyd
2015-12-21 11:15:04 -05:00
Michael Davidsaver
05d3dbf453 add epicsTimeZoneTest 2015-12-21 11:13:36 -05:00
Andrew Johnson
695f516cbf Fix softIoc build with space in path to Base
Also improved error messages from makeInstallDir.pl.
2015-12-10 13:26:21 -06:00
Ralph Lange
fda3824ab4 Set version back to -DEV, add "not released" to RELEASE_NOTES 2015-11-22 18:10:40 +01:00
Andrew Johnson
b099ea2966 Clean up g++ unused-but-set-variable warning in pcas 2015-10-18 21:41:46 -05:00
Andrew Johnson
c4b128dfff Merged changes from 3.15 branch, to revno 12702 2015-09-18 18:13:58 -05:00
Ralph Lange
a245c3805a ioc/db/test: fix Makefile to link against librt on Solaris and Linux only 2015-09-16 17:33:20 +02:00
Ralph Lange
122b9d3185 db/test: Link dbStressTest against librt on all targets (not just solaris)
man clock_gettime: "Link with -lrt (only for glibc versions before 2.17)."
2015-09-16 13:43:43 +02:00
Andrew Johnson
4081377c4b Build fixes for Solaris 2015-09-10 13:26:37 -05:00
Andrew Johnson
405f50533e Fix for linux-x86 2015-09-10 09:56:50 -05:00
Michael Davidsaver
8f0950288a dbChannelTest missing call to testdbPrepare() 2015-09-08 10:04:06 -04:00
Michael Davidsaver
3551513297 dbStressLock: attempt to fix solaris linking failure 2015-09-08 09:52:52 -04:00
Michael Davidsaver
fc775d76aa asTest: copy+paste error
Didn't remove all use of local variable
2015-09-07 15:37:46 -04:00
Michael Davidsaver
7195372caf release note for new locking 2015-09-07 15:23:04 -04:00
Michael Davidsaver
655735fa13 asTest: fix link with mingw
This test has device support, which the registerRDD script
always emits as dllimport, so it fails to link if the dsets
are part of the executable itself.

Use iocsh to avoid calling the RDD function directly since
a DLL can't link against functions in the main executable.
2015-09-07 15:06:02 -04:00
Michael Davidsaver
8aea808b1d libCom: cross mingw imports posix time.h by default
at least mingw 4.9.1 for Debian 8 i386 host.
2015-09-07 10:50:48 -04:00
Andrew Johnson
66e126bae8 Merged Micheal's lockopt branch 2015-09-06 23:15:21 -05:00
Andrew Johnson
8ce86bb061 Undefine LOCKSET_DEBUG & LOCKSET_NOFREE 2015-08-31 16:08:08 -05:00
Andrew Johnson
f2ff270644 Fix comment 2015-08-31 15:55:39 -05:00
Andrew Johnson
895b415968 dbCaLinkTest fixes
* Attempt to fix Windows build failures
* Use C-style comments, not C++
2015-08-31 11:29:46 -05:00
Michael Davidsaver
87999c7047 dbLock: better error check when recursive locking attempted 2015-08-31 11:20:35 -04:00
Michael Davidsaver
9331ca3f44 dbLockTest: check additional recursive case 2015-08-31 11:17:34 -04:00
Andrew Johnson
1587c44d79 db/test: Add missing dependency 2015-08-28 15:19:23 -05:00
Michael Davidsaver
51f0a4509b std/rec/test: missing testdbPrepare() 2015-08-27 15:43:05 -04:00
Michael Davidsaver
94f00cb0c6 dbCa: more verbose error message in eventCallback 2015-08-27 12:44:50 -04:00
Michael Davidsaver
4544be090d std/rec/test: add linkRetargetLinkTest 2015-08-27 12:13:49 -04:00
Michael Davidsaver
15db30ba79 dbUnitTest: add testMonitor
Synchronize unittests by waiting for monitor updates
2015-08-27 13:46:22 -04:00
Andrew Johnson
cd79a7b53d dbCa: use dbf_type_to_DBR_TIME() macro 2015-08-27 10:30:42 -05:00
Michael Davidsaver
717d7ff4eb db/test: dbStressTest conditional TIME_STATS 2015-08-25 18:00:39 -04:00
Andrew Johnson
696e00eec9 Cleanup in callback.c 2015-08-24 12:18:23 -05:00
Andrew Johnson
a81d123ea5 Undo s/printf/errlogPrintf/ in iocsh commands 2015-08-24 11:26:25 -05:00
Andrew Johnson
f21fce0b42 Remove some unnecesary #includes 2015-08-24 10:47:51 -05:00
Andrew Johnson
6fffcf9985 Make dbLockTest work when LOCKSET_DEBUG undefined 2015-08-24 10:32:37 -05:00
Michael Davidsaver
d890c80961 dbLock: restore initialization of PV_LINK
PV_LINK -> DB_LINK must happen in doResolveLinks after add_record
2015-08-20 15:52:30 -04:00
Andrew Johnson
d49fc94314 Merge changes from 3.15 branch, to revno 12701 2015-08-20 11:24:07 -05:00
Andrew Johnson
536c6e91ff Export private dbLock*Ref() functions for tests 2015-08-19 11:26:47 -05:00
Michael Davidsaver
35c07a958f libCom/osi: osiLocalAddr() missing free() 2015-08-19 07:28:53 -04:00
Ambroz Bizjak
67aa96b957 freeListItemsAvail.patch from lp:1479316 2015-08-18 15:51:38 -04:00
Michael Davidsaver
041423092f osdnetIntf.c cleanup indentation 2015-08-18 15:51:38 -04:00
Michael Davidsaver
869410d6f6 libCom/osi: osiLocalAddr() avoid race on initialization 2015-08-18 15:46:22 -04:00
Ambroz Bizjak
6862ef6580 timerQueueActive.patch from lp:1479316 2015-08-18 14:39:27 -04:00
Ambroz Bizjak
b6aea68304 epicsSingletonMutex.patch from lp:1479316 2015-08-18 14:38:18 -04:00
Michael Davidsaver
9d00978176 src/ca/client: no need to remove const 2015-08-18 14:33:44 -04:00
Ambroz Bizjak
b6fbea9610 repeaterSubscribeTimer.patch from lp:1479316 2015-08-18 14:24:10 -04:00
Michael Davidsaver
180f40c1f7 dbLock: fix unlock w/o lock during iocInit 2015-08-18 11:09:35 -04:00
Michael Davidsaver
d5832354e8 iocInit: Don't free LSET until scan tasks have stopped 2015-08-18 11:09:35 -04:00
Michael Davidsaver
44980a1dac dbContextReadNotifyCache: variable length for CAC gets 2015-08-18 10:46:20 -04:00
Michael Davidsaver
af1daea3e7 dbContext: local CA variable length 2015-08-18 10:46:20 -04:00
Michael Davidsaver
b2716f0a19 dbCa: subscribe to variable length arrays 2015-08-18 10:46:20 -04:00
Michael Davidsaver
050b9f9a48 ioc/db/test: dbChannelTest use dbUnitTest.h 2015-08-18 09:07:18 -04:00
Michael Davidsaver
4ab6aa79e3 ioc/db/test: fixup arrShorthandTest to use dbUnitTest 2015-08-18 09:07:18 -04:00
Michael Davidsaver
1e39224836 dbCa: simplify shutdown
Don't need partial shutdown case anymore.
Also avoid race when destorying startStopEvent
2015-08-18 09:07:18 -04:00
Michael Davidsaver
f99fbe1964 move dbCaShutdown earlier
shutdown dbCa thread before free'ing records
2015-08-18 09:07:18 -04:00
Michael Davidsaver
aa04941119 ioc/db/test: wrong variable for -I 2015-08-18 09:07:18 -04:00
Andrew Johnson
46853d6862 Clean up compiler warnings 2015-08-16 17:43:10 -05:00
Michael Davidsaver
02ac91aa2a epicsReadline: ensure readline context is zero'd
Otherwise ->osd != NULL for the default RTEMS readline impl
2015-08-12 10:17:31 -05:00
Andrew Johnson
f1b403b59a Merged calinktest branch 2015-07-28 14:22:06 -05:00
Andrew Johnson
877a409de1 Fix podToHtml build issue on Windows 2015-07-24 15:59:38 -05:00
Andrew Johnson
7395d1d88d Make dbCa routines macros to dbLink wrappers 2015-07-24 14:06:45 -05:00
Andrew Johnson
4d3831ce58 Merged changes from 3.15 branch to revno 12699 2015-07-24 12:01:53 -05:00
Andrew Johnson
1d99ad6cfc Move closer to having pluggable link types.
All link types now have a plink->precord pointer.
Add link support entry tables for const, DB and CA link types.
Route all link I/O options through the new plink->lset.
2015-07-22 14:56:48 -05:00
Andrew Johnson
bd4d8ec26b Merged lockopt branch 2015-08-21 22:44:11 -05:00
Andrew Johnson
a5e1f367d6 Merged cbshutdown changes from 3.15 branch, revno 12690 2015-07-16 11:54:40 -05:00
Michael Davidsaver
eb31fcedeb iocInit: dbCa shutdown order 2015-07-13 17:51:46 -04:00
Michael Davidsaver
dd6edb103c scanIoTest: set # of tests 2015-07-13 17:51:46 -04:00
Andrew Johnson
50e6a305b1 Merged changes from 3.15 branch to revno 12689 2015-07-13 13:42:10 -05:00
Andrew Johnson
58602cfa80 Clean up in src/ioc/db/test/scanIoTest.c 2015-07-10 16:22:31 -05:00
Andrew Johnson
1e68f3899d Merged gen-vcs-version branch 2015-07-10 14:02:01 -05:00
Michael Davidsaver
8b3d37d392 genVersionHeader: work with git repo w/o tags
--always is necessary for (new) repositories w/o any tags.
--abbrev=20 to get more than the default 7 characters of the hash
2015-07-10 14:19:44 -04:00
Andrew Johnson
ed4bcd831f Update example template, release notes
Changed device support to use the long string input rectype.
Moved the device registration into its own file.
Used _APPNAME_ more, trying to prevent future name clashes.
2015-07-10 12:28:49 -05:00
Andrew Johnson
837111296e Updates to build rules and genVersionHeader.pl script
Added -q (quiet) flag, renamed INSTALL_QUIETLY build variable.
Moved GENVERSION variables into normal locations.
Changed from :: rule to : rule with FORCE dependency.
Removed use of --git-dir, only works when CWD is TOP.
Simplified some parts, more perlish.
Added VCS used to generated output file.
Expanded on the -v (verbose) output.
2015-07-10 12:09:37 -05:00
Michael Davidsaver
9198428619 dbLock: minor
must match following condition
2015-07-09 17:16:39 -04:00
Michael Davidsaver
8f3fcc2787 dbLock: fix initialization of self links
initPVLinks() doesn't correctly handle case where a record links
to itself.  This results it being added twice to lockRecordList,
which corrupts the list count, and the lockset ref. counter.

The error appears in dbLockCleanupRecords() when not all
locksets are free'd.
2015-07-09 17:07:49 -04:00
Kukhee Kim
a308fb7c1e fill zeros into buffer when BALG is changed to LIFO from FIFO. 2015-07-09 13:34:37 -07:00
Andrew Johnson
60823bd2fb Some edits to the Perl script 2015-07-08 16:46:34 -05:00
Andrew Johnson
d0790c3060 Merged epicsTime-status branch 2015-07-08 10:54:28 -05:00
Andrew Johnson
9f0d34656a Remove old libCom HTML files 2015-07-06 11:27:03 -05:00
Andrew Johnson
f6cdbe2693 tools: Support for DB files, added dbExpand.pl 2015-07-06 11:26:35 -05:00
Andrew Johnson
3a54e97758 libCom: epicsReadline refactoring 2015-06-29 17:45:18 -05:00
Andrew Johnson
155017bf09 Removed references to VxWorks 5.x 2015-06-09 15:36:28 -05:00
Andrew Johnson
961dc450c4 Merged changes from 3.15 branch up to Revno 12686 2015-06-05 13:48:57 -05:00
Andrew Johnson
53d01f6fb4 Show how to make time providers backwards-compatible 2015-06-03 00:15:39 -05:00
Michael Davidsaver
7fd4ac5a67 update template
use waveform as long string
use app. name in header and macro name
2015-06-01 19:19:24 -04:00
Michael Davidsaver
1240f09797 genVersionHeader: update HG, add SVN and BZR
Update the HG case to use --cwd to avoid dependence on shell syntax.
2015-06-01 19:19:24 -04:00
Michael Davidsaver
7947807e09 fix typo 2015-05-18 21:46:01 -04:00
Andrew Johnson
3a7cb33234 Fixed some build issues in db/test 2015-04-22 18:01:20 -05:00
Andrew Johnson
6b6eb5ff6a Merged fix from 3.15 branch, up to revno 12671 2015-04-22 16:54:30 -05:00
Andrew Johnson
e4f336de94 Perl style fixes, document more Perl programs 2015-04-21 15:09:24 -05:00
Andrew Johnson
fa23ba8a6a Merged changes from 3.15 branch up to revno 12670 2015-04-16 18:12:59 -05:00
Andrew Johnson
ee8bf98f94 Generate documentation from Perl modules
Add rules to generate HTML from POD in Perl modules and scripts.
Generate docs from several EPICS/ Perl modules.
2015-04-15 18:01:26 -05:00
Andrew Johnson
30b8872150 Merged changes from 3.15 branch, to revno 12662 2015-04-09 16:13:54 -05:00
Michael Davidsaver
7a8266c419 ioc/db/test: ensure dbCaLinkTest tests dbContextReadNotifyCacheAllocator
Read the same size twice to ensure that a cached buffer is reused
2015-04-09 14:55:56 -04:00
Michael Davidsaver
0c16937a5a ioc/db/test: extend dbCaLinkTest to check CAC get operation 2015-04-09 14:36:14 -04:00
Michael Davidsaver
ad6f55d92f dbStatic: always clear parser tempList
Warn if tempList is not empty when it should be
2015-04-08 18:37:12 -04:00
Michael Davidsaver
e852de64ff dbStatic: fix leak in parser
"% cdef" not free'd
2015-04-02 18:58:15 -04:00
Michael Davidsaver
f72ebb9fb9 dbmf: add flag to disable freelist 2015-04-02 18:58:15 -04:00
Michael Davidsaver
6ac1e050bd dbCa: missing inc. ref. when scan requests are queued
Missing caLinkInc() in scanComplete() drops ref.
2015-03-24 18:44:28 -04:00
Michael Davidsaver
ff4c88ed05 dbLock: comments 2015-03-24 14:18:11 -04:00
Michael Davidsaver
7a5d4cf6cc dbLock: minor 2015-03-24 14:18:11 -04:00
Michael Davidsaver
765fb7c63e dbLock: remove some unnecessary code
no need to hold spinlock for lockRecordList
the lockRecordList is protected by the lockSet::lock
2015-03-24 14:18:11 -04:00
Michael Davidsaver
fa4678798c iocInit: remove no-op
The work which was done here is moved to dbCloseLinks()
2015-03-24 14:18:11 -04:00
Michael Davidsaver
127bdfd9aa dbLock: comments 2015-03-24 14:18:11 -04:00
Michael Davidsaver
8fea2f5ae6 dbLock: default build options
Enable extra debugging.
Disable lockSet free list.
Enable recomputeCnt optimization
2015-03-24 14:18:11 -04:00
Michael Davidsaver
07bb2fbef7 dbLock: no c++ comments in c code 2015-03-24 14:18:11 -04:00
Michael Davidsaver
58a8a07cc4 dbAccess.c: dbLocker needs at most two refs 2015-03-24 14:18:11 -04:00
Michael Davidsaver
a78abd0070 dbLock: describe build options 2015-03-24 14:18:11 -04:00
Michael Davidsaver
c26b02c20d dbCaTest: adjust locking in dbcar() 2015-03-24 14:18:11 -04:00
Michael Davidsaver
ee297dc558 dbLock: use new backref tracking 2015-03-24 14:18:11 -04:00
Michael Davidsaver
8ce0ba1e54 dbLink: backward link tracking 2015-03-24 14:18:11 -04:00
Michael Davidsaver
b8a7da18d2 iocInit: links now initialized in dbLockInitRecords() 2015-03-24 14:18:11 -04:00
Michael Davidsaver
8af3ffb653 dbAccess: multi-locking in dbPutFieldLink
Use new locking API in dbPutFieldLink()
Adjust dbAddLink() and dbRemoveLink()
to pass a dbLocker* through to lockSet merge/split
2015-03-24 14:18:11 -04:00
Michael Davidsaver
ffd188bea3 dbLockTest and dbStressLock 2015-03-24 14:14:46 -04:00
Michael Davidsaver
af89b716f4 dbLock: multi-locking
new API to lock 2 or more lockSets simultaneously
removes global locks for dbScanLock() only
one global lock for debugging/freelist
Introduce dbLockPvt.h for internal API
2015-03-24 14:14:46 -04:00
Michael Davidsaver
adcde46e9e populate RDES early 2015-03-24 14:12:00 -04:00
Michael Davidsaver
c9d889ef3e asTest: quiet 2015-03-19 11:26:48 -04:00
Andrew Johnson
8344b4ed79 Merged changes from 3.15 up to revno 12658 2015-03-18 17:01:34 -05:00
Michael Davidsaver
3a647fb38b iocInit: prepareLinks after autosave pass0
Allow autosave to restore link fields again...
2015-03-17 19:13:33 -04:00
Michael Davidsaver
8bf765365f std/rec/test: add asTest to RTEMS test harness 2015-03-17 15:26:08 -04:00
Michael Davidsaver
e7037cc519 dbStatic: fix CONSTANT links
constantStr==NULL and constantStr=="" have different
meanings for recGblInitConstantLink() which is used with DOL.
constantStr should remain NULL unless explicitly set
by either initial("") or field(..., "")
2015-03-17 11:34:36 -04:00
Michael Davidsaver
0d41dd0113 std/rec/test: add asTest 2015-03-16 18:47:58 -04:00
Michael Davidsaver
52fc47dec5 start dbCaLinkTest 2015-03-13 15:24:07 -04:00
Michael Davidsaver
e73ed24631 db/test: copy arrRecord from std/filters/test 2015-03-13 15:24:07 -04:00
Michael Davidsaver
40cb21df9f std/filters/test: add input link to arrRecord 2015-03-13 15:24:07 -04:00
Michael Davidsaver
40d4d608ed std/filters/test: add simple processing callback to arrRecord 2015-03-13 15:24:07 -04:00
Michael Davidsaver
77efc973da iocInit: free PPNR 2015-03-13 15:24:07 -04:00
Michael Davidsaver
3e8ba7d7cf dbCa: enable unittests 2015-03-13 15:24:07 -04:00
Michael Davidsaver
a7a87372aa dbNotify: cleanup freelist 2015-03-13 15:24:07 -04:00
Michael Davidsaver
a014314384 db/test: add simple processing callback to xRecord 2015-03-13 15:24:07 -04:00
Michael Davidsaver
8ef1e0d177 re-write scanIoTest 2015-03-13 15:24:07 -04:00
Michael Davidsaver
b9cbf7a3ac Add xRecord device support 2015-03-13 15:24:06 -04:00
Michael Davidsaver
4bd4b93649 dbCa: update comments on locking 2015-03-13 15:15:03 -04:00
Michael Davidsaver
c120027768 dbCa: fix race in scanComplete
Use a reference counter in caLink to ensure it isn't
free'd while callbacks remain in the scanOnce queue.
2015-03-13 15:15:03 -04:00
Andrew Johnson
cc3a01d460 Merged changes from 3.15 branch up to revno 12655 2015-03-11 17:20:40 -05:00
Michael Davidsaver
06beb2cafb dbCaTest: use dbCaIsLinkConnected() instead of inspecting caLink* 2015-02-20 12:14:23 -05:00
Michael Davidsaver
be06b37b42 dbPutLinkTest: don't use variadic macros
MSVC doesn't seem to support them correctly
2015-02-18 10:47:42 -05:00
Michael Davidsaver
a526d0eb04 dbPutLinkTest: attempt MSVC workaround
Apparently MSVC doesn't play well with variadic macros.
2015-02-17 16:57:20 -05:00
Andrew Johnson
cd228ccf0e Merged dbscan-update branch 2015-02-17 14:12:38 -06:00
Michael Davidsaver
bbbf05414d dbScan: rename scanOnce3 to scanOnceCallback 2015-02-17 11:31:05 -05:00
Andrew Johnson
d7d45beedd Merged changes from 3.15 branch, revno 12629 2015-02-04 11:48:08 -06:00
Andrew Johnson
a7ad9a1256 Merged Michael's link-parsing branch 2015-02-03 00:38:23 -06:00
Michael Davidsaver
92641e9160 dbCaStatsTest: set # of tests 2015-01-05 17:45:00 -05:00
Michael Davidsaver
368223f783 db/test: devExtend() only during pass 0 2015-01-05 17:43:45 -05:00
Michael Davidsaver
85e21863dc dbStaticLib: set pdbbase=NULL after freeing 2015-01-05 17:43:17 -05:00
Michael Davidsaver
b9b8cde5f6 dbScan: avoid mustAlloc in eventNameToHandle()
Can already return NULL for other error conditions
2015-01-05 16:26:48 -05:00
Michael Davidsaver
6d7a0327e0 dbScanTest: start with test of scanOnce3() 2015-01-05 16:26:47 -05:00
Michael Davidsaver
d897c9b686 dbCa: use scanOnce3() to prevent once queue overflow
Prevent CP links to high rate records from
overflowing the once queue.
2015-01-05 16:26:47 -05:00
Michael Davidsaver
ff14d811a7 dbCaPvt.h: normalize indentation 2015-01-05 16:26:47 -05:00
Michael Davidsaver
2ba2b90b06 dbScan: add scanOnce3() w/ completion callback 2015-01-05 16:26:47 -05:00
Michael Davidsaver
7d50f62aed dbScan: allow direct scan of I/O Intr list
add scanIoImmediate() to allow drivers to implement
I/O Intr scanning without the overhead of maintaining
their own scan lists.
2015-01-05 16:26:47 -05:00
Michael Davidsaver
4518e8fa7f dbLink: re-intorduce struct lset and dbDb_lset"
without initLink, addLink, and loadLink
2014-12-29 18:16:26 -05:00
Andrew Johnson
e06b0a87b2 Merged changes from upstream 2014-12-23 12:21:37 -06:00
Andrew Johnson
c16c4590ec Replace epicsTimeERROR with specific error status values
Introduced error status values for epicsTime routines.
The epicsTimeERROR identifier no longer exists, so code that uses it will
no longer compile (deliberately). Replace all uses of it with a specific
error status value.
2014-12-02 15:25:09 -06:00
Andrew Johnson
b3ad20dec7 Merge perlbin fix from 3.14 2014-12-02 11:35:27 -06:00
Andrew Johnson
0f23784b32 Tweak to callback API. 2014-12-02 11:30:37 -06:00
Andrew Johnson
42a3cf8e11 Set version numbers for 3.16.0.0-DEV 2014-12-01 15:31:18 -06:00
Michael Davidsaver
79497c359c add release note 2014-08-29 11:05:56 -07:00
Michael Davidsaver
75321140ef update example template 2014-08-29 10:58:18 -07:00
Michael Davidsaver
95d0d97f6a more consistent macro names 2014-08-29 10:58:18 -07:00
Michael Davidsaver
e24d4637c9 genVersionHeader: use date+time as the default version number 2014-08-29 09:56:32 -07:00
Kukhee Kim
e5b354708e Implement LIFO behavior in compress record 2014-08-28 12:11:51 -07:00
Michael Davidsaver
bc9255ba1c add version header to example template 2014-08-28 10:38:24 -07:00
Michael Davidsaver
3f462bd032 check errors 2014-08-27 17:24:44 -07:00
Ralph Lange
6a018dce35 tools: Add support for Darcs to genVersionHeader.pl 2014-08-27 11:06:57 -07:00
Ralph Lange
55cf45364c tools: Make genVersionHeader.pl log more detailed and streamlined with other EPICS build output 2014-08-27 11:03:54 -07:00
Michael Davidsaver
3885c809a6 introduce genVersionHeader.pl 2014-08-22 15:23:51 -04:00
Michael Davidsaver
5847f98f89 oops 2014-08-01 13:55:33 -04:00
Michael Davidsaver
6ed997aacc Invalid HW links no longer partially initialized 2014-08-01 11:28:10 -04:00
Michael Davidsaver
5a8915c051 INST_IO no longer accept invalid string as empty 2014-08-01 11:28:10 -04:00
Michael Davidsaver
a919d7e09d dbLink: pass DBADDR* to dbAddLink()
Now we know whether a DB or CA link is created
before calling dbAddLink().
2014-08-01 11:28:10 -04:00
Michael Davidsaver
c5da1e3f89 dbAccess: update dbPutFieldLink() to use dbParseLink()/dbSetLink()
Now returns early for invalid link strings w/o modifying the
field, or detaching device support.
2014-08-01 11:28:10 -04:00
Michael Davidsaver
33804bd7ea iocInit: initialize links in iocBuild
Done before any driver, record, or device supports run.
Also before the hooks used by autosave.
2014-08-01 11:28:10 -04:00
Michael Davidsaver
200355bc00 dbStatic: re-write dbPutString() to use new link parsing
dbPutString() now has two behaviors.  If link_type==CONSTANT
and constantStr==NULL, the link has not yet been initialized.
Just set the ->text field.

If the link has been initialized, then use dbSetLink() to
make the assignment.

Eliminate special handling for DBF_DEVICE.
2014-08-01 11:28:10 -04:00
Michael Davidsaver
480797c0e0 db/test: extend dbPutLinkTest to cover dbParseLink() 2014-08-01 11:28:10 -04:00
Michael Davidsaver
470a9f9fc9 Introduce struct dbLinkInfo and dbParseLink()
This structure holds the processed form of any link type.
It is populated by dbParseLink(), which can be called w/o
record locking.

dbCanSetLink() checks the the parsed link, and a DBLINK field
have compatible types for assignment.
Record locking is required, but this does not modify the field.

dbSetLink() makes the actual assignment.
2014-08-01 11:28:10 -04:00
Michael Davidsaver
99626f03cd dbLink: remove struct lset and dbDb_lset
unnecessary exposure of internal API
2014-08-01 11:28:10 -04:00
Michael Davidsaver
8c60e7e2cb track INP/OUT field in struct dbFldDes
avoid string comparisons
2014-08-01 11:28:10 -04:00
263 changed files with 17599 additions and 6254 deletions

10
README
View File

@@ -2,14 +2,14 @@
EPICS Base - the central core of a control system toolkit
---------------------------------------------------------
Copyright (c) 1991-2003 The University of Chicago, as Operator
of Argonne National Laboratory.
Copyright UChicago Argonne LLC, as Operator of Argonne
National Laboratory.
Copyright (c) 1991-2003 The Regents of the University of
California, as Operator of Los Alamos National Laboratory.
EPICS Base Versions 3.13.7 and higher are distributed
subject to a Software License Agreement found in the
file LICENSE that is included with this distribution.
EPICS Base is distributed subject to a Software License
Agreement found in the file LICENSE that is included with
this distribution.
---------------------------------------------------------

View File

@@ -3,13 +3,12 @@
# National Laboratory.
# Copyright (c) 2002 The Regents of the University of California, as
# Operator of Los Alamos National Laboratory.
# EPICS BASE Versions 3.13.7
# and higher are distributed subject to a Software License Agreement found
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
#---------------------------------------------------------------
# Epics base directories
# EPICS Base directories
EPICS_BASE_HOST_BIN = $(EPICS_BASE)/bin/$(EPICS_HOST_ARCH)
EPICS_BASE_HOST_LIB = $(EPICS_BASE)/lib/$(EPICS_HOST_ARCH)
@@ -19,12 +18,12 @@ ifdef T_A
endif
#---------------------------------------------------------------
# Epics base Ioc libraries
# EPICS Base Ioc libraries
EPICS_BASE_IOC_LIBS += dbRecStd dbCore ca Com
#---------------------------------------------------------------
# Epics base Host libraries
# EPICS Base Host libraries
EPICS_BASE_HOST_LIBS += cas gdd
EPICS_BASE_HOST_LIBS += ca Com
@@ -42,13 +41,11 @@ endif # BASE_TOP
#---------------------------------------------------------------
# Base c preprocessor flags
BASE_CPPFLAGS =
# osithread default stack
OSITHREAD_USE_DEFAULT_STACK = NO
OSITHREAD_DEFAULT_STACK_FLAGS_YES = -DOSITHREAD_USE_DEFAULT_STACK
OSITHREAD_DEFAULT_STACK_FLAGS_NO =
BASE_CPPFLAGS += $(OSITHREAD_DEFAULT_STACK_FLAGS_$(OSITHREAD_USE_DEFAULT_STACK))
BASE_CPPFLAGS = $(OSITHREAD_DEFAULT_STACK_FLAGS_$(OSITHREAD_USE_DEFAULT_STACK))
#---------------------------------------------------------------
# Where to find the installed build tools
@@ -59,7 +56,7 @@ TOOLS = $(abspath $(EPICS_BASE_HOST_BIN))
FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) $(TOP)/src/tools/$(1)))
#---------------------------------------------------------------
# Epics base build tools and tool flags
# EPICS Base build tools and tool flags
MAKEBPT = $(TOOLS)/makeBpt$(HOSTEXE)
DBEXPAND = $(PERL) $(TOOLS)/dbdExpand.pl
@@ -68,11 +65,11 @@ DBTOMENUH = $(PERL) $(TOOLS)/dbdToMenuH.pl
REGISTERRECORDDEVICEDRIVER = $(PERL) $(TOOLS)/registerRecordDeviceDriver.pl
CONVERTRELEASE = $(PERL) $(call FIND_TOOL,convertRelease.pl)
FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl
GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG)
#-------------------------------------------------------
#---------------------------------------------------------------
# tools for installing libraries and products
INSTALL_QUIETLY := $(if $(findstring s,$(MAKEFLAGS)),-q,)
INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(INSTALL_QUIETLY)
INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(QUIET_FLAG)
INSTALL_PRODUCT = $(INSTALL)
INSTALL_LIBRARY = $(INSTALL)
@@ -82,7 +79,7 @@ MKMF = $(PERL) $(TOOLS)/mkmf.pl
REPLACEVAR = $(PERL) $(TOOLS)/replaceVAR.pl
#---------------------------------------------------------------
# private versions of lex/yacc from EPICS
# Our versions of lex (flex) and yacc (antelope)
EYACC = $(TOOLS)/antelope$(HOSTEXE)
ELEX = $(TOOLS)/e_flex$(HOSTEXE) -S$(EPICS_BASE)/include/flex.skel.static
@@ -90,28 +87,6 @@ YACC = $(EYACC)
LEX = $(ELEX)
#---------------------------------------------------------------
# Our use of msi is incompatible with older versions
# The 3.15 version of msi supports new options
MSI3_15 = $(EPICS_BASE_HOST_BIN)/msi
#---------------------------------------------------------------
# External tools and tool flags - must be in path or defined in application
ADL2DL ?= adl2dl
# sch2edif compiler and flags
SCH2EDIF = sch2edif
SCH2EDIF_PATH =
SCH2EDIF_SYSFLAGS = -n -ap -p.+..+$(SCH2EDIF_PATH)+$(CAPFAST_TEMPLATES)/sym+
SCH2EDIF_FLAGS =
# e2db and flags
# - again there is an assumption where edb.def is installed.
E2DB ?= e2db
E2DB_SYSFLAGS = -ate -d $(CAPFAST_TEMPLATES)/edb.def
E2DB_FLAGS =
DBST ?= dbst

View File

@@ -17,23 +17,34 @@
# EPICS_SITE_VERSION is defined in CONFIG_SITE for sites that want a local
# version number to be included in the reported version string.
# In 3.15 we still define BASE_3_14 so "ifdef BASE_3_14" means
# 3.14 or later, but "ifeq ($(BASE_3_14),YES)" means 3.14 only.
# We define BASE_3_14 and BASE_3_15 as NO and BASE_3_16 as YES, so
# ifdef BASE_3_14
# true for 3.14 or later
# ifdef BASE_3_15
# true for 3.15 or later
# ifeq ($(BASE_3_14),YES)
# true for 3.14.x only
# ifeq ($(BASE_3_15),YES)
# true for 3.15 only
# ifeq ($(BASE_3_16),YES)
# true for 3.16 only.
BASE_3_14 = NO
BASE_3_15 = YES
BASE_3_15 = NO
BASE_3_16 = YES
# EPICS_VERSION must be a number >0 and <256
EPICS_VERSION = 3
# EPICS_REVISION must be a number >=0 and <256
EPICS_REVISION = 15
EPICS_REVISION = 16
# EPICS_MODIFICATION must be a number >=0 and <256
EPICS_MODIFICATION = 3
EPICS_MODIFICATION = 0
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
# Not included if zero
EPICS_PATCH_LEVEL = 0
EPICS_PATCH_LEVEL = 1
# This will end in -DEV between official releases
#EPICS_DEV_SNAPSHOT=-DEV

View File

@@ -40,10 +40,6 @@ BUILD_ARCHS = $(EPICS_HOST_ARCH) $(CROSS1) $(CROSS2)
# otherwise override this in os/CONFIG_SITE.<host_arch>.Common
PERL = perl -CSD
#-------------------------------------------------------
# dbst based database optimization default
DB_OPT = NO
#-------------------------------------------------------
# Check configure/RELEASE file for consistency
CHECK_RELEASE_YES = checkRelease
@@ -85,6 +81,7 @@ IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION))
# Make echo output - suppress echoing if make's '-s' flag is set
NOP = :
ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo)
QUIET_FLAG := $(if $(findstring s,$(MAKEFLAGS)),-q,)
#-------------------------------------------------------
ifdef T_A
@@ -334,6 +331,14 @@ COMPILE.cpp = $(CCC) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES)
# C preprocessor command
PREPROCESS.cpp = $(CPP) $(CPPFLAGS) $(INCLUDES) $< > $@
#--------------------------------------------------
# genVersion header defaults
# C macro name
GENVERSIONMACRO = VCSVERSION
# C macro default value (empty to use date+time)
GENVERSIONDEFAULT =
#--------------------------------------------------
# Header dependency file generation

View File

@@ -57,8 +57,11 @@ EPICS_TS_NTP_INET=
# Prompt string
# IOCSH_HISTSIZE
# Number of lines of command history to keep.
# IOCSH_HISTEDIT_DISABLE
# Prevents use of readline or equivalent if defined.
IOCSH_PS1="epics> "
IOCSH_HISTSIZE=50
IOCSH_HISTEDIT_DISABLE=
# Log Server:
# EPICS_IOC_LOG_INET

View File

@@ -8,10 +8,12 @@
#*************************************************************************
#RULES.Db
# Set db substitutions file suffix
# Set db substitutions and template file suffixes
SUBST_SUFFIX ?= .substitutions
TEMPL_SUFFIX ?= .template
##################################################### vpath
#---------------------------------------------------------------
# vpath
vpath %.pm $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD))
vpath %.pod $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD))
@@ -19,12 +21,13 @@ vpath %.dbd $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD))
vpath %.db $(USR_VPATH) $(SRC_DIRS) $(dir $(DB))
vpath %.vdb $(USR_VPATH) $(SRC_DIRS) $(dir $(DB))
vpath %$(SUBST_SUFFIX) $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
vpath %.template $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
vpath %$(TEMPL_SUFFIX) $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
vpath bpt%.data $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
vpath %.acf $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
vpath %.acs $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
##################################################### dbflags dbdflags
#---------------------------------------------------------------
# dbflags dbdflags
DBD_SEARCH_DIRS = . .. $(COMMON_DIR) $(SRC_DIRS) $(INSTALL_DBD) $(RELEASE_DBD_DIRS)
DB_SEARCH_DIRS = . .. $(COMMON_DIR) $(SRC_DIRS) $(INSTALL_DB) $(RELEASE_DB_DIRS)
@@ -33,13 +36,15 @@ DBDFLAGS = $(USR_DBDFLAGS) $(CMD_DBDFLAGS) $(addprefix -I,$(DBD_SEARCH_DIRS))
DBFLAGS = $($*_DBFLAGS) $(USR_DBFLAGS) $(CMD_DBFLAGS) $(addprefix -I,$(DB_SEARCH_DIRS))
REGRDDFLAGS = $(DBDFLAGS) $($*_REGRDDFLAGS) $(USR_REGRDDFLAGS) $(CMD_REGRDDFLAGS)
##################################################### Targets
#---------------------------------------------------------------
# Targets
# ---------------------------------------------------
# To allow os specific dbd files AND have the -j option work properly,
CROSS_TARGET_OS_TYPES = $(sort $(foreach target, \
$(EPICS_HOST_ARCH) $(CROSS_COMPILER_TARGET_ARCHS),$(firstword $(subst -, ,$(target)))))
CROSS_TARGET_OS_TYPES = $(sort $(foreach target, \
$(EPICS_HOST_ARCH) $(CROSS_COMPILER_TARGET_ARCHS), \
$(firstword $(subst -, ,$(target)))))
DBD += $(foreach type, $(CROSS_TARGET_OS_TYPES), $(DBD_$(type)))
# Users add os specific dbd files to a Makefile as follows
@@ -86,31 +91,28 @@ SOURCE_DB_bbb = $(foreach dir, $(GENERIC_SRC_DIRS), $(SOURCE_DB_aaa) )
SOURCE_DB_aaa = $(addsuffix /$(file), $(dir) )
COMMONS = $(COMMON_DIR)/*.dbd $(COMMON_DIR)/*.db $(COMMON_DIR)/*.h \
$(COMMON_DIR)/*$(SUBST_SUFFIX) $(COMMON_DIR)/*.template
$(COMMON_DIR)/*$(SUBST_SUFFIX) $(COMMON_DIR)/*$(TEMPL_SUFFIX)
# Remove trailing numbers (to 99) on stem
TEMPLATE1=$(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%,$(patsubst %4,%, \
$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%,$(patsubst %8,%,$(patsubst %9,%, \
$*))))))))))
TEMPLATE2=$(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%,$(patsubst %4,%, \
$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%,$(patsubst %8,%,$(patsubst %9,%, \
$(TEMPLATE1)))))))))))
TEMPLATE3=$(addsuffix .template,$(addprefix ../,$(TEMPLATE2)))
TEMPLATE_FILENAME=$(firstword $(wildcard $($*_TEMPLATE) $(addprefix ../,$($*_TEMPLATE)) ../$*.template $(TEMPLATE3) ../template))
# dbst based database optimization
ifeq '$(DB_OPT)' 'YES'
RAW=.raw
DBS = $(filter %.db,$(DB)) $(addsuffix $(RAW),$(filter %.db,$(DB)))
COMMON_DBS = $(addprefix $(COMMON_DIR)/,$(DBS))
endif
TEMPLATE1 = $(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%, \
$(patsubst %4,%,$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%, \
$(patsubst %8,%,$(patsubst %9,%,$*))))))))))
TEMPLATE2 = $(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%, \
$(patsubst %4,%,$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%, \
$(patsubst %8,%,$(patsubst %9,%,$(TEMPLATE1)))))))))))
TEMPLATE3 = $(addsuffix $(TEMPL_SUFFIX),$(addprefix ../,$(TEMPLATE2)))
TEMPLATE_FILENAME = $(firstword $(wildcard $($*_TEMPLATE) \
$(addprefix ../,$($*_TEMPLATE)) ../$*$(TEMPL_SUFFIX) $(TEMPLATE3) \
../template))
INSTALL_DB_INSTALLS = $(addprefix $(INSTALL_DB)/,$(notdir $(DB_INSTALLS)))
INSTALL_DBD_INSTALLS = $(addprefix $(INSTALL_DBD)/,$(notdir $(DBD_INSTALLS)))
COMMONDEP_TARGET = $(COMMON_DIR)/$(basename $@)
##################################################### acf files
#---------------------------------------------------------------
# acf files
# An access security configuration file, *.acf, can be created from
# an *.acs file (has format of acf file plus #include "filename" lines)
@@ -123,7 +125,8 @@ ACF_INCLUDES = -I. $(TARGET_INCLUDES) $(USR_INCLUDES)\
ACFDEPENDS_CMD = $(MKMF) -m $@ $(ACF_INCLUDES) $(COMMONDEP_TARGET) $<
ACF_CMD = $(CPP) $(ACF_CPPFLAGS) $(ACF_INCLUDES) $< > $@
##################################################### dependancies
#---------------------------------------------------------------
# dependencies
HINC += $(addsuffix .h,$(DBDINC_NAME))
COMMON_DBDINC += $(addprefix $(COMMON_DIR)/,$(HINC))
@@ -133,12 +136,12 @@ DBDDEPENDS_FILES += $(addsuffix $(DEP),$(HINC) \
$(patsubst $(COMMON_DIR)/%,%, \
$(filter-out $(COMMON_DIR)/bpt%.dbd,$(COMMON_DBDS))))
#####################################################
#---------------------------------------------------------------
ifndef T_A
DEP = .d
TEMPLATE3+=$(addsuffix .template, $(TEMPLATE2))
TEMPLATE3 += $(addsuffix $(TEMPL_SUFFIX), $(TEMPLATE2))
COMMON_DIR = .
INSTALL_DBDS =
@@ -153,18 +156,13 @@ ACTIONS += install
ACTIONS += buildInstall
ACTIONS += runtests tapfiles
actionArchTargets = $(foreach x, $(ACTIONS),\ $(foreach arch,$(BUILD_ARCHS), $(x)$(DIVIDER)$(arch)))
actionArchTargets = $(foreach action, $(ACTIONS), \
$(foreach arch, $(BUILD_ARCHS), $(action)$(DIVIDER)$(arch)))
cleanArchTargets = $(foreach arch, $(BUILD_ARCHS), clean$(DIVIDER)$(arch))
cleanArchTargets = $(foreach arch,$(BUILD_ARCHS), clean$(DIVIDER)$(arch))
-include $(TOP)/configure/CONFIG_APP_INCLUDE
all: install
ifeq ($(EPICS_HOST_ARCH),$T_A)
host: install
else
# Do nothing
host:
endif
install: buildInstall
@@ -172,20 +170,30 @@ buildInstall : build
rebuild: clean install
.PHONY: all host $(ACTIONS)
.PHONY: all $(ACTIONS)
$(actionArchTargets) $(BUILD_ARCHS): install
$(cleanArchTargets): clean
.PHONY: $(BUILD_ARCHS) $(actionArchTargets) $(cleanArchTargets)
endif # T_A defined
else
# T_A is defined
ifeq ($(EPICS_HOST_ARCH),$(T_A))
host: install
else
host:
endif
.PHONY: host
endif # T_A
ifneq (,$(strip $(DBDDEPENDS_FILES)))
-include $(DBDDEPENDS_FILES)
endif
##################################################### build dependancies, clean rule
#---------------------------------------------------------------
# build dependancies, clean rule
inc : $(COMMON_INC) $(INSTALL_INC)
@@ -205,7 +213,8 @@ db_clean :
realclean: clean
##################################################### Dependency files
#---------------------------------------------------------------
# Dependency files
%Record.h$(DEP): $(COMMON_DIR)/%Record.dbd
@$(RM) $@
@@ -256,19 +265,19 @@ menu%.h$(DEP): ../menu%.dbd
@$(DBEXPAND) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $($*_DBD) > $@
@echo "$(COMMONDEP_TARGET): ../Makefile" >> $@
%.db$(RAW)$(DEP): %$(SUBST_SUFFIX)
%.db$(DEP): %$(SUBST_SUFFIX)
@$(RM) $@
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) -S$< $(TEMPLATE_FILENAME) > $@
%.db$(RAW)$(DEP): ../%$(SUBST_SUFFIX)
%.db$(DEP): ../%$(SUBST_SUFFIX)
@$(RM) $@
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) -S$< $(TEMPLATE_FILENAME) > $@
%.db$(RAW)$(DEP): %.template
%.db$(DEP): %$(TEMPL_SUFFIX)
@$(RM) $@
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) $< > $@
%.db$(RAW)$(DEP): ../%.template
%.db$(DEP): ../%$(TEMPL_SUFFIX)
@$(RM) $@
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) $< > $@
@@ -282,14 +291,8 @@ menu%.h$(DEP): ../menu%.dbd
.PRECIOUS: %$(DEP)
##################################################### CapFast filter
$(COMMON_DIR)/%.edf: ../%.sch $(DEPSCHS)
@$(RM) $@
@if [ ! -f cad.rc -a -r ../cad.rc ] ; then ln -s ../cad.rc ; fi
$(SCH2EDIF) $(SCH2EDIF_SYSFLAGS) $(SCH2EDIF_FLAGS) -o $@ $<
##################################################### Substitution files
#---------------------------------------------------------------
# Substitution files
# WARNING: CREATESUBSTITUTIONS script needs output dir on command line
@@ -310,25 +313,21 @@ $(INSTALL_DB)/%$(SUBST_SUFFIX): ../%$(SUBST_SUFFIX)
.PRECIOUS: $(COMMON_DIR)/%$(SUBST_SUFFIX)
##################################################### Template files
#---------------------------------------------------------------
# Template files
$(COMMON_DIR)/%.template: $(COMMON_DIR)/%.edf
@$(RM) $@
$(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $@.VAR $<
@$(REPLACEVAR) < $@.VAR > $@
@$(RM) $@.VAR
$(INSTALL_DB)/%.template: %.template
$(INSTALL_DB)/%$(TEMPL_SUFFIX): %$(TEMPL_SUFFIX)
$(ECHO) "Installing template file $@"
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
$(INSTALL_DB)/%.template: ../%.template
$(INSTALL_DB)/%$(TEMPL_SUFFIX): ../%$(TEMPL_SUFFIX)
$(ECHO) "Installing template file $@"
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
.PRECIOUS: $(COMMON_DIR)/%.template
.PRECIOUS: $(COMMON_DIR)/%$(TEMPL_SUFFIX)
##################################################### INC files
#---------------------------------------------------------------
# INC files
$(COMMON_DIR)/%Record.h: $(COMMON_DIR)/%Record.dbd
@$(RM) $(notdir $@)
@@ -362,7 +361,8 @@ $(COMMON_DIR)/menu%.h: ../menu%.dbd
.PRECIOUS: $(COMMON_DIR)/%.h
##################################################### DBD files
#---------------------------------------------------------------
# DBD files
$(COMMON_DIR)/bpt%.dbd: bpt%.data
@$(RM) $(notdir $@)
@@ -425,7 +425,8 @@ $(foreach file, $(DBD_INSTALLS), $(eval $(call DBD_INSTALLS_template, $(file))))
.PRECIOUS: $(COMMON_DBDS) $(COMMON_DIR)/%.dbd
##################################################### HTML files
#---------------------------------------------------------------
# HTML files
$(COMMON_DIR)/%.html: %.dbd.pod $(TOOLS)/dbdToHtml.pl
@$(RM) $(notdir $@)
@@ -444,37 +445,44 @@ $(COMMON_DIR)/%.html: %.pm $(TOOLS)/podToHtml.pl
$(COMMON_DIR)/%.html: ../%.pm $(TOOLS)/podToHtml.pl
@$(RM) $(notdir $@)
$(PERL) $(TOOLS)/podToHtml.pl -o $(notdir $@) $<
$(PERL) $(TOOLS)/podToHtml.pl -s -o $(notdir $@) $<
@$(MKDIR) $(dir $@)
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/%.html: ../%.pl $(TOOLS)/podToHtml.pl
@$(RM) $(notdir $@)
$(PERL) $(TOOLS)/podToHtml.pl -s -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
.PRECIOUS: $(COMMON_DIR)/%.html %.html
##################################################### DB files
#---------------------------------------------------------------
# DB files
$(COMMON_DIR)/%.db$(RAW): $(COMMON_DIR)/%.edf
$(COMMON_DIR)/%.db: $(COMMON_DIR)/%.edf
$(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $*.VAR $<
@$(REPLACEVAR) < $*.VAR > $@
@$(RM) $*.VAR
$(COMMON_DIR)/%.db$(RAW): %$(SUBST_SUFFIX)
$(COMMON_DIR)/%.db: %$(SUBST_SUFFIX)
$(ECHO) "Inflating database from $< $(TEMPLATE_FILENAME)"
@$(RM) $(notdir $@)
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) -S$< $(TEMPLATE_FILENAME)
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/%.db$(RAW): ../%$(SUBST_SUFFIX)
$(COMMON_DIR)/%.db: ../%$(SUBST_SUFFIX)
$(ECHO) "Inflating database from $< $(TEMPLATE_FILENAME)"
@$(RM) $(notdir $@)
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) -S$< $(TEMPLATE_FILENAME)
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/%.db$(RAW): %.template
$(COMMON_DIR)/%.db: %$(TEMPL_SUFFIX)
$(ECHO) "Inflating database from $<"
@$(RM) $(notdir $@)
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/%.db$(RAW): ../%.template
$(COMMON_DIR)/%.db: ../%$(TEMPL_SUFFIX)
$(ECHO) "Inflating database from $<"
@$(RM) $(notdir $@)
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) $<
@@ -492,22 +500,6 @@ $(COMMON_DIR)/%.acf: ../%.acs
.PRECIOUS: $(COMMON_DIR)/%.acf
# dbst based database optimization
ifeq '$(DB_OPT)' 'YES'
$(COMMON_DIR)/%.db$(RAW): ../%.db
@$(RM) $@
$(CP) $< $@
$(COMMON_DIR)/%.db: $(COMMON_DIR)/%.db$(RAW)
$(ECHO) "Optimizing database $@"
@$(RM) $@
$(DBST) . $< -d > $@
.PRECIOUS: $(COMMON_DIR)/%.db
.PRECIOUS: $(DB:%=$(COMMON_DIR)/%$(RAW))
else
$(INSTALL_DB)/%: %
$(ECHO) "Installing $@"
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
@@ -515,7 +507,6 @@ $(INSTALL_DB)/%: %
$(INSTALL_DB)/%: ../%
$(ECHO) "Installing $@"
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
endif
$(INSTALL_DB)/%.db: $(COMMON_DIR)/%.db
$(ECHO) "Installing created db file $@"
@@ -531,8 +522,8 @@ $(foreach file, $(DB_INSTALLS), $(eval $(call DB_INSTALLS_template, $(file))))
.PRECIOUS: $(COMMON_DIR)/%.edf
.PRECIOUS: $(COMMON_DBS)
##################################################### register record,device,driver support
#---------------------------------------------------------------
# register record,device,driver support
%_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd
@$(RM) $@
@@ -548,4 +539,3 @@ $(foreach file, $(DB_INSTALLS), $(eval $(call DB_INSTALLS_template, $(file))))
.PRECIOUS: %_registerRecordDeviceDriver.cpp
##################################################### END OF FILE

View File

@@ -168,7 +168,7 @@ ifdef RES
endif
$(DIRECTORY_TARGETS) :
$(MKDIR) -p $@
$(MKDIR) $@
$(PRODNAME): $(INSTALL_LIB_INSTALLS)
@@ -346,6 +346,14 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES)
@$(RM) $@
$(PERL) $(TOOLS)/makeTestfile.pl $@ $<
#---------------------------------------------------------------
# Generate header with version number from VCS
ifneq ($(GENVERSION),)
$(COMMON_DIR)/$(GENVERSION): FORCE
$(GENVERSIONHEADER) -t $(TOP) -N $(GENVERSIONMACRO) -V "$(GENVERSIONDEFAULT)" $@
endif
#---------------------------------------------------------------
# Install rules for BIN_INSTALLS and LIB_INSTALLS
@@ -479,7 +487,7 @@ $(INSTALL_TEMPLATES_SUBDIR)/%: %
.PRECIOUS: $(COMMON_INC)
.PHONY: all host inc build install clean rebuild buildInstall build_clean
.PHONY: runtests tapfiles checkRelease warnRelease noCheckRelease
.PHONY: runtests tapfiles checkRelease warnRelease noCheckRelease FORCE
endif # BASE_RULES_BUILD
# EOF RULES_BUILD

View File

@@ -26,14 +26,14 @@ POSIX_LDLIBS += -lpthread
ARCH_DEP_CFLAGS += -m32
ARCH_DEP_LDFLAGS += -m32
# Compiler defines _X86_ 1
# Compiler defines __MSVCRT__ 1
# 32-bit compiler defines _X86_ 1
# Compiler defines __CYGWIN__ 1
# Compiler defines __CYGWIN32__ 1
# 32-bit compiler defines __CYGWIN32__ 1
# Compiler defines __unix__ 1
# Compiler defines __unix 1
# Compiler defines unix 1
# This macro now deprecated, use __CYGWIN__ in the future
OP_SYS_CPPFLAGS += -DCYGWIN32
EXE = .exe

View File

@@ -1,10 +1,9 @@
# CONFIG.Common.vxWorksCommon
#
# $Revision-Id$
# This file is maintained by the build community.
#
# Definitions for vxWorks target archs
# Sites may override these definitions in CONFIG_SITE.Common.vxWorksCommon
# Override these definitions in CONFIG_SITE.Common.vxWorksCommon
# or CONFIG_SITE.<host>.vxWorksCommon
#-------------------------------------------------------
@@ -66,8 +65,6 @@ VXWORKS_MAJOR_VERSION = $(basename $(basename $(VXWORKS_VERSION)))
# These are needed for vxWorks 6.x; the GNU toolset version number
# is in the path to the compiler tools:
VX_GNU_VERSION_5.4 = 2.95
VX_GNU_VERSION_5.5 = 2.96
VX_GNU_VERSION_6.0 = 3.3.2
VX_GNU_VERSION_6.1 = 3.3.2
VX_GNU_VERSION_6.2 = 3.3.2
@@ -83,30 +80,21 @@ VX_GNU_VERSION = $(VX_GNU_VERSION_$(VXWORKS_VERSION))
VX_GNU_MAJOR_VERSION = $(basename $(basename $(VX_GNU_VERSION)))
#--------------------------------------------------
# Fix WIND_BASE for vxWorks 6.x on linux
# NB: We know the value of WIND_HOST_TYPE here, but not VXWORKS_VERSION
# Fix old Linux WIND_HOST_TYPE
ifeq ($(WIND_HOST_TYPE),x86-linux)
WIND_HOST_TYPE_5 = x86-linux
WIND_HOST_TYPE_6 = x86-linux2
WIND_HOST_TYPE = $(WIND_HOST_TYPE_$(VXWORKS_MAJOR_VERSION))
WIND_HOST_TYPE = x86-linux2
endif
#--------------------------------------------------
# vxWorks directory definitions
VX_DIR_5 = $(WIND_BASE)
VX_DIR_6 = $(WIND_BASE)/vxworks-$(VXWORKS_VERSION)
VX_DIR = $(VX_DIR_$(VXWORKS_MAJOR_VERSION))
VX_DIR = $(WIND_BASE)/vxworks-$(VXWORKS_VERSION)
VX_INCLUDE_DIRS_5 = $(VX_DIR)/target/h
VX_INCLUDE_DIRS_6 = $(VX_DIR)/target/h $(VX_DIR)/target/h/wrn/coreip
GNU_TARGET_INCLUDE_DIR = $(VX_INCLUDE_DIRS_$(VXWORKS_MAJOR_VERSION))
GNU_TARGET_INCLUDE_DIR = $(VX_DIR)/target/h $(VX_DIR)/target/h/wrn/coreip
#--------------------------------------------------
# vxWorks GNU directories
GNU_DIR_5 = $(WIND_BASE)/host/$(WIND_HOST_TYPE)
GNU_DIR_6 = $(WIND_BASE)/gnu/$(VX_GNU_VERSION)-vxworks-$(VXWORKS_VERSION)/$(WIND_HOST_TYPE)
GNU_DIR = $(GNU_DIR_$(VXWORKS_MAJOR_VERSION))
GNU_DIR = $(WIND_BASE)/gnu/$(VX_GNU_VERSION)-vxworks-$(VXWORKS_VERSION)/$(WIND_HOST_TYPE)
#--------------------------------------------------
# Wind River moved nm out of GNU_BIN in some versions
@@ -126,9 +114,7 @@ NM = $(NM_DIR)/$(CMPLR_PREFIX)nm$(CMPLR_SUFFIX)$(HOSTEXE)
#--------------------------------------------------
# A linker script is essential for munching from vxWorks 6.6 onwards
# (i.e. with versions that use gcc 4.1.2 or later). It can be used
# with any vxWorks 5 or 6 version, but apparently should not be used
# when compiling for 68K (which isn't supported in vxWorks 6 anyway)
# (i.e. with versions that use gcc 4.1.2 or later).
MUNCH_LDFLAGS_6 = -T $(VX_DIR)/target/h/tool/gnu/ldscripts/link.OUT
MUNCH_LDFLAGS = $(MUNCH_LDFLAGS_$(VXWORKS_MAJOR_VERSION))
@@ -143,11 +129,10 @@ export TOOL_FAMILY = GNU
OP_SYS_CPPFLAGS += -DvxWorks=vxWorks
OP_SYS_CFLAGS += -fno-builtin
# Fix for vxWorks 5 headers that use macros defined in vxWorks.h but
# Fix for vxWorks headers that use macros defined in vxWorks.h but
# which don't actually include vxWorks.h themselves, for example the
# target/h/sys/stat.h file which uses ULONG. This also stops dbDefs.h
# from defining the OFFSET macro, which generates lots of warnings in
# both vxWorks 5 and 6.
# from defining the OFFSET macro, which generates lots of warnings.
OP_SYS_INCLUDE_CPPFLAGS += -include $(VX_DIR)/target/h/vxWorks.h
#--------------------------------------------------
@@ -160,7 +145,6 @@ OPT_CXXFLAGS_YES = -O2
CODE_CFLAGS =
#
# For vxWorks versions before 6.3 we need this g++ compiler flag
CODE_CXXFLAGS_5 = -fno-implicit-templates
CODE_CXXFLAGS_6.0 = -fno-implicit-templates
CODE_CXXFLAGS_6.1 = -fno-implicit-templates
CODE_CXXFLAGS_6.2 = -fno-implicit-templates

View File

@@ -11,7 +11,7 @@
CP = cp
MV = mv
RM = rm -f
MKDIR = mkdir
MKDIR = mkdir -p
RMDIR = rm -rf
CAT = cat

View File

@@ -0,0 +1,33 @@
# CONFIG.cygwin-x86.linux-arm
#
# Definitions for cywgin-x86 host - linux-arm target builds
# Override these settings in CONFIG_SITE.cygwin-x86.linux-arm
#-------------------------------------------------------
VALID_BUILDS = Ioc
GNU_TARGET = arm-linux
# prefix of compiler tools
CMPLR_SUFFIX =
CMPLR_PREFIX = $(addsuffix -,$(GNU_TARGET))
# Provide a link-time path for shared libraries
SHRLIBDIR_RPATH_LDFLAGS_YES += $(SHRLIB_DEPLIB_DIRS:%=-Wl,-rpath-link,%)
SHRLIBDIR_LDFLAGS += $(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH))
# Provide a link-time path for products
PRODDIR_RPATH_LDFLAGS_YES += $(PROD_DEPLIB_DIRS:%=-Wl,-rpath-link,%)
PRODDIR_LDFLAGS += $(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH))
# Provide a link-time path for readline
RUNTIME_LDFLAGS_READLINE_YES = -Wl,-rpath-link,$(GNU_DIR)/lib
RUNTIME_LDFLAGS_READLINE = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH))
RUNTIME_LDFLAGS_READLINE_CURSES = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH))
RUNTIME_LDFLAGS_READLINE_NCURSES = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH))
# Library flags
STATIC_LDFLAGS_YES= -Wl,-Bstatic
STATIC_LDFLAGS_NO=
STATIC_LDLIBS_YES= -Wl,-Bdynamic
STATIC_LDLIBS_NO=

View File

@@ -0,0 +1,7 @@
# CONFIG.cygwin-x86_64.linux-arm
#
# Definitions for cygwin-x86_64 host - linux-arkm targets
# Override these settings in CONFIG_SITE.cygwin-x86_64.linux-arm
#-------------------------------------------------------
include $(CONFIG)/os/CONFIG.cygwin-x86.linux-arm

View File

@@ -10,4 +10,4 @@
#Include definitions common to unix hosts
include $(CONFIG)/os/CONFIG.UnixCommon.Common
WIND_HOST_TYPE = x86-linux
WIND_HOST_TYPE = x86-linux2

View File

@@ -10,4 +10,4 @@
#Include definitions common to unix hosts
include $(CONFIG)/os/CONFIG.UnixCommon.Common
WIND_HOST_TYPE = x86-linux
WIND_HOST_TYPE = x86-linux2

View File

@@ -10,4 +10,4 @@
#Include definitions common to unix hosts
include $(CONFIG)/os/CONFIG.UnixCommon.Common
WIND_HOST_TYPE = x86-linux
WIND_HOST_TYPE = x86-linux2

View File

@@ -10,4 +10,4 @@
#Include definitions common to unix hosts
include $(CONFIG)/os/CONFIG.UnixCommon.Common
WIND_HOST_TYPE = x86-linux
WIND_HOST_TYPE = x86-linux2

View File

@@ -10,4 +10,4 @@
#Include definitions common to unix hosts
include $(CONFIG)/os/CONFIG.UnixCommon.Common
WIND_HOST_TYPE = x86-linux
WIND_HOST_TYPE = x86-linux2

View File

@@ -0,0 +1,28 @@
# CONFIG.linux-x86.windows-x64-mingw
#
# Definitions for linux-x86 host windows-x64-mingw target builds
# Override these definitions in CONFIG_SITE.linux-x86.windows-x64-mingw
#-------------------------------------------------------
# Include common gnu compiler definitions
include $(CONFIG)/CONFIG.gnuCommon
# Add resource compiler
RCCMD = $(GNU_BIN)/$(CMPLR_PREFIX)windres$(CMPLR_SUFFIX) $(INCLUDES) $< $@
# Remove -fPIC flags, add out-implib
SHRLIB_CFLAGS =
SHRLIB_LDFLAGS = -shared \
-Wl,--out-implib,$(DLLSTUB_PREFIX)$*$(DLLSTUB_SUFFIX)
LOADABLE_SHRLIB_LDFLAGS = -shared \
-Wl,--out-implib,$(DLLSTUB_PREFIX)$*$(DLLSTUB_SUFFIX)
# No need to explicitly link with gcc library
GNU_LDLIBS_YES =
# Link with winsock2
OP_SYS_LDLIBS = -lws2_32
# Use static compiler-support libraries
OP_SYS_LDFLAGS += -static-libgcc -static-libstdc++
# There is no compiler flag for static libwinpthread

View File

@@ -10,4 +10,4 @@
#Include definitions common to unix hosts
include $(CONFIG)/os/CONFIG.UnixCommon.Common
WIND_HOST_TYPE = x86-linux
WIND_HOST_TYPE = x86-linux2

View File

@@ -0,0 +1,9 @@
# CONFIG.linux-x86_64.windows-x64-mingw
#
# Definitions for linux-x86_64 host windows-x64-mingw target builds
# Override these definitions in CONFIG_SITE.linux-x86_64.windows-x64-mingw
#-------------------------------------------------------
# Settings as for the linux-x86 host architecture
include $(CONFIG)/os/CONFIG.linux-x86.windows-x64-mingw

View File

@@ -1,25 +1,17 @@
# CONFIG_SITE.Common.vxWorksCommon
#
# Site specific definitions for vxWorks target builds.
# Only the local epics system manager should modify this file
# Compiler options can vary with the vxWorks version number, so we
# need to know that. However don't include any third-level digits
# (e.g. the .2 in 5.5.2) because we don't need them.
# need to know that. Do not include any third-level digits.
# Note: vxWorks 5.4.x (Tornado 2.0.x) is not supported
# Note: vxWorks 5.4.x and 5.5.x (Tornado 2.x) are not supported.
# VxWorks 6.0 through 6.5 use older, untested versions of GCC.
VXWORKS_VERSION = 5.5
#VXWORKS_VERSION = 6.0
#VXWORKS_VERSION = 6.1
#VXWORKS_VERSION = 6.2
#VXWORKS_VERSION = 6.3
#VXWORKS_VERSION = 6.4
#VXWORKS_VERSION = 6.5
#VXWORKS_VERSION = 6.6
#VXWORKS_VERSION = 6.7
#VXWORKS_VERSION = 6.8
#VXWORKS_VERSION = 6.9
VXWORKS_VERSION = 6.9
# Sites may override the following path for a particular host
@@ -27,10 +19,8 @@ VXWORKS_VERSION = 5.5
# CONFIG_SITE.$(EPICS_HOST_ARCH).vxWorksCommon file.
# WIND_BASE is where you installed the Wind River software.
# Under vxWorks 6.x this is *not* the same as the old VX_DIR setting
WIND_BASE = /usr/local/vw/tornado22-$(ARCH_CLASS)
#WIND_BASE = /usr/local/vw/vxWorks-$(VXWORKS_VERSION)
WIND_BASE = /usr/local/vw/vxWorks-$(VXWORKS_VERSION)
#WIND_BASE = /ade/vxWorks/$(VXWORKS_VERSION)
@@ -39,9 +29,9 @@ WIND_BASE = /usr/local/vw/tornado22-$(ARCH_CLASS)
#WORKBENCH_VERSION = 2.6
#WORKBENCH_VERSION = 3.0
#WORKBENCH_VERSION = 3.2
#WORKBENCH_VERSION = 3.3
WORKBENCH_VERSION = 3.3
# Utilities Version number, required from vxWorks 6.8 and later
#UTILITIES_VERSION = 1.0
UTILITIES_VERSION = 1.0

View File

@@ -6,5 +6,5 @@
# Site override definitions for cygwin-x86 host builds
#-------------------------------------------------------
#CROSS_COMPILER_TARGET_ARCHS =
#CROSS_COMPILER_TARGET_ARCHS = linux-arm

View File

@@ -0,0 +1,16 @@
# CONFIG_SITE.cygwin-x86.linux-arm
#
# $Revision-Id$
#
# Site specific definitions for cygwin-x86 host - linux-arm target builds
#-------------------------------------------------------
# Tools install path
GNU_DIR = /usr/local/arm-linux
# GNU crosscompiler target name
GNU_TARGET = arm-linux
STATIC_BUILD = YES
SHARED_LIBRARIES = NO

View File

@@ -0,0 +1,16 @@
# CONFIG_SITE.cygwin-x86_64.linux-arm
#
# $Revision-Id$
#
# Site specific definitions for cygwin-x86_64 host - linux-arm target builds
#-------------------------------------------------------
# Tools install path
GNU_DIR = /usr/local/arm-linux
# GNU crosscompiler target name
GNU_TARGET = arm-linux
STATIC_BUILD = YES
SHARED_LIBRARIES = NO

View File

@@ -0,0 +1,20 @@
# CONFIG_SITE.linux-x86.windows-x64-mingw
#
# Configuration for linux-x86 host windows-x64-mingw target builds
#-------------------------------------------------------
# Early versions of the MinGW cross-build tools can only build
# static (non-DLL) libraries. For example RHEL's cross-gcc 4.4.6
# needs these uncommented, cross-gcc 4.6.3 for Ubuntu does not:
#SHARED_LIBRARIES = NO
#STATIC_BUILD = YES
# The cross-build tools are in $(GNU_DIR)/bin
# Default is /usr
#GNU_DIR = /usr/local
# Different distribution cross-build packages use different prefixes:
# Ubuntu:
#CMPLR_PREFIX = i686-w64-mingw32-
# RHEL:
CMPLR_PREFIX = x86_64-w64-mingw32-

View File

@@ -0,0 +1,8 @@
# CONFIG_SITE.linux-x86_64.windows-x64-mingw
#
# Configuration for linux-x86_64 host windows-x64-mingw target builds
#-------------------------------------------------------
# Inherit from the linux-x86 host architecture
include $(CONFIG)/os/CONFIG_SITE.linux-x86.windows-x64-mingw

View File

@@ -4,40 +4,36 @@
<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 R3.15.3</title>
<title>Known Problems in R3.16.0.1</title>
</head>
<body>
<h1 style="text-align: center">EPICS Base R3.15.3: Known Problems</h1>
<h1 style="text-align: center">EPICS Base R3.16.0.1: Known Problems</h1>
<p>Any patch files linked below should be applied at the root of the
base-3.15.3 tree. Download them, then use the GNU Patch program as
base-3.16.0.1 tree. Download them, then use the GNU Patch program as
follows:</p>
<blockquote><pre>% <b>cd <i>/path/to/</i>base-3.15.3</b>
<blockquote><pre>% <b>cd <i>/path/to/</i>base-3.16.0.1</b>
% <b>patch -p0 &lt; <i>/path/to/</i>file.patch</b></pre></blockquote>
<p>The following significant problems have been reported with this
version of EPICS Base:</p>
<p>The following problems were known by the developers at the time of this
release:</p>
<ul>
<li>See LaunchPad bug
<a href="https://bugs.launchpad.net/epics-base/+bug/1466129">#1466129</a>:
The feature that allows an IOC to bind to a single network interface is not
working correctly. The bug mentioned above contains a patch that has been
tested on Linux-64, a complete fix is expected for 3.15.4. Note that Windows
systems are not affected by this bug.</li>
<!-- Items added after release should be formatted thusly:
<li>YYYY-MM-DD: Description of problem
...</li>
-->
<li>Parallel builds ("make -j") on native Windows are not working properly.
Builds tend to hang (saturating one core); interrupting and running make
again usually finishes the build. Limiting the number of parallel jobs using
something like "make -j8" also helps prevent this problem. Sequential builds
always work and are recommended for automated build environments.</li>
<li>IOCs running on some versions of Cygwin may display warnings at iocInit
about duplicate EPICS CA Address list entries. These warnings might be due
to a bug in Cygwin; they are benign and can be ignored.</li>
<li>64-bit Windows builds of the CAS library may not work with some compilers.
The code in <tt>src/legacy/gdd</tt> is currently incompatible with the LLP64
model that Windows uses for its 64-bit ABI.</li>
The code in <tt>src/legacy/gdd</tt> is incompatible with the LLP64 model
that Windows uses for its 64-bit ABI.</li>
</ul>

View File

@@ -1,6 +1,6 @@
Installation Instructions
EPICS Base Release 3.15.3
EPICS Base Release 3.16.0
--------------------------------------------------------------------------
@@ -85,17 +85,17 @@
as processes on the host platform.
vxWorks
You must have vxWorks 5.5.x or 6.x installed if any of your target
systems are vxWorks systems; the C++ compiler for vxWorks 5.4 is now too
old to support. The vxWorks installation provides the cross-compiler and
header files needed to build for these targets. The absolute path to and
the version number of the vxWorks installation must be set in the
You must have vxWorks 6 installed if any of your target systems are
vxWorks systems; the C++ compilers for vxWorks 5.x are now too old to
support. The vxWorks installation provides the cross-compiler and header
files needed to build for these targets. The absolute path to and the
version number of the vxWorks installation must be set in the
base/configure/os/CONFIG_SITE.Common.vxWorksCommon file or in one of its
target-specific overrides.
Consult the vxWorks 5.x or vxWorks 6.x EPICS web pages about and the
vxWorks documentation for information about configuring your vxWorks
operating system for use with EPICS.
Consult the vxWorks 6.x EPICS web pages and the vxWorks documentation
for information about configuring your vxWorks operating system for use
with EPICS.
RTEMS
For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or

View File

@@ -9,7 +9,7 @@
<BODY>
<CENTER>
<H1>Installation Instructions</H1>
<H2>EPICS Base Release 3.15.3</H2><BR>
<H2>EPICS Base Release 3.16.0</H2><BR>
</CENTER>
<HR>
<H3> Table of Contents</H3>
@@ -90,17 +90,16 @@
as processes on the host platform.</P>
<P><B>vxWorks</B><BR>
You must have vxWorks 5.5.x or 6.x installed if any of your target systems are
vxWorks systems; the C++ compiler for vxWorks 5.4 is now too old to support.
The vxWorks installation provides the cross-compiler and header files needed to
You must have vxWorks 6 installed if any of your target systems are vxWorks
systems; the C++ compilers for vxWorks 5.x are now too old to support. The
vxWorks installation provides the cross-compiler and header files needed to
build for these targets. The absolute path to and the version number of the
vxWorks installation must be set in the
base/configure/os/CONFIG_SITE.Common.vxWorksCommon file or in one of its
target-specific overrides.</P>
<P>Consult the <a href="http://www.aps.anl.gov/epics/base/tornado.php">vxWorks
5.x</a> or <a href="http://www.aps.anl.gov/epics/base/vxWorks6.php">vxWorks
6.x</a> EPICS web pages about and the vxWorks documentation for information
<P>Consult the <a href="http://www.aps.anl.gov/epics/base/vxWorks6.php">vxWorks
6.x</a> EPICS web pages and the vxWorks documentation for information
about configuring your vxWorks operating system for use with EPICS.</P>
<P><B>RTEMS</B><BR>

File diff suppressed because it is too large Load Diff

View File

@@ -39,7 +39,7 @@
</blockquote>
<p>This document describes the procedures and provides a checklist of tasks
that should be performed when creating new releases of EPICS Base.</p>
that should be performed when creating production releases of EPICS Base.</p>
<h3>The Release Process</h3>
@@ -141,17 +141,17 @@ relevent roles unless the Release Manager designates otherwise:</p>
<td>Tag the module in Bazaar, using these tag conventions:
<ul>
<li>
<tt>R3.15.3-pre<i>n</i></tt>
<tt>R3.16.1-pre<i>n</i></tt>
&mdash; pre-release tag
</li>
<li>
<tt>R3.15.3-rc<i>n</i></tt>
<tt>R3.16.1-rc<i>n</i></tt>
&mdash; release candidate tag, note the <tt>rc</tt> is now
lower-case</li>
</ul>
<blockquote><tt>
cd ~/base/mirror-3.15<br />
bzr tag R3.15.3-rc<i>n</i>
cd ~/base/mirror-3.16<br />
bzr tag R3.16.1-rc<i>n</i>
</tt></blockquote>
</td>
</tr>
@@ -163,10 +163,10 @@ relevent roles unless the Release Manager designates otherwise:</p>
<blockquote><tt>
cd ~/base<br />
bzr export --keywords=publish
--root=base-3.15.3-rc<i>n</i>
-r tag:R3.15.3-rc<i>n</i>
base-3.15.3-rc<i>n</i>.tar.gz
mirror-3.15
--root=base-3.16.1-rc<i>n</i>
-r tag:R3.16.1-rc<i>n</i>
base-3.16.1-rc<i>n</i>.tar.gz
mirror-3.16
</tt></blockquote>
This requires that the Bazaar keywords plugin is installed and
configured properly.
@@ -279,8 +279,8 @@ relevent roles unless the Release Manager designates otherwise:</p>
<td>Release Manager</td>
<td>Tag the module in Bazaar:
<blockquote><tt>
cd ~/base/mirror-3.15<br />
bzr tag R3.15.3</i>
cd ~/base/mirror-3.16<br />
bzr tag R3.16.1</i>
</tt></blockquote>
</td>
</tr>
@@ -292,10 +292,10 @@ relevent roles unless the Release Manager designates otherwise:</p>
<blockquote><tt>
cd ~/base<br />
bzr export --keywords=publish
--root=base-3.15.3
-r tag:R3.15.3
base-3.15.3.tar.gz
mirror-3.15
--root=base-3.16.1
-r tag:R3.16.1
base-3.16.1.tar.gz
mirror-3.16
</tt></blockquote>
This requires that the Bazaar keywords plugin is installed and
configured properly.

View File

@@ -841,23 +841,18 @@ seconds. See also <a href="#Disconnect">EPICS_CA_CONN_TMO</a> and
Interval</a>.</p>
<p>CA servers build a list of addresses to send beacons to during
initialization. If EPICS_CAS_AUTO_BEACON_ADDR_LIST has the value "YES" then the
beacon address list will be automatically configured to contain the broadcast
addresses of all LAN interfaces found in the host and the destination address
of all point-to-point interfaces found in the host. However, if the user also
initialization. If EPICS_CAS_AUTO_BEACON_ADDR_LIST has the value "YES"
(the default) this list will be automatically populated with the broadcast
addresses of all network interfaces. However, if the user also
defines EPICS_CAS_INTF_ADDR_LIST then beacon address list automatic
configuration is constrained to the network interfaces specified therein, and
therefore only the broadcast addresses of the specified LAN interfaces, and the
destination addresses of all specified point-to-point links, will be
therefore only the broadcast addresses of the specified LAN interfaces, will be
automatically configured.</p>
<p>If EPICS_CAS_BEACON_ADDR_LIST is defined then its contents will be used to
augment any automatic configuration of the beacon address list. Individual
entries in EPICS_CAS_BEACON_ADDR_LIST may override the destination port number
if ":nnn" follows the host name or IP address there. Alternatively, when both
EPICS_CAS_BEACON_ADDR_LIST and EPICS_CAS_INTF_ADDR_LIST are not defined then
the contents of EPICS_CA_ADDR_LIST is used to augment the list. Otherwise, the
list is not augmented.</p>
if ":nnn" follows the host name or IP address there.</p>
<p>The EPICS_CAS_BEACON_PORT parameter specifies the destination port for
server beacons. The only exception to this occurs when ports are specified in
@@ -868,20 +863,18 @@ specified in EPICS_CA_REPEATER_PORT.</p>
<h4>Binding a Server to a Limited Set of Network Interfaces</h4>
<p>The parameter EPICS_CAS_INTF_ADDR_LIST allows a ca server to bind itself to,
and therefore accept messages only over, a limited set of the local host's
and therefore accept messages received by, a limited set of the local host's
network interfaces (each specified by its IP address). On UNIX systems type
"netstat -i" (type "ipconfig" on windows) to see a list of the local host's
network interfaces. Specifically, UDP search messages addressed to both the IP
addresses in EPICS_CAS_INTF_ADDR_LIST and also to the broadcast addresses of
the corresponding LAN interfaces will be accepted by the server. By default,
"netstat -ie" (type "ipconfig" on windows) to see a list of the local host's
network interfaces. By default,
the CA server is accessible from all network interfaces configured into its
host.</p>
<p>In R3.14 and previous releases the CA server employed by iocCore did not
implement the EPICS_CAS_INTF_ADDR_LIST feature. In this release the iocCore
server will read the first IP address from the parameter variable and use that
to select which interface to bind to. Any additional IP addresses will be
ignored and a warning message displayed during IOC initialization.</p>
<p>Until R3.15.4 the CA server employed by iocCore did not
implement the EPICS_CAS_INTF_ADDR_LIST feature.</p>
<p>Prior to R3.15.4 CA servers would build the beacon address list
using EPICS_CA_ADDR_LIST if EPICS_CAS_BEACON_ADDR_LIST was no set.</p>
<h4>Ignoring Process Variable Name Resolution Requests From Certain Hosts</h4>

View File

@@ -22,7 +22,7 @@ extern "C" {
epicsShareFunc void epicsShareAPI configureChannelAccessAddressList
( struct ELLLIST *pList, SOCKET sock, unsigned short port );
epicsShareFunc void epicsShareAPI addAddrToChannelAccessAddressList
epicsShareFunc int epicsShareAPI addAddrToChannelAccessAddressList
( struct ELLLIST *pList, const ENV_PARAM *pEnv,
unsigned short port, int ignoreNonDefaultPort );

View File

@@ -73,7 +73,7 @@ static char *getToken ( const char **ppString, char *pBuf, unsigned bufSIze )
/*
* addAddrToChannelAccessAddressList ()
*/
extern "C" void epicsShareAPI addAddrToChannelAccessAddressList
extern "C" int epicsShareAPI addAddrToChannelAccessAddressList
( ELLLIST *pList, const ENV_PARAM *pEnv,
unsigned short port, int ignoreNonDefaultPort )
{
@@ -82,11 +82,11 @@ extern "C" void epicsShareAPI addAddrToChannelAccessAddressList
const char *pToken;
struct sockaddr_in addr;
char buf[32u]; /* large enough to hold an IP address */
int status;
int status, ret = -1;
pStr = envGetConfigParamPtr (pEnv);
if (!pStr) {
return;
return ret;
}
while ( ( pToken = getToken (&pStr, buf, sizeof (buf) ) ) ) {
@@ -104,7 +104,7 @@ extern "C" void epicsShareAPI addAddrToChannelAccessAddressList
pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode));
if (pNewNode==NULL) {
fprintf ( stderr, "addAddrToChannelAccessAddressList(): no memory available for configuration\n");
return;
break;
}
pNewNode->addr.ia = addr;
@@ -113,9 +113,10 @@ extern "C" void epicsShareAPI addAddrToChannelAccessAddressList
* LOCK applied externally
*/
ellAdd (pList, &pNewNode->node);
ret = 0; /* success if anything is added to the list */
}
return;
return ret;
}
/*

View File

@@ -66,6 +66,8 @@ void repeaterSubscribeTimer::shutdown (
epicsTimerNotify::expireStatus repeaterSubscribeTimer::
expire ( const epicsTime & /* currentTime */ )
{
epicsGuard < epicsMutex > guard ( this->stateMutex );
static const unsigned nTriesToMsg = 50;
if ( this->attempts > nTriesToMsg && ! this->once ) {
callbackManager mgr ( this->ctxNotify, this->cbMutex );
@@ -92,12 +94,15 @@ epicsTimerNotify::expireStatus repeaterSubscribeTimer::
void repeaterSubscribeTimer::show ( unsigned /* level */ ) const
{
epicsGuard < epicsMutex > guard ( this->stateMutex );
::printf ( "repeater subscribe timer: attempts=%u registered=%u once=%u\n",
this->attempts, this->registered, this->once );
}
void repeaterSubscribeTimer::confirmNotify ()
{
epicsGuard < epicsMutex > guard ( this->stateMutex );
this->registered = true;
}

View File

@@ -70,6 +70,7 @@ private:
repeaterTimerNotify & iiu;
epicsMutex & cbMutex;
cacContextNotify & ctxNotify;
mutable epicsMutex stateMutex;
unsigned attempts;
bool registered;
bool once;

View File

@@ -163,6 +163,19 @@ udpiiu::udpiiu (
throwWithLocation ( noSocket () );
}
#ifdef IP_ADD_MEMBERSHIP
{
int flag = 1;
if ( setsockopt ( this->sock, IPPROTO_IP, IP_MULTICAST_LOOP,
(char *) &flag, sizeof ( flag ) ) == -1 ) {
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
errlogPrintf("CAC: failed to set mcast loopback\n");
}
}
#endif
int boolValue = true;
int status = setsockopt ( this->sock, SOL_SOCKET, SO_BROADCAST,
(char *) &boolValue, sizeof ( boolValue ) );

View File

@@ -141,6 +141,39 @@ caStatus caServerI::attachInterface ( const caNetAddr & addrIn,
return S_cas_success;
}
void caServerI::addMCast(const osiSockAddr& addr)
{
#ifdef IP_ADD_MEMBERSHIP
epicsGuard < epicsMutex > locker ( this->mutex );
tsDLIter < casIntfOS > iter = this->intfList.firstIter ();
while ( iter.valid () ) {
struct ip_mreq mreq;
memset(&mreq, 0, sizeof(mreq));
mreq.imr_interface = iter->serverAddress().getSockIP().sin_addr;
mreq.imr_multiaddr = addr.ia.sin_addr;
if ( setsockopt ( iter->casDGIntfIO::getFD (), IPPROTO_IP,
IP_ADD_MEMBERSHIP, (char *) &mreq,
sizeof ( mreq ) ) < 0) {
struct sockaddr_in temp;
char name[40];
char sockErrBuf[64];
temp.sin_family = AF_INET;
temp.sin_addr = mreq.imr_multiaddr;
temp.sin_port = addr.ia.sin_port;
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
ipAddrToDottedIP (&temp, name, sizeof(name));
fprintf(stderr, "CAS: Socket mcast join %s failed with \"%s\"\n",
name, sockErrBuf );
}
iter++;
}
#endif
}
void caServerI::sendBeacon ( ca_uint32_t beaconNo )
{
epicsGuard < epicsMutex > locker ( this->mutex );

View File

@@ -103,6 +103,8 @@ private:
caStatus attachInterface ( const caNetAddr & addr, bool autoBeaconAddr,
bool addConfigAddr );
virtual void addMCast(const osiSockAddr&);
void sendBeacon ( ca_uint32_t beaconNo );
caServerI ( const caServerI & );

View File

@@ -354,9 +354,11 @@ caStatus casDGClient::searchFailResponse ( const caHdrLargeArray * mp )
status = this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0,
mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, 0 );
this->out.commitMsg ();
if ( status == S_cas_success ) {
this->out.commitMsg ();
}
return S_cas_success;
return S_cas_success;
}
/*

View File

@@ -14,6 +14,7 @@
//
#include <ctype.h>
#include <list>
#include "epicsSignal.h"
#include "envDefs.h"
@@ -92,6 +93,9 @@ void caServerIO::locateInterfaces ()
autoBeaconAddr = true;
}
typedef std::list<osiSockAddr> mcastAddrs_t;
mcastAddrs_t mcastAddrs;
//
// bind to the the interfaces specified - otherwise wildcard
// with INADDR_ANY and allow clients to attach from any interface
@@ -114,6 +118,15 @@ void caServerIO::locateInterfaces ()
pToken);
continue;
}
epicsUInt32 top = ntohl(saddr.sin_addr.s_addr)>>24;
if (saddr.sin_family==AF_INET && top>=224 && top<=239) {
osiSockAddr oaddr;
oaddr.ia = saddr;
mcastAddrs.push_back(oaddr);
continue;
}
stat = this->attachInterface (caNetAddr(saddr), autoBeaconAddr, configAddrOnceFlag);
if (stat) {
errMessage(stat, "unable to attach explicit interface");
@@ -131,6 +144,10 @@ void caServerIO::locateInterfaces ()
errMessage(stat, "unable to attach any interface");
}
}
for (mcastAddrs_t::const_iterator it = mcastAddrs.begin(); it!=mcastAddrs.end(); ++it) {
this->addMCast(*it);
}
}
//

View File

@@ -47,6 +47,8 @@ private:
virtual caStatus attachInterface (
const caNetAddr & addr, bool autoBeaconAddr,
bool addConfigAddr ) = 0;
virtual void addMCast(const osiSockAddr&) = 0;
};
#endif // caServerIOh

View File

@@ -19,6 +19,11 @@
#include "addrList.h"
#include "errlog.h"
#if defined(_MSC_VER)
# include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#endif
#define epicsExportSharedSymbols
#include "casDGIntfIO.h"
#include "ipIgnoreEntry.h"
@@ -148,26 +153,8 @@ casDGIntfIO::casDGIntfIO ( caServerI & serverIn, clientBufMemoryManager & memMgr
}
if ( addConfigBeaconAddr ) {
//
// by default use EPICS_CA_ADDR_LIST for the
// beacon address list
//
const ENV_PARAM *pParam;
if ( envGetConfigParamPtr ( & EPICS_CAS_INTF_ADDR_LIST ) ||
envGetConfigParamPtr ( & EPICS_CAS_BEACON_ADDR_LIST ) ) {
pParam = & EPICS_CAS_BEACON_ADDR_LIST;
}
else {
pParam = & EPICS_CA_ADDR_LIST;
}
//
// add in the configured addresses
//
addAddrToChannelAccessAddressList (
& BCastAddrList, pParam, beaconPort, pParam == & EPICS_CA_ADDR_LIST );
& BCastAddrList, &EPICS_CAS_BEACON_ADDR_LIST, beaconPort, 0 );
}
removeDuplicateAddresses ( & this->beaconAddrList, & BCastAddrList, 0 );
@@ -267,7 +254,7 @@ casDGIntfIO::~casDGIntfIO()
// avoid use of ellFree because problems on windows occur if the
// free is in a different DLL than the malloc
ELLNODE * nnode = this->beaconAddrList.node.next;
ELLNODE * nnode = ellFirst(&this->beaconAddrList);
while ( nnode )
{
ELLNODE * pnode = nnode;
@@ -418,52 +405,24 @@ bufSizeT casDGIntfIO ::
}
void casDGIntfIO::sendBeaconIO ( char & msg, unsigned length,
aitUint16 & portField, aitUint32 & addrField )
aitUint16 & portField, aitUint32 & )
{
caNetAddr addr = this->serverAddress ();
struct sockaddr_in inetAddr = addr.getSockIP();
osiSockAddrNode *pAddr;
int status;
char buf[64];
portField = inetAddr.sin_port; // the TCP port
for (pAddr = reinterpret_cast <osiSockAddrNode *> ( ellFirst ( & this->beaconAddrList ) );
pAddr; pAddr = reinterpret_cast <osiSockAddrNode *> ( ellNext ( & pAddr->node ) ) ) {
status = connect ( this->beaconSock, &pAddr->addr.sa, sizeof ( pAddr->addr.sa ) );
if (status<0) {
char sockErrBuf[64];
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
ipAddrToDottedIP ( & pAddr->addr.ia, buf, sizeof ( buf ) );
errlogPrintf ( "%s: CA beacon routing (connect to \"%s\") error was \"%s\"\n",
__FILE__, buf, sockErrBuf );
}
else {
osiSockAddr sockAddr;
osiSocklen_t size = ( osiSocklen_t ) sizeof ( sockAddr.sa );
status = getsockname ( this->beaconSock, &sockAddr.sa, &size );
if ( status < 0 ) {
char sockErrBuf[64];
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
errlogPrintf ( "%s: CA beacon routing (getsockname) error was \"%s\"\n",
__FILE__, sockErrBuf );
}
else if ( sockAddr.sa.sa_family == AF_INET ) {
addrField = sockAddr.ia.sin_addr.s_addr;
for (ELLNODE *pNode = ellFirst(&this->beaconAddrList); pNode; pNode = ellNext(pNode))
{
osiSockAddrNode *pAddr = reinterpret_cast<osiSockAddrNode *>(pNode);
status = send ( this->beaconSock, &msg, length, 0 );
if ( status < 0 ) {
char sockErrBuf[64];
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
ipAddrToA ( &pAddr->addr.ia, buf, sizeof(buf) );
errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\"\n",
__FILE__, buf, sockErrBuf );
}
else {
unsigned statusAsLength = static_cast < unsigned > ( status );
assert ( statusAsLength == length );
}
}
ssize_t status = sendto(this->beaconSock, &msg, length, 0, &pAddr->addr.sa, sizeof(pAddr->addr.ia));
if ( status != length ) {
char sockErrBuf[64], buf[64];
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
ipAddrToA ( &pAddr->addr.ia, buf, sizeof(buf) );
errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\" (%u)\n",
__FILE__, buf, sockErrBuf, (unsigned)status );
}
}
}

View File

@@ -24,7 +24,6 @@
#include "dbDefs.h"
#include "epicsAtomic.h"
#include "epicsEvent.h"
#include "epicsExit.h"
#include "epicsInterrupt.h"
#include "epicsRingPointer.h"
#include "epicsString.h"
@@ -92,7 +91,7 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2};
int callbackSetQueueSize(int size)
{
if (callbackIsInit) {
errlogPrintf("Callback system already initialized\n");
fprintf(stderr, "Callback system already initialized\n");
return -1;
}
callbackQueueSize = size;
@@ -102,7 +101,7 @@ int callbackSetQueueSize(int size)
int callbackParallelThreads(int count, const char *prio)
{
if (callbackIsInit) {
errlogPrintf("Callback system already initialized\n");
fprintf(stderr, "Callback system already initialized\n");
return -1;
}
@@ -112,7 +111,7 @@ int callbackParallelThreads(int count, const char *prio)
count = callbackParallelThreadsDefault;
if (count < 1) count = 1;
if (!prio || strcmp(prio, "") == 0 || strcmp(prio, "*") == 0) {
if (!prio || *prio == 0 || strcmp(prio, "*") == 0) {
int i;
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
@@ -121,30 +120,30 @@ int callbackParallelThreads(int count, const char *prio)
}
else {
dbMenu *pdbMenu;
int i;
if (!pdbbase) {
errlogPrintf("callbackParallelThreads: pdbbase not set\n");
fprintf(stderr, "callbackParallelThreads: pdbbase not set\n");
return -1;
}
/* Find prio in menuPriority */
pdbMenu = dbFindMenu(pdbbase, "menuPriority");
if (pdbMenu) {
int i, gotMatch = 0;
for (i = 0; i < pdbMenu->nChoice; i++) {
gotMatch = (epicsStrCaseCmp(prio, pdbMenu->papChoiceValue[i])==0) ? TRUE : FALSE;
if (gotMatch)
break;
}
if (gotMatch) {
callbackQueue[i].threadsConfigured = count;
return 0;
}
else {
errlogPrintf("Unknown priority \"%s\"\n", prio);
return -1;
}
if (!pdbMenu) {
fprintf(stderr, "callbackParallelThreads: No Priority menu\n");
return -1;
}
for (i = 0; i < pdbMenu->nChoice; i++) {
if (epicsStrCaseCmp(prio, pdbMenu->papChoiceValue[i]) == 0)
goto found;
}
fprintf(stderr, "callbackParallelThreads: "
"Unknown priority \"%s\"\n", prio);
return -1;
found:
callbackQueue[i].threadsConfigured = count;
}
return 0;
}

View File

@@ -47,14 +47,16 @@ typedef struct callbackPvt {
typedef void (*CALLBACKFUNC)(struct callbackPvt*);
#define callbackSetCallback(PFUN,PCALLBACK)\
( (PCALLBACK)->callback = (PFUN) )
#define callbackSetPriority(PRIORITY,PCALLBACK)\
( (PCALLBACK)->priority = (PRIORITY) )
#define callbackSetUser(USER,PCALLBACK)\
( (PCALLBACK)->user = (void *)(USER) )
#define callbackGetUser(USER,PCALLBACK)\
( (USER) = (void *)((CALLBACK *)(PCALLBACK))->user )
#define callbackSetCallback(PFUN, PCALLBACK) \
( (PCALLBACK)->callback = (PFUN) )
#define callbackSetPriority(PRIORITY, PCALLBACK) \
( (PCALLBACK)->priority = (PRIORITY) )
#define callbackGetPriority(PRIORITY, PCALLBACK) \
( (PRIORITY) = (PCALLBACK)->priority )
#define callbackSetUser(USER, PCALLBACK) \
( (PCALLBACK)->user = (void *) (USER) )
#define callbackGetUser(USER, PCALLBACK) \
( (USER) = (PCALLBACK)->user )
epicsShareFunc void callbackInit(void);
epicsShareFunc void callbackStop(void);

View File

@@ -50,11 +50,12 @@
#include "dbFldTypes.h"
#include "dbFldTypes.h"
#include "dbLink.h"
#include "dbLock.h"
#include "dbLockPvt.h"
#include "dbNotify.h"
#include "dbScan.h"
#include "dbServer.h"
#include "dbStaticLib.h"
#include "dbStaticPvt.h"
#include "devSup.h"
#include "epicsEvent.h"
#include "link.h"
@@ -812,11 +813,10 @@ long dbGet(DBADDR *paddr, short dbrType,
void *pbuffer, long *options, long *nRequest, void *pflin)
{
char *pbuf = pbuffer;
void *pfieldsave;
void *pfieldsave = paddr->pfield;
db_field_log *pfl = (db_field_log *)pflin;
short field_type;
long no_elements;
long offset;
long capacity, no_elements, offset;
struct rset *prset;
long status = 0;
@@ -826,18 +826,33 @@ long dbGet(DBADDR *paddr, short dbrType,
return 0;
if (!pfl || pfl->type == dbfl_type_rec) {
field_type = paddr->field_type;
no_elements = paddr->no_elements;
field_type = paddr->field_type;
no_elements = capacity = paddr->no_elements;
/* Update field info from record
* may modify paddr->pfield
*/
if (paddr->pfldDes->special == SPC_DBADDR &&
(prset = dbGetRset(paddr)) &&
prset->get_array_info) {
status = prset->get_array_info(paddr, &no_elements, &offset);
} else
offset = 0;
} else {
field_type = pfl->field_type;
no_elements = pfl->no_elements;
field_type = pfl->field_type;
no_elements = capacity = pfl->no_elements;
offset = 0;
}
if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK)
return getLinkValue(paddr, dbrType, pbuf, nRequest);
if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK) {
status = getLinkValue(paddr, dbrType, pbuf, nRequest);
goto done;
}
if (paddr->special == SPC_ATTRIBUTE)
return getAttrValue(paddr, dbrType, pbuf, nRequest);
if (paddr->special == SPC_ATTRIBUTE) {
status = getAttrValue(paddr, dbrType, pbuf, nRequest);
goto done;
}
/* Check for valid request */
if (INVALID_DB_REQ(dbrType) || field_type > DBF_DEVICE) {
@@ -845,26 +860,13 @@ long dbGet(DBADDR *paddr, short dbrType,
sprintf(message, "dbGet: Request type is %d\n", dbrType);
recGblDbaddrError(S_db_badDbrtype, paddr, message);
return S_db_badDbrtype;
status = S_db_badDbrtype;
goto done;
}
/* For SPC_DBADDR fields, the rset function
* get_array_info() is allowed to modify
* paddr->pfield. So we store the original
* value and restore it later.
*/
pfieldsave = paddr->pfield;
/* Update field info */
if (paddr->pfldDes->special == SPC_DBADDR &&
(prset = dbGetRset(paddr)) &&
prset->get_array_info) {
status = prset->get_array_info(paddr, &no_elements, &offset);
} else
offset = 0;
if (offset == 0 && (!nRequest || no_elements == 1)) {
if (nRequest) *nRequest = 1;
if (nRequest)
*nRequest = 1;
if (!pfl || pfl->type == dbfl_type_rec) {
status = dbFastGetConvertRoutine[field_type][dbrType]
(paddr->pfield, pbuf, paddr);
@@ -883,10 +885,11 @@ long dbGet(DBADDR *paddr, short dbrType,
}
} else {
long n;
long (*convert)();
GETCONVERTFUNC convert;
if (nRequest) {
if (no_elements<(*nRequest)) *nRequest = no_elements;
if (no_elements < *nRequest)
*nRequest = no_elements;
n = *nRequest;
} else {
n = 1;
@@ -901,11 +904,11 @@ long dbGet(DBADDR *paddr, short dbrType,
status = S_db_badDbrtype;
goto done;
}
/* convert database field and place it in the buffer */
/* convert data into the caller's buffer */
if (n <= 0) {
;/*do nothing*/
} else if (!pfl || pfl->type == dbfl_type_rec) {
status = convert(paddr, pbuf, n, no_elements, offset);
status = convert(paddr, pbuf, n, capacity, offset);
} else {
DBADDR localAddr = *paddr; /* Structure copy */
@@ -916,7 +919,7 @@ long dbGet(DBADDR *paddr, short dbrType,
localAddr.pfield = (char *) &pfl->u.v.field;
else
localAddr.pfield = (char *) pfl->u.r.field;
status = convert(&localAddr, pbuf, n, no_elements, offset);
status = convert(&localAddr, pbuf, n, capacity, offset);
}
}
done:
@@ -940,19 +943,25 @@ devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset) {
static long dbPutFieldLink(DBADDR *paddr,
short dbrType, const void *pbuffer, long nRequest)
{
dbLinkInfo link_info;
DBADDR *pdbaddr = NULL;
dbCommon *precord = paddr->precord;
dbCommon *lockrecs[2];
dbLocker locker;
dbFldDes *pfldDes = paddr->pfldDes;
long special = paddr->special;
struct link *plink = (struct link *)paddr->pfield;
const char *pstring = (const char *)pbuffer;
DBENTRY dbEntry;
struct dsxt *old_dsxt = NULL;
struct dset *new_dset = NULL;
struct dsxt *new_dsxt = NULL;
devSup *new_devsup = NULL;
long status;
int isDevLink;
short scan;
STATIC_ASSERT(DBLOCKER_NALLOC>=2);
switch (dbrType) {
case DBR_CHAR:
case DBR_UCHAR:
@@ -967,31 +976,57 @@ static long dbPutFieldLink(DBADDR *paddr,
return S_db_badDbrtype;
}
dbInitEntry(pdbbase, &dbEntry);
status = dbFindRecord(&dbEntry, precord->name);
if (!status) status = dbFindField(&dbEntry, pfldDes->name);
if (status) goto finish;
status = dbParseLink(pstring, pfldDes->field_type, &link_info);
if (status)
return status;
if (link_info.ltype == PV_LINK &&
(link_info.modifiers & (pvlOptCA | pvlOptCP | pvlOptCPP)) == 0) {
DBADDR tempaddr;
if (dbNameToAddr(link_info.target, &tempaddr)==0) {
/* This will become a DB link. */
pdbaddr = malloc(sizeof(*pdbaddr));
if (!pdbaddr) {
status = S_db_noMemory;
goto cleanup;
}
*pdbaddr = tempaddr; /* struct copy */
}
}
isDevLink = ellCount(&precord->rdes->devList) > 0 &&
(strcmp(pfldDes->name, "INP") == 0 ||
strcmp(pfldDes->name, "OUT") == 0);
pfldDes->isDevLink;
dbLockSetGblLock();
dbLockSetRecordLock(precord);
memset(&locker, 0, sizeof(locker));
lockrecs[0] = precord;
lockrecs[1] = pdbaddr ? pdbaddr->precord : NULL;
dbLockerPrepare(&locker, lockrecs, 2);
dbScanLockMany(&locker);
scan = precord->scan;
if (isDevLink) {
devSup *pdevSup = dbDTYPtoDevSup(precord->rdes, precord->dtyp);
if (pdevSup) {
new_dset = pdevSup->pdset;
new_dsxt = pdevSup->pdsxt;
new_devsup = dbDTYPtoDevSup(precord->rdes, precord->dtyp);
if (new_devsup) {
new_dset = new_devsup->pdset;
new_dsxt = new_devsup->pdsxt;
}
}
if (dbCanSetLink(plink, &link_info, new_devsup)) {
/* link type mis-match prevents assignment */
status = S_dbLib_badField;
goto unlock;
}
if (isDevLink) {
if (precord->dset) {
pdevSup = dbDSETtoDevSup(precord->rdes, precord->dset);
if (pdevSup)
old_dsxt = pdevSup->pdsxt;
devSup *old_devsup = dbDSETtoDevSup(precord->rdes, precord->dset);
if (old_devsup)
old_dsxt = old_devsup->pdsxt;
}
if (new_dsxt == NULL ||
@@ -1017,13 +1052,11 @@ static long dbPutFieldLink(DBADDR *paddr,
switch (plink->type) { /* Old link type */
case DB_LINK:
case CA_LINK:
dbRemoveLink(plink);
case CONSTANT:
dbRemoveLink(&locker, plink); /* link type becomes PV_LINK */
break;
case PV_LINK:
case CONSTANT:
break; /* do nothing */
case MACRO_LINK:
break; /* should never get here */
@@ -1037,7 +1070,7 @@ static long dbPutFieldLink(DBADDR *paddr,
if (special) status = dbPutSpecial(paddr, 0);
if (!status) status = dbPutString(&dbEntry, pstring);
if (!status) status = dbSetLink(plink, &link_info, new_devsup);
if (!status && special) status = dbPutSpecial(paddr, 1);
@@ -1064,11 +1097,9 @@ static long dbPutFieldLink(DBADDR *paddr,
switch (plink->type) { /* New link type */
case PV_LINK:
dbAddLink(precord, plink, pfldDes->field_type);
break;
case CONSTANT:
break; /* do nothing */
dbAddLink(&locker, plink, pfldDes->field_type, pdbaddr);
break;
case DB_LINK:
case CA_LINK:
@@ -1094,9 +1125,10 @@ postScanEvent:
if (scan != precord->scan)
db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG);
unlock:
dbLockSetGblUnlock();
finish:
dbFinishEntry(&dbEntry);
dbScanUnlockMany(&locker);
dbLockerFinalize(&locker);
cleanup:
free(link_info.target);
return status;
}

View File

@@ -205,6 +205,7 @@ private:
cacContextNotify & notify;
epics_auto_ptr < cacContext > pNetContext;
char * pStateNotifyCache;
bool isolated;
cacChannel & createChannel (
epicsGuard < epicsMutex > &,

View File

@@ -6,14 +6,13 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* $Revision-Id$
/* dbCa.c
*
* Original Authors: Bob Dalesio and Marty Kraimer
* Date: 26MAR96
*
*/
#define EPICS_DBCA_PRIVATE_API
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
@@ -30,6 +29,7 @@
#include "epicsPrint.h"
#include "epicsString.h"
#include "epicsThread.h"
#include "epicsAtomic.h"
#include "epicsTime.h"
#include "errlog.h"
#include "errMdef.h"
@@ -46,13 +46,17 @@
#include "dbCaPvt.h"
#include "dbCommon.h"
#include "db_convert.h"
#include "dbLink.h"
#include "dbLock.h"
#include "dbScan.h"
#include "link.h"
#include "recSup.h"
/* defined in dbContext.cpp
* Setup local CA access
*/
extern void dbServiceIOInit();
extern int dbServiceIsolate;
static ELLLIST workList = ELLLIST_INIT; /* Work list for dbCaTask */
static epicsMutexId workListLock; /*Mutual exclusions semaphores for workList*/
@@ -60,7 +64,7 @@ static epicsEventId workListEvent; /*wakeup event for dbCaTask*/
static int removesOutstanding = 0;
#define removesOutstandingWarning 10000
static volatile enum {
static volatile enum dbCaCtl_t {
ctlInit, ctlRun, ctlPause, ctlExit
} dbCaCtl;
static epicsEventId startStopEvent;
@@ -70,57 +74,47 @@ struct ca_client_context * dbCaClientContext;
/* Forward declarations */
static void dbCaTask(void *);
static lset dbCa_lset;
#define printLinks(pcaLink) \
errlogPrintf("%s has DB CA link to %s\n",\
pcaLink->plink->value.pv_link.precord->name, pcaLink->pvname)
pcaLink->plink->precord->name, pcaLink->pvname)
static int dbca_chan_count;
/* caLink locking
*
* workListLock
* This is only used to put request into and take them out of workList.
* While this is locked no other locks are taken
* Lock ordering:
* dbScanLock -> caLink.lock -> workListLock
*
* dbScanLock
* dbCaAddLink and dbCaRemoveLink are only called by dbAccess or iocInit
* They are only called by dbAccess when it has a global lock on lock set.
* It is assumed that ALL other dbCaxxx calls are made only if dbScanLock
* is already active. These routines are intended for use by record/device
* support.
* workListLock:
* Guards access to workList.
*
* caLink.lock
* Any code that use a caLink takes this lock and releases it when done
* dbScanLock:
* All dbCa* functions operating on a single link may only be called when
* the record containing the DBLINK is locked. Including:
* dbCaGet*()
* isConnected()
* dbCaPutLink()
* scanForward()
* dbCaAddLinkCallback()
* dbCaRemoveLink()
*
* dbCaTask and the channel access callbacks NEVER access anything in the
* records except after locking caLink.lock and checking that caLink.plink
* is not null. They NEVER call dbScanLock.
* Guard the pointer plink.value.pv_link.pvt, but not the struct caLink
* which is pointed to.
*
* The above is necessary to prevent deadlocks and attempts to use a caLink
* that has been deleted.
* caLink.lock:
* Guards the caLink structure (but not the struct DBLINK)
*
* Just a few words about handling dbCaRemoveLink because this is when
* it is essential that nothing tries to use a caLink that has been freed.
* The dbCaTask only locks caLink, and must not lock the record (a violation of lock order).
*
* dbCaRemoveLink is called when links are being modified. This is only
* done with the dbScan mechanism guranteeing that nothing from
* database access trys to access the record containing the caLink.
* During link modification or IOC shutdown the pca->plink pointer (guarded by caLink.lock)
* is used as a flag to indicate that a link is no longer active.
*
* Thus the problem is to make sure that nothing from channel access
* accesses a caLink that is deleted. This is done as follows.
* References to the struct caLink are owned by the dbCaTask, and any scanOnceCallback()
* which is in progress.
*
* dbCaRemoveLink does the following:
* epicsMutexMustLock(pca->lock);
* pca->plink = 0;
* plink->value.pv_link.pvt = 0;
* epicsMutexUnlock(pca->lock);
* addAction(pca,CA_CLEAR_CHANNEL);
*
* dbCaTask issues a ca_clear_channel and then frees the caLink.
*
* If any channel access callback gets called before the ca_clear_channel
* it finds pca->plink==0 and does nothing. Once ca_clear_channel
* is called no other callback for this caLink will be called.
* The libca and scanOnceCallback callbacks take no action if pca->plink==NULL.
*
* dbCaPutLinkCallback causes an additional complication because
* when dbCaRemoveLink is called the callback may not have occured.
@@ -162,11 +156,23 @@ static void addAction(caLink *pca, short link_action)
epicsEventSignal(workListEvent);
}
static void dbCaLinkFree(caLink *pca)
static void caLinkInc(caLink *pca)
{
assert(epicsAtomicGetIntT(&pca->refcount)>0);
epicsAtomicIncrIntT(&pca->refcount);
}
static void caLinkDec(caLink *pca)
{
int cnt;
dbCaCallback callback;
void *userPvt = 0;
cnt = epicsAtomicDecrIntT(&pca->refcount);
assert(cnt>=0);
if(cnt>0)
return;
if (pca->chid) {
ca_clear_channel(pca->chid);
--dbca_chan_count;
@@ -187,10 +193,46 @@ static void dbCaLinkFree(caLink *pca)
if (callback) callback(userPvt);
}
/* Block until worker thread has processed all previously queued actions.
* Does not prevent additional actions from being queued.
*/
void dbCaSync(void)
{
epicsEventId wake;
caLink templink;
/* we only partially initialize templink.
* It has no link field and no subscription
* so the worker must handle it early
*/
memset(&templink, 0, sizeof(templink));
templink.refcount = 1;
wake = epicsEventMustCreate(epicsEventEmpty);
templink.lock = epicsMutexMustCreate();
templink.userPvt = wake;
addAction(&templink, CA_SYNC);
epicsEventMustWait(wake);
/* Worker holds workListLock when calling epicsEventMustTrigger()
* we cycle through workListLock to ensure worker call to
* epicsEventMustTrigger() returns before we destroy the event.
*/
epicsMutexMustLock(workListLock);
epicsMutexUnlock(workListLock);
assert(templink.refcount==1);
epicsMutexDestroy(templink.lock);
epicsEventDestroy(wake);
}
void dbCaCallbackProcess(void *userPvt)
{
struct link *plink = (struct link *)userPvt;
dbCommon *pdbCommon = plink->value.pv_link.precord;
dbCommon *pdbCommon = plink->precord;
dbScanLock(pdbCommon);
pdbCommon->rset->process(pdbCommon);
@@ -199,40 +241,25 @@ void dbCaCallbackProcess(void *userPvt)
void dbCaShutdown(void)
{
if (dbCaCtl == ctlRun || dbCaCtl == ctlPause) {
dbCaCtl = ctlExit;
epicsEventSignal(workListEvent);
epicsEventMustWait(startStopEvent);
epicsEventDestroy(startStopEvent);
} else {
/* manually cleanup queue since dbCa thread isn't running
* which only happens in unit tests
*/
caLink *pca;
epicsMutexMustLock(workListLock);
while((pca=(caLink*)ellGet(&workList))!=NULL) {
if(pca->link_action&CA_CLEAR_CHANNEL) {
dbCaLinkFree(pca);
}
}
epicsMutexUnlock(workListLock);
}
enum dbCaCtl_t cur = dbCaCtl;
assert(cur == ctlRun || cur == ctlPause);
dbCaCtl = ctlExit;
epicsEventSignal(workListEvent);
epicsEventMustWait(startStopEvent);
}
void dbCaLinkInitIsolated(void)
static void dbCaLinkInitImpl(int isolate)
{
dbServiceIsolate = isolate;
dbServiceIOInit();
if (!workListLock)
workListLock = epicsMutexMustCreate();
if (!workListEvent)
workListEvent = epicsEventMustCreate(epicsEventEmpty);
dbCaCtl = ctlExit;
}
void dbCaLinkInit(void)
{
dbServiceIOInit();
dbCaLinkInitIsolated();
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
if(!startStopEvent)
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
dbCaCtl = ctlPause;
epicsThreadCreate("dbCaLink", epicsThreadPriorityMedium,
@@ -241,6 +268,16 @@ void dbCaLinkInit(void)
epicsEventMustWait(startStopEvent);
}
void dbCaLinkInitIsolated(void)
{
dbCaLinkInitImpl(1);
}
void dbCaLinkInit(void)
{
dbCaLinkInitImpl(0);
}
void dbCaRun(void)
{
if (dbCaCtl == ctlPause) {
@@ -265,6 +302,7 @@ void dbCaAddLinkCallback(struct link *plink,
assert(!plink->value.pv_link.pvt);
pca = (caLink *)dbCalloc(1, sizeof(caLink));
pca->refcount = 1;
pca->lock = epicsMutexMustCreate();
pca->plink = plink;
pca->pvname = epicsStrDup(plink->value.pv_link.pvname);
@@ -273,13 +311,20 @@ void dbCaAddLinkCallback(struct link *plink,
pca->userPvt = userPvt;
epicsMutexMustLock(pca->lock);
plink->lset = &dbCa_lset;
plink->type = CA_LINK;
plink->value.pv_link.pvt = pca;
addAction(pca, CA_CONNECT);
epicsMutexUnlock(pca->lock);
}
void dbCaRemoveLink(struct link *plink)
long dbCaAddLink(struct dbLocker *locker, struct link *plink, short dbfType)
{
dbCaAddLinkCallback(plink, 0, 0, NULL);
return 0;
}
void dbCaRemoveLink(struct dbLocker *locker, struct link *plink)
{
caLink *pca = (caLink *)plink->value.pv_link.pvt;
@@ -287,6 +332,9 @@ void dbCaRemoveLink(struct link *plink)
epicsMutexMustLock(pca->lock);
pca->plink = 0;
plink->value.pv_link.pvt = 0;
plink->value.pv_link.pvlMask = 0;
plink->type = PV_LINK;
plink->lset = NULL;
/* Unlock before addAction or dbCaTask might free first */
epicsMutexUnlock(pca->lock);
addAction(pca, CA_CLEAR_CHANNEL);
@@ -346,15 +394,15 @@ long dbCaGetLink(struct link *plink,short dbrType, void *pdest,
assert(pca->pgetNative);
status = fConvert(pca->pgetNative, pdest, 0);
} else {
long ntoget = *nelements;
unsigned long ntoget = *nelements;
struct dbAddr dbAddr;
long (*aConvert)(struct dbAddr *paddr, void *to, long nreq, long nto, long off);
aConvert = dbGetConvertRoutine[newType][dbrType];
assert(pca->pgetNative);
if (ntoget > pca->nelements)
ntoget = pca->nelements;
if (ntoget > pca->usedelements)
ntoget = pca->usedelements;
*nelements = ntoget;
memset((void *)&dbAddr, 0, sizeof(dbAddr));
@@ -409,6 +457,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
if (!pca->pputNative) {
pca->pputNative = dbCalloc(pca->nelements,
dbr_value_size[ca_field_type(pca->chid)]);
pca->putnelements = 0;
/* Fixed and disabled by ANJ, see comment above.
plink->value.pv_link.pvlMask |= pvlOptOutNative;
*/
@@ -418,6 +467,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
fConvert = dbFastPutConvertRoutine[dbrType][newType];
status = fConvert(pbuffer, pca->pputNative, 0);
pca->putnelements = 1;
} else {
struct dbAddr dbAddr;
long (*aConvert)(struct dbAddr *paddr, const void *from, long nreq, long nfrom, long off);
@@ -430,10 +480,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
if(nRequest>pca->nelements)
nRequest = pca->nelements;
status = aConvert(&dbAddr, pbuffer, nRequest, pca->nelements, 0);
if(nRequest<pca->nelements) {
long elemsize = dbr_value_size[ca_field_type(pca->chid)];
memset(nRequest*elemsize+(char*)pca->pputNative, 0, (pca->nelements-nRequest)*elemsize);
}
pca->putnelements = nRequest;
}
link_action |= CA_WRITE_NATIVE;
pca->gotOutNative = TRUE;
@@ -453,7 +500,13 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
return status;
}
int dbCaIsLinkConnected(const struct link *plink)
long dbCaPutLink(struct link *plink, short dbrType,
const void *pbuffer, long nRequest)
{
return dbCaPutLinkCallback(plink, dbrType, pbuffer, nRequest, 0, NULL);
}
static int isConnected(const struct link *plink)
{
caLink *pca;
@@ -463,7 +516,7 @@ int dbCaIsLinkConnected(const struct link *plink)
return pca->isConnected;
}
void dbCaScanFwdLink(struct link *plink) {
static void scanForward(struct link *plink) {
short fwdLinkValue = 1;
if (plink->value.pv_link.pvlMask & pvlOptFWD)
@@ -482,7 +535,7 @@ void dbCaScanFwdLink(struct link *plink) {
return -1; \
}
long dbCaGetNelements(const struct link *plink, long *nelements)
static long getElements(const struct link *plink, long *nelements)
{
caLink *pca;
@@ -492,7 +545,7 @@ long dbCaGetNelements(const struct link *plink, long *nelements)
return 0;
}
long dbCaGetAlarm(const struct link *plink,
static long getAlarm(const struct link *plink,
epicsEnum16 *pstat, epicsEnum16 *psevr)
{
caLink *pca;
@@ -504,7 +557,7 @@ long dbCaGetAlarm(const struct link *plink,
return 0;
}
long dbCaGetTimeStamp(const struct link *plink,
static long getTimeStamp(const struct link *plink,
epicsTimeStamp *pstamp)
{
caLink *pca;
@@ -515,7 +568,7 @@ long dbCaGetTimeStamp(const struct link *plink,
return 0;
}
int dbCaGetLinkDBFtype(const struct link *plink)
static int getDBFtype(const struct link *plink)
{
caLink *pca;
int type;
@@ -546,7 +599,7 @@ long dbCaGetAttributes(const struct link *plink,
return 0;
}
long dbCaGetControlLimits(const struct link *plink,
static long getControlLimits(const struct link *plink,
double *low, double *high)
{
caLink *pca;
@@ -562,7 +615,7 @@ long dbCaGetControlLimits(const struct link *plink,
return gotAttributes ? 0 : -1;
}
long dbCaGetGraphicLimits(const struct link *plink,
static long getGraphicLimits(const struct link *plink,
double *low, double *high)
{
caLink *pca;
@@ -578,7 +631,7 @@ long dbCaGetGraphicLimits(const struct link *plink,
return gotAttributes ? 0 : -1;
}
long dbCaGetAlarmLimits(const struct link *plink,
static long getAlarmLimits(const struct link *plink,
double *lolo, double *low, double *high, double *hihi)
{
caLink *pca;
@@ -596,7 +649,7 @@ long dbCaGetAlarmLimits(const struct link *plink,
return gotAttributes ? 0 : -1;
}
long dbCaGetPrecision(const struct link *plink, short *precision)
static long getPrecision(const struct link *plink, short *precision)
{
caLink *pca;
int gotAttributes;
@@ -608,7 +661,7 @@ long dbCaGetPrecision(const struct link *plink, short *precision)
return gotAttributes ? 0 : -1;
}
long dbCaGetUnits(const struct link *plink,
static long getUnits(const struct link *plink,
char *units, int unitsSize)
{
caLink *pca;
@@ -623,6 +676,50 @@ long dbCaGetUnits(const struct link *plink,
return gotAttributes ? 0 : -1;
}
static void scanComplete(void *raw, dbCommon *prec)
{
caLink *pca = raw;
epicsMutexMustLock(pca->lock);
if(!pca->plink) {
/* IOC shutdown or link re-targeted. Do nothing. */
} else if(pca->scanningOnce==0) {
errlogPrintf("dbCa.c complete callback w/ scanningOnce==0\n");
} else if(--pca->scanningOnce){
/* another scan is queued */
if(scanOnceCallback(prec, scanComplete, raw)) {
errlogPrintf("dbCa.c failed to re-queue scanOnce\n");
} else
caLinkInc(pca);
}
epicsMutexUnlock(pca->lock);
caLinkDec(pca);
}
/* must be called with pca->lock held */
static void scanLinkOnce(dbCommon *prec, caLink *pca) {
if(pca->scanningOnce==0) {
if(scanOnceCallback(prec, scanComplete, pca)) {
errlogPrintf("dbCa.c failed to queue scanOnce\n");
} else
caLinkInc(pca);
}
if(pca->scanningOnce<5)
pca->scanningOnce++;
/* else too many scans queued */
}
static lset dbCa_lset = {
dbCaRemoveLink,
isConnected,
getDBFtype, getElements,
dbCaGetLink,
getControlLimits, getGraphicLimits, getAlarmLimits,
getPrecision, getUnits,
getAlarm, getTimeStamp,
dbCaPutLink,
scanForward
};
static void connectionCallback(struct connection_handler_args arg)
{
caLink *pca;
@@ -637,13 +734,13 @@ static void connectionCallback(struct connection_handler_args arg)
pca->isConnected = (ca_state(arg.chid) == cs_conn);
if (!pca->isConnected) {
struct pv_link *ppv_link = &plink->value.pv_link;
dbCommon *precord = ppv_link->precord;
dbCommon *precord = plink->precord;
pca->nDisconnect++;
if (precord &&
((ppv_link->pvlMask & pvlOptCP) ||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)))
scanOnce(precord);
scanLinkOnce(precord, pca);
goto done;
}
pca->hasReadAccess = ca_read_access(arg.chid);
@@ -673,6 +770,7 @@ static void connectionCallback(struct connection_handler_args arg)
}
pca->gotFirstConnection = TRUE;
pca->nelements = ca_element_count(arg.chid);
pca->usedelements = 0;
pca->dbrType = ca_field_type(arg.chid);
if ((plink->value.pv_link.pvlMask & pvlOptInpNative) && !pca->pgetNative) {
link_action |= CA_MONITOR_NATIVE;
@@ -711,7 +809,7 @@ static void eventCallback(struct event_handler_args arg)
if (!plink) goto done;
monitor = pca->monitor;
userPvt = pca->userPvt;
precord = plink->value.pv_link.precord;
precord = plink->precord;
if (arg.status != ECA_NORMAL) {
if (precord) {
if (arg.status != ECA_NORDACCESS &&
@@ -725,6 +823,7 @@ static void eventCallback(struct event_handler_args arg)
goto done;
}
assert(arg.dbr);
assert(arg.count<=pca->nelements);
size = arg.count * dbr_value_size[arg.type];
if (arg.type == DBR_TIME_STRING &&
ca_field_type(pca->chid) == DBR_ENUM) {
@@ -741,10 +840,12 @@ static void eventCallback(struct event_handler_args arg)
case DBR_TIME_DOUBLE:
assert(pca->pgetNative);
memcpy(pca->pgetNative, dbr_value_ptr(arg.dbr, arg.type), size);
pca->usedelements = arg.count;
pca->gotInNative = TRUE;
break;
default:
errMessage(-1, "dbCa: eventCallback Logic Error\n");
errlogPrintf("dbCa: eventCallback Logic Error. dbr=%ld dbf=%d\n",
arg.type, ca_field_type(pca->chid));
break;
}
pdbr_time_double = (struct dbr_time_double *)arg.dbr;
@@ -756,7 +857,7 @@ static void eventCallback(struct event_handler_args arg)
if ((ppv_link->pvlMask & pvlOptCP) ||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))
scanOnce(precord);
scanLinkOnce(precord, pca);
}
done:
epicsMutexUnlock(pca->lock);
@@ -825,11 +926,11 @@ static void accessRightsCallback(struct access_rights_handler_args arg)
pca->hasWriteAccess = ca_write_access(arg.chid);
if (pca->hasReadAccess && pca->hasWriteAccess) goto done;
ppv_link = &plink->value.pv_link;
precord = ppv_link->precord;
precord = plink->precord;
if (precord &&
((ppv_link->pvlMask & pvlOptCP) ||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)))
scanOnce(precord);
scanLinkOnce(precord, pca);
done:
epicsMutexUnlock(pca->lock);
}
@@ -856,7 +957,7 @@ static void getAttribEventCallback(struct event_handler_args arg)
getAttributes = pca->getAttributes;
getAttributesPvt = pca->getAttributesPvt;
if (arg.status != ECA_NORMAL) {
dbCommon *precord = plink->value.pv_link.precord;
dbCommon *precord = plink->precord;
if (precord) {
errlogPrintf("dbCa: getAttribEventCallback record %s error %s\n",
precord->name, ca_message(arg.status));
@@ -912,11 +1013,15 @@ static void dbCaTask(void *arg)
break; /* workList is empty */
}
link_action = pca->link_action;
if (link_action&CA_SYNC)
epicsEventMustTrigger((epicsEventId)pca->userPvt); /* dbCaSync() requires workListLock to be held here */
pca->link_action = 0;
if (link_action & CA_CLEAR_CHANNEL) --removesOutstanding;
epicsMutexUnlock(workListLock); /* Give back immediately */
if (link_action&CA_SYNC)
continue;
if (link_action & CA_CLEAR_CHANNEL) { /* This must be first */
dbCaLinkFree(pca);
caLinkDec(pca);
/* No alarm is raised. Since link is changing so what? */
continue; /* No other link_action makes sense */
}
@@ -945,11 +1050,11 @@ static void dbCaTask(void *arg)
assert(pca->pputNative);
if (pca->putType == CA_PUT) {
status = ca_array_put(
pca->dbrType, pca->nelements,
pca->dbrType, pca->putnelements,
pca->chid, pca->pputNative);
} else if (pca->putType==CA_PUT_CALLBACK) {
status = ca_array_put_callback(
pca->dbrType, pca->nelements,
pca->dbrType, pca->putnelements,
pca->chid, pca->pputNative,
putComplete, pca);
} else {
@@ -999,15 +1104,15 @@ static void dbCaTask(void *arg)
}
}
if (link_action & CA_MONITOR_NATIVE) {
size_t element_size;
element_size = dbr_value_size[ca_field_type(pca->chid)];
epicsMutexMustLock(pca->lock);
pca->pgetNative = dbCalloc(pca->nelements, element_size);
pca->elementSize = dbr_value_size[ca_field_type(pca->chid)];
pca->pgetNative = dbCalloc(pca->nelements, pca->elementSize);
epicsMutexUnlock(pca->lock);
status = ca_add_array_event(
ca_field_type(pca->chid)+DBR_TIME_STRING,
ca_element_count(pca->chid),
dbf_type_to_DBR_TIME(ca_field_type(pca->chid)),
0, /* dynamic size */
pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0);
if (status != ECA_NORMAL) {
errlogPrintf("dbCaTask ca_add_array_event %s\n",

View File

@@ -1,20 +1,17 @@
/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* Copyright (c) 2015 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbCa.h */
/* dbCa.h */
#ifndef INCdbCah
#define INCdbCah
#include "shareLib.h"
#include "epicsTime.h"
#include "link.h"
#include "dbLink.h"
#ifdef __cplusplus
extern "C" {
@@ -29,48 +26,60 @@ epicsShareFunc void dbCaRun(void);
epicsShareFunc void dbCaPause(void);
epicsShareFunc void dbCaShutdown(void);
struct dbLocker;
epicsShareFunc void dbCaAddLinkCallback(struct link *plink,
dbCaCallback connect, dbCaCallback monitor, void *userPvt);
#define dbCaAddLink(plink) dbCaAddLinkCallback((plink), 0, 0, 0)
epicsShareFunc void dbCaRemoveLink(struct link *plink);
epicsShareFunc long dbCaAddLink(struct dbLocker *locker, struct link *plink, short dbfType);
epicsShareFunc void dbCaRemoveLink(struct dbLocker *locker, struct link *plink);
epicsShareFunc long dbCaGetLink(struct link *plink,
short dbrType, void *pbuffer, epicsEnum16 *pstat, epicsEnum16 *psevr,
long *nRequest);
epicsShareFunc long dbCaGetAttributes(const struct link *plink,
dbCaCallback callback, void *userPvt);
epicsShareFunc long dbCaPutLinkCallback(struct link *plink,
short dbrType, const void *pbuffer,long nRequest,
dbCaCallback callback, void *userPvt);
#define dbCaPutLink(plink, dbrType, pbuffer, nRequest) \
dbCaPutLinkCallback((plink), (dbrType), (pbuffer), (nRequest), 0, 0)
epicsShareFunc int dbCaIsLinkConnected(const struct link *plink);
epicsShareFunc void dbCaScanFwdLink(struct link *plink);
/* The following are available after the link is connected*/
epicsShareFunc long dbCaGetNelements(const struct link *plink,
long *nelements);
#define dbCaGetSevr(plink, severity) \
dbCaGetAlarm((plink), NULL, (severity))
epicsShareFunc long dbCaGetAlarm(const struct link *plink,
epicsEnum16 *status, epicsEnum16 *severity);
epicsShareFunc long dbCaGetTimeStamp(const struct link *plink,
epicsTimeStamp *pstamp);
epicsShareFunc int dbCaGetLinkDBFtype(const struct link *plink);
/*The following are available after attribute request is complete*/
epicsShareFunc long dbCaGetAttributes(const struct link *plink,
dbCaCallback callback, void *userPvt);
epicsShareFunc long dbCaGetControlLimits(const struct link *plink,
double *low, double *high);
epicsShareFunc long dbCaGetGraphicLimits(const struct link *plink,
double *low, double *high);
epicsShareFunc long dbCaGetAlarmLimits(const struct link *plink,
double *lolo, double *low, double *high, double *hihi);
epicsShareFunc long dbCaGetPrecision(const struct link *plink,
short *precision);
epicsShareFunc long dbCaGetUnits(const struct link *plink,
char *units, int unitsSize);
epicsShareFunc long dbCaPutLink(struct link *plink,short dbrType,
const void *pbuffer,long nRequest);
extern struct ca_client_context * dbCaClientContext;
#ifdef EPICS_DBCA_PRIVATE_API
epicsShareFunc void dbCaSync(void);
#endif
/* These macros are for backwards compatibility */
#define dbCaIsLinkConnected(link) \
dbIsLinkConnected(link)
#define dbCaGetLinkDBFtype(link) \
dbGetLinkDBFtype(link)
#define dbCaGetNelements(link, nelements) \
dbGetNelements(link, nelements)
#define dbCaGetSevr(link, sevr) \
dbGetAlarm(link, NULL, sevr)
#define dbCaGetAlarm(link, stat, sevr) \
dbGetAlarm(link, stat, sevr)
#define dbCaGetTimeStamp(link, pstamp) \
dbGetTimeStamp(link, pstamp)
#define dbCaGetControlLimits(link, low, high) \
dbGetControlLimits(link, low, high)
#define dbCaGetGraphicLimits(link, low, high) \
dbGetGraphicLimits(link, low, high)
#define dbCaGetAlarmLimits(link, lolo, low, high, hihi) \
dbGetAlarmLimits(link, lolo, low, high, hihi)
#define dbCaGetPrecision(link, prec) \
dbGetPrecision(link, prec)
#define dbCaGetUnits(link, units, unitSize) \
dbGetUnits(link, units, unitSize)
#define dbCaScanFwdLink(link) \
dbScanFwdLink(link)
#ifdef __cplusplus
}
#endif

View File

@@ -6,14 +6,11 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbCaPvt.h */
/****************************************************************
*
* Current Author: Bob Dalesio
* Contributing Author: Marty Kraimer
* Date: 08APR96
*
****************************************************************/
/* dbCaPvt.h
*
* Original Authors: Bob Dalesio, Marty Kraimer
*
*/
#ifndef INC_dbCaPvt_H
#define INC_dbCaPvt_H
@@ -32,61 +29,67 @@
#define CA_MONITOR_NATIVE 0x10
#define CA_MONITOR_STRING 0x20
#define CA_GET_ATTRIBUTES 0x40
#define CA_SYNC 0x1000
/* write type */
#define CA_PUT 0x1
#define CA_PUT_CALLBACK 0x2
typedef struct caLink
{
ELLNODE node;
epicsMutexId lock;
struct link *plink;
char *pvname;
chid chid;
short link_action;
/* The following have new values after each data event*/
epicsEnum16 sevr;
epicsEnum16 stat;
epicsTimeStamp timeStamp;
/* The following have values after connection*/
short dbrType;
long nelements;
char hasReadAccess;
char hasWriteAccess;
char isConnected;
char gotFirstConnection;
/* The following are for dbCaAddLinkCallback */
dbCaCallback connect;
dbCaCallback monitor;
void *userPvt;
/* The following are for write request */
short putType;
dbCaCallback putCallback;
void *putUserPvt;
/* The following are for access to additional attributes*/
char gotAttributes;
dbCaCallback getAttributes;
void *getAttributesPvt;
/* The following have values after getAttribEventCallback*/
double controlLimits[2];
double displayLimits[2];
double alarmLimits[4];
short precision;
char units[MAX_UNITS_SIZE]; /* units of value */
/* The following are for handling data*/
void *pgetNative;
char *pgetString;
void *pputNative;
char *pputString;
char gotInNative;
char gotInString;
char gotOutNative;
char gotOutString;
char newOutNative;
char newOutString;
/* The following are for dbcar*/
unsigned long nDisconnect;
unsigned long nNoWrite; /*only modified by dbCaPutLink*/
ELLNODE node;
int refcount;
epicsMutexId lock;
struct link *plink;
char *pvname;
chid chid;
short link_action;
/* The following have new values after each data event*/
epicsEnum16 sevr;
epicsEnum16 stat;
epicsTimeStamp timeStamp;
/* The following have values after connection*/
short dbrType;
size_t elementSize; /* size of one element in pgetNative */
unsigned long nelements; /* PVs max array size */
unsigned long usedelements; /* currently used in pgetNative */
unsigned long putnelements; /* currently used in pputNative */
char hasReadAccess;
char hasWriteAccess;
char isConnected;
char gotFirstConnection;
/* The following are for dbCaAddLinkCallback */
dbCaCallback connect;
dbCaCallback monitor;
void *userPvt;
/* The following are for write request */
short putType;
dbCaCallback putCallback;
void *putUserPvt;
/* The following are for access to additional attributes*/
char gotAttributes;
dbCaCallback getAttributes;
void *getAttributesPvt;
/* The following have values after getAttribEventCallback*/
double controlLimits[2];
double displayLimits[2];
double alarmLimits[4];
short precision;
char units[MAX_UNITS_SIZE]; /* units of value */
/* The following are for handling data*/
void *pgetNative;
char *pgetString;
void *pputNative;
char *pputString;
char gotInNative;
char gotInString;
char gotOutNative;
char gotOutString;
char newOutNative;
char newOutString;
unsigned char scanningOnce;
/* The following are for dbcar*/
unsigned long nDisconnect;
unsigned long nNoWrite; /*only modified by dbCaPutLink*/
}caLink;
#endif /* INC_dbCaPvt_H */

View File

@@ -87,10 +87,10 @@ long dbcar(char *precordname, int level)
!dbIsAlias(pdbentry)) {
pdbRecordType = pdbentry->precordType;
precord = (dbCommon *)pdbentry->precnode->precord;
dbScanLock(precord);
for (j=0; j<pdbRecordType->no_links; j++) {
pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[j]];
plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
dbLockSetGblLock();
if (plink->type == CA_LINK) {
ncalinks++;
pca = (caLink *)plink->value.pv_link.pvt;
@@ -135,8 +135,8 @@ long dbcar(char *precordname, int level)
}
}
}
dbLockSetGblUnlock();
}
dbScanUnlock(precord);
if (precordname) goto done;
}
status = dbNextRecord(pdbentry);
@@ -189,10 +189,8 @@ void dbcaStats(int *pchans, int *pdiscon)
dbFldDes *pdbFldDes = pdbRecordType->papFldDes[i];
plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
if (plink->type == CA_LINK) {
caLink *pca = (caLink *)plink->value.pv_link.pvt;
ncalinks++;
if (pca && pca->chid && ca_state(pca->chid) == cs_conn) {
if (dbCaIsLinkConnected(plink)) {
nconnected++;
}
}

View File

@@ -19,7 +19,6 @@
#include "cantProceed.h"
#include "epicsAssert.h"
#include "epicsExit.h"
#include "epicsString.h"
#include "errlog.h"
#include "freeList.h"
@@ -704,21 +703,27 @@ void dbChannelShow(dbChannel *chan, int level, const unsigned short indent)
int pre = ellCount(&chan->pre_chain);
int post = ellCount(&chan->post_chain);
printf("%*schannel name: %s\n", indent, "", chan->name);
printf("%*s field_type=%s (%d bytes), dbr_type=%s, %ld element%s, %d filter%s", indent, "",
dbGetFieldTypeString(chan->addr.field_type), chan->addr.field_size,
dbGetFieldTypeString(chan->addr.dbr_field_type), elems, elems == 1 ? "" : "s",
count, count == 1 ? "" : "s");
if (count)
printf(" (%d pre eventq, %d post eventq)\n", pre, post);
else
printf("\n");
if (level > 0)
dbChannelFilterShow(chan, level - 1, indent + 2);
if (count) {
printf("%*s final field_type=%s (%dB), %ld element%s\n", indent, "",
dbGetFieldTypeString(chan->final_type), chan->final_field_size,
felems, felems == 1 ? "" : "s");
printf("%*sChannel: '%s'\n", indent, "", chan->name);
if (level > 0) {
printf("%*sfield_type=%s (%d bytes), dbr_type=%s, %ld element%s",
indent + 4, "",
dbGetFieldTypeString(chan->addr.field_type),
chan->addr.field_size,
dbGetFieldTypeString(chan->addr.dbr_field_type),
elems, elems == 1 ? "" : "s");
if (count)
printf("\n%*s%d filter%s (%d pre eventq, %d post eventq)\n",
indent + 4, "", count, count == 1 ? "" : "s", pre, post);
else
printf(", no filters\n");
if (level > 1)
dbChannelFilterShow(chan, level - 2, indent + 8);
if (count) {
printf("%*sfinal field_type=%s (%dB), %ld element%s\n", indent + 4, "",
dbGetFieldTypeString(chan->final_type),
chan->final_field_size,
felems, felems == 1 ? "" : "s");
}
}
}

118
src/ioc/db/dbChannelNOOP.h Normal file
View File

@@ -0,0 +1,118 @@
#ifndef DBCHANNELNOOP_H
#define DBCHANNELNOOP_H
#include <string.h>
#include <string>
#include "cacIO.h"
#include "caerr.h"
/** @brief A channel which never connects
*
* Used when dbCa is placed in isolated mode for unittests
*/
class dbChannelNOOP : public cacChannel
{
std::string myname;
public:
dbChannelNOOP(const char *name, cacChannelNotify &notify)
:cacChannel(notify)
,myname(name)
{}
virtual void destroy (
CallbackGuard & /*callbackGuard*/,
epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ )
{
delete this; // goodbye cruel world
}
virtual unsigned getName (
epicsGuard < epicsMutex > &,
char * pBuf, unsigned bufLen ) const throw ()
{
const char* name = myname.c_str();
if(bufLen>myname.size()+1) {
bufLen=myname.size()+1;
}
memcpy(pBuf, name, bufLen);
pBuf[--bufLen] = '\0';
return bufLen;
}
// !! deprecated, avoid use !!
virtual const char * pName (
epicsGuard < epicsMutex > & guard ) const throw ()
{return myname.c_str();}
virtual void show (
epicsGuard < epicsMutex > &,
unsigned level ) const
{}
virtual void initiateConnect (
epicsGuard < epicsMutex > & )
{}
virtual unsigned requestMessageBytesPending (
epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ )
{return 0;}
virtual void flush (
epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ )
{}
virtual ioStatus read (
epicsGuard < epicsMutex > &mut,
unsigned type, arrayElementCount count,
cacReadNotify &notify, ioid * = 0 )
{
notify.exception(mut, ECA_NORDACCESS, "dbChannelNOOP", type, count);
return iosSynch;
}
virtual void write (
epicsGuard < epicsMutex > &,
unsigned type, arrayElementCount count,
const void *pValue )
{}
virtual ioStatus write (
epicsGuard < epicsMutex > &mut,
unsigned type, arrayElementCount count,
const void */*pValue*/, cacWriteNotify & notify, ioid * = 0 )
{
notify.exception(mut, ECA_NOWTACCESS, "dbChannelNOOP", type, count);
return iosSynch;
}
virtual void subscribe (
epicsGuard < epicsMutex > &mut, unsigned type,
arrayElementCount count, unsigned /*mask*/, cacStateNotify & notify,
ioid * = 0 )
{
// should never subscribe
notify.exception(mut, ECA_BADMASK, "dbChannelNOOP", type, count);
}
virtual void ioCancel (
CallbackGuard & callbackGuard,
epicsGuard < epicsMutex > & mutualExclusionGuard,
const ioid & )
{}
virtual void ioShow (
epicsGuard < epicsMutex > &,
const ioid &, unsigned level ) const
{}
virtual short nativeType (
epicsGuard < epicsMutex > & ) const
{return 0;} // DBR_STRING
virtual arrayElementCount nativeElementCount (
epicsGuard < epicsMutex > & ) const
{return 1;}
};
#endif // DBCHANNELNOOP_H

View File

@@ -92,6 +92,12 @@
interest(4)
extra("ELLLIST mlis")
}
field(BKLNK,DBF_NOACCESS) {
prompt("Backwards link tracking")
special(SPC_NOMOD)
interest(4)
extra("ELLLIST bklnk")
}
field(DISP,DBF_UCHAR) {
prompt("Disable putField")
}

View File

@@ -39,6 +39,7 @@
#include "dbCAC.h"
#include "dbChannel.h"
#include "dbChannelIO.h"
#include "dbChannelNOOP.h"
#include "dbPutNotifyBlocker.h"
class dbService : public cacService {
@@ -61,9 +62,16 @@ cacContext & dbService::contextCreate (
mutualExclusion, notify );
}
extern "C" int dbServiceIsolate;
int dbServiceIsolate = 0;
extern "C" void dbServiceIOInit ()
{
caInstallDefaultService ( dbs );
static int init=0;
if(!init) {
caInstallDefaultService ( dbs );
init=1;
}
}
dbBaseIO::dbBaseIO () {}
@@ -72,7 +80,8 @@ dbContext::dbContext ( epicsMutex & cbMutexIn,
epicsMutex & mutexIn, cacContextNotify & notifyIn ) :
readNotifyCache ( mutexIn ), ctx ( 0 ),
stateNotifyCacheSize ( 0 ), mutex ( mutexIn ), cbMutex ( cbMutexIn ),
notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 )
notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 ),
isolated(dbServiceIsolate)
{
}
@@ -92,7 +101,10 @@ cacChannel & dbContext::createChannel (
dbChannel *dbch = dbChannel_create ( pName );
if ( ! dbch ) {
if ( ! this->pNetContext.get() ) {
if ( isolated ) {
return *new dbChannelNOOP(pName, notifyIn);
} else if ( ! this->pNetContext.get() ) {
this->pNetContext.reset (
& this->notify.createNetworkContext (
this->mutex, this->cbMutex ) );
@@ -142,7 +154,8 @@ void dbContext::callStateNotify ( struct dbChannel * dbch,
const struct db_field_log * pfl,
cacStateNotify & notifyIn )
{
unsigned long size = dbr_size_n ( type, count );
long realcount = (count==0)?dbChannelElements(dbch):count;
unsigned long size = dbr_size_n ( type, realcount );
if ( type > INT_MAX ) {
epicsGuard < epicsMutex > guard ( this->mutex );
@@ -169,8 +182,13 @@ void dbContext::callStateNotify ( struct dbChannel * dbch,
this->stateNotifyCacheSize = size;
}
void *pvfl = (void *) pfl;
int status = dbChannel_get ( dbch, static_cast <int> ( type ),
this->pStateNotifyCache, static_cast <int> ( count ), pvfl );
int status;
if(count==0) /* fetch actual number of elements (dynamic array) */
status = dbChannel_get_count( dbch, static_cast <int> ( type ),
this->pStateNotifyCache, &realcount, pvfl );
else /* fetch requested number of elements, truncated or zero padded */
status = dbChannel_get( dbch, static_cast <int> ( type ),
this->pStateNotifyCache, realcount, pvfl );
if ( status ) {
epicsGuard < epicsMutex > guard ( this->mutex );
notifyIn.exception ( guard, ECA_GETFAIL,
@@ -178,7 +196,7 @@ void dbContext::callStateNotify ( struct dbChannel * dbch,
}
else {
epicsGuard < epicsMutex > guard ( this->mutex );
notifyIn.current ( guard, type, count, this->pStateNotifyCache );
notifyIn.current ( guard, type, realcount, this->pStateNotifyCache );
}
}

View File

@@ -61,27 +61,32 @@ void dbContextReadNotifyCache::callReadNotify (
return;
}
if ( dbChannelElements(dbch) < 0 ) {
const long maxcount = dbChannelElements(dbch);
if ( maxcount < 0 ) {
notify.exception ( guard, ECA_BADCOUNT,
"database has negetive element count",
type, count);
return;
}
if ( count > static_cast < unsigned long > ( dbChannelElements(dbch) ) ) {
} else if ( count > (unsigned long)maxcount ) {
notify.exception ( guard, ECA_BADCOUNT,
"element count out of range (high side)",
type, count);
return;
}
unsigned long size = dbr_size_n ( type, count );
long realcount = (count==0)?maxcount:count;
unsigned long size = dbr_size_n ( type, realcount );
privateAutoDestroyPtr ptr ( _allocator, size );
int status;
{
epicsGuardRelease < epicsMutex > unguard ( guard );
status = dbChannel_get ( dbch, static_cast <int> ( type ),
ptr.get (), static_cast <long> ( count ), 0 );
if ( count==0 )
status = dbChannel_get_count ( dbch, (int)type, ptr.get(), &realcount, 0);
else
status = dbChannel_get ( dbch, (int)type, ptr.get (), realcount, 0 );
}
if ( status ) {
notify.exception ( guard, ECA_GETFAIL,
@@ -90,7 +95,7 @@ void dbContextReadNotifyCache::callReadNotify (
}
else {
notify.completion (
guard, type, count, ptr.get () );
guard, type, realcount, ptr.get () );
}
}

View File

@@ -1,14 +1,13 @@
/*************************************************************************\
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbLink.c */
/* $Id$ */
/*
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbLink.c
*
* Original Authors: Bob Dalesio, Marty Kraimer
* Current Author: Andrew Johnson
*/
@@ -45,7 +44,7 @@
#include "dbFldTypes.h"
#include "dbFldTypes.h"
#include "dbLink.h"
#include "dbLock.h"
#include "dbLockPvt.h"
#include "dbNotify.h"
#include "dbScan.h"
#include "dbStaticLib.h"
@@ -79,7 +78,7 @@ static void inherit_severity(const struct pv_link *ppv_link, dbCommon *pdest,
/* How to identify links in error messages */
static const char * link_field_name(const struct link *plink)
{
const struct dbCommon *precord = plink->value.pv_link.precord;
const struct dbCommon *precord = plink->precord;
const dbRecordType *pdbRecordType = precord->rdes;
dbFldDes * const *papFldDes = pdbRecordType->papFldDes;
const short *link_ind = pdbRecordType->link_ind;
@@ -97,13 +96,28 @@ static const char * link_field_name(const struct link *plink)
/***************************** Constant Links *****************************/
/* Forward definition */
static lset dbConst_lset;
static void dbConstInitLink(struct link *plink)
{
plink->lset = &dbConst_lset;
}
static void dbConstAddLink(struct link *plink)
{
plink->lset = &dbConst_lset;
}
static long dbConstLoadLink(struct link *plink, short dbrType, void *pbuffer)
{
if (!plink->value.constantStr)
return S_db_badField;
plink->lset = &dbConst_lset;
/* Constant strings are always numeric */
if (dbrType== DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
dbrType = DBF_USHORT;
return dbFastPutConvertRoutine[DBR_STRING][dbrType]
@@ -116,7 +130,7 @@ static long dbConstGetNelements(const struct link *plink, long *nelements)
return 0;
}
static long dbConstGetLink(struct link *plink, short dbrType, void *pbuffer,
static long dbConstGetValue(struct link *plink, short dbrType, void *pbuffer,
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest)
{
if (pnRequest)
@@ -124,8 +138,23 @@ static long dbConstGetLink(struct link *plink, short dbrType, void *pbuffer,
return 0;
}
static lset dbConst_lset = {
NULL,
NULL,
NULL, dbConstGetNelements,
dbConstGetValue,
NULL, NULL, NULL,
NULL, NULL,
NULL, NULL,
NULL,
NULL
};
/***************************** Database Links *****************************/
/* Forward definition */
static lset dbDb_lset;
static long dbDbInitLink(struct link *plink, short dbfType)
{
DBADDR dbaddr;
@@ -136,45 +165,46 @@ static long dbDbInitLink(struct link *plink, short dbfType)
if (status)
return status;
plink->lset = &dbDb_lset;
plink->type = DB_LINK;
pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
*pdbAddr = dbaddr; /* structure copy */
plink->value.pv_link.pvt = pdbAddr;
dbLockSetMerge(plink->value.pv_link.precord, pdbAddr->precord);
ellAdd(&dbaddr.precord->bklnk, &plink->value.pv_link.backlinknode);
/* merging into the same lockset is deferred to the caller.
* cf. initPVLinks()
*/
dbLockSetMerge(NULL, plink->precord, dbaddr.precord);
assert(plink->precord->lset->plockSet == dbaddr.precord->lset->plockSet);
return 0;
}
static long dbDbAddLink(struct link *plink, short dbfType)
static void dbDbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget)
{
DBADDR dbaddr;
long status;
DBADDR *pdbAddr;
status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr);
if (status)
return status;
plink->lset = &dbDb_lset;
plink->type = DB_LINK;
pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
*pdbAddr = dbaddr; /* structure copy */
plink->value.pv_link.pvt = pdbAddr;
plink->value.pv_link.pvt = ptarget;
ellAdd(&ptarget->precord->bklnk, &plink->value.pv_link.backlinknode);
dbLockSetRecordLock(pdbAddr->precord);
dbLockSetMerge(plink->value.pv_link.precord, pdbAddr->precord);
return 0;
/* target record is already locked in dbPutFieldLink() */
dbLockSetMerge(locker, plink->precord, ptarget->precord);
}
static void dbDbRemoveLink(struct link *plink)
static void dbDbRemoveLink(dbLocker *locker, struct link *plink)
{
free(plink->value.pv_link.pvt);
DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt;
plink->value.pv_link.pvt = 0;
plink->value.pv_link.getCvt = 0;
plink->value.pv_link.pvlMask = 0;
plink->value.pv_link.lastGetdbrType = 0;
plink->type = PV_LINK;
dbLockSetSplit(plink->value.pv_link.precord);
plink->lset = NULL;
ellDelete(&pdbAddr->precord->bklnk, &plink->value.pv_link.backlinknode);
dbLockSetSplit(locker, plink->precord, pdbAddr->precord);
free(pdbAddr);
}
static int dbDbIsLinkConnected(const struct link *plink)
static int dbDbIsConnected(const struct link *plink)
{
return TRUE;
}
@@ -199,7 +229,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
{
struct pv_link *ppv_link = &plink->value.pv_link;
DBADDR *paddr = ppv_link->pvt;
dbCommon *precord = plink->value.pv_link.precord;
dbCommon *precord = plink->precord;
long status;
/* scan passive records if link is process passive */
@@ -363,7 +393,7 @@ static long dbDbPutValue(struct link *plink, short dbrType,
const void *pbuffer, long nRequest)
{
struct pv_link *ppv_link = &plink->value.pv_link;
struct dbCommon *psrce = ppv_link->precord;
struct dbCommon *psrce = plink->precord;
DBADDR *paddr = (DBADDR *) ppv_link->pvt;
dbCommon *pdest = paddr->precord;
long status = dbPut(paddr, dbrType, pbuffer, nRequest);
@@ -394,23 +424,37 @@ static long dbDbPutValue(struct link *plink, short dbrType,
static void dbDbScanFwdLink(struct link *plink)
{
dbCommon *precord = plink->value.pv_link.precord;
dbCommon *precord = plink->precord;
dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt;
dbScanPassive(precord, paddr->precord);
}
lset dbDb_lset = { dbDbInitLink, dbDbAddLink, NULL, dbDbRemoveLink,
dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue,
dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp,
dbDbPutValue, dbDbScanFwdLink };
static lset dbDb_lset = {
dbDbRemoveLink,
dbDbIsConnected,
dbDbGetDBFtype, dbDbGetElements,
dbDbGetValue,
dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
dbDbGetPrecision, dbDbGetUnits,
dbDbGetAlarm, dbDbGetTimeStamp,
dbDbPutValue,
dbDbScanFwdLink
};
/***************************** Generic Link API *****************************/
void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
void dbInitLink(struct link *plink, short dbfType)
{
plink->value.pv_link.precord = precord;
struct dbCommon *precord = plink->precord;
if (plink->type == CONSTANT) {
dbConstInitLink(plink);
return;
}
if (plink->type != PV_LINK)
return;
if (plink == &precord->tsel)
recGblTSELwasModified(plink);
@@ -425,7 +469,7 @@ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
if (dbfType == DBF_INLINK)
plink->value.pv_link.pvlMask |= pvlOptInpNative;
dbCaAddLink(plink);
dbCaAddLink(NULL, plink, dbfType);
if (dbfType == DBF_FWDLINK) {
char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
@@ -442,24 +486,29 @@ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
}
}
void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType)
void dbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget)
{
plink->value.pv_link.precord = precord;
struct dbCommon *precord = plink->precord;
if (plink->type == CONSTANT) {
dbConstAddLink(plink);
return;
}
if (plink == &precord->tsel)
recGblTSELwasModified(plink);
if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) {
/* Can we make it a DB link? */
if (!dbDbAddLink(plink, dbfType))
return;
if (ptarget) {
/* It's a DB link */
dbDbAddLink(locker, plink, dbfType, ptarget);
return;
}
/* Make it a CA link */
if (dbfType == DBF_INLINK)
plink->value.pv_link.pvlMask |= pvlOptInpNative;
dbCaAddLink(plink);
dbCaAddLink(locker, plink, dbfType);
if (dbfType == DBF_FWDLINK) {
char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
@@ -470,69 +519,60 @@ void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType)
long dbLoadLink(struct link *plink, short dbrType, void *pbuffer)
{
switch (plink->type) {
case CONSTANT:
if (plink->type == CONSTANT)
return dbConstLoadLink(plink, dbrType, pbuffer);
}
/* Could pass a type hint to the other link types here */
return S_db_notFound;
}
void dbRemoveLink(struct link *plink)
void dbRemoveLink(dbLocker *locker, struct link *plink)
{
switch (plink->type) {
case DB_LINK:
dbDbRemoveLink(plink);
break;
case CA_LINK:
dbCaRemoveLink(plink);
break;
default:
cantProceed("dbRemoveLink: Unexpected link type %d\n", plink->type);
lset *plset = plink->lset;
if (plset) {
if (plset->removeLink)
plset->removeLink(locker, plink);
plink->lset = NULL;
}
plink->type = PV_LINK;
plink->value.pv_link.pvlMask = 0;
}
int dbIsLinkConnected(const struct link *plink)
{
switch (plink->type) {
case DB_LINK:
return dbDbIsLinkConnected(plink);
case CA_LINK:
return dbCaIsLinkConnected(plink);
}
return FALSE;
lset *plset = plink->lset;
if (!plset || !plset->isConnected)
return FALSE;
return plset->isConnected(plink);
}
int dbGetLinkDBFtype(const struct link *plink)
{
switch (plink->type) {
case DB_LINK:
return dbDbGetDBFtype(plink);
case CA_LINK:
return dbCaGetLinkDBFtype(plink);
}
return -1;
lset *plset = plink->lset;
if (!plset || !plset->getDBFtype)
return -1;
return plset->getDBFtype(plink);
}
long dbGetNelements(const struct link *plink, long *nelements)
{
switch (plink->type) {
case CONSTANT:
return dbConstGetNelements(plink, nelements);
case DB_LINK:
return dbDbGetElements(plink, nelements);
case CA_LINK:
return dbCaGetNelements(plink, nelements);
}
return S_db_badField;
lset *plset = plink->lset;
if (!plset || !plset->getElements)
return S_db_badField;
return plset->getElements(plink, nelements);
}
long dbGetLink(struct link *plink, short dbrType, void *pbuffer,
long *poptions, long *pnRequest)
{
struct dbCommon *precord = plink->value.pv_link.precord;
struct dbCommon *precord = plink->precord;
epicsEnum16 sevr = 0, stat = 0;
lset *plset = plink->lset;
long status;
if (poptions && *poptions) {
@@ -540,21 +580,10 @@ long dbGetLink(struct link *plink, short dbrType, void *pbuffer,
*poptions = 0;
}
switch (plink->type) {
case CONSTANT:
status = dbConstGetLink(plink, dbrType, pbuffer, &stat, &sevr,
pnRequest);
break;
case DB_LINK:
status = dbDbGetValue(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);
break;
case CA_LINK:
status = dbCaGetLink(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);
break;
default:
cantProceed("dbGetLinkValue: Illegal link type %d\n", plink->type);
status = -1;
}
if (!plset || !plset->getValue)
return -1;
status = plset->getValue(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);
if (status) {
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
} else {
@@ -565,104 +594,88 @@ long dbGetLink(struct link *plink, short dbrType, void *pbuffer,
long dbGetControlLimits(const struct link *plink, double *low, double *high)
{
switch (plink->type) {
case DB_LINK:
return dbDbGetControlLimits(plink, low, high);
case CA_LINK:
return dbCaGetControlLimits(plink, low, high);
}
return S_db_notFound;
lset *plset = plink->lset;
if (!plset || !plset->getControlLimits)
return S_db_notFound;
return plset->getControlLimits(plink, low, high);
}
long dbGetGraphicLimits(const struct link *plink, double *low, double *high)
{
switch (plink->type) {
case DB_LINK:
return dbDbGetGraphicLimits(plink, low, high);
case CA_LINK:
return dbCaGetGraphicLimits(plink, low, high);
}
return S_db_notFound;
lset *plset = plink->lset;
if (!plset || !plset->getGraphicLimits)
return S_db_notFound;
return plset->getGraphicLimits(plink, low, high);
}
long dbGetAlarmLimits(const struct link *plink, double *lolo, double *low,
double *high, double *hihi)
{
switch (plink->type) {
case DB_LINK:
return dbDbGetAlarmLimits(plink, lolo, low, high, hihi);
case CA_LINK:
return dbCaGetAlarmLimits(plink, lolo, low, high, hihi);
}
return S_db_notFound;
lset *plset = plink->lset;
if (!plset || !plset->getAlarmLimits)
return S_db_notFound;
return plset->getAlarmLimits(plink, lolo, low, high, hihi);
}
long dbGetPrecision(const struct link *plink, short *precision)
{
switch (plink->type) {
case DB_LINK:
return dbDbGetPrecision(plink, precision);
case CA_LINK:
return dbCaGetPrecision(plink, precision);
}
return S_db_notFound;
lset *plset = plink->lset;
if (!plset || !plset->getPrecision)
return S_db_notFound;
return plset->getPrecision(plink, precision);
}
long dbGetUnits(const struct link *plink, char *units, int unitsSize)
{
switch (plink->type) {
case DB_LINK:
return dbDbGetUnits(plink, units, unitsSize);
case CA_LINK:
return dbCaGetUnits(plink, units, unitsSize);
}
return S_db_notFound;
lset *plset = plink->lset;
if (!plset || !plset->getUnits)
return S_db_notFound;
return plset->getUnits(plink, units, unitsSize);
}
long dbGetAlarm(const struct link *plink, epicsEnum16 *status,
epicsEnum16 *severity)
{
switch (plink->type) {
case DB_LINK:
return dbDbGetAlarm(plink, status, severity);
case CA_LINK:
return dbCaGetAlarm(plink, status, severity);
}
return S_db_notFound;
lset *plset = plink->lset;
if (!plset || !plset->getAlarm)
return S_db_notFound;
return plset->getAlarm(plink, status, severity);
}
long dbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
{
switch (plink->type) {
case DB_LINK:
return dbDbGetTimeStamp(plink, pstamp);
case CA_LINK:
return dbCaGetTimeStamp(plink, pstamp);
}
return S_db_notFound;
lset *plset = plink->lset;
if (!plset || !plset->getTimeStamp)
return S_db_notFound;
return plset->getTimeStamp(plink, pstamp);
}
long dbPutLink(struct link *plink, short dbrType, const void *pbuffer,
long nRequest)
{
lset *plset = plink->lset;
long status;
switch (plink->type) {
case CONSTANT:
status = 0;
break;
case DB_LINK:
status = dbDbPutValue(plink, dbrType, pbuffer, nRequest);
break;
case CA_LINK:
status = dbCaPutLink(plink, dbrType, pbuffer, nRequest);
break;
default:
cantProceed("dbPutLinkValue: Illegal link type %d\n", plink->type);
status = -1;
}
if (!plset || !plset->putValue)
return S_db_notFound;
status = plset->putValue(plink, dbrType, pbuffer, nRequest);
if (status) {
struct dbCommon *precord = plink->value.pv_link.precord;
struct dbCommon *precord = plink->precord;
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
}
@@ -671,14 +684,10 @@ long dbPutLink(struct link *plink, short dbrType, const void *pbuffer,
void dbScanFwdLink(struct link *plink)
{
switch (plink->type) {
case DB_LINK:
dbDbScanFwdLink(plink);
break;
case CA_LINK:
dbCaScanFwdLink(plink);
break;
}
lset *plset = plink->lset;
if (plset && plset->scanForward)
plset->scanForward(plink);
}
/* Helper functions for long string support */

View File

@@ -6,8 +6,7 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* dbLink.h
/* dbLink.h
*
* Created on: Mar 21, 2010
* Author: Andrew Johnson
@@ -20,21 +19,21 @@
#include "shareLib.h"
#include "epicsTypes.h"
#include "epicsTime.h"
#include "dbAddr.h"
#ifdef __cplusplus
extern "C" {
#endif
struct dbLocker;
typedef struct lset {
long (*initLink)(struct link *plink, short dbfType);
long (*addLink)(struct link *plink, short dbfType);
long (*loadLink)(struct link *plink, short dbrType, void *pbuffer);
void (*removeLink)(struct link *plink);
int (*isLinkConnected)(const struct link *plink);
void (*removeLink)(struct dbLocker *locker, struct link *plink);
int (*isConnected)(const struct link *plink);
int (*getDBFtype)(const struct link *plink);
long (*getElements)(const struct link *plink, long *nelements);
long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest);
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest);
long (*getControlLimits)(const struct link *plink, double *lo, double *hi);
long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi);
long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo,
@@ -46,19 +45,19 @@ typedef struct lset {
long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);
long (*putValue)(struct link *plink, short dbrType,
const void *pbuffer, long nRequest);
void (*scanFwdLink)(struct link *plink);
void (*scanForward)(struct link *plink);
} lset;
#define dbGetSevr(PLINK, PSEVERITY) \
dbGetAlarm((PLINK), NULL, (PSEVERITY))
#define dbGetSevr(link, sevr) \
dbGetAlarm(link, NULL, sevr)
epicsShareFunc void dbInitLink(struct dbCommon *precord, struct link *plink,
short dbfType);
epicsShareFunc void dbAddLink(struct dbCommon *precord, struct link *plink,
short dbfType);
epicsShareFunc void dbInitLink(struct link *plink, short dbfType);
epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
DBADDR *ptarget);
epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
void *pbuffer);
epicsShareFunc void dbRemoveLink(struct link *plink);
epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink);
epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);
epicsShareFunc int dbIsLinkConnected(const struct link *plink);
epicsShareFunc int dbGetLinkDBFtype(const struct link *plink);
@@ -77,7 +76,7 @@ epicsShareFunc long dbGetAlarm(const struct link *plink, epicsEnum16 *status,
epicsEnum16 *severity);
epicsShareFunc long dbGetTimeStamp(const struct link *plink,
epicsTimeStamp *pstamp);
epicsShareFunc long dbPutLink(struct link *, short dbrType,
epicsShareFunc long dbPutLink(struct link *plink, short dbrType,
const void *pbuffer, long nRequest);
epicsShareFunc void dbScanFwdLink(struct link *plink);

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,7 @@
#ifndef INCdbLockh
#define INCdbLockh
#include "ellLib.h"
#include "shareLib.h"
#ifdef __cplusplus
@@ -21,21 +22,26 @@ extern "C" {
struct dbCommon;
struct dbBase;
typedef struct dbLocker dbLocker;
epicsShareFunc void dbScanLock(struct dbCommon *precord);
epicsShareFunc void dbScanUnlock(struct dbCommon *precord);
epicsShareFunc dbLocker *dbLockerAlloc(struct dbCommon **precs,
size_t nrecs,
unsigned int flags);
epicsShareFunc void dbLockerFree(dbLocker *);
epicsShareFunc void dbScanLockMany(dbLocker*);
epicsShareFunc void dbScanUnlockMany(dbLocker*);
epicsShareFunc unsigned long dbLockGetLockId(
struct dbCommon *precord);
epicsShareFunc void dbLockInitRecords(struct dbBase *pdbbase);
epicsShareFunc void dbLockCleanupRecords(struct dbBase *pdbbase);
epicsShareFunc void dbLockSetMerge(
struct dbCommon *pfirst,struct dbCommon *psecond);
epicsShareFunc void dbLockSetSplit(struct dbCommon *psource);
/*The following are for code that modifies lock sets*/
epicsShareFunc void dbLockSetGblLock(void);
epicsShareFunc void dbLockSetGblUnlock(void);
epicsShareFunc void dbLockSetRecordLock(struct dbCommon *precord);
/* Lock Set Report */
epicsShareFunc long dblsr(char *recordname,int level);
@@ -47,6 +53,10 @@ epicsShareFunc long dbLockShowLocked(int level);
/*KLUDGE to support field TPRO*/
epicsShareFunc int * dbLockSetAddrTrace(struct dbCommon *precord);
/* debugging */
epicsShareFunc unsigned long dbLockGetRefs(struct dbCommon*);
epicsShareFunc unsigned long dbLockCountSets(void);
#ifdef __cplusplus
}
#endif

110
src/ioc/db/dbLockPvt.h Normal file
View File

@@ -0,0 +1,110 @@
/*************************************************************************\
* Copyright (c) 2014 Brookhaven Science Assoc., as Operator of Brookhaven
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#ifndef DBLOCKPVT_H
#define DBLOCKPVT_H
#include "dbLock.h"
#include "epicsSpin.h"
/* Define to enable additional error checking */
#undef LOCKSET_DEBUG
/* Define to disable the free list for lockSets */
#undef LOCKSET_NOFREE
/* Define to disable use of recomputeCnt optimization */
#undef LOCKSET_NOCNT
/* except for refcount (and lock), all members of dbLockSet
* are guarded by its lock.
*/
typedef struct dbLockSet {
ELLNODE node;
ELLLIST lockRecordList; /* holds lockRecord::node */
epicsMutexId lock;
unsigned long id;
int refcount;
#ifdef LOCKSET_DEBUG
int ownercount;
epicsThreadId owner;
#endif
dbLocker *ownerlocker;
ELLNODE lockernode;
int trace; /*For field TPRO*/
} lockSet;
struct lockRecord;
/* dbCommon.LSET is a plockRecord.
* Except for spin and plockSet, all members of lockRecord are guarded
* by the present lockset lock.
* plockSet is guarded by spin.
*/
typedef struct lockRecord {
ELLNODE node; /* in lockSet::lockRecordList */
/* The association between lockRecord and lockSet
* can only be changed while the lockSet is held,
* and the lockRecord's spinlock is held.
* It may be read which either lock is held.
*/
lockSet *plockSet;
/* the association between lockRecord and dbCommon never changes */
dbCommon *precord;
epicsSpinId spin;
/* temp used during lockset split.
* lockSet must be locked for access
*/
ELLNODE compnode;
unsigned int compflag;
} lockRecord;
typedef struct {
lockRecord *plr;
/* the last lock found associated with the ref.
* not stable unless lock is locked, or ref spin
* is locked.
*/
lockSet *plockSet;
} lockRecordRef;
#define DBLOCKER_NALLOC 2
/* a dbLocker can only be used by a single thread. */
struct dbLocker {
ELLLIST locked;
#ifndef LOCKSET_NOCNT
size_t recomp; /* snapshot of recomputeCnt when refs[] cache updated */
#endif
size_t maxrefs;
lockRecordRef refs[DBLOCKER_NALLOC]; /* actual length is maxrefs */
};
/* These are exported for testing only */
epicsShareFunc lockSet* dbLockGetRef(lockRecord *lr); /* lookup lockset and increment ref count */
epicsShareFunc void dbLockIncRef(lockSet* ls);
epicsShareFunc void dbLockDecRef(lockSet *ls);
/* Calling dbLockerPrepare directly is an internal
* optimization used when dbLocker on the stack.
* nrecs must be <=DBLOCKER_NALLOC.
*/
void dbLockerPrepare(struct dbLocker *locker,
struct dbCommon **precs,
size_t nrecs);
void dbLockerFinalize(dbLocker *);
void dbLockSetMerge(struct dbLocker *locker,
struct dbCommon *pfirst,
struct dbCommon *psecond);
void dbLockSetSplit(struct dbLocker *locker,
struct dbCommon *psource,
struct dbCommon *psecond);
#endif /* DBLOCKPVT_H */

View File

@@ -25,7 +25,6 @@
#include "ellLib.h"
#include "epicsAssert.h"
#include "epicsEvent.h"
#include "epicsExit.h"
#include "epicsMutex.h"
#include "epicsString.h"
#include "epicsThread.h"
@@ -109,6 +108,15 @@ static void notifyCallback(CALLBACK *pcallback);
(listnode)->isOnList=0; \
}
static void notifyFree(void *raw)
{
notifyPvt *pnotifyPvt = raw;
assert(pnotifyPvt->magic==MAGIC);
epicsEventDestroy(pnotifyPvt->cancelEvent);
epicsEventDestroy(pnotifyPvt->userCallbackEvent);
free(pnotifyPvt);
}
static void notifyInit(processNotify *ppn)
{
notifyPvt *pnotifyPvt;
@@ -301,7 +309,7 @@ static void notifyCallback(CALLBACK *pcallback)
void dbProcessNotifyExit(void)
{
assert(ellCount(&pnotifyGlobal->freeList)==0);
ellFree2(&pnotifyGlobal->freeList, &notifyFree);
epicsMutexDestroy(pnotifyGlobal->lock);
free(pnotifyGlobal);
pnotifyGlobal = NULL;

View File

@@ -25,11 +25,9 @@
#include "dbDefs.h"
#include "ellLib.h"
#include "epicsEvent.h"
#include "epicsExit.h"
#include "epicsInterrupt.h"
#include "epicsMutex.h"
#include "epicsPrint.h"
#include "epicsRingPointer.h"
#include "epicsRingBytes.h"
#include "epicsStdio.h"
#include "epicsStdlib.h"
#include "epicsString.h"
@@ -64,7 +62,7 @@ static volatile enum ctl scanCtl;
static int onceQueueSize = 1000;
static epicsEventId onceSem;
static epicsRingPointerId onceQ;
static epicsRingBytesId onceQ;
static epicsThreadId onceTaskId;
static void *exitOnce;
@@ -176,7 +174,7 @@ void scanCleanup(void)
deletePeriodic();
ioscanDestroy();
epicsRingPointerDelete(onceQ);
epicsRingBytesDelete(onceQ);
free(periodicTaskId);
papPeriodic = NULL;
@@ -457,7 +455,9 @@ event_list *eventNameToHandle(const char *eventname)
if (strcmp(pel->event_name, eventname) == 0) break;
}
if (pel == NULL) {
pel = dbCalloc(1, sizeof(event_list));
pel = calloc(1, sizeof(event_list));
if (!pel)
goto done;
strcpy(pel->event_name, eventname);
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
callbackSetUser(&pel->scan_list[prio], &pel->callback[prio]);
@@ -475,6 +475,7 @@ event_list *eventNameToHandle(const char *eventname)
pevent_list[e] = pel;
}
}
done:
epicsMutexUnlock(event_lock);
return pel;
}
@@ -581,6 +582,28 @@ unsigned int scanIoRequest(IOSCANPVT piosh)
return queued;
}
unsigned int scanIoImmediate(IOSCANPVT piosh, int prio)
{
io_scan_list *piosl;
if (prio<0 || prio>=NUM_CALLBACK_PRIORITIES)
return S_db_errArg;
else if (scanCtl != ctlRun)
return 0;
piosl = &piosh->iosl[prio];
if (ellCount(&piosl->scan_list.list) == 0)
return 0;
scanList(&piosl->scan_list);
if (piosh->cb)
piosh->cb(piosh->arg, piosh, prio);
return 1 << prio;
}
/* May not be called while a scan request is queued or running */
void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg)
{
@@ -588,15 +611,27 @@ void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg)
piosh->arg = arg;
}
void scanOnce(struct dbCommon *precord)
int scanOnce(struct dbCommon *precord) {
return scanOnceCallback(precord, NULL, NULL);
}
typedef struct {
struct dbCommon *prec;
once_complete cb;
void *usr;
} onceEntry;
int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr)
{
static int newOverflow = TRUE;
int lockKey;
onceEntry ent;
int pushOK;
lockKey = epicsInterruptLock();
pushOK = epicsRingPointerPush(onceQ, precord);
epicsInterruptUnlock(lockKey);
ent.prec = precord;
ent.cb = cb;
ent.usr = usr;
pushOK = epicsRingBytesPut(onceQ, (void*)&ent, sizeof(ent));
if (!pushOK) {
if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n");
@@ -605,6 +640,8 @@ void scanOnce(struct dbCommon *precord)
newOverflow = TRUE;
}
epicsEventSignal(onceSem);
return !pushOK;
}
static void onceTask(void *arg)
@@ -613,14 +650,24 @@ static void onceTask(void *arg)
epicsEventSignal(startStopEvent);
while (TRUE) {
void *precord;
epicsEventMustWait(onceSem);
while ((precord = epicsRingPointerPop(onceQ))) {
if (precord == &exitOnce) goto shutdown;
dbScanLock(precord);
dbProcess(precord);
dbScanUnlock(precord);
while(1) {
onceEntry ent;
int bytes = epicsRingBytesGet(onceQ, (void*)&ent, sizeof(ent));
if(bytes==0)
break;
if(bytes!=sizeof(ent)) {
errlogPrintf("onceTask: received incomplete %d of %u\n",
bytes, (unsigned)sizeof(ent));
continue; /* what to do? */
} else if (ent.prec == (void*)&exitOnce) goto shutdown;
dbScanLock(ent.prec);
dbProcess(ent.prec);
dbScanUnlock(ent.prec);
if(ent.cb)
ent.cb(ent.usr, ent.prec);
}
}
@@ -637,7 +684,7 @@ int scanOnceSetQueueSize(int size)
static void initOnce(void)
{
if ((onceQ = epicsRingPointerCreate(onceQueueSize)) == NULL) {
if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) {
cantProceed("initOnce: Ring buffer create failed\n");
}
if(!onceSem)
@@ -815,9 +862,11 @@ static void spawnPeriodic(int ind)
static void ioscanCallback(CALLBACK *pcallback)
{
ioscan_head *piosh = (ioscan_head *) pcallback->user;
int prio = pcallback->priority;
ioscan_head *piosh;
int prio;
callbackGetUser(piosh, pcallback);
callbackGetPriority(prio, pcallback);
scanList(&piosh->iosl[prio].scan_list);
if (piosh->cb)
piosh->cb(piosh->arg, piosh, prio);

View File

@@ -39,10 +39,11 @@ struct ioscan_head;
typedef struct ioscan_head *IOSCANPVT;
typedef struct event_list *EVENTPVT;
typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio);
struct dbCommon;
typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio);
typedef void (*once_complete)(void *usr, struct dbCommon*);
epicsShareFunc long scanInit(void);
epicsShareFunc void scanRun(void);
epicsShareFunc void scanPause(void);
@@ -55,7 +56,8 @@ epicsShareFunc void post_event(int event) EPICS_DEPRECATED;
epicsShareFunc void scanAdd(struct dbCommon *);
epicsShareFunc void scanDelete(struct dbCommon *);
epicsShareFunc double scanPeriod(int scan);
epicsShareFunc void scanOnce(struct dbCommon *);
epicsShareFunc int scanOnce(struct dbCommon *);
epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr);
epicsShareFunc int scanOnceSetQueueSize(int size);
/*print periodic lists*/
@@ -69,6 +71,7 @@ epicsShareFunc int scanpiol(void);
epicsShareFunc void scanIoInit(IOSCANPVT *ppios);
epicsShareFunc unsigned int scanIoRequest(IOSCANPVT pios);
epicsShareFunc unsigned int scanIoImmediate(IOSCANPVT pios, int prio);
epicsShareFunc void scanIoSetComplete(IOSCANPVT, io_scan_complete, void *usr);
#ifdef __cplusplus

View File

@@ -15,19 +15,35 @@
#include "dbmf.h"
#include "epicsUnitTest.h"
#include "osiFileName.h"
#include "osiUnistd.h"
#include "registry.h"
#include "epicsEvent.h"
#define epicsExportSharedSymbols
#include "dbAccess.h"
#include "dbBase.h"
#include "dbChannel.h"
#include "dbEvent.h"
#include "dbStaticLib.h"
#include "dbUnitTest.h"
#include "initHooks.h"
#include "iocInit.h"
static dbEventCtx testEvtCtx;
static epicsMutexId testEvtLock;
static ELLLIST testEvtList; /* holds testMonitor::node */
struct testMonitor {
ELLNODE node;
dbEventSubscription sub;
epicsEventId event;
unsigned count;
};
void testdbPrepare(void)
{
/* No-op at the moment */
if(!testEvtLock)
testEvtLock = epicsMutexMustCreate();
}
void testdbReadDatabase(const char* file,
@@ -37,19 +53,35 @@ void testdbReadDatabase(const char* file,
if(!path)
path = "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common";
if(dbReadDatabase(&pdbbase, file, path, substitutions))
testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)",
file, path, substitutions);
if(dbReadDatabase(&pdbbase, file, path, substitutions)) {
char buf[100];
const char *cwd = getcwd(buf, sizeof(buf));
if(!cwd)
cwd = "<directory too long>";
testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)\n from: \"%s\"",
file, path, substitutions, cwd);
}
}
void testIocInitOk(void)
{
if(iocBuildIsolated() || iocRun())
testAbort("Failed to start up test database");
if(!(testEvtCtx=db_init_events()))
testAbort("Failed to initialize test dbEvent context");
if(DB_EVENT_OK!=db_start_events(testEvtCtx, "CAS-test", NULL, NULL, epicsThreadPriorityCAServerLow))
testAbort("Failed to start test dbEvent context");
}
void testIocShutdownOk(void)
{
epicsMutexMustLock(testEvtLock);
if(ellCount(&testEvtList))
testDiag("Warning, testing monitors still active at testIocShutdownOk()");
epicsMutexUnlock(testEvtLock);
db_close_events(testEvtCtx);
testEvtCtx = NULL;
if(iocShutdown())
testAbort("Failed to shutdown test database");
}
@@ -199,3 +231,90 @@ dbCommon* testdbRecordPtr(const char* pv)
return addr.precord;
}
static
void testmonupdate(void *user_arg, struct dbChannel *chan,
int eventsRemaining, struct db_field_log *pfl)
{
testMonitor *mon = user_arg;
epicsMutexMustLock(testEvtLock);
mon->count++;
epicsMutexUnlock(testEvtLock);
epicsEventMustTrigger(mon->event);
}
testMonitor* testMonitorCreate(const char* pvname, unsigned mask, unsigned opt)
{
long status;
testMonitor *mon;
dbChannel *chan;
assert(testEvtCtx);
mon = callocMustSucceed(1, sizeof(*mon), "testMonitorCreate");
mon->event = epicsEventMustCreate(epicsEventEmpty);
chan = dbChannelCreate(pvname);
if(!chan)
testAbort("testMonitorCreate - dbChannelCreate(\"%s\") fails", pvname);
if(!!(status=dbChannelOpen(chan)))
testAbort("testMonitorCreate - dbChannelOpen(\"%s\") fails w/ %ld", pvname, status);
mon->sub = db_add_event(testEvtCtx, chan, &testmonupdate, mon, mask);
if(!mon->sub)
testAbort("testMonitorCreate - db_add_event(\"%s\") fails", pvname);
db_event_enable(mon->sub);
epicsMutexMustLock(testEvtLock);
ellAdd(&testEvtList, &mon->node);
epicsMutexUnlock(testEvtLock);
return mon;
}
void testMonitorDestroy(testMonitor *mon)
{
if(!mon) return;
db_event_disable(mon->sub);
epicsMutexMustLock(testEvtLock);
ellDelete(&testEvtList, &mon->node);
epicsMutexUnlock(testEvtLock);
db_cancel_event(mon->sub);
epicsEventDestroy(mon->event);
free(mon);
}
void testMonitorWait(testMonitor *mon)
{
static const double delay = 60.0;
switch(epicsEventWaitWithTimeout(mon->event, delay))
{
case epicsEventOK:
return;
case epicsEventWaitTimeout:
default:
testAbort("testMonitorWait() exceeded %g second timeout", delay);
}
}
unsigned testMonitorCount(testMonitor *mon, unsigned reset)
{
unsigned count;
epicsMutexMustLock(testEvtLock);
count = mon->count;
if(reset) {
mon->count = 0;
epicsEventWaitWithTimeout(mon->event, 0); /* clear the event */
}
epicsMutexUnlock(testEvtLock);
return count;
}

View File

@@ -57,6 +57,26 @@ epicsShareFunc void testdbVGetFieldEqual(const char* pv, short dbrType, va_list
epicsShareFunc dbCommon* testdbRecordPtr(const char* pv);
typedef struct testMonitor testMonitor;
/* Begin monitoring the named PV for changes */
epicsShareFunc testMonitor* testMonitorCreate(const char* pvname, unsigned dbe_mask, unsigned opt);
/* End monitoring */
epicsShareFunc void testMonitorDestroy(testMonitor*);
/* Return immediately if it has been updated since create, last wait,
* or reset (count w/ reset=1).
* Otherwise, block until the value of the target PV is updated.
*/
epicsShareFunc void testMonitorWait(testMonitor*);
/* Return the number of monitor events which have occured since create,
* or a pervious reset (called reset=1).
* Calling w/ reset=0 only returns the count.
* Calling w/ reset=1 resets the count to zero and ensures that the next
* wait will block unless subsequent events occur. Returns the previous
* count.
*/
epicsShareFunc unsigned testMonitorCount(testMonitor*, unsigned reset);
#ifdef __cplusplus
}
#endif

View File

@@ -161,14 +161,9 @@ void recGblGetControlDouble(const struct dbAddr *paddr,
&pcd->upper_ctrl_limit, &pcd->lower_ctrl_limit);
}
int recGblInitConstantLink(
struct link *plink,short dbftype,void *pdest)
int recGblInitConstantLink(struct link *plink, short dbftype, void *pdest)
{
long status = dbLoadLink(plink, dbftype, pdest);
if (status)
return FALSE;
return TRUE;
return !dbLoadLink(plink, dbftype, pdest);
}
unsigned short recGblResetAlarms(void *precord)

View File

@@ -10,17 +10,25 @@ TOP=../../../..
include $(TOP)/configure/CONFIG
# Allow access to private headers in db/
USR_CPPFLAGS = -I ../..
TESTLIBRARY = dbTestIoc
dbTestIoc_SRCS += arrRecord.c
dbTestIoc_SRCS += xRecord.c
dbTestIoc_SRCS += dbLinkdset.c
dbTestIoc_SRCS += devx.c
dbTestIoc_LIBS = dbCore ca Com
TARGETS += $(COMMON_DIR)/dbTestIoc.dbd
dbTestIoc_DBD += menuGlobal.dbd
dbTestIoc_DBD += menuConvert.dbd
dbTestIoc_DBD += menuScan.dbd
#dbTestIoc_DBD += arrRecord.dbd
dbTestIoc_DBD += xRecord.dbd
dbTestIoc_DBD += arrRecord.dbd
dbTestIoc_DBD += devx.dbd
dbTestIoc_DBD += dbLinkdset.dbd
TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db
@@ -28,6 +36,12 @@ testHarness_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
PROD_LIBS = dbTestIoc dbCore ca Com
TESTPROD_HOST += dbScanTest
dbScanTest_SRCS += dbScanTest.c
dbScanTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += dbScanTest.c
TESTS += dbScanTest
TESTPROD_HOST += dbShutdownTest
dbShutdownTest_SRCS += dbShutdownTest.c
dbShutdownTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
@@ -39,7 +53,7 @@ dbPutLinkTest_SRCS += dbPutLinkTest.c
dbPutLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += dbPutLinkTest.c
TESTS += dbPutLinkTest
TESTFILES += ../dbPutLinkTest.db
TESTFILES += ../dbPutLinkTest.db ../dbBadLink.db
TESTPROD_HOST += dbLockTest
dbLockTest_SRCS += dbLockTest.c
@@ -48,6 +62,14 @@ testHarness_SRCS += dbLockTest.c
TESTS += dbLockTest
TESTFILES += ../dbLockTest.db
TESTPROD_HOST += dbStressTest
dbStressTest_SRCS += dbStressLock.c
dbStressTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
dbStressTest_SYS_LIBS_solaris += rt
dbStressTest_SYS_LIBS_Linux += rt
TESTS += dbStressTest
TESTFILES += ../dbStressLock.db
TESTPROD_HOST += testdbConvert
testdbConvert_SRCS += testdbConvert.c
testHarness_SRCS += testdbConvert.c
@@ -73,20 +95,23 @@ dbCaStatsTest_SRCS += dbCaStatsTest.c
dbCaStatsTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += dbCaStatsTest.c
TESTS += dbCaStatsTest
TESTFILES += ../dbCaStatsTest.db
TESTFILES += ../dbCaStats.db
TESTPROD_HOST += dbCaLinkTest
dbCaLinkTest_SRCS += dbCaLinkTest.c
dbCaLinkTest_SRCS += dbCACTest.cpp
dbCaLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += dbCaLinkTest.c
testHarness_SRCS += dbCACTest.cpp
TESTS += dbCaLinkTest
TESTFILES += ../dbCaLinkTest1.db ../dbCaLinkTest2.db ../dbCaLinkTest3.db
TARGETS += $(COMMON_DIR)/scanIoTest.dbd
scanIoTest_DBD += menuGlobal.dbd
scanIoTest_DBD += menuConvert.dbd
scanIoTest_DBD += menuScan.dbd
scanIoTest_DBD += yRecord.dbd
TESTPROD_HOST += scanIoTest
scanIoTest_SRCS += scanIoTest.c
scanIoTest_REGRDDFLAGS = -l
scanIoTest_SRCS += scanIoTest_registerRecordDeviceDriver.cpp
scanIoTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += scanIoTest.c
testHarness_SRCS += scanIoTest_registerRecordDeviceDriver.cpp
TESTFILES += $(COMMON_DIR)/scanIoTest.dbd ../scanIoTest.db
TESTFILES += ../scanIoTest.db
TESTS += scanIoTest
TESTPROD_HOST += dbChannelTest
@@ -95,6 +120,16 @@ dbChannelTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += dbChannelTest.c
TESTS += dbChannelTest
TARGETS += $(COMMON_DIR)/dbChArrTest.dbd
dbChArrTest_DBD += arrRecord.dbd
TESTPROD_HOST += dbChArrTest
dbChArrTest_SRCS += dbChArrTest.cpp
dbChArrTest_SRCS += dbChArrTest_registerRecordDeviceDriver.cpp
testHarness_SRCS += dbChArrTest.cpp
testHarness_SRCS += dbChArrTest_registerRecordDeviceDriver.cpp
TESTFILES += $(COMMON_DIR)/dbChArrTest.dbd ../dbChArrTest.db
TESTS += dbChArrTest
TESTPROD_HOST += chfPluginTest
chfPluginTest_SRCS += chfPluginTest.c
chfPluginTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
@@ -132,7 +167,11 @@ TESTSCRIPTS_HOST += $(TESTS:%=%.t)
include $(TOP)/configure/RULES
xRecord$(DEP): $(COMMON_DIR)/xRecord.h
arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h
dbCaLinkTest$(DEP): $(COMMON_DIR)/xRecord.h $(COMMON_DIR)/arrRecord.h
dbPutLinkTest$(DEP): $(COMMON_DIR)/xRecord.h
scanIoTest$(DEP): $(COMMON_DIR)/yRecord.h
dbStressLock$(DEP): $(COMMON_DIR)/xRecord.h
devx$(DEP): $(COMMON_DIR)/xRecord.h
scanIoTest$(DEP): $(COMMON_DIR)/xRecord.h
xRecord$(DEP): $(COMMON_DIR)/xRecord.h

141
src/ioc/db/test/arrRecord.c Normal file
View File

@@ -0,0 +1,141 @@
/*************************************************************************\
* Copyright (c) 2010 Brookhaven National Laboratory.
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
* fuer Materialien und Energie GmbH.
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* arrRecord.c - minimal array record for test purposes: no processing */
/*
* Author: Ralph Lange <Ralph.Lange@bessy.de>
*
* vaguely implemented like parts of recWaveform.c by Bob Dalesio
*
*/
#include <stdio.h>
#include "dbDefs.h"
#include "epicsPrint.h"
#include "dbAccess.h"
#include "dbEvent.h"
#include "dbFldTypes.h"
#include "recSup.h"
#include "recGbl.h"
#include "cantProceed.h"
#define GEN_SIZE_OFFSET
#include "arrRecord.h"
#undef GEN_SIZE_OFFSET
#include "epicsExport.h"
/* Create RSET - Record Support Entry Table*/
#define report NULL
#define initialize NULL
static long init_record(arrRecord *, int);
static long process(arrRecord *);
#define special NULL
#define get_value NULL
static long cvt_dbaddr(DBADDR *);
static long get_array_info(DBADDR *, long *, long *);
static long put_array_info(DBADDR *, long);
#define get_units NULL
#define get_precision NULL
#define get_enum_str NULL
#define get_enum_strs NULL
#define put_enum_str NULL
#define get_graphic_double NULL
#define get_control_double NULL
#define get_alarm_double NULL
rset arrRSET = {
RSETNUMBER,
report,
initialize,
init_record,
process,
special,
get_value,
cvt_dbaddr,
get_array_info,
put_array_info,
get_units,
get_precision,
get_enum_str,
get_enum_strs,
put_enum_str,
get_graphic_double,
get_control_double,
get_alarm_double
};
epicsExportAddress(rset, arrRSET);
static long init_record(arrRecord *prec, int pass)
{
if (pass == 0) {
if (prec->nelm <= 0)
prec->nelm = 1;
if (prec->ftvl > DBF_ENUM)
prec->ftvl = DBF_UCHAR;
prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl),
"arr calloc failed");
if (prec->nelm == 1) {
prec->nord = 1;
} else {
prec->nord = 0;
}
return 0;
}
return 0;
}
static long process(arrRecord *prec)
{
if(prec->clbk)
(*prec->clbk)(prec);
prec->pact = TRUE;
recGblGetTimeStamp(prec);
recGblFwdLink(prec);
prec->pact = FALSE;
return 0;
}
static long cvt_dbaddr(DBADDR *paddr)
{
arrRecord *prec = (arrRecord *) paddr->precord;
paddr->pfield = prec->bptr;
paddr->no_elements = prec->nelm;
paddr->field_type = prec->ftvl;
paddr->field_size = dbValueSize(prec->ftvl);
paddr->dbr_field_type = prec->ftvl;
return 0;
}
static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
{
arrRecord *prec = (arrRecord *) paddr->precord;
*no_elements = prec->nord;
*offset = prec->off;
return 0;
}
static long put_array_info(DBADDR *paddr, long nNew)
{
arrRecord *prec = (arrRecord *) paddr->precord;
prec->nord = nNew;
if (prec->nord > prec->nelm)
prec->nord = prec->nelm;
return 0;
}

View File

@@ -0,0 +1,42 @@
include "menuGlobal.dbd"
include "menuConvert.dbd"
include "menuScan.dbd"
recordtype(arr) {
include "dbCommon.dbd"
field(VAL, DBF_NOACCESS) {
prompt("Value")
special(SPC_DBADDR)
pp(TRUE)
extra("void *val")
}
field(NELM, DBF_ULONG) {
prompt("Number of Elements")
special(SPC_NOMOD)
initial("1")
}
field(FTVL, DBF_MENU) {
prompt("Field Type of Value")
special(SPC_NOMOD)
menu(menuFtype)
}
field(NORD, DBF_ULONG) {
prompt("Number elements read")
special(SPC_NOMOD)
}
field(OFF, DBF_ULONG) {
prompt("Offset into array")
}
field(BPTR, DBF_NOACCESS) {
prompt("Buffer Pointer")
special(SPC_NOMOD)
extra("void *bptr")
}
field(INP, DBF_INLINK) {
prompt("Input Link")
}
field(CLBK, DBF_NOACCESS) {
prompt("Processing callback")
special(SPC_NOMOD)
extra("void (*clbk)(struct arrRecord*)")
}
}

View File

@@ -22,7 +22,9 @@
#include "dbStaticLib.h"
#include "dbAccessDefs.h"
#include "registry.h"
#include "epicsUnitTest.h"
#include "errlog.h"
#include "epicsExit.h"
#include "dbUnitTest.h"
#include "testMain.h"
#include "osiFileName.h"
@@ -85,22 +87,20 @@ MAIN(arrShorthandTest)
testPlan(26);
db_init_events();
dbChannelInit();
testdbPrepare();
if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd",
"." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
testAbort("Database description 'dbTestIoc.dbd' not found");
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
if (dbReadDatabase(&pdbbase, "xRecord.db",
"." OSI_PATH_LIST_SEPARATOR "..", NULL))
testAbort("Test database 'xRecord.db' not found");
testdbReadDatabase("xRecord.db", NULL, NULL);
testHead("Register plugin");
testOk(!chfPluginRegister("arr", &myPif, opts), "register fake arr plugin");
eltc(0);
testIocInitOk();
eltc(1);
#define TESTBAD(Title, Expr) \
testDiag(Title); \
testOk(!(pch = dbChannelCreate("x." Expr)), "dbChannelCreate (" Expr ") fails"); \
@@ -129,9 +129,8 @@ MAIN(arrShorthandTest)
TESTGOOD("range [s::e]", "[2::4]", 2, 1, 4);
TESTGOOD("range with incr [s:i:e]", "[2:3:4]", 2, 3, 4);
dbFreeBase(pdbbase);
registryFree();
pdbbase = NULL;
testIocShutdownOk();
testdbCleanup();
return testDone();
}

View File

@@ -418,7 +418,7 @@ static void channelRegisterPost(dbChannel *chan, void *user,
static void channel_report(dbChannel *chan, void *user, int level,
const unsigned short indent)
{
testOk(level == R_LEVEL - 1, "channel_report: level correct");
testOk(level == R_LEVEL - 2, "channel_report: level correct %u == %u", level, R_LEVEL-2);
if (user == puser1) {
testOk(e1 & e_report, "channel_report (1) called");
c1 |= e_report;

View File

@@ -0,0 +1,84 @@
/*************************************************************************\
* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Part of dbCaLinkTest, compiled seperately to avoid
* dbAccess.h vs. db_access.h conflicts
*/
#include <stdio.h>
#include <vector>
#include <stdexcept>
#include <epicsEvent.h>
#include "epicsUnitTest.h"
#include "cadef.h"
#define testECA(OP) if((OP)!=ECA_NORMAL) {testAbort("%s", #OP);} else {testPass("%s", #OP);}
void putgetarray(chid chanid, double first, size_t count)
{
testDiag("putgetarray(%f,%u)", first, (unsigned)count);
std::vector<double> buf(count);
for(size_t i=0; i<count ;i++)
buf[i] = first + i*1.0;
testDiag("Put");
testECA(ca_array_put(DBR_DOUBLE, count, chanid, &buf[0]));
testECA(ca_pend_io(1.0));
testDiag("Get");
std::vector<double> buf2(count);
testECA(ca_array_get(DBR_DOUBLE, count, chanid, &buf2[0]));
testECA(ca_pend_io(1.0));
for(size_t i=0; i<count ;i++)
testOk(buf[i]==buf2[i], "%f == %f", buf[i], buf2[i]);
}
struct CATestContext
{
CATestContext()
{
if(ca_context_create(ca_enable_preemptive_callback)!=ECA_NORMAL)
throw std::runtime_error("Failed to create CA context");
}
~CATestContext()
{
ca_context_destroy();
}
};
extern "C"
void dbCaLinkTest_testCAC(void)
{
try {
CATestContext ctxt;
chid chanid = 0;
testECA(ca_create_channel("target1", NULL, NULL, 0, &chanid));
testECA(ca_pend_io(1.0));
putgetarray(chanid, 1.0, 1);
putgetarray(chanid, 2.0, 2);
// repeat to ensure a cache hit in dbContextReadNotifyCacheAllocator
putgetarray(chanid, 2.0, 2);
putgetarray(chanid, 5.0, 5);
testECA(ca_clear_channel(chanid));
}catch(std::exception& e){
testAbort("Unexpected exception in testCAC: %s", e.what());
}
}

View File

@@ -0,0 +1,612 @@
/*************************************************************************\
* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "epicsString.h"
#include "dbUnitTest.h"
#include "epicsThread.h"
#include "cantProceed.h"
#include "epicsEvent.h"
#include "iocInit.h"
#include "dbBase.h"
#include "link.h"
#include "dbAccess.h"
#include "epicsStdio.h"
#include "dbEvent.h"
/* Declarations from cadef.h and db_access.h which we can't include here */
typedef void * chid;
epicsShareExtern const unsigned short dbr_value_size[];
epicsShareExtern short epicsShareAPI ca_field_type (chid chan);
#define MAX_UNITS_SIZE 8
#include "dbCaPvt.h"
#include "errlog.h"
#include "testMain.h"
#include "xRecord.h"
#include "arrRecord.h"
#define testOp(FMT,A,OP,B) testOk((A)OP(B), #A " ("FMT") " #OP " " #B " ("FMT")", A,B)
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
static epicsEventId waitEvent;
static unsigned waitCounter;
static
void waitCB(void *unused)
{
if(waitEvent)
epicsEventMustTrigger(waitEvent);
waitCounter++; /* TODO: atomic */
}
static
void startWait(DBLINK *plink)
{
caLink *pca = plink->value.pv_link.pvt;
assert(!waitEvent);
waitEvent = epicsEventMustCreate(epicsEventEmpty);
assert(pca);
epicsMutexMustLock(pca->lock);
assert(!pca->monitor && !pca->userPvt);
pca->monitor = &waitCB;
epicsMutexUnlock(pca->lock);
testDiag("Preparing to wait on pca=%p", pca);
}
static
void waitForUpdate(DBLINK *plink)
{
caLink *pca = plink->value.pv_link.pvt;
assert(pca);
testDiag("Waiting on pca=%p", pca);
epicsEventMustWait(waitEvent);
epicsMutexMustLock(pca->lock);
pca->monitor = NULL;
pca->userPvt = NULL;
epicsMutexUnlock(pca->lock);
epicsEventDestroy(waitEvent);
waitEvent = NULL;
}
static
void putLink(DBLINK *plink, short dbr, const void*buf, long nReq)
{
long ret;
waitEvent = epicsEventMustCreate(epicsEventEmpty);
ret = dbCaPutLinkCallback(plink, dbr, buf, nReq,
&waitCB, NULL);
if(ret) {
testFail("putLink fails %ld\n", ret);
} else {
epicsEventMustWait(waitEvent);
testPass("putLink ok\n");
}
epicsEventDestroy(waitEvent);
waitEvent = NULL;
}
static void testNativeLink(void)
{
xRecord *psrc, *ptarg;
DBLINK *psrclnk;
epicsInt32 temp;
long nReq;
testDiag("Link to a scalar numeric field");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target CA");
eltc(0);
testIocInitOk();
eltc(1);
psrc = (xRecord*)testdbRecordPtr("source");
ptarg= (xRecord*)testdbRecordPtr("target");
psrclnk = &psrc->lnk;
/* ensure this is really a CA link */
testOk1(dbLockGetLockId((dbCommon*)psrc)!=dbLockGetLockId((dbCommon*)ptarg));
testOk1(psrclnk->type==CA_LINK);
startWait(psrclnk);
dbScanLock((dbCommon*)ptarg);
ptarg->val = 42;
db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
dbScanUnlock((dbCommon*)ptarg);
waitForUpdate(psrclnk);
dbScanLock((dbCommon*)psrc);
/* local CA_LINK connects immediately */
testOk1(dbCaIsLinkConnected(psrclnk));
nReq = 422;
testOk1(dbCaGetNelements(psrclnk, &nReq)==0);
testOp("%ld",nReq,==,1l);
nReq = 1;
temp = 0x0f0f0f0f;
testOk1(dbGetLink(psrclnk, DBR_LONG, (void*)&temp, NULL, &nReq)==0);
testOp("%d",temp,==,42);
dbScanUnlock((dbCommon*)psrc);
temp = 1010;
nReq = 1;
putLink(psrclnk, DBR_LONG, (void*)&temp, nReq);
dbScanLock((dbCommon*)ptarg);
testOk1(ptarg->val==1010);
dbScanUnlock((dbCommon*)ptarg);
testIocShutdownOk();
testdbCleanup();
}
static void testStringLink(void)
{
xRecord *psrc, *ptarg;
DBLINK *psrclnk;
char temp[MAX_STRING_SIZE];
long nReq;
testDiag("Link to a string field");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target.DESC CA");
eltc(0);
testIocInitOk();
eltc(1);
psrc = (xRecord*)testdbRecordPtr("source");
ptarg= (xRecord*)testdbRecordPtr("target");
psrclnk = &psrc->lnk;
/* ensure this is really a CA link */
testOk1(dbLockGetLockId((dbCommon*)psrc)!=dbLockGetLockId((dbCommon*)ptarg));
testOk1(psrclnk->type==CA_LINK);
startWait(psrclnk);
dbScanLock((dbCommon*)ptarg);
strcpy(ptarg->desc, "hello");
db_post_events(ptarg, &ptarg->desc, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
dbScanUnlock((dbCommon*)ptarg);
waitForUpdate(psrclnk);
dbScanLock((dbCommon*)psrc);
/* local CA_LINK connects immediately */
testOk1(dbCaIsLinkConnected(psrclnk));
nReq = 1;
memset(temp, '!', sizeof(temp));
testOk1(dbGetLink(psrclnk, DBR_STRING, (void*)&temp, NULL, &nReq)==0);
testOk(strcmp(temp, "hello")==0, "%s == hello", temp);
dbScanUnlock((dbCommon*)psrc);
strcpy(temp, "world");
putLink(psrclnk, DBR_STRING, (void*)&temp, nReq);
dbScanLock((dbCommon*)ptarg);
testOk(strcmp(ptarg->desc, "world")==0, "%s == world", ptarg->desc);
dbScanUnlock((dbCommon*)ptarg);
testIocShutdownOk();
testdbCleanup();
}
static void wasproc(xRecord *prec)
{
waitCB(NULL);
}
static void testCP(void)
{
xRecord *psrc, *ptarg;
testDiag("Link CP modifier");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target CP");
psrc = (xRecord*)testdbRecordPtr("source");
ptarg= (xRecord*)testdbRecordPtr("target");
/* hook in before IOC init */
waitCounter=0;
psrc->clbk = &wasproc;
assert(!waitEvent);
waitEvent = epicsEventMustCreate(epicsEventEmpty);
eltc(0);
testIocInitOk();
eltc(1);
epicsEventMustWait(waitEvent);
dbScanLock((dbCommon*)psrc);
testOp("%u",waitCounter,==,1); /* initial processing */
dbScanUnlock((dbCommon*)psrc);
dbScanLock((dbCommon*)ptarg);
ptarg->val = 42;
db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
dbScanUnlock((dbCommon*)ptarg);
epicsEventMustWait(waitEvent);
dbScanLock((dbCommon*)psrc);
testOp("%u",waitCounter,==,2); /* process due to monitor update */
dbScanUnlock((dbCommon*)psrc);
testIocShutdownOk();
testdbCleanup();
epicsEventDestroy(waitEvent);
waitEvent = NULL;
}
static void fillArray(epicsInt32 *buf, unsigned count, epicsInt32 first)
{
for(;count;count--,first++)
*buf++ = first;
}
static void fillArrayDouble(double *buf, unsigned count, double first)
{
for(;count;count--,first++)
*buf++ = first;
}
static void checkArray(const char *msg,
epicsInt32 *buf, epicsInt32 first,
unsigned used)
{
int match = 1;
unsigned i;
epicsInt32 x, *b;
for(b=buf,x=first,i=0;i<used;i++,x++,b++)
match &= (*b)==x;
testOk(match, "%s", msg);
if(!match) {
for(b=buf,x=first,i=0;i<used;i++,x++,b++)
if((*b)!=x)
testDiag("%u %u != %u", i, (unsigned)*b, (unsigned)x);
}
}
static void checkArrayDouble(const char *msg,
double *buf, double first,
unsigned used)
{
int match = 1;
unsigned i;
double x, *b;
for(b=buf,x=first,i=0;i<used;i++,x++,b++)
match &= (*b)==x;
testOk(match, "%s", msg);
if(!match) {
for(b=buf,x=first,i=0;i<used;i++,x++,b++)
if((*b)!=x)
testDiag("%u %u != %u", i, (unsigned)*b, (unsigned)x);
}
}
static void spoilputbuf(DBLINK *lnk)
{
caLink *pca = lnk->value.pv_link.pvt;
if(lnk->type!=CA_LINK || !pca->pputNative)
return;
epicsMutexMustLock(pca->lock);
memset(pca->pputNative, '!',
pca->nelements*dbr_value_size[ca_field_type(pca->chid)]);
epicsMutexUnlock(pca->lock);
}
static void testArrayLink(unsigned nsrc, unsigned ntarg)
{
char buf[100];
arrRecord *psrc, *ptarg;
DBLINK *psrclnk;
epicsInt32 *bufsrc, *buftarg, *tmpbuf;
long nReq;
unsigned num_min, num_max;
testDiag("Link to a array numeric field");
/* source.INP = "target CA" */
epicsSnprintf(buf, sizeof(buf), "TARGET=target CA,FTVL=LONG,SNELM=%u,TNELM=%u",
nsrc, ntarg);
testDiag("%s", buf);
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbCaLinkTest2.db", NULL, buf);
psrc = (arrRecord*)testdbRecordPtr("source");
ptarg= (arrRecord*)testdbRecordPtr("target");
psrclnk = &psrc->inp;
eltc(0);
testIocInitOk();
eltc(1);
bufsrc = psrc->bptr;
buftarg= ptarg->bptr;
num_max=num_min=psrc->nelm;
if(num_min>ptarg->nelm)
num_min=ptarg->nelm;
if(num_max<ptarg->nelm)
num_max=ptarg->nelm;
/* always request more than can possibly be filled */
num_max += 2;
tmpbuf = callocMustSucceed(num_max, sizeof(*tmpbuf), "tmpbuf");
startWait(psrclnk);
dbScanLock((dbCommon*)ptarg);
fillArray(buftarg, ptarg->nelm, 1);
ptarg->nord = ptarg->nelm;
db_post_events(ptarg, ptarg->bptr, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
dbScanUnlock((dbCommon*)ptarg);
waitForUpdate(psrclnk);
dbScanLock((dbCommon*)psrc);
testDiag("fetch source.INP into source.BPTR");
nReq = psrc->nelm;
if(dbGetLink(psrclnk, DBR_LONG, bufsrc, NULL, &nReq)==0) {
testPass("dbGetLink");
testOp("%ld",nReq,==,(long)num_min);
checkArray("array update", bufsrc, 1, nReq);
} else {
testFail("dbGetLink");
testSkip(2, "dbGetLink fails");
}
testDiag("fetch source.INP into temp buffer w/ larger capacity");
nReq = num_max;
if(dbGetLink(psrclnk, DBR_LONG, tmpbuf, NULL, &nReq)==0) {
testPass("dbGetLink");
testOp("%ld",nReq,==,(long)ntarg);
checkArray("array update", tmpbuf, 1, nReq);
} else {
testFail("dbGetLink");
testSkip(2, "dbGetLink fails");
}
dbScanUnlock((dbCommon*)psrc);
fillArray(bufsrc, psrc->nelm, 2);
/* write buffer allocated on first put */
putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm);
dbScanLock((dbCommon*)ptarg);
testOp("%ld",(long)ptarg->nord,==,(long)num_min);
dbScanUnlock((dbCommon*)ptarg);
/* write again to ensure that buffer is completely updated */
spoilputbuf(psrclnk);
fillArray(bufsrc, psrc->nelm, 3);
putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm);
dbScanLock((dbCommon*)ptarg);
testOp("%ld",(long)ptarg->nord,==,(long)num_min);
checkArray("array update", buftarg, 3, num_min);
dbScanUnlock((dbCommon*)ptarg);
testIocShutdownOk();
testdbCleanup();
free(tmpbuf);
/* records don't cleanup after themselves
* so do here to silence valgrind
*/
free(bufsrc);
free(buftarg);
}
static void softarr(arrRecord *prec)
{
long nReq = prec->nelm;
long status = dbGetLink(&prec->inp, DBR_DOUBLE, prec->bptr, NULL, &nReq);
if(status) {
testFail("dbGetLink() -> %ld", status);
} else {
testPass("dbGetLink() succeeds");
prec->nord = nReq;
if(nReq>0)
testDiag("%s.VAL[0] - %f", prec->name, *(double*)prec->bptr);
}
waitCB(NULL);
}
static void testreTargetTypeChange(void)
{
arrRecord *psrc, *ptarg1, *ptarg2;
double *bufsrc, *buftarg1;
epicsInt32 *buftarg2;
testDiag("Retarget an link to a PV with a different type DOUBLE->LONG");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbCaLinkTest3.db", NULL, "NELM=5,TARGET=target1 CP");
psrc = (arrRecord*)testdbRecordPtr("source");
ptarg1= (arrRecord*)testdbRecordPtr("target1");
ptarg2= (arrRecord*)testdbRecordPtr("target2");
/* hook in before IOC init */
waitCounter=0;
psrc->clbk = &softarr;
assert(!waitEvent);
waitEvent = epicsEventMustCreate(epicsEventEmpty);
eltc(0);
testIocInitOk();
eltc(1);
epicsEventMustWait(waitEvent); /* wait for initial processing */
bufsrc = psrc->bptr;
buftarg1= ptarg1->bptr;
buftarg2= ptarg2->bptr;
testDiag("Update one with original target");
dbScanLock((dbCommon*)ptarg2);
fillArray(buftarg2, ptarg2->nelm, 2);
ptarg2->nord = ptarg2->nelm;
dbScanUnlock((dbCommon*)ptarg2);
/* initialize buffers */
dbScanLock((dbCommon*)ptarg1);
fillArrayDouble(buftarg1, ptarg1->nelm, 1);
ptarg1->nord = ptarg1->nelm;
db_post_events(ptarg1, ptarg1->bptr, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
dbScanUnlock((dbCommon*)ptarg1);
epicsEventMustWait(waitEvent); /* wait for update */
dbScanLock((dbCommon*)psrc);
testOp("%ld",(long)psrc->nord,==,(long)5);
checkArrayDouble("array update", bufsrc, 1, 5);
dbScanUnlock((dbCommon*)psrc);
testDiag("Retarget");
testdbPutFieldOk("source.INP", DBR_STRING, "target2 CP");
epicsEventMustWait(waitEvent); /* wait for update */
dbScanLock((dbCommon*)psrc);
testOp("%ld",(long)psrc->nord,==,(long)5);
checkArrayDouble("array update", bufsrc, 2, 5);
dbScanUnlock((dbCommon*)psrc);
testIocShutdownOk();
testdbCleanup();
/* records don't cleanup after themselves
* so do here to silence valgrind
*/
free(bufsrc);
free(buftarg1);
free(buftarg2);
}
void dbCaLinkTest_testCAC(void);
static void testCAC(void)
{
arrRecord *psrc, *ptarg1, *ptarg2;
double *bufsrc, *buftarg1;
epicsInt32 *buftarg2;
testDiag("Check local CA through libca");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbCaLinkTest3.db", NULL, "NELM=5,TARGET=target1 CP");
psrc = (arrRecord*)testdbRecordPtr("source");
ptarg1= (arrRecord*)testdbRecordPtr("target1");
ptarg2= (arrRecord*)testdbRecordPtr("target2");
eltc(0);
testIocInitOk();
eltc(1);
bufsrc = psrc->bptr;
buftarg1= ptarg1->bptr;
buftarg2= ptarg2->bptr;
dbCaLinkTest_testCAC();
testIocShutdownOk();
testdbCleanup();
/* records don't cleanup after themselves
* so do here to silence valgrind
*/
free(bufsrc);
free(buftarg1);
free(buftarg2);
}
MAIN(dbCaLinkTest)
{
testPlan(99);
testNativeLink();
testStringLink();
testCP();
testArrayLink(1,1);
testArrayLink(10,1);
testArrayLink(1,10);
testArrayLink(10,10);
testreTargetTypeChange();
testCAC();
return testDone();
}

View File

@@ -0,0 +1,5 @@
record(x, "target") {}
record(x, "source") {
field(LNK, "$(TARGET)")
}

View File

@@ -0,0 +1,10 @@
record(arr, "target") {
field(FTVL, "$(TFTVL=$(FTVL=))")
field(NELM, "$(TNELM=$(NELM=))")
}
record(arr, "source") {
field(INP, "$(TARGET)")
field(FTVL, "$(SFTVL=$(FTVL=))")
field(NELM, "$(SNELM=$(NELM=))")
}

View File

@@ -0,0 +1,14 @@
record(arr, "target1") {
field(FTVL, "DOUBLE")
field(NELM, "$(TNELM=$(NELM=))")
}
record(arr, "target2") {
field(FTVL, "LONG")
field(NELM, "$(TNELM=$(NELM=))")
}
record(arr, "source") {
field(INP, "$(TARGET)")
field(FTVL, "DOUBLE")
field(NELM, "$(SNELM=$(NELM=))")
}

View File

@@ -0,0 +1,246 @@
/*************************************************************************\
* Copyright (c) 2010 Brookhaven National Laboratory.
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
* fuer Materialien und Energie GmbH.
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2003 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to the Software License Agreement
* found in the file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Authors: Ralph Lange <ralph.lange@gmx.de>,
* Andrew Johnson <anj@aps.anl.gov>
*/
#include <stddef.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include "registryFunction.h"
#include "epicsThread.h"
#include "epicsExit.h"
#include "epicsStdio.h"
#include "epicsString.h"
#include "envDefs.h"
#include "dbStaticLib.h"
#include "dbmf.h"
#include "registry.h"
#include "dbAddr.h"
#include "dbAccess.h"
#include "asDbLib.h"
#include "iocInit.h"
#include "iocsh.h"
#include "dbChannel.h"
#include "epicsUnitTest.h"
#include "testMain.h"
#include "osiFileName.h"
extern "C" {
int dbChArrTest_registerRecordDeviceDriver(struct dbBase *pdbbase);
}
#define CA_SERVER_PORT "65535"
const char *server_port = CA_SERVER_PORT;
static void createAndOpen(const char *name, dbChannel**pch)
{
testOk(!!(*pch = dbChannelCreate(name)), "dbChannel %s created", name);
testOk(!(dbChannelOpen(*pch)), "dbChannel opened");
testOk((ellCount(&(*pch)->pre_chain) == 0), "no filters in pre chain");
testOk((ellCount(&(*pch)->post_chain) == 0), "no filters in post chain");
}
extern "C" {
static void freeArray(db_field_log *pfl) {
if (pfl->type == dbfl_type_ref) {
free(pfl->u.r.field);
}
}
}
static void testHead (const char *title, const char *typ = "") {
const char *line = "------------------------------------------------------------------------------";
testDiag("%s", line);
testDiag(title, typ);
testDiag("%s", line);
}
static void check(short dbr_type) {
dbChannel *pch;
db_field_log *pfl;
dbAddr valaddr;
dbAddr offaddr;
const char *offname = NULL, *valname = NULL, *typname = NULL;
epicsInt32 buf[26];
long off, req;
int i;
switch (dbr_type) {
case DBR_LONG:
offname = "i32.OFF";
valname = "i32.VAL";
typname = "long";
break;
case DBR_DOUBLE:
offname = "f64.OFF";
valname = "f64.VAL";
typname = "double";
break;
case DBR_STRING:
offname = "c40.OFF";
valname = "c40.VAL";
typname = "string";
break;
default:
testDiag("Invalid data type %d", dbr_type);
}
(void) dbNameToAddr(offname, &offaddr);
(void) dbNameToAddr(valname, &valaddr);
testHead("Ten %s elements", typname);
/* Fill the record's array field with data, 10..19 */
epicsInt32 ar[10] = {10,11,12,13,14,15,16,17,18,19};
(void) dbPutField(&valaddr, DBR_LONG, ar, 10);
/* Open a channel to it, make sure no filters present */
createAndOpen(valname, &pch);
testOk(pch->final_type == valaddr.field_type,
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
testOk(pch->final_no_elements == valaddr.no_elements,
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
/* TEST1 sets the record's OFF field, then requests 10 elements from the channel,
* passing in a transparent db_field_log and converting the data to LONG on the way in.
* It checks that it got back the expected data and the right number of elements.
*/
#define TEST1(Size, Offset, Text, Expected) \
testDiag("Reading from offset = %d (%s)", Offset, Text); \
off = Offset; req = 10; \
memset(buf, 0, sizeof(buf)); \
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
pfl = db_create_read_log(pch); \
testOk(pfl && pfl->type == dbfl_type_rec, "Valid pfl, type = rec"); \
testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
for (i=0; i<Size; i++) \
testDiag("Element %d expected %d got %d", i, Expected[i], buf[i]); \
db_delete_field_log(pfl);
const epicsInt32 res_10_0[] = {10,11,12,13,14,15,16,17,18,19};
TEST1(10, 0, "no offset", res_10_0);
const epicsInt32 res_10_4[] = {14,15,16,17,18,19,10,11,12,13};
TEST1(10, 4, "wrapped", res_10_4);
/* Partial array */
testHead("Five %s elements", typname);
off = 0; /* Reset offset for writing the next buffer */
(void) dbPutField(&offaddr, DBR_LONG, &off, 1);
(void) dbPutField(&valaddr, DBR_LONG, &ar[5], 5);
createAndOpen(valname, &pch);
testOk(pch->final_type == valaddr.field_type,
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
testOk(pch->final_no_elements == valaddr.no_elements,
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
const epicsInt32 res_5_0[] = {15,16,17,18,19};
TEST1(5, 0, "no offset", res_5_0);
const epicsInt32 res_5_3[] = {18,19,15,16,17};
TEST1(5, 3, "wrapped", res_5_3);
/* TEST2 sets the record's OFF field, then requests 15 elements from the channel
* but passes in a db_field_log with alternate data, converting that data to LONG.
* It checks that it got back the expected data and the right number of elements.
*/
#define TEST2(Size, Offset, Text, Expected) \
testDiag("Reading from offset = %d (%s)", Offset, Text); \
off = Offset; req = 15; \
memset(buf, 0, sizeof(buf)); \
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
pfl = db_create_read_log(pch); \
pfl->type = dbfl_type_ref; \
pfl->field_type = DBF_CHAR; \
pfl->field_size = 1; \
pfl->no_elements = 26; \
pfl->u.r.dtor = freeArray; \
pfl->u.r.field = epicsStrDup("abcdefghijklmnopqrsstuvwxyz"); \
testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
for (i=0; i<Size; i++) \
testDiag("Element %d expected '%c' got '%c'", i, Expected[i], buf[i]); \
db_delete_field_log(pfl);
testHead("Fifteen letters from field-log instead of %s", typname);
const epicsInt32 res_15[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o'};
TEST2(15, 0, "no offset", res_15);
TEST2(15, 10, "ignored", res_15);
dbChannelDelete(pch);
}
static dbEventCtx evtctx;
extern "C" {
static void dbChArrTestCleanup(void* junk)
{
dbFreeBase(pdbbase);
registryFree();
pdbbase=0;
db_close_events(evtctx);
dbmfFreeChunks();
}
}
MAIN(dbChArrTest)
{
testPlan(102);
/* Prepare the IOC */
epicsEnvSet("EPICS_CA_SERVER_PORT", server_port);
if (dbReadDatabase(&pdbbase, "dbChArrTest.dbd",
"." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
testAbort("Database description not loaded");
dbChArrTest_registerRecordDeviceDriver(pdbbase);
if (dbReadDatabase(&pdbbase, "dbChArrTest.db",
"." OSI_PATH_LIST_SEPARATOR "..", NULL))
testAbort("Test database not loaded");
epicsAtExit(&dbChArrTestCleanup,NULL);
/* Start the IOC */
iocInit();
evtctx = db_init_events();
check(DBR_LONG);
check(DBR_DOUBLE);
check(DBR_STRING);
return testDone();
}

View File

@@ -0,0 +1,15 @@
record(arr, "i32") {
field(DESC, "test array record")
field(NELM, "10")
field(FTVL, "LONG")
}
record(arr, "f64") {
field(DESC, "test array record")
field(NELM, "10")
field(FTVL, "DOUBLE")
}
record(arr, "c40") {
field(DESC, "test array record")
field(NELM, "10")
field(FTVL, "STRING")
}

View File

@@ -18,7 +18,7 @@
#include "dbAccessDefs.h"
#include "registry.h"
#include "recSup.h"
#include "epicsUnitTest.h"
#include "dbUnitTest.h"
#include "testMain.h"
#include "osiFileName.h"
#include "errlog.h"
@@ -158,17 +158,16 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */
testPlan(76);
if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd",
"." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
testAbort("Database description 'dbTestIoc.dbd' not found");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
if (dbReadDatabase(&pdbbase, "xRecord.db",
"." OSI_PATH_LIST_SEPARATOR "..", NULL))
testAbort("Test database 'xRecord.db' not found");
testdbReadDatabase("xRecord.db", NULL, NULL);
dbChannelInit();
eltc(0);
testIocInitOk();
eltc(1);
r = e = 0;
/* dbChannelTest() checks record and field names */
@@ -242,7 +241,7 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */
testOk1(!!(pch = dbChannelCreate("x.{\"scalar\":null}")));
e = e_report;
dbChannelShow(pch, 1, 2);
dbChannelShow(pch, 2, 2);
e = e_close;
if (pch) dbChannelDelete(pch);
@@ -267,9 +266,8 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */
e = e_start | e_start_map | e_abort;
testOk1(!dbChannelCreate("x.{\"scalar\":{}}"));
dbFreeBase(pdbbase);
registryFree();
pdbbase = NULL;
testIocShutdownOk();
testdbCleanup();
return testDone();
}

View File

@@ -12,9 +12,10 @@ long link_test_extend(struct dbCommon *junk)
static dsxt xrecextend = {&link_test_extend, &link_test_extend};
static
long link_test_init(int junk)
long link_test_init(int pass)
{
devExtend(&xrecextend);
if (pass == 0)
devExtend(&xrecextend);
return 0;
}
@@ -28,7 +29,6 @@ long link_test_noop(void *junk)
static dset devxLTest ## LTYPE = {4, NULL, &link_test_init, &link_test_noop, &link_test_noop}; \
epicsExportAddress(dset, devxLTest ## LTYPE);
DEFDSET(Soft)
DEFDSET(VME_IO)
DEFDSET(CAMAC_IO)
DEFDSET(AB_IO)

View File

@@ -1,5 +1,3 @@
device(x, CONSTANT,devxLTestSoft,"Soft Channel")
device(x, VME_IO, devxLTestVME_IO, "Unit Test VME_IO")
device(x, CAMAC_IO, devxLTestCAMAC_IO, "Unit Test CAMAC_IO")
device(x, AB_IO, devxLTestAB_IO, "Unit Test AB_IO")

View File

@@ -9,7 +9,15 @@
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
*/
#include "dbLock.h"
#include <stdlib.h>
#include "epicsSpin.h"
#include "epicsMutex.h"
#include "dbCommon.h"
#include "epicsThread.h"
#include "dbLockPvt.h"
#include "dbStaticLib.h"
#include "dbUnitTest.h"
#include "testMain.h"
@@ -28,15 +36,19 @@ void compareSets(int match, const char *A, const char *B)
rA = testdbRecordPtr(A);
rB = testdbRecordPtr(B);
actual = dbLockGetLockId(rA)==dbLockGetLockId(rB);
actual = rA->lset->plockSet==rB->lset->plockSet;
testOk(match==actual, "dbLockGetLockId(\"%s\")%c=dbLockGetLockId(\"%s\")",
A, match?'=':'!', B);
}
#define testIntOk1(A, OP, B) testOk((A) OP (B), "%s (%d) %s %s (%d)", #A, A, #OP, #B, B);
#define testPtrOk1(A, OP, B) testOk((A) OP (B), "%s (%p) %s %s (%p)", #A, A, #OP, #B, B);
static
void testSets(void) {
testDiag("Check initial creation of DB links");
DBENTRY entry;
long status;
testdbPrepare();
@@ -48,6 +60,27 @@ void testSets(void) {
testIocInitOk();
eltc(1);
testDiag("Check that all records have initialized lockRecord and lockSet");
dbInitEntry(pdbbase, &entry);
for(status = dbFirstRecordType(&entry);
!status;
status = dbNextRecordType(&entry)) {
for(status = dbFirstRecord(&entry);
!status;
status = dbNextRecord(&entry)) {
dbCommon *prec = entry.precnode->precord;
testOk(prec->lset!=NULL, "%s.LSET != NULL", prec->name);
if(prec->lset!=NULL)
testOk(prec->lset->plockSet!=NULL, "%s.LSET.plockSet != NULL", prec->name);
else
testSkip(1, "lockRecord missing");
}
}
dbFinishEntry(&entry);
testDiag("Check initial creation of DB links");
/* reca is by itself */
compareSets(0, "reca", "recb");
compareSets(0, "reca", "recc");
@@ -71,6 +104,310 @@ void testSets(void) {
compareSets(1, "rece", "recf");
testOk1(testdbRecordPtr("reca")->lset->plockSet->refcount==1);
testOk1(testdbRecordPtr("recb")->lset->plockSet->refcount==2);
testOk1(testdbRecordPtr("recd")->lset->plockSet->refcount==3);
testIocShutdownOk();
testdbCleanup();
}
static void testSingleLock(void)
{
dbCommon *prec;
testDiag("testing dbScanLock()/dbScanUnlock()");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbLockTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
prec = testdbRecordPtr("reca");
testOk1(prec->lset->plockSet->refcount==1);
dbScanLock(prec);
/* scan lock does not keep a reference to the lockSet */
testOk1(prec->lset->plockSet->refcount==1);
dbScanUnlock(prec);
dbScanLock(prec);
dbScanLock(prec);
/* scan lock can be recursive */
testOk1(prec->lset->plockSet->refcount==1);
dbScanUnlock(prec);
dbScanUnlock(prec);
testIocShutdownOk();
testdbCleanup();
}
static void testMultiLock(void)
{
dbCommon *prec[8];
dbLocker *plockA;
#ifdef LOCKSET_DEBUG
epicsThreadId myself = epicsThreadGetIdSelf();
#endif
testDiag("Test multi-locker function (lock everything)");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbLockTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
prec[0] = testdbRecordPtr("reca");
prec[1] = testdbRecordPtr("recb");
prec[2] = testdbRecordPtr("recc");
prec[3] = NULL;
prec[4] = testdbRecordPtr("recd");
prec[5] = testdbRecordPtr("rece");
prec[6] = testdbRecordPtr("recf");
prec[7] = testdbRecordPtr("recg");
testDiag("Test init refcounts");
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,1);
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,2);
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,3);
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,1);
plockA = dbLockerAlloc(prec, 8, 0);
if(!plockA)
testAbort("dbLockerAlloc() failed");
testDiag("After locker created");
/* locker takes 7 references, one for each lockRecord. */
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,2);
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,4);
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,6);
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,2);
#ifdef LOCKSET_DEBUG
testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,NULL);
testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,NULL);
testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,NULL);
testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,NULL);
#endif
dbScanLockMany(plockA);
testDiag("After locker locked");
/* locker takes 4 references, one for each lockSet. */
testIntOk1(ellCount(&plockA->locked),==,4);
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,3);
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,5);
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,7);
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,3);
#ifdef LOCKSET_DEBUG
testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,myself);
testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,myself);
testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,myself);
testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,myself);
#endif
/* recursive locking of individual records is allowed */
dbScanLock(prec[0]);
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,3);
dbScanUnlock(prec[0]);
/* recursive locking with dbScanLockMany() isn't
* dbScanLockMany(plockA); <-- would fail
*/
dbScanUnlockMany(plockA);
testDiag("After locker unlocked");
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,2);
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,4);
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,6);
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,2);
#ifdef LOCKSET_DEBUG
testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,NULL);
testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,NULL);
testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,NULL);
testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,NULL);
#endif
dbLockerFree(plockA);
testDiag("After locker free'd");
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,1);
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,2);
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,3);
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,1);
testIocShutdownOk();
testdbCleanup();
}
static void testLinkBreak(void)
{
dbCommon *precB, *precC;
testDiag("Test break link");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbLockTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
precB = testdbRecordPtr("recb");
precC = testdbRecordPtr("recc");
testOk1(precB->lset->plockSet==precC->lset->plockSet);
testOk1(precB->lset->plockSet->refcount==2);
/* break the link between B and C */
testdbPutFieldOk("recb.SDIS", DBR_STRING, "");
testOk1(precB->lset->plockSet!=precC->lset->plockSet);
testIntOk1(precB->lset->plockSet->refcount, ==, 1);
testIntOk1(precC->lset->plockSet->refcount, ==, 1);
testIocShutdownOk();
testdbCleanup();
}
static void testLinkMake(void)
{
dbCommon *precA, *precG;
lockSet *lA, *lG;
testDiag("Test make link");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbLockTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
precA = testdbRecordPtr("reca");
lA = dbLockGetRef(precA->lset);
precG = testdbRecordPtr("recg");
lG = dbLockGetRef(precG->lset);
testPtrOk1(precA->lset->plockSet, !=, precG->lset->plockSet);
testIntOk1(precA->lset->plockSet->refcount, ==, 2);
testIntOk1(precG->lset->plockSet->refcount, ==, 2);
/* make a link between A and G */
testdbPutFieldOk("reca.SDIS", DBR_STRING, "recg");
testPtrOk1(precA->lset->plockSet, ==, precG->lset->plockSet);
testIntOk1(precA->lset->plockSet->refcount, ==, 3);
if(precA->lset->plockSet==lG) {
testIntOk1(lA->refcount, ==, 1);
testIntOk1(lG->refcount, ==, 3);
} else {
testIntOk1(lA->refcount, ==, 3);
testIntOk1(lG->refcount, ==, 1);
}
dbLockDecRef(lG);
dbLockDecRef(lA);
testIocShutdownOk();
testdbCleanup();
}
static void testLinkChange(void)
{
dbCommon *precB, *precC, *precG;
lockSet *lB, *lG;
testDiag("Test re-target link");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbLockTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
precB = testdbRecordPtr("recb");
precC = testdbRecordPtr("recc");
precG = testdbRecordPtr("recg");
lB = dbLockGetRef(precB->lset);
lG = dbLockGetRef(precG->lset);
testPtrOk1(lB,==,precC->lset->plockSet);
testPtrOk1(lB,!=,lG);
testIntOk1(lB->refcount,==,3);
testIntOk1(lG->refcount,==,2);
/* break the link between B and C and replace it
* with a link between B and G
*/
testdbPutFieldOk("recb.SDIS", DBR_STRING, "recg");
testPtrOk1(precB->lset->plockSet,==,lB);
testPtrOk1(precG->lset->plockSet,==,lB);
testPtrOk1(precC->lset->plockSet,!=,lB);
testPtrOk1(precC->lset->plockSet,!=,lG);
testIntOk1(lB->refcount,==,3);
testIntOk1(lG->refcount,==,1);
testIntOk1(precC->lset->plockSet->refcount,==,1);
dbLockDecRef(lB);
dbLockDecRef(lG);
testIocShutdownOk();
testdbCleanup();
}
static void testLinkNOP(void)
{
dbCommon *precB, *precC;
testDiag("Test re-target link to the same destination");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbLockTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
precB = testdbRecordPtr("recb");
precC = testdbRecordPtr("recc");
testOk1(precB->lset->plockSet==precC->lset->plockSet);
testOk1(precB->lset->plockSet->refcount==2);
/* renew link between B and C */
testdbPutFieldOk("recb.SDIS", DBR_STRING, "recc");
testOk1(precB->lset->plockSet==precC->lset->plockSet);
testOk1(precB->lset->plockSet->refcount==2);
testIocShutdownOk();
testdbCleanup();
@@ -78,7 +415,17 @@ void testSets(void) {
MAIN(dbLockTest)
{
testPlan(15);
#ifdef LOCKSET_DEBUG
testPlan(100);
#else
testPlan(88);
#endif
testSets();
testSingleLock();
testMultiLock();
testLinkBreak();
testLinkMake();
testLinkChange();
testLinkNOP();
return testDone();
}

View File

@@ -19,3 +19,6 @@ record(x, "rece") {
record(x, "recf") {
}
record(x, "recg") {
}

View File

@@ -20,6 +20,7 @@
#include "dbAccess.h"
#include "registry.h"
#include "dbStaticLib.h"
#include "dbStaticPvt.h"
#include "osiFileName.h"
#include "dbmf.h"
#include "errlog.h"
@@ -28,8 +29,124 @@
#include "testMain.h"
static
int testStrcmp(int expect, const char *A, const char *B) {
static const char op[] = "<=>";
int ret = strcmp(A,B);
testOk(ret==expect, "\"%s\" %c= \"%s\"",
A, op[expect+1], B);
return ret==expect;
}
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
#define TEST_CONSTANT(SET, EXPECT) {SET, {CONSTANT, EXPECT}}
#define TEST_PV_LINK(SET, PV, MOD) {SET, {PV_LINK, PV, MOD}}
static const struct testParseDataT {
const char * const str;
dbLinkInfo info;
} testParseData[] = {
TEST_CONSTANT("", ""),
TEST_CONSTANT("0.1", "0.1"),
TEST_CONSTANT(" 0.2\t ", "0.2"),
TEST_PV_LINK("0.1a", "0.1a", 0),
TEST_PV_LINK(" hello ", "hello", 0),
TEST_PV_LINK(" hellox MSI", "hellox", pvlOptMSI),
TEST_PV_LINK(" world MSICP", "world", pvlOptMSI|pvlOptCP),
{"#C14 S145 @testing", {VME_IO, "testing", 0, "CS", {14, 145}}},
{"#B11 C12 N13 A14 F15 @cparam", {CAMAC_IO, "cparam", 0, "BCNAF", {11, 12, 13, 14, 15}}},
{" #B111 C112 N113 @cparam", {CAMAC_IO, "cparam", 0, "BCN", {111, 112, 113}}},
{" @hello world ", {INST_IO, "hello world", 0, "", /*{}*/}},
{NULL}
};
static void testLinkParse(void)
{
const struct testParseDataT *td = testParseData;
dbLinkInfo info;
testDiag("link parsing");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
for(;td->str; td++) {
int i, N;
testDiag("Parse \"%s\"", td->str);
testOk1(dbParseLink(td->str, DBF_INLINK, &info)==0);
testOk1(info.ltype==td->info.ltype);
if(td->info.target)
testStrcmp(0, info.target, td->info.target);
if(info.ltype==td->info.ltype) {
switch(info.ltype) {
case PV_LINK:
testOk1(info.modifiers==td->info.modifiers);
break;
case VME_IO:
testStrcmp(0, info.hwid, td->info.hwid);
N = strlen(td->info.hwid);
for(i=0; i<N; i++)
testOk(info.hwnums[i]==td->info.hwnums[i], "%d == %d",
info.hwnums[i], td->info.hwnums[i]);
}
}
free(info.target);
}
testIocShutdownOk();
testdbCleanup();
}
static const char *testParseFailData[] = {
"#",
"#S",
"#ABC",
"#A0 B",
"#A0 B @",
"#A0 B C @",
"#R1 M2 D3 E4 @oops", /* RF_IO has no parm */
"#C1 S2", /* VME_IO needs parm */
NULL
};
static void testLinkFailParse(void)
{
const char * const *td = testParseFailData;
dbLinkInfo info;
testDiag("link parsing of invalid input");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
for(;*td; td++) {
testDiag("Expect failure \"%s\"", *td);
testOk1(dbParseLink(*td, DBF_INLINK, &info)==S_dbLib_badField);
}
testIocShutdownOk();
testdbCleanup();
}
static const struct testDataT {
const char * const linkstring;
short linkType;
@@ -39,6 +156,7 @@ static const struct testDataT {
{"", CONSTANT, 0},
{"0", CONSTANT, 0},
{"42", CONSTANT, 0},
{"0x1", CONSTANT, 0},
{"x1", DB_LINK, 0, "x1 NPP NMS"},
{"x1.VAL", DB_LINK, 0, "x1.VAL NPP NMS"},
@@ -77,17 +195,17 @@ static void testCADBSet(void)
prec = (xRecord*)testdbRecordPtr("x1");
plink = &prec->lnk;
for(;td->linkstring;td++) {
for (;td->linkstring;td++) {
testDiag("x1.LNK <- \"%s\"", td->linkstring);
testdbPutFieldOk("x1.LNK", DBF_STRING, td->linkstring);
if(td->linkback)
if (td->linkback)
testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkback);
else
testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkstring);
testOk1(plink->type==td->linkType);
if(plink->type==td->linkType) {
if (plink->type==td->linkType) {
switch(td->linkType) {
case CONSTANT:
if(plink->value.constantStr)
@@ -327,7 +445,7 @@ static void testLinkInitFail(void)
testOk1(plink->type==VME_IO);
testOk1(plink->value.vmeio.parm!=NULL);
testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C200 S0 @");
testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C0 S0 @");
prec = (xRecord*)testdbRecordPtr("eVME_IO2");
plink = &prec->inp;
@@ -364,9 +482,8 @@ static void testLinkFail(void)
/* INST_IO doesn't accept empty string */
testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "");
/* INST_IO accepts invalid input as empty string */
testdbPutFieldOk("rINST_IO.INP", DBR_STRING, "abc");
testdbGetFieldEqual("rINST_IO.INP", DBR_STRING, "@");
/* INST_IO doesn't accept empty string */
testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "abc");
/* syntax errors */
testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "#S201 C200 @another VME_IO");
@@ -387,7 +504,9 @@ static void testLinkFail(void)
MAIN(dbPutLinkTest)
{
testPlan(200);
testPlan(251);
testLinkParse();
testLinkFailParse();
testCADBSet();
testHWInitSet();
testHWMod();

View File

@@ -0,0 +1,73 @@
/*************************************************************************\
* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
*/
#include <string.h>
#include "dbScan.h"
#include "epicsEvent.h"
#include "dbUnitTest.h"
#include "testMain.h"
#include "dbAccess.h"
#include "errlog.h"
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
static epicsEventId waiter;
static int called;
static dbCommon *prec;
static void onceComp(void *junk, dbCommon *prec)
{
testOk1(junk==(void*)&waiter);
testOk1(strcmp(prec->name, "reca")==0);
called = 1;
epicsEventMustTrigger(waiter);
}
static void testOnce(void)
{
testDiag("check scanOnceCallback() callback");
waiter = epicsEventMustCreate(epicsEventEmpty);
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbLockTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
prec = testdbRecordPtr("reca");
testDiag("scanOnce %s", prec->name);
scanOnceCallback(prec, onceComp, &waiter);
testDiag("Waiting");
epicsEventMustWait(waiter);
testOk1(called==1);
if(!called)
testSkip(2, "callback failed to run");
testIocShutdownOk();
testdbCleanup();
epicsEventDestroy(waiter);
}
MAIN(dbScanTest)
{
testPlan(3);
testOnce();
return testDone();
}

View File

@@ -0,0 +1,358 @@
/*************************************************************************\
* Copyright (c) 2014 Brookhaven Science Assoc. as operator of Brookhaven
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Lockset stress test.
*
* The test stratagy is for N threads to contend for M records.
* Each thread will perform one of three operations:
* 1) Lock a single record.
* 2) Lock several records.
* 3) Retarget the TSEL link of a record
*
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
*/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "envDefs.h"
#include "epicsEvent.h"
#include "epicsStdlib.h"
#include "epicsSpin.h"
#include "epicsThread.h"
#include "epicsMutex.h"
#include "dbCommon.h"
#include "dbLockPvt.h"
#include "dbStaticLib.h"
#include "dbUnitTest.h"
#include "testMain.h"
#include "dbAccess.h"
#include "errlog.h"
#include "xRecord.h"
#if defined(CLOCK_REALTIME) && defined(CLOCK_MONOTONIC) && !defined(_WIN32)
# define TIME_STATS
#endif
#define testIntOk1(A, OP, B) testOk((A) OP (B), "%s (%d) %s %s (%d)", #A, A, #OP, #B, B);
#define testPtrOk1(A, OP, B) testOk((A) OP (B), "%s (%p) %s %s (%p)", #A, A, #OP, #B, B);
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
/* number of seconds for the test to run */
static double runningtime = 18.0;
/* number of worker threads */
static unsigned int nworkers = 5;
static unsigned int nrecords;
#define MAXLOCK 20
static dbCommon **precords;
typedef struct {
int id;
unsigned long N[3];
#ifdef TIME_STATS
double X[3];
double X2[3];
#endif
unsigned int done;
epicsEventId donevent;
dbCommon *prec[MAXLOCK];
} workerPriv;
/* hopefully a uniform random number in [0.0, 1.0] */
static
double getRand(void)
{
return rand()/(double)RAND_MAX;
}
static
void doSingle(workerPriv *p)
{
size_t recn = (size_t)(getRand()*(nrecords-1));
dbCommon *prec = precords[recn];
xRecord *px = (xRecord*)prec;
dbScanLock(prec);
px->val++;
dbScanUnlock(prec);
}
static volatile int bitbucket;
static
void doMulti(workerPriv *p)
{
int sum = 0;
size_t i;
size_t nlock = 2 + (size_t)(getRand()*(MAXLOCK-3));
size_t nrec = (size_t)(getRand()*(nrecords-1));
dbLocker *locker;
assert(nlock>=2);
assert(nlock<nrecords);
for(i=0; i<nlock; i++, nrec=(nrec+1)%nrecords) {
p->prec[i] = precords[nrec];
}
locker = dbLockerAlloc(p->prec, nlock, 0);
if(!locker)
testAbort("locker allocation fails");
dbScanLockMany(locker);
for(i=0; i<nlock; i++) {
xRecord *px = (xRecord*)p->prec[i];
sum += px->val;
}
dbScanUnlockMany(locker);
dbLockerFree(locker);
}
static
void doreTarget(workerPriv *p)
{
char scratchsrc[60];
char scratchdst[MAX_STRING_SIZE];
long ret;
DBADDR dbaddr;
double action = getRand();
size_t nsrc = (size_t)(getRand()*(nrecords-1));
size_t ntarg = (size_t)(getRand()*(nrecords-1));
xRecord *psrc = (xRecord*)precords[nsrc];
xRecord *ptarg = (xRecord*)precords[ntarg];
strcpy(scratchsrc, psrc->name);
strcat(scratchsrc, ".TSEL");
ret = dbNameToAddr(scratchsrc, &dbaddr);
if(ret)
testAbort("bad record name? %ld", ret);
if(action<=0.6) {
scratchdst[0] = '\0';
} else {
strcpy(scratchdst, ptarg->name);
}
ret = dbPutField(&dbaddr, DBR_STRING, ptarg->name, 1);
if(ret)
testAbort("put fails with %ld", ret);
}
static
void worker(void *raw)
{
#ifdef TIME_STATS
struct timespec before;
#endif
workerPriv *priv = raw;
testDiag("worker %d is %p", priv->id, epicsThreadGetIdSelf());
#ifdef TIME_STATS
clock_gettime(CLOCK_MONOTONIC, &before);
#endif
while(!priv->done) {
double sel = getRand();
#ifdef TIME_STATS
struct timespec after;
double duration;
#endif
int act;
if(sel<0.33) {
doSingle(priv);
act = 0;
} else if(sel<0.66) {
doMulti(priv);
act = 1;
} else {
doreTarget(priv);
act = 2;
}
#ifdef TIME_STATS
clock_gettime(CLOCK_MONOTONIC, &after);
duration = (double)((long)after.tv_nsec - (long)before.tv_nsec);
duration *= 1e-9;
duration += (double)(after.tv_sec - before.tv_sec);
#endif
priv->N[act]++;
#ifdef TIME_STATS
priv->X[act] += duration;
priv->X2[act] += duration*duration;
#endif
}
epicsEventMustTrigger(priv->donevent);
}
MAIN(dbStressTest)
{
DBENTRY ent;
long status;
unsigned int i;
workerPriv *priv;
char *nwork=getenv("NWORK");
epicsTimeStamp seed;
epicsTimeGetCurrent(&seed);
srand(seed.nsec);
if(nwork) {
long val = 0;
epicsParseLong(nwork, &val, 0, NULL);
if(val>2)
nworkers = val;
}
testPlan(80+nworkers*3);
priv = callocMustSucceed(nworkers, sizeof(*priv), "no memory");
testDiag("lock set stress test");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbStressLock.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
/* collect an array of all records */
dbInitEntry(pdbbase, &ent);
for(status = dbFirstRecordType(&ent);
!status;
status = dbNextRecordType(&ent))
{
for(status = dbFirstRecord(&ent);
!status;
status = dbNextRecord(&ent))
{
if(ent.precnode->flags&DBRN_FLAGS_ISALIAS)
continue;
nrecords++;
}
}
if(nrecords<2)
testAbort("where are the records!");
precords = callocMustSucceed(nrecords, sizeof(*precords), "no mem");
for(status = dbFirstRecordType(&ent), i = 0;
!status;
status = dbNextRecordType(&ent))
{
for(status = dbFirstRecord(&ent);
!status;
status = dbNextRecord(&ent))
{
if(ent.precnode->flags&DBRN_FLAGS_ISALIAS)
continue;
precords[i++] = ent.precnode->precord;
}
}
dbFinishEntry(&ent);
testDiag("Running with %u workers and %u records",
nworkers, nrecords);
for(i=0; i<nworkers; i++) {
priv[i].id = i;
priv[i].donevent = epicsEventMustCreate(epicsEventEmpty);
}
for(i=0; i<nworkers; i++) {
epicsThreadMustCreate("runner", epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackSmall),
&worker, &priv[i]);
}
testDiag("All started. Will run for %f sec", runningtime);
epicsThreadSleep(runningtime);
testDiag("Stopping");
for(i=0; i<nworkers; i++) {
priv[i].done = 1;
}
for(i=0; i<nworkers; i++) {
epicsEventMustWait(priv[i].donevent);
epicsEventDestroy(priv[i].donevent);
}
testDiag("All stopped");
testDiag("Validate lockSet ref counts");
dbInitEntry(pdbbase, &ent);
for(status = dbFirstRecordType(&ent);
!status;
status = dbNextRecordType(&ent))
{
for(status = dbFirstRecord(&ent);
!status;
status = dbNextRecord(&ent))
{
dbCommon *prec = ent.precnode->precord;
lockSet *ls;
if(ent.precnode->flags&DBRN_FLAGS_ISALIAS)
continue;
ls = prec->lset->plockSet;
testOk(ellCount(&ls->lockRecordList)==ls->refcount, "%s only lockRecords hold refs. %d == %d",
prec->name,ellCount(&ls->lockRecordList),ls->refcount);
testOk1(ls->ownerlocker==NULL);
}
}
dbFinishEntry(&ent);
testDiag("Statistics");
for(i=0; i<nworkers; i++) {
testDiag("Worker %u", i);
testDiag("N = %lu %lu %lu", priv[i].N[0], priv[i].N[1], priv[i].N[2]);
#ifdef TIME_STATS
testDiag("X = %g %g %g", priv[i].X[0], priv[i].X[1], priv[i].X[2]);
testDiag("X2= %g %g %g", priv[i].X2[0], priv[i].X2[1], priv[i].X2[2]);
#endif
testOk1(priv[i].N[0]>0);
testOk1(priv[i].N[1]>0);
testOk1(priv[i].N[2]>0);
}
testIocShutdownOk();
testdbCleanup();
free(priv);
free(precords);
return testDone();
}

View File

@@ -0,0 +1,40 @@
record(x, "rec01") {}
record(x, "rec02") {}
record(x, "rec03") {}
record(x, "rec04") {}
record(x, "rec05") {}
record(x, "rec06") {}
record(x, "rec07") {}
record(x, "rec08") {}
record(x, "rec09") {}
record(x, "rec10") {}
record(x, "rec11") {}
record(x, "rec12") {}
record(x, "rec13") {}
record(x, "rec14") {}
record(x, "rec15") {}
record(x, "rec16") {}
record(x, "rec17") {}
record(x, "rec18") {}
record(x, "rec19") {}
record(x, "rec20") {}
record(x, "rec21") {}
record(x, "rec22") {}
record(x, "rec23") {}
record(x, "rec24") {}
record(x, "rec25") {}
record(x, "rec26") {}
record(x, "rec27") {}
record(x, "rec28") {}
record(x, "rec29") {}
record(x, "rec30") {}
record(x, "rec31") {}
record(x, "rec32") {}
record(x, "rec33") {}
record(x, "rec34") {}
record(x, "rec35") {}
record(x, "rec36") {}
record(x, "rec37") {}
record(x, "rec38") {}
record(x, "rec39") {}
record(x, "rec40") {}

163
src/ioc/db/test/devx.c Normal file
View File

@@ -0,0 +1,163 @@
/*************************************************************************\
* Copyright (c) 2014 Brookhaven Science Assoc. as Operator of Brookhaven
* National Lab
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include <string.h>
#include <stdio.h>
#include <epicsAssert.h>
#include <cantProceed.h>
#include <ellLib.h>
#include <dbDefs.h>
#include <dbAccess.h>
#include <dbScan.h>
#include <devSup.h>
#include <recGbl.h>
#include <link.h>
#include <dbLink.h>
#include <xRecord.h>
#include <epicsExport.h>
#include "devx.h"
/* xRecord DTYP="Scan I/O"
*
* dset to test I/O Intr scanning.
* INP="@drvname" names a "driver" which
* provides a IOSCANPVT.
* The driver also defines a callback function which
* is invoked when the record is processed.
*/
struct ELLLIST xdrivers;
/* Add a new "driver" with the given group id
* and processing callback
*/
xdrv* xdrv_add(int group, xdrvcb cb, void *arg)
{
xdrv *drv=callocMustSucceed(1, sizeof(*drv), "xdrv_add");
drv->cb = cb;
drv->arg = arg;
drv->group = group;
scanIoInit(&drv->scan);
ellAdd(&xdrivers, &drv->drvnode);
return drv;
}
/* Trigger the named "driver" group to scan */
xdrv *xdrv_get(int group)
{
ELLNODE *cur;
for(cur=ellFirst(&xdrivers); cur; cur=ellNext(cur)) {
xdrv *curd = CONTAINER(cur, xdrv, drvnode);
if(curd->group==group) {
return curd;
}
}
cantProceed("xdrv_get() for non-existant group");
return NULL;
}
/* Free all "driver" groups and record private structures.
* Call after testdbCleanup()
*/
void xdrv_reset()
{
ELLNODE *cur;
while((cur=ellGet(&xdrivers))!=NULL) {
ELLNODE *cur2;
xdrv *curd = CONTAINER(cur, xdrv, drvnode);
while((cur2=ellGet(&curd->privlist))!=NULL) {
xpriv *priv = CONTAINER(cur2, xpriv, privnode);
free(priv);
}
free(curd);
}
}
static long xscanio_init_record(xRecord *prec)
{
ELLNODE *cur;
xpriv *priv;
xdrv *drv = NULL;
int group, member;
assert(prec->inp.type==INST_IO);
if(sscanf(prec->inp.value.instio.string, "%d %d",
&group, &member)!=2)
cantProceed("xscanio_init_record invalid INP string");
for(cur=ellFirst(&xdrivers); cur; cur=ellNext(cur)) {
xdrv *curd = CONTAINER(cur, xdrv, drvnode);
if(curd->group==group) {
drv = curd;
break;
}
}
assert(drv!=NULL);
priv = mallocMustSucceed(sizeof(*priv), "xscanio_init_record");
priv->prec = prec;
priv->drv = drv;
priv->member = member;
ellAdd(&drv->privlist, &priv->privnode);
prec->dpvt = priv;
return 0;
}
static long xscanio_get_ioint_info(int cmd, xRecord *prec, IOSCANPVT *ppvt)
{
xpriv *priv = prec->dpvt;
if(!priv || !priv->drv)
return 0;
*ppvt = priv->drv->scan;
return 0;
}
static long xscanio_read(xRecord *prec)
{
xpriv *priv = prec->dpvt;
if(!priv || !priv->drv)
return 0;
if(priv->drv->cb)
(*priv->drv->cb)(priv, priv->drv->arg);
return 0;
}
static xdset devxScanIO = {
5, NULL, NULL,
&xscanio_init_record,
&xscanio_get_ioint_info,
&xscanio_read
};
epicsExportAddress(dset, devxScanIO);
/* basic DTYP="Soft Channel" */
static long xsoft_init_record(xRecord *prec)
{
if(prec->inp.type==CONSTANT)
recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val);
return 0;
}
static long xsoft_read(xRecord *prec)
{
if(prec->inp.type==CONSTANT)
return 0;
dbGetLink(&prec->inp, DBR_DOUBLE, &prec->val, NULL, NULL);
return 0;
}
static struct xdset devxSoft = {
5, NULL, NULL,
&xsoft_init_record,
NULL,
&xsoft_read
};
epicsExportAddress(dset, devxSoft);

2
src/ioc/db/test/devx.dbd Normal file
View File

@@ -0,0 +1,2 @@
device(x, CONSTANT, devxSoft, "Soft Channel")
device(x, INST_IO , devxScanIO, "Scan I/O")

52
src/ioc/db/test/devx.h Normal file
View File

@@ -0,0 +1,52 @@
/*************************************************************************\
* Copyright (c) 2014 Brookhaven Science Assoc. as Operator of Brookhaven
* National Lab
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#ifndef DEVXSCANIO_H
#define DEVXSCANIO_H
#include <ellLib.h>
#include <dbScan.h>
#include <shareLib.h>
struct xRecord;
struct xpriv;
epicsShareExtern struct ELLLIST xdrivers;
typedef void (*xdrvcb)(struct xpriv *, void *);
typedef struct {
ELLNODE drvnode;
IOSCANPVT scan;
xdrvcb cb;
void *arg;
ELLLIST privlist;
int group;
} xdrv;
typedef struct xpriv {
ELLNODE privnode;
xdrv *drv;
struct xRecord *prec;
int member;
} xpriv;
epicsShareFunc xdrv *xdrv_add(int group, xdrvcb cb, void *arg);
epicsShareFunc xdrv *xdrv_get(int group);
epicsShareFunc void xdrv_reset();
typedef struct xdset {
long number;
long (*report)(int);
long (*init)(int);
long (*init_record)(struct xRecord *);
long (*get_ioint_info)(int, struct xRecord*, IOSCANPVT*);
long (*process)(struct xRecord *);
} xdset;
#endif /* DEVXSCANIO_H */

View File

@@ -22,9 +22,11 @@ int callbackParallelTest(void);
int dbStateTest(void);
int dbCaStatsTest(void);
int dbShutdownTest(void);
int dbScanTest(void);
int scanIoTest(void);
int dbLockTest(void);
int dbPutLinkTest(void);
int dbCaLinkTest(void);
int testDbChannel(void);
int chfPluginTest(void);
int arrShorthandTest(void);
@@ -40,9 +42,11 @@ void epicsRunDbTests(void)
runTest(dbStateTest);
runTest(dbCaStatsTest);
runTest(dbShutdownTest);
runTest(dbScanTest);
runTest(scanIoTest);
runTest(dbLockTest);
runTest(dbPutLinkTest);
runTest(dbCaLinkTest);
runTest(testDbChannel);
runTest(arrShorthandTest);
runTest(recGblCheckDeadbandTest);

View File

@@ -12,6 +12,8 @@
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "epicsEvent.h"
#include "epicsMessageQueue.h"
@@ -26,9 +28,6 @@
#include "dbLock.h"
#include "dbUnitTest.h"
#include "dbCommon.h"
#include "registry.h"
#include "registryRecordType.h"
#include "registryDeviceSupport.h"
#include "recSup.h"
#include "devSup.h"
#include "iocInit.h"
@@ -38,482 +37,272 @@
#include "testMain.h"
#include "osiFileName.h"
#define GEN_SIZE_OFFSET
#include "yRecord.h"
#include "epicsExport.h"
#define ONE_THREAD_LOOPS 101
#define PAR_THREAD_LOOPS 53
#define CB_THREAD_LOOPS 13
#include "devx.h"
#include "xRecord.h"
#define NO_OF_THREADS 7
#define NO_OF_MEMBERS 5
#define NO_OF_GROUPS 11
STATIC_ASSERT(NUM_CALLBACK_PRIORITIES==3);
#define NO_OF_MID_THREADS 3
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
static int noOfGroups = NO_OF_GROUPS;
static int noOfIoscans = NO_OF_GROUPS;
static IOSCANPVT *ioscanpvt; /* Soft interrupt sources */
static ELLLIST *pvtList; /* Per group private part lists */
static int executionOrder;
static int orderFail;
static int testNo;
static epicsMessageQueueId *mq; /* Per group message queue */
static epicsEventId *barrier; /* Per group barrier event */
static int *cbCounter;
struct pvtY {
ELLNODE node;
yRecord *prec;
int group;
int member;
int count;
int processed;
int callback;
};
/* test2: priority and ioscan index for each group
* used priorities are expressed in the bit pattern of (ioscan index + 1) */
struct groupItem {
int prio;
int ioscan;
} groupTable[12] = {
{ 0, 0 },
{ 1, 1 },
{ 0, 2 }, { 1, 2 },
{ 2, 3 },
{ 0, 4 }, { 2, 4 },
{ 1, 5 }, { 2, 5 },
{ 0, 6 }, { 1, 6 }, { 2, 6 }
};
static int recsProcessed = 1;
static int noDoubleCallback = 1;
void scanIoTest_registerRecordDeviceDriver(struct dbBase *);
long count_bits(long n) {
unsigned int c; /* c accumulates the total bits set in v */
for (c = 0; n; c++)
n &= n - 1; /* clear the least significant bit set */
return c;
}
/*************************************************************************\
* yRecord: minimal record needed to test I/O Intr scanning
\*************************************************************************/
static long get_ioint_info(int cmd, yRecord *prec, IOSCANPVT *ppvt)
static void loadRecord(int group, int member, const char *prio)
{
struct pvtY *pvt = (struct pvtY *)(prec->dpvt);
if (testNo == 2)
*ppvt = ioscanpvt[groupTable[pvt->group].ioscan];
else
*ppvt = ioscanpvt[pvt->group];
return 0;
char buf[40];
sprintf(buf, "GROUP=%d,MEMBER=%d,PRIO=%s",
group, member, prio);
testdbReadDatabase("scanIoTest.db", NULL, buf);
}
struct ydset {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN process;
} devY = {
5,
NULL,
NULL,
NULL,
get_ioint_info,
NULL
};
epicsExportAddress(dset, devY);
typedef struct {
int hasprocd[NUM_CALLBACK_PRIORITIES];
int getcomplete[NUM_CALLBACK_PRIORITIES];
epicsEventId wait[NUM_CALLBACK_PRIORITIES];
epicsEventId wake[NUM_CALLBACK_PRIORITIES];
} testsingle;
static long init_record(yRecord *prec, int pass)
static void testcb(xpriv *priv, void *raw)
{
struct pvtY *pvt;
testsingle *td = raw;
int prio = priv->prec->prio;
if (pass == 0) return 0;
pvt = (struct pvtY *) calloc(1, sizeof(struct pvtY));
prec->dpvt = pvt;
pvt->prec = prec;
sscanf(prec->name, "g%dm%d", &pvt->group, &pvt->member);
ellAdd(&pvtList[pvt->group], &pvt->node);
return 0;
testOk1(td->hasprocd[prio]==0);
td->hasprocd[prio] = 1;
}
static long process(yRecord *prec)
static void testcomp(void *raw, IOSCANPVT scan, int prio)
{
struct pvtY *pvt = (struct pvtY *)(prec->dpvt);
testsingle *td = raw;
if (testNo == 0) {
/* Single callback thread */
if (executionOrder != pvt->member) {
orderFail = 1;
}
pvt->count++;
if (++executionOrder == NO_OF_MEMBERS) executionOrder = 0;
} else {
pvt->count++;
if (pvt->member == 0) {
epicsMessageQueueSend(mq[pvt->group], NULL, 0);
epicsEventMustWait(barrier[pvt->group]);
}
}
pvt->processed = 1;
return 0;
testOk1(td->hasprocd[prio]==1);
testOk1(td->getcomplete[prio]==0);
td->getcomplete[prio] = 1;
epicsEventMustTrigger(td->wait[prio]);
epicsEventMustWait(td->wake[prio]);
}
rset yRSET={
4,
NULL, /* report */
NULL, /* initialize */
init_record,
process
};
epicsExportAddress(rset, yRSET);
static void startMockIoc(void) {
char substitutions[256];
int i, j;
char *prio[] = { "LOW", "MEDIUM", "HIGH" };
if (testNo == 2) {
noOfGroups = 12;
noOfIoscans = 7;
}
ioscanpvt = calloc(noOfIoscans, sizeof(IOSCANPVT));
mq = calloc(noOfGroups, sizeof(epicsMessageQueueId));
barrier = calloc(noOfGroups, sizeof(epicsEventId));
pvtList = calloc(noOfGroups, sizeof(ELLLIST));
cbCounter = calloc(noOfGroups, sizeof(int));
if (dbReadDatabase(&pdbbase, "scanIoTest.dbd",
"." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
testAbort("Error reading database description 'scanIoTest.dbd'");
callbackParallelThreads(1, "Low");
callbackParallelThreads(NO_OF_MID_THREADS, "Medium");
callbackParallelThreads(NO_OF_THREADS, "High");
for (i = 0; i < noOfIoscans; i++) {
scanIoInit(&ioscanpvt[i]);
}
for (i = 0; i < noOfGroups; i++) {
mq[i] = epicsMessageQueueCreate(NO_OF_MEMBERS, 1);
barrier[i] = epicsEventMustCreate(epicsEventEmpty);
ellInit(&pvtList[i]);
}
scanIoTest_registerRecordDeviceDriver(pdbbase);
for (i = 0; i < noOfGroups; i++) {
for (j = 0; j < NO_OF_MEMBERS; j++) {
sprintf(substitutions, "GROUP=%d,MEMBER=%d,PRIO=%s", i, j,
testNo==0?"LOW":(testNo==1?"HIGH":prio[groupTable[i].prio]));
if (dbReadDatabase(&pdbbase, "scanIoTest.db",
"." OSI_PATH_LIST_SEPARATOR "..", substitutions))
testAbort("Error reading test database 'scanIoTest.db'");
}
}
testIocInitOk();
}
static void stopMockIoc(void) {
static void testSingleThreading(void)
{
int i;
testsingle data[2];
xdrv *drvs[2];
memset(data, 0, sizeof(data));
for(i=0; i<2; i++) {
int p;
for(p=0; p<NUM_CALLBACK_PRIORITIES; p++) {
data[i].wake[p] = epicsEventMustCreate(epicsEventEmpty);
data[i].wait[p] = epicsEventMustCreate(epicsEventEmpty);
}
}
testDiag("Test single-threaded I/O Intr scanning");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
/* create two scan lists with one record on each of three priorities */
/* group#, member#, priority */
loadRecord(0, 0, "LOW");
loadRecord(1, 0, "LOW");
loadRecord(0, 1, "MEDIUM");
loadRecord(1, 1, "MEDIUM");
loadRecord(0, 2, "HIGH");
loadRecord(1, 2, "HIGH");
drvs[0] = xdrv_add(0, &testcb, &data[0]);
drvs[1] = xdrv_add(1, &testcb, &data[1]);
scanIoSetComplete(drvs[0]->scan, &testcomp, &data[0]);
scanIoSetComplete(drvs[1]->scan, &testcomp, &data[1]);
eltc(0);
testIocInitOk();
eltc(1);
testDiag("Scan first list");
scanIoRequest(drvs[0]->scan);
testDiag("Scan second list");
scanIoRequest(drvs[1]->scan);
testDiag("Wait for first list to complete");
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++)
epicsEventMustWait(data[0].wait[i]);
testDiag("Wait one more second");
epicsThreadSleep(1.0);
testOk1(data[0].hasprocd[0]==1);
testOk1(data[0].hasprocd[1]==1);
testOk1(data[0].hasprocd[2]==1);
testOk1(data[0].getcomplete[0]==1);
testOk1(data[0].getcomplete[1]==1);
testOk1(data[0].getcomplete[2]==1);
testOk1(data[1].hasprocd[0]==0);
testOk1(data[1].hasprocd[1]==0);
testOk1(data[1].hasprocd[2]==0);
testOk1(data[1].getcomplete[0]==0);
testOk1(data[1].getcomplete[1]==0);
testOk1(data[1].getcomplete[2]==0);
testDiag("Release the first scan and wait for the second");
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++)
epicsEventMustTrigger(data[0].wake[i]);
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++)
epicsEventMustWait(data[1].wait[i]);
testOk1(data[0].hasprocd[0]==1);
testOk1(data[0].hasprocd[1]==1);
testOk1(data[0].hasprocd[2]==1);
testOk1(data[0].getcomplete[0]==1);
testOk1(data[0].getcomplete[1]==1);
testOk1(data[0].getcomplete[2]==1);
testOk1(data[1].hasprocd[0]==1);
testOk1(data[1].hasprocd[1]==1);
testOk1(data[1].hasprocd[2]==1);
testOk1(data[1].getcomplete[0]==1);
testOk1(data[1].getcomplete[1]==1);
testOk1(data[1].getcomplete[2]==1);
testDiag("Release the second scan and complete");
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++)
epicsEventMustTrigger(data[1].wake[i]);
testIocShutdownOk();
epicsThreadSleep(0.1);
for (i = 0; i < noOfGroups; i++) {
epicsMessageQueueDestroy(mq[i]); mq[i] = NULL;
epicsEventDestroy(barrier[i]); barrier[i] = NULL;
ellFree(&pvtList[i]);
}
free(mq);
free(barrier);
free(pvtList);
free(cbCounter);
testdbCleanup();
xdrv_reset();
for(i=0; i<2; i++) {
int p;
for(p=0; p<NUM_CALLBACK_PRIORITIES; p++) {
epicsEventDestroy(data[i].wake[p]);
epicsEventDestroy(data[i].wait[p]);
}
}
}
static void checkProcessed(void *user, IOSCANPVT ioscan, int prio) {
struct pvtY *pvt;
int group = -1;
typedef struct {
int hasprocd;
int getcomplete;
epicsEventId wait;
epicsEventId wake;
} testmulti;
static void testcbmulti(xpriv *priv, void *raw)
{
testmulti *td = raw;
td += priv->member;
testOk1(td->hasprocd==0);
td->hasprocd = 1;
epicsEventMustTrigger(td->wait);
epicsEventMustWait(td->wake);
td->getcomplete = 1;
}
static void testcompmulti(void *raw, IOSCANPVT scan, int prio)
{
int *mask = raw;
testOk(((*mask)&(1<<prio))==0, "(0x%x)&(0x%x)==0", *mask, 1<<prio);
*mask |= 1<<prio;
}
static void testMultiThreading(void)
{
int i;
int masks[2];
testmulti data[6];
xdrv *drvs[2];
for (i = 0; i < noOfGroups; i++) {
if (ioscanpvt[groupTable[i].ioscan] == ioscan
&& groupTable[i].prio == prio) {
group = i;
break;
}
memset(masks, 0, sizeof(masks));
memset(data, 0, sizeof(data));
for(i=0; i<NELEMENTS(data); i++) {
data[i].wake = epicsEventMustCreate(epicsEventEmpty);
data[i].wait = epicsEventMustCreate(epicsEventEmpty);
}
if (group == -1)
testAbort("invalid ioscanpvt in scanio callback");
cbCounter[group]++;
for (pvt = (struct pvtY *)ellFirst(&pvtList[group]);
pvt;
pvt = (struct pvtY *)ellNext(&pvt->node)) {
if (pvt->callback == 1) {
testDiag("callback for rec %s arrived twice\n", pvt->prec->name);
noDoubleCallback = 0;
}
if (pvt->processed == 0) {
testDiag("rec %s was not processed\n", pvt->prec->name);
recsProcessed = 0;
}
pvt->callback = 1;
testDiag("Test multi-threaded I/O Intr scanning");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
/* create two scan lists with one record on each of three priorities */
/* group#, member#, priority */
loadRecord(0, 0, "LOW");
loadRecord(1, 1, "LOW");
loadRecord(0, 2, "MEDIUM");
loadRecord(1, 3, "MEDIUM");
loadRecord(0, 4, "HIGH");
loadRecord(1, 5, "HIGH");
drvs[0] = xdrv_add(0, &testcbmulti, data);
drvs[1] = xdrv_add(1, &testcbmulti, data);
scanIoSetComplete(drvs[0]->scan, &testcompmulti, &masks[0]);
scanIoSetComplete(drvs[1]->scan, &testcompmulti, &masks[1]);
/* just enough workers to process all records concurrently */
callbackParallelThreads(2, "LOW");
callbackParallelThreads(2, "MEDIUM");
callbackParallelThreads(2, "HIGH");
eltc(0);
testIocInitOk();
eltc(1);
testDiag("Scan first list");
testOk1(scanIoRequest(drvs[0]->scan)==0x7);
testDiag("Scan second list");
testOk1(scanIoRequest(drvs[1]->scan)==0x7);
testDiag("Wait for everything to start");
for(i=0; i<NELEMENTS(data); i++)
epicsEventMustWait(data[i].wait);
testDiag("Wait one more second");
epicsThreadSleep(1.0);
for(i=0; i<NELEMENTS(data); i++) {
testOk(data[i].hasprocd==1, "data[%d].hasprocd==1 (%d)", i, data[i].hasprocd);
testOk(data[i].getcomplete==0, "data[%d].getcomplete==0 (%d)", i, data[i].getcomplete);
}
testDiag("Release all and complete");
for(i=0; i<NELEMENTS(data); i++)
epicsEventMustTrigger(data[i].wake);
testIocShutdownOk();
for(i=0; i<NELEMENTS(data); i++) {
testOk(data[i].getcomplete==1, "data[%d].getcomplete==0 (%d)", i, data[i].getcomplete);
}
testOk1(masks[0]==0x7);
testOk1(masks[1]==0x7);
testdbCleanup();
xdrv_reset();
for(i=0; i<NELEMENTS(data); i++) {
epicsEventDestroy(data[i].wake);
epicsEventDestroy(data[i].wait);
}
}
/*************************************************************************\
* scanIoTest: Test I/O Intr scanning
* including parallel callback threads and scanio callbacks
\*************************************************************************/
MAIN(scanIoTest)
{
int i, j;
int loop;
int max_one, max_one_all;
int parallel, parallel_all;
int result;
int cbCountOk;
long waiting;
struct pvtY *pvt;
testPlan(10);
if (noOfGroups < NO_OF_THREADS)
testAbort("ERROR: This test requires number of ioscan sources >= number of parallel threads");
/**************************************\
* Single callback thread
\**************************************/
testNo = 0;
startMockIoc();
testDiag("Testing single callback thread");
testDiag(" using %d ioscan sources, %d records for each, and %d loops",
noOfGroups, NO_OF_MEMBERS, ONE_THREAD_LOOPS);
for (j = 0; j < ONE_THREAD_LOOPS; j++) {
for (i = 0; i < noOfIoscans; i++) {
scanIoRequest(ioscanpvt[i]);
}
}
epicsThreadSleep(1.0);
testOk((orderFail==0), "No out-of-order processing");
result = 1;
for (i = 0; i < noOfGroups; i++) {
for (pvt = (struct pvtY *)ellFirst(&pvtList[i]);
pvt;
pvt = (struct pvtY *)ellNext(&pvt->node)) {
if (pvt->count != ONE_THREAD_LOOPS) result = 0;
}
}
testOk(result, "All per-record process counters match number of loops");
stopMockIoc();
/**************************************\
* Multiple parallel callback threads
\**************************************/
testNo = 1;
startMockIoc();
testDiag("Testing multiple parallel callback threads");
testDiag(" using %d ioscan sources, %d records for each, %d loops, and %d parallel threads",
noOfIoscans, NO_OF_MEMBERS, PAR_THREAD_LOOPS, NO_OF_THREADS);
for (j = 0; j < PAR_THREAD_LOOPS; j++) {
for (i = 0; i < noOfIoscans; i++) {
scanIoRequest(ioscanpvt[i]);
}
}
/* With parallel cb threads, order and distribution to threads are not guaranteed.
* We have stop barrier events for each request (in the first record).
* Test schedule:
* - After the requests have been put in the queue, NO_OF_THREADS threads should have taken
* one request each.
* - Each barrier event is given PAR_THREAD_LOOPS times.
* - Whenever things stop, there should be four threads waiting, one request each.
* - After all loops, each record should have processed PAR_THREAD_LOOPS times.
*/
max_one_all = 1;
parallel_all = 1;
for (loop = 0; loop < (PAR_THREAD_LOOPS * noOfGroups) / NO_OF_THREADS + 1; loop++) {
max_one = 1;
parallel = 0;
waiting = 0;
j = 0;
do {
epicsThreadSleep(0.001);
j++;
for (i = 0; i < noOfGroups; i++) {
int l = epicsMessageQueuePending(mq[i]);
while (epicsMessageQueueTryReceive(mq[i], NULL, 0) != -1);
if (l == 1) {
waiting |= 1 << i;
} else if (l > 1) {
max_one = 0;
}
}
parallel = count_bits(waiting);
} while (j < 5 && parallel < NO_OF_THREADS);
if (!max_one) max_one_all = 0;
if (loop < (PAR_THREAD_LOOPS * noOfGroups) / NO_OF_THREADS) {
if (!(parallel == NO_OF_THREADS)) parallel_all = 0;
} else {
/* In the last run of the loop only the remaining requests are processed */
if (!(parallel == PAR_THREAD_LOOPS * noOfGroups % NO_OF_THREADS)) parallel_all = 0;
}
for (i = 0; i < noOfGroups; i++) {
if (waiting & (1 << i)) {
epicsEventTrigger(barrier[i]);
}
}
}
testOk(max_one_all, "No thread took more than one request per loop");
testOk(parallel_all, "Correct number of requests were being processed in parallel in each loop");
epicsThreadSleep(0.1);
result = 1;
for (i = 0; i < noOfGroups; i++) {
for (pvt = (struct pvtY *)ellFirst(&pvtList[i]);
pvt;
pvt = (struct pvtY *)ellNext(&pvt->node)) {
if (pvt->count != PAR_THREAD_LOOPS) {
testDiag("Process counter for record %s (%d) does not match loop count (%d)",
pvt->prec->name, pvt->count, PAR_THREAD_LOOPS);
result = 0;
}
}
}
testOk(result, "All per-record process counters match number of loops");
stopMockIoc();
/**************************************\
* Scanio callback mechanism
\**************************************/
testNo = 2;
startMockIoc();
for (i = 0; i < noOfIoscans; i++) {
scanIoSetComplete(ioscanpvt[i], checkProcessed, NULL);
}
testDiag("Testing scanio callback mechanism");
testDiag(" using %d ioscan sources, %d records for each, %d loops, and 1 LOW / %d MEDIUM / %d HIGH parallel threads",
noOfIoscans, NO_OF_MEMBERS, CB_THREAD_LOOPS, NO_OF_MID_THREADS, NO_OF_THREADS);
result = 1;
for (j = 0; j < CB_THREAD_LOOPS; j++) {
for (i = 0; i < noOfIoscans; i++) {
int prio_used;
prio_used = scanIoRequest(ioscanpvt[i]);
if (i+1 != prio_used)
result = 0;
}
}
testOk(result, "All requests return the correct priority callback mask (all 7 permutations covered)");
/* Test schedule:
* After the requests have been put in the queue, it is checked
* - that each callback arrives exactly once,
* - after all records in the group have been processed.
*/
/* loop count times 4 since (worst case) one loop triggers 4 groups for the single LOW thread */
for (loop = 0; loop < CB_THREAD_LOOPS * 4; loop++) {
max_one = 1;
parallel = 0;
waiting = 0;
j = 0;
do {
epicsThreadSleep(0.001);
j++;
for (i = 0; i < noOfGroups; i++) {
int l = epicsMessageQueuePending(mq[i]);
while (epicsMessageQueueTryReceive(mq[i], NULL, 0) != -1);
if (l == 1) {
waiting |= 1 << i;
} else if (l > 1) {
max_one = 0;
}
}
parallel = count_bits(waiting);
} while (j < 5);
\
for (i = 0; i < noOfGroups; i++) {
if (waiting & (1 << i)) {
for (pvt = (struct pvtY *)ellFirst(&pvtList[i]);
pvt;
pvt = (struct pvtY *)ellNext(&pvt->node)) {
pvt->processed = 0;
pvt->callback = 0;
/* record processing will set this at the end of process() */
}
epicsEventTrigger(barrier[i]);
}
}
}
epicsThreadSleep(0.1);
testOk(recsProcessed, "Each callback occured after all records in the group were processed");
testOk(noDoubleCallback, "No double callbacks occured in any loop");
result = 1;
cbCountOk = 1;
for (i = 0; i < noOfGroups; i++) {
if (cbCounter[i] != CB_THREAD_LOOPS) {
testDiag("Callback counter for group %d (%d) does not match loop count (%d)",
i, cbCounter[i], CB_THREAD_LOOPS);
cbCountOk = 0;
}
for (pvt = (struct pvtY *)ellFirst(&pvtList[i]);
pvt;
pvt = (struct pvtY *)ellNext(&pvt->node)) {
if (pvt->count != CB_THREAD_LOOPS) {
testDiag("Process counter for record %s (%d) does not match loop count (%d)",
pvt->prec->name, pvt->count, CB_THREAD_LOOPS);
result = 0;
}
}
}
testOk(result, "All per-record process counters match number of loops");
testOk(cbCountOk, "All per-group callback counters match number of loops");
stopMockIoc();
testPlan(152);
testSingleThreading();
testDiag("run a second time to verify shutdown and restart works");
testSingleThreading();
testMultiThreading();
testDiag("run a second time to verify shutdown and restart works");
testMultiThreading();
return testDone();
}

View File

@@ -1,4 +1,6 @@
record(y, g$(GROUP)m$(MEMBER)) {
record(x, "g$(GROUP)m$(MEMBER)") {
field(DTYP, "Scan I/O")
field(INP , "@$(GROUP) $(MEMBER)")
field(SCAN, "I/O Intr")
field(PRIO, "$(PRIO)")
}

View File

@@ -16,22 +16,47 @@
#include "dbAccessDefs.h"
#include "recSup.h"
#include "recGbl.h"
#include "devSup.h"
#include "dbScan.h"
#define GEN_SIZE_OFFSET
#include "xRecord.h"
#include <epicsExport.h>
#include "devx.h"
static long init_record(xRecord *prec, int pass)
{
long ret = 0;
xdset *xset = (xdset*)prec->dset;
if(!pass) return 0;
if(!xset) {
recGblRecordError(S_dev_noDSET, prec, "x: init_record");
return S_dev_noDSET;
}
if(xset->init_record)
ret = (*xset->init_record)(prec);
return ret;
}
static long process(xRecord *prec)
{
long ret = 0;
xdset *xset = (xdset*)prec->dset;
if(prec->clbk)
(*prec->clbk)(prec);
prec->pact = TRUE;
if(xset && xset->process)
ret = (*xset->process)(prec);
recGblGetTimeStamp(prec);
recGblFwdLink(prec);
prec->pact = FALSE;
return 0;
return ret;
}
static rset xRSET = {
RSETNUMBER, NULL, NULL, NULL, process
RSETNUMBER, NULL, NULL, init_record, process
};
epicsExportAddress(rset,xRSET);

View File

@@ -11,4 +11,9 @@ recordtype(x) {
field(INP, DBF_INLINK) {
prompt("Input Link")
}
field(CLBK, DBF_NOACCESS) {
prompt("Processing callback")
special(SPC_NOMOD)
extra("void (*clbk)(struct xRecord*)")
}
}

View File

@@ -1,10 +0,0 @@
# This is a minimal I/O scanned record
recordtype(y) {
include "dbCommon.dbd"
field(VAL, DBF_LONG) {
prompt("Value")
}
}
device(y,CONSTANT,devY,"ScanIO Test")

View File

@@ -76,8 +76,9 @@ typedef struct dbFldDes{ /* field description */
short indRecordType; /*within dbRecordType.papFldDes */
short special; /*Special processing requirements */
dbfType field_type; /*Field type as defined in dbFldTypes.h */
unsigned int process_passive:1;/*should dbPutField process passive */
unsigned int prop:1;/*field is a metadata, post DBE_PROPERTY on change*/
unsigned int process_passive:1;/*should dbPutField process passive */
unsigned int prop:1;/*field is a metadata, post DBE_PROPERTY on change*/
unsigned int isDevLink:1; /* true for INP/OUT fields */
ctType base; /*base for integer to string conversions*/
short promptgroup; /*prompt, i.e. gui group */
short interest; /*interest level */

View File

@@ -49,6 +49,9 @@ epicsExportAddress(int,dbRecordsOnceOnly);
epicsShareDef int dbBptNotMonotonic=0;
epicsExportAddress(int,dbBptNotMonotonic);
epicsShareDef int dbQuietMacroWarnings=0;
epicsExportAddress(int,dbQuietMacroWarnings);
/*private routines */
static void yyerrorAbort(char *str);
static void allocTemp(void *pvoid);
@@ -201,6 +204,10 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
char *penv;
char **macPairs;
if(ellCount(&tempList)) {
epicsPrintf("dbReadCOM: Parser stack dirty %d\n", ellCount(&tempList));
}
if(*ppdbbase == 0) *ppdbbase = dbAllocBase();
pdbbase = *ppdbbase;
if(path && strlen(path)>0) {
@@ -230,6 +237,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
free((void *)macPairs);
mac_input_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char));
}
macSuppressWarning(macHandle,dbQuietMacroWarnings);
}
pinputFile = dbCalloc(1,sizeof(inputFile));
if(filename) {
@@ -258,9 +266,10 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
ellAdd(&inputFileList,&pinputFile->node);
status = pvt_yy_parse();
if (yyAbort)
while (ellCount(&tempList))
popFirstTemp();
if (ellCount(&tempList) && !yyAbort)
epicsPrintf("dbReadCOM: Parser stack dirty w/o error. %d\n", ellCount(&tempList));
while (ellCount(&tempList))
popFirstTemp(); /* Memory leak on parser failure */
dbFreePath(pdbbase);
if(!status) { /*add RTYP and VERS as an attribute */
@@ -320,10 +329,9 @@ static int db_yyinput(char *buf, int max_size)
if(fgetsRtn) {
int exp = macExpandString(macHandle,mac_input_buffer,
my_buffer,MY_BUFFER_SIZE);
if(exp < 0) {
errPrintf(0,__FILE__, __LINE__,
"macExpandString failed for file %s",
pinputFileNow->filename);
if (exp < 0) {
fprintf(stderr, "Warning: '%s' line %d has undefined macros\n",
pinputFileNow->filename, pinputFileNow->line_num+1);
}
}
} else {
@@ -487,6 +495,8 @@ static void dbRecordtypeFieldHead(char *name,char *type)
allocTemp(pdbFldDes);
pdbFldDes->name = epicsStrDup(name);
pdbFldDes->as_level = ASL1;
pdbFldDes->isDevLink = strcmp(pdbFldDes->name, "INP")==0 ||
strcmp(pdbFldDes->name, "OUT")==0;
i = dbFindFieldType(type);
if (i < 0)
yyerrorAbort("Illegal Field Type");

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More