Compare commits
126 Commits
R3.14.11-p
...
R3.14.11-r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c28520bea6 | ||
|
|
bd4784a858 | ||
|
|
8c2278784c | ||
|
|
2caf1a4f50 | ||
|
|
1fba8dd866 | ||
|
|
87eace1bd4 | ||
|
|
db3a655374 | ||
|
|
8ae0c8960f | ||
|
|
ec26c0dc52 | ||
|
|
ea539fceb6 | ||
|
|
782ff1b303 | ||
|
|
2fb6b2100f | ||
|
|
0012042a5e | ||
|
|
e0d16659e1 | ||
|
|
8303cf053b | ||
|
|
78fc566dc4 | ||
|
|
3961c81740 | ||
|
|
0fdda3f794 | ||
|
|
9802e6c629 | ||
|
|
547c5d06ea | ||
|
|
50ddd62502 | ||
|
|
9fc48c9a6b | ||
|
|
e4075da4d7 | ||
|
|
8ace886cfe | ||
|
|
6c61c0de34 | ||
|
|
e581e88223 | ||
|
|
f4cbdec5ee | ||
|
|
b344841365 | ||
|
|
b867dabad0 | ||
|
|
0cea525682 | ||
|
|
2df7da052a | ||
|
|
59b820d2f5 | ||
|
|
3fda8dc2b0 | ||
|
|
3a335c88f0 | ||
|
|
44a6e9a005 | ||
|
|
1f129d3739 | ||
|
|
720236ed39 | ||
|
|
57c9f9344f | ||
|
|
64cb41f489 | ||
|
|
6fe047731f | ||
|
|
56c4c92588 | ||
|
|
562db1e24c | ||
|
|
1b70afa54b | ||
|
|
f316b4ca81 | ||
|
|
6d8cfeef01 | ||
|
|
b1b72e8cf0 | ||
|
|
cde59e262b | ||
|
|
40b6d1b0ba | ||
|
|
4464cfedaa | ||
|
|
b9fbf2e45b | ||
|
|
42ce1bd2db | ||
|
|
7de356519d | ||
|
|
ff5ca5e041 | ||
|
|
d1bb71809b | ||
|
|
7a49a17d1b | ||
|
|
7cf77b40e6 | ||
|
|
e387c06f59 | ||
|
|
c035566d7d | ||
|
|
57e5406684 | ||
|
|
b4948b4ff6 | ||
|
|
15f6b8c682 | ||
|
|
4868904839 | ||
|
|
1a2fa1bc86 | ||
|
|
e3a61ce4e4 | ||
|
|
efdee3c31a | ||
|
|
ee44663d89 | ||
|
|
90db5a4ab0 | ||
|
|
72dbaa8a0f | ||
|
|
458689a252 | ||
|
|
0daf347ef3 | ||
|
|
e32d8d77dc | ||
|
|
ffe7823c22 | ||
|
|
913f724ebf | ||
|
|
4e5fa9b6b3 | ||
|
|
a6b0ffebca | ||
|
|
7246366222 | ||
|
|
191668023b | ||
|
|
7a23b74a76 | ||
|
|
511d818d18 | ||
|
|
f8565139c2 | ||
|
|
30a02b365f | ||
|
|
b4bc931c72 | ||
|
|
e5d3815280 | ||
|
|
ebd65e6e34 | ||
|
|
8c45eb4a19 | ||
|
|
e1bb171f44 | ||
|
|
656c2462d3 | ||
|
|
f4ec20c8f0 | ||
|
|
72e1dba496 | ||
|
|
001b947702 | ||
|
|
c0d4317ade | ||
|
|
290ec3e22c | ||
|
|
d6b887b363 | ||
|
|
fbebea304b | ||
|
|
dd1d2c10bd | ||
|
|
d3e3137265 | ||
|
|
d764e7d4df | ||
|
|
49ddec4294 | ||
|
|
ce778819bd | ||
|
|
d5bffdb13d | ||
|
|
ba11940aad | ||
|
|
8725e4a67a | ||
|
|
a6e57ba17a | ||
|
|
7d137254af | ||
|
|
31fb3775fd | ||
|
|
1ba658b452 | ||
|
|
6eb25148c5 | ||
|
|
4ade695a60 | ||
|
|
bea22985b6 | ||
|
|
fd6d1ce69c | ||
|
|
844ed6345a | ||
|
|
7315f02888 | ||
|
|
1ba6da438b | ||
|
|
c986597f1c | ||
|
|
38b81b44be | ||
|
|
8382eee11a | ||
|
|
7d80ab72b0 | ||
|
|
3d86367330 | ||
|
|
16a6357ab4 | ||
|
|
f4bfc3928d | ||
|
|
4282d3e9f4 | ||
|
|
1e68d1f89b | ||
|
|
03b6345fe0 | ||
|
|
055bb953b9 | ||
|
|
01d223fafd | ||
|
|
7665d1340c |
@@ -80,6 +80,10 @@ INSTALL_JAVA = $(INSTALL_LOCATION)/javalib
|
||||
#Directory for OS independant build created files
|
||||
COMMON_DIR = ../O.Common
|
||||
|
||||
#-------------------------------------------------------
|
||||
# Make echo output - suppress echoing if make's '-s' flag is set
|
||||
ECHO := $(if $(findstring s,$(MAKEFLAGS)),\#,@echo)
|
||||
|
||||
#-------------------------------------------------------
|
||||
ifdef T_A
|
||||
|
||||
@@ -123,10 +127,6 @@ CMPLR_PREFIX=
|
||||
LIB_PREFIX=
|
||||
SHRLIB_PREFIX= $(LIB_PREFIX)
|
||||
|
||||
#-------------------------------------------------------
|
||||
# Make echo output - suppress echoing if make's '-s' flag is set
|
||||
ECHO := $(if $(findstring s,$(MAKEFLAGS)),\#,@echo)
|
||||
|
||||
#--------------------------------------------------
|
||||
# vpath directories
|
||||
POSIX_YES = os/posix
|
||||
|
||||
@@ -106,6 +106,7 @@ MAKEDBDEPENDS = $(PERL) $(TOOLS)/makeDbDepends.pl
|
||||
|
||||
ifndef T_A
|
||||
|
||||
ECHO := $(if $(findstring s,$(MAKEFLAGS)),\#,@echo)
|
||||
COMMON_DIR = .
|
||||
INSTALL_DBDS =
|
||||
INSTALL_DBS =
|
||||
@@ -192,28 +193,28 @@ $(INSTALL_DB)/%.template: %.template
|
||||
$(COMMON_DIR)/%Record.h: $(COMMON_DIR)/%Record.dbd
|
||||
@$(RM) $@$(DEP)
|
||||
@$(DBDDEPENDS_CMD)
|
||||
$(ECHO) "$<:../Makefile" >> $@$(DEP)
|
||||
@echo "$<:../Makefile" >> $@$(DEP)
|
||||
@$(RM) $@
|
||||
$(DBTORECORDTYPEH) $(DBDFLAGS) $< $@
|
||||
|
||||
$(COMMON_DIR)/%Record.h: %Record.dbd
|
||||
@$(RM) $@$(DEP)
|
||||
@$(DBDDEPENDS_CMD)
|
||||
$(ECHO) "$<:../Makefile" >> $@$(DEP)
|
||||
@echo "$<:../Makefile" >> $@$(DEP)
|
||||
@$(RM) $@
|
||||
$(DBTORECORDTYPEH) $(DBDFLAGS) $< $@
|
||||
|
||||
$(COMMON_DIR)/menu%.h: $(COMMON_DIR)/menu%.dbd
|
||||
@$(RM) $@$(DEP)
|
||||
@$(DBDDEPENDS_CMD)
|
||||
$(ECHO) "$<:../Makefile" >> $@$(DEP)
|
||||
@echo "$<:../Makefile" >> $@$(DEP)
|
||||
@$(RM) $@
|
||||
$(DBTOMENUH) $(DBDFLAGS) $< $@
|
||||
|
||||
$(COMMON_DIR)/menu%.h: menu%.dbd
|
||||
@$(RM) $@$(DEP)
|
||||
@$(DBDDEPENDS_CMD)
|
||||
$(ECHO) "$<:../Makefile" >> $@$(DEP)
|
||||
@echo "$<:../Makefile" >> $@$(DEP)
|
||||
@$(RM) $@
|
||||
$(DBTOMENUH) $(DBDFLAGS) $< $@
|
||||
|
||||
@@ -228,7 +229,7 @@ $(COMMON_DIR)/bpt%.dbd: bpt%.data
|
||||
$(COMMON_DIR)/%.dbd: $(COMMON_DIR)/%Include.dbd
|
||||
@$(RM) $@$(DEP)
|
||||
@$(DBDDEPENDS_CMD)
|
||||
$(ECHO) "$<:../Makefile" >> $@$(DEP)
|
||||
@echo "$<:../Makefile" >> $@$(DEP)
|
||||
$(ECHO) "Expanding dbd"
|
||||
@$(RM) $@
|
||||
$(DBEXPAND) $(DBDFLAGS) -o $@ $<
|
||||
@@ -279,7 +280,7 @@ $(COMMON_DIR)/%.db$(RAW): $(COMMON_DIR)/%.edf
|
||||
$(COMMON_DIR)/%.db$(RAW): %.substitutions
|
||||
@$(RM) $@$(DEP)
|
||||
$(MAKEDBDEPENDS) $@ $< $(TEMPLATE_FILENAME) >> $@$(DEP)
|
||||
$(ECHO) "$@:$(TEMPLATE_FILENAME)" >> $@$(DEP)
|
||||
@echo "$@:$(TEMPLATE_FILENAME)" >> $@$(DEP)
|
||||
$(ECHO) "Inflating database from $< $(TEMPLATE_FILENAME)"
|
||||
@$(RM) $@
|
||||
$(MSI) $(DBFLAGS) -S$< $(TEMPLATE_FILENAME) > msi.tmp
|
||||
|
||||
@@ -31,7 +31,7 @@ endif
|
||||
ifeq ($(wildcard $(INSTALL_LOCATION_LIB)/*),)
|
||||
@$(RMDIR) $(INSTALL_LOCATION_LIB)
|
||||
endif
|
||||
$(ECHO)
|
||||
@echo
|
||||
# The echo above stops a "nothing to be done for cleandirs" message
|
||||
|
||||
distclean: realclean realuninstall
|
||||
@@ -52,32 +52,32 @@ uninstallDirs:
|
||||
@$(RMDIR) $(UNINSTALL_DIRS)
|
||||
|
||||
help:
|
||||
$(ECHO) "Usage: gnumake [options] [target] ..."
|
||||
$(ECHO) "Targets supported by all Makefiles:"
|
||||
$(ECHO) " install - Builds and installs all targets (default rule)"
|
||||
$(ECHO) " all - Same as install"
|
||||
$(ECHO) " buildInstall - Same as install"
|
||||
$(ECHO) " clean - Removes the O.<arch> dirs created by running make"
|
||||
$(ECHO) " In O.<arch> dir, clean removes build created files"
|
||||
$(ECHO) " realclean - Removes ALL O.<arch> dirs"
|
||||
$(ECHO) " Cannot be used within an O.<arch> dir"
|
||||
$(ECHO) " rebuild - Same as clean install"
|
||||
$(ECHO) " inc - Installs header files"
|
||||
$(ECHO) " build - Builds all targets"
|
||||
$(ECHO) " archclean - Removes O.<arch> dirs but not O.Common dir"
|
||||
$(ECHO) "\"Partial\" build targets supported by Makefiles:"
|
||||
$(ECHO) " inc.<arch> - Installs <arch> only header files."
|
||||
$(ECHO) " install.<arch> - Builds and installs <arch> only."
|
||||
$(ECHO) " clean.<arch> - Cleans <arch> binaries in O.<arch> dirs only."
|
||||
$(ECHO) " build.<arch> - Builds <arch> only."
|
||||
$(ECHO) "Targets supported by top level Makefile:"
|
||||
$(ECHO) " uninstall - Cleans directories created by the install."
|
||||
$(ECHO) " realuninstall - Removes ALL install dirs"
|
||||
$(ECHO) " distclean - Same as realclean realuninstall."
|
||||
$(ECHO) " cvsclean - Removes cvs .#* files in all dirs of directory tree"
|
||||
$(ECHO) " help - Prints this list of valid make targets "
|
||||
$(ECHO) "Indiv. object targets are supported by O.<arch> level Makefile .e.g"
|
||||
$(ECHO) " xxxRecord.o"
|
||||
@echo "Usage: gnumake [options] [target] ..."
|
||||
@echo "Targets supported by all Makefiles:"
|
||||
@echo " install - Builds and installs all targets (default rule)"
|
||||
@echo " all - Same as install"
|
||||
@echo " buildInstall - Same as install"
|
||||
@echo " clean - Removes the O.<arch> dirs created by running make"
|
||||
@echo " In O.<arch> dir, clean removes build created files"
|
||||
@echo " realclean - Removes ALL O.<arch> dirs"
|
||||
@echo " Cannot be used within an O.<arch> dir"
|
||||
@echo " rebuild - Same as clean install"
|
||||
@echo " inc - Installs header files"
|
||||
@echo " build - Builds all targets"
|
||||
@echo " archclean - Removes O.<arch> dirs but not O.Common dir"
|
||||
@echo "\"Partial\" build targets supported by Makefiles:"
|
||||
@echo " inc.<arch> - Installs <arch> only header files."
|
||||
@echo " install.<arch> - Builds and installs <arch> only."
|
||||
@echo " clean.<arch> - Cleans <arch> binaries in O.<arch> dirs only."
|
||||
@echo " build.<arch> - Builds <arch> only."
|
||||
@echo "Targets supported by top level Makefile:"
|
||||
@echo " uninstall - Cleans directories created by the install."
|
||||
@echo " realuninstall - Removes ALL install dirs"
|
||||
@echo " distclean - Same as realclean realuninstall."
|
||||
@echo " cvsclean - Removes cvs .#* files in all dirs of directory tree"
|
||||
@echo " help - Prints this list of valid make targets "
|
||||
@echo "Indiv. object targets are supported by O.<arch> level Makefile .e.g"
|
||||
@echo " xxxRecord.o"
|
||||
|
||||
.PHONY : $(uninstallArchTargets)
|
||||
.PHONY : uninstall help cleandirs distclean uninstallDirs realuninstall
|
||||
|
||||
@@ -28,7 +28,7 @@ LOADABLE_SHRLIB_PREFIX = lib
|
||||
# <lib> -> lib<lib>.a
|
||||
LIBNAME = $(BUILD_LIBRARY:%=$(LIB_PREFIX)%$(LIB_SUFFIX))
|
||||
# <lib> -> lib<lib>.so.<version>
|
||||
SHRLIBNAME_YES = $(BUILD_LIBRARY:%=$(LIB_PREFIX)%$(SHRLIB_SUFFIX))
|
||||
SHRLIBNAME_YES = $(BUILD_LIBRARY:%=$(SHRLIB_PREFIX)%$(SHRLIB_SUFFIX))
|
||||
LOADABLE_SHRLIBNAME = $(LOADABLE_BUILD_LIBRARY:%=$(LOADABLE_SHRLIB_PREFIX)%$(LOADABLE_SHRLIB_SUFFIX))
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
@@ -13,7 +13,6 @@ ARCH_CLASS = arm
|
||||
|
||||
# Set a special definition for network order of Netwinder ARM floating point
|
||||
ARCH_DEP_CPPFLAGS += -D_ARM_NWFP_
|
||||
ARCH_DEP_CPPFLAGS += -mcpu=arm9 -marm
|
||||
|
||||
ifeq ($(BUILD_CLASS),CROSS)
|
||||
VALID_BUILDS = Ioc
|
||||
|
||||
@@ -4,34 +4,21 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
<title>Known Problems R3.14.10-RC1</title>
|
||||
<title>Known Problems in R3.14.11</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 style="text-align: center">EPICS Base R3.14.10: Known Problems</h1>
|
||||
<h1 style="text-align: center">EPICS Base R3.14.11: Known Problems</h1>
|
||||
|
||||
<ul>
|
||||
<li>Parallel make (<tt>make -j</tt>) does not work on cygwin-x86 targets,
|
||||
probably due to a missing dependency in the EPICS build rules.</li>
|
||||
|
||||
<li>Some older Perl versions do not properly install the xsubpp program. This
|
||||
will prevent the build in src/cap5 from completing — the build will
|
||||
finish with an error like this:
|
||||
|
||||
<blockquote><pre>/bin/sh: /bin/xsubpp: not found
|
||||
make[3]: *** [Cap5.c] Error 1
|
||||
make[3]: Leaving directory `/home/phoebus3/ANJ/epics/base/3-14-dev/src/cap5/O.solaris-x86'
|
||||
make[2]: *** [install.solaris-x86] Error 2
|
||||
make[2]: Leaving directory `/home/phoebus3/ANJ/epics/base/3-14-dev/src/cap5'
|
||||
make[1]: *** [cap5.install] Error 2
|
||||
make[1]: Leaving directory `/home/phoebus3/ANJ/epics/base/3-14-dev/src'
|
||||
make: *** [src.install] Error 2</pre></blockquote>
|
||||
|
||||
As long as you don't intend to use the Perl5 CA interface this error is
|
||||
harmless, as cap5 is the last directory to be compiled in Base. If you need
|
||||
the Perl5 CA inteface, fix your Perl installation so that the perl binary
|
||||
and the xsubpp program (or soft links to them) are both found in the same
|
||||
directory.</li>
|
||||
<li>The libCom test suite program epicsCalcTest test #66 fails on some linux
|
||||
systems. This is not a bug in EPICS and will not be fixed; it actually
|
||||
demonstrates that the GCC optimizer is not generating the same result for
|
||||
the expression <tt>isinf(-Inf)</tt> that the glibc function returns at
|
||||
runtime. Both versions return a non-zero result for infinite arguments, but
|
||||
the GCC optimized version applies the sign of its infinite argument to its
|
||||
return value, whereas glibc always returns +1.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -169,10 +169,10 @@ Software requirements
|
||||
|
||||
Host system storage requirements
|
||||
|
||||
The GNU zipped tar file is approximately 1.4 MB in size. The unzipped
|
||||
untarred distribution source tree is approximately 7.3 MB. The build created
|
||||
files for each host take approximately 40 MB and the build created files for
|
||||
each target take approximately 10 MB.
|
||||
The GNU zipped tar file is approximately 1.5 MB in size. The unzipped
|
||||
untarred distribution source tree is approximately 7.4 MB. The build created
|
||||
files for each host take approximately 37 MB and the build created files for
|
||||
each cross target take approximately 15 MB.
|
||||
|
||||
Documentation
|
||||
|
||||
|
||||
@@ -177,10 +177,10 @@ RTEMS-uC5282
|
||||
are used.</P>
|
||||
</BLOCKQUOTE>
|
||||
<H3><A NAME="0_0_7"> Host system storage requirements</A></H3>
|
||||
<BLOCKQUOTE>The GNU zipped tar file is approximately 1.4 MB in size. The
|
||||
unzipped untarred distribution source tree is approximately 7.3 MB. The
|
||||
build created files for each host take approximately 40 MB and the
|
||||
build created files for each target take approximately 10 MB.</BLOCKQUOTE>
|
||||
<BLOCKQUOTE>The GNU zipped tar file is approximately 1.5 MB in size. The
|
||||
unzipped untarred distribution source tree is approximately 7.4 MB. The
|
||||
build created files for each host take approximately 37 MB and the
|
||||
build created files for each cross target take approximately 15 MB.</BLOCKQUOTE>
|
||||
<H3><A NAME="0_0_8"> Documentation</A></H3>
|
||||
<BLOCKQUOTE>EPICS documentation is available on the WWW via the EPICS
|
||||
home page at APS: URL<A href="http://www.aps.anl.gov/epics">
|
||||
|
||||
@@ -3,16 +3,145 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
|
||||
<title>EPICS Base R3.14.x Release Notes</title>
|
||||
<title>EPICS Base R3.14.11-CVS Release Notes</title>
|
||||
</head>
|
||||
|
||||
<body lang="en">
|
||||
<h1 align="center">EPICS Base Release 3.14.1x</h1>
|
||||
<h1 align="center">EPICS Base Release 3.14.11-CVS</h1>
|
||||
|
||||
<h2 align="center">Changes between 3.14.10 and 3.14.11</h2>
|
||||
<!-- Insert new items below here ... -->
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
|
||||
<h4>Several macros and includes removed from dbDefs.h</h4>
|
||||
<h4>Build system dependency change</h4>
|
||||
|
||||
<p>In order to get GNU make parallel execution (-j option) to work proprely for
|
||||
multiple target architectures, a new dependency had to be added. Now all
|
||||
cross-compiled builds depend on their host build. This means that when a
|
||||
<tt>make <i>crosstargetarch</i></tt> command is issued, the EPICS_HOST_ARCH
|
||||
target build will be executed first, followed by the <i>crosstargetarch</i>
|
||||
build. Builds done in an O.<i>arch</i> directory will still only build the
|
||||
<i>arch</i> targets however.</p>
|
||||
|
||||
<h4>Channel Access changes</h4>
|
||||
|
||||
<p>Mantis 361 fix - ca_add_fd_registration users might not receive select
|
||||
wakeup</p>
|
||||
|
||||
<p>Mantis 359 fix - ca client library flow control mode related issues</p>
|
||||
|
||||
<p>Mantis 357 fix - high throughput multithreaded ca client appl thread could
|
||||
be trapped in lib.</p>
|
||||
|
||||
<ul>
|
||||
<li>Discovered during code review. Not seen in practice, but possible</li>
|
||||
</ul>
|
||||
|
||||
<p>Mantis 285 fix - CA Documentation doesn't distinguish sync groups from
|
||||
ca_put_callback()</p>
|
||||
|
||||
<p>Mantis 346 fix - deleting the chid passed in from within put cb handler
|
||||
causes failure</p>
|
||||
|
||||
<p>Mantis 284 fix - channel clear protocol warning messages from PCAS</p>
|
||||
|
||||
<p>Mantis 237 fix - SEGV from simple CA client during context destroy</p>
|
||||
|
||||
<p>Mantis 242 fix - invalid DBR type not screened in client library when
|
||||
doing a put</p>
|
||||
|
||||
<h4>Portable Channel Access Server changes</h4>
|
||||
|
||||
<p> These changes impact the Gateway (Proxy server) and other servers but not
|
||||
the IOC.</h4>
|
||||
|
||||
<p>Mantis 360 fix - server is unresponsive for one of its clients, when
|
||||
async io postponed and in flow control mode</p>
|
||||
|
||||
<p>Mantis 358 fix - PCAS service snap-in has no way to determine if its a put,
|
||||
or a put calback.</p>
|
||||
|
||||
<p>Mantis 356 fix - medm display sometimes hangs until the motor stops when
|
||||
controling motor through gw.</p>
|
||||
|
||||
<p>Mantis 350 fix - Incoming data corruption under heavy CAS load.</p>
|
||||
|
||||
<p>Mantis 340 fix - leak when performing a read and conversion fails.</p>
|
||||
|
||||
<p>Mantis 348 fix - A call to 'assert (item.pList == this)'
|
||||
failed in ../../../../src/cas/generic/st/ioBlocked.cc line 112</p>
|
||||
|
||||
<p>Mantis 345 fix - Compilation warning: complaint about missing
|
||||
gddDestructor</p>
|
||||
|
||||
<p>Mantis 343 fix - gddScalar::new() operator is not fully thread safe</p>
|
||||
|
||||
<p>Mantis 333 fix - resTable::removeAll() does not reset the item count</p>
|
||||
|
||||
<p>Mantis 335 fix - excas fails in clearOutstandingReads - maybe requires an
|
||||
R3.13 client</p>
|
||||
|
||||
<p>Mantis 329 fix - GW hang, pthread_mutex_lock failed: error Invalid
|
||||
argument message</p>
|
||||
|
||||
<p>Mantis 352 fix - gateway hangs temporarily under heavy load on 8-core
|
||||
64bit RHEL5</p>
|
||||
|
||||
<ul>
|
||||
<li>High throughput performance appears to be much better now for both scalars
|
||||
and large arrays, but more testing needed in operational gateways</li>
|
||||
</ul>
|
||||
|
||||
<h4>Timer Queue Library</h4>
|
||||
|
||||
<p>Mantis 336 fix - timer queue should have try / catch block around call to
|
||||
user's expiration callback</p>
|
||||
|
||||
<p>Mantis 332 fix - epicsTimerTest failure, windows vista 64, dual core
|
||||
SMP system</p>
|
||||
|
||||
<h4>LibCom</h4>
|
||||
|
||||
<p>Mantis 328 fixed - orderly shutdown for soft IOC fails</p>
|
||||
|
||||
<h4>Application configure files</h4>
|
||||
|
||||
<p>The configuration directory files installed by makeBaseApp.pl have been
|
||||
changing in recent releases to make them work more like the files in the Base
|
||||
configuration directory. The CONFIG_APP file has gone, and its functionality is
|
||||
now performed by the CONFIG file which should only be modified in exceptional
|
||||
circumstances. The variables that used to be set in the CONFIG file now appear
|
||||
in the new CONFIG_SITE file, and can be overridden for specific combinations of
|
||||
host and target architectures by creating a file with name matching one of these
|
||||
patterns:</p>
|
||||
|
||||
<ul>
|
||||
<li>CONFIG_SITE.<host-arch>.Common</li>
|
||||
<li>CONFIG_SITE.Common.<target-arch></li>
|
||||
<li>CONFIG_SITE.<host-arch>.<target-arch></li>
|
||||
</ul>
|
||||
|
||||
<p>Note that the setting for <tt>CHECK_RELEASE</tt> in the CONFIG_SITE files is
|
||||
not compatible with previous releases of Base; if you are creating an
|
||||
application that has to work with earlier releases, move the
|
||||
<tt>CHECK_RELEASE</tt> setting back to the configure/Makefile where it used to
|
||||
live.</p>
|
||||
|
||||
<p>The RELEASE file(s) can now define the variable <tt>RULES</tt> if you wish
|
||||
the application to use build rules from some module other than EPICS_BASE. The
|
||||
rules must appear in a configure subdirectory just like they do in Base.</p>
|
||||
|
||||
<h4>Compile-time assertions</h4>
|
||||
|
||||
<p>A new macro has been added to epicsAssert.h which performs assertion checks
|
||||
at compile-time. <tt>STATIC_ASSERT(<i>expr</i>)</tt> can only be used when
|
||||
<tt><i>expr</i></tt> can be evaluated by the compiler, and will cause the
|
||||
compilation to fail if it evaluates to false. The resulting compiler error
|
||||
message might appear a little obscure, but it does provide some explanation and
|
||||
contains the line where the failure was discovered. Future versions of the C++
|
||||
standard will probably contain a similar facility <tt>static_assert(<i>expr</i>,
|
||||
<i>message</i>)</tt> but will require compiler support to be implemented.</p>
|
||||
|
||||
<h4>Several changes made to dbDefs.h</h4>
|
||||
|
||||
<p>The definitions for the macros <tt>YES</tt>, <tt>NO</tt>, <tt>NONE</tt>,
|
||||
<tt>min()</tt> and <tt>max()</tt> have been deleted. <tt>YES</tt> and
|
||||
@@ -21,15 +150,15 @@ from the menuYesNo.h file where they were used in several record types. The
|
||||
other macros were not being used anywhere in Base, sncseq or Asyn.</p>
|
||||
|
||||
<p>The macro <tt>LOCAL</tt> that was a synonym for <tt>static</tt> is now
|
||||
deprecated and will be deleted soon, please adjust your code to use the latter
|
||||
keyword. Any uses of the <tt>READONLY</tt> macro from shareLib.h must now be
|
||||
replaced by the keyword <tt>const</tt>.</p>
|
||||
deprecated and will be deleted in R3.15, please adjust your code to use the
|
||||
latter keyword. All uses of the <tt>READONLY</tt> macro from shareLib.h must
|
||||
now be replaced by the keyword <tt>const</tt> as the macro has been deleted.</p>
|
||||
|
||||
<p>The dbDefs.h file was also unnecessarily including some other libCom header
|
||||
<p>The dbDefs.h file was unnecessarily including various other libCom header
|
||||
files which may have to be manually added to out-of-tree source files that
|
||||
relied on this. The general rule for header files is that a header should only
|
||||
include other headers that are needed for its own inclusion in any source
|
||||
file. The <tt>#include</tt> statements that might need to be added are:</p>
|
||||
include other headers that are needed for its own inclusion in any source file.
|
||||
The <tt>#include</tt> statements that might need to be added are:</p>
|
||||
|
||||
<ul>
|
||||
<li>#include <stdarg.h></li>
|
||||
@@ -39,6 +168,17 @@ file. The <tt>#include</tt> statements that might need to be added are:</p>
|
||||
<li>#include "epicsTypes.h"</li>
|
||||
</ul>
|
||||
|
||||
<p>A new macro <tt>CONTAINER(pointer, type, member)</tt> has been added which
|
||||
calculates and returns a pointer to the parent structure when given a pointer to
|
||||
a member, the structure type and the name of the member in that structure. On
|
||||
GNU compilers the type of the pointer is checked to ensure that it matches the
|
||||
member, so code using this macro should be built using gcc for additional
|
||||
confidence.</p>
|
||||
|
||||
<h4>Long-deprecated errSymFind() function deleted</h4>
|
||||
|
||||
<p>This functionality was replaced by errSymLookup() many releases ago.</p>
|
||||
|
||||
<h4>Perl CA library shutdown</h4>
|
||||
|
||||
<p>The Perl CA library has been modified to properly flush the Channel Access
|
||||
@@ -266,6 +406,16 @@ clients, although by no means all clients support it yet.</p>
|
||||
now uses _NSGetEnviron() to get the pointer to the environment string
|
||||
table.</p>
|
||||
|
||||
<h4>gpHash argument type changed</h4>
|
||||
|
||||
<p>Out-of-tree users of libCom's gpHash routines should change the type of their
|
||||
pointer to their gpHash table to avoid compiler warnings. The first argument to
|
||||
all of the <tt>gph...()</tt> routines used to be a <tt>void *</tt> (or a
|
||||
pointer to one for <tt>gphInit()</tt>) but is now a <tt>struct
|
||||
gphPvt *</tt> (<tt>struct gphPvt **</tt> for <tt>gphInit</tt>) for
|
||||
better type safety. The definition of the structure has not been made public,
|
||||
although a declaration is provided in <tt>gpHash.h</tt>.</p>
|
||||
|
||||
<h4>New hash functions in epicsString.h</h4>
|
||||
|
||||
<p>The existing routines used to hash strings have been replaced by a new
|
||||
@@ -465,7 +615,7 @@ time provider you need to use RTEMS 4.9.1 or newer.</p>
|
||||
<h4>RTEMS epicsEventWaitWithTimeout</h4>
|
||||
|
||||
<p>Correctly return epicsEventWaitTimeout when event is not pending and
|
||||
timeout value is 0.0 secondsmore agressive.</p>
|
||||
timeout value is 0.0 seconds.</p>
|
||||
|
||||
<h4>epicsRingPointer, epicsRingBytes</h4>
|
||||
|
||||
|
||||
@@ -19,10 +19,14 @@
|
||||
* RTEMS CONFIGURATION *
|
||||
***********************************************************************
|
||||
*/
|
||||
|
||||
#define CONFIGURE_UNIFIED_WORK_AREAS
|
||||
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
|
||||
|
||||
#if __RTEMS_MAJOR__>4 || ( __RTEMS_MAJOR__==4 && __RTEMS_MINOR__>9 )
|
||||
# define CONFIGURE_UNIFIED_WORK_AREAS
|
||||
#else
|
||||
# define CONFIGURE_EXECUTIVE_RAM_SIZE (2000*1024)
|
||||
#endif
|
||||
|
||||
#define CONFIGURE_MAXIMUM_TASKS rtems_resource_unlimited(30)
|
||||
#define CONFIGURE_MAXIMUM_SEMAPHORES rtems_resource_unlimited(500)
|
||||
#define CONFIGURE_MAXIMUM_TIMERS rtems_resource_unlimited(20)
|
||||
|
||||
@@ -1617,6 +1617,16 @@ the application Developer's Guide.</p>
|
||||
<td>the default, nnn is one, enables synchronous scanning, and if nnn is
|
||||
zero it turns on asynchronous scanning</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-ad <n.n></td>
|
||||
<td>set the delay before asynchronous operations complete (defaults to
|
||||
0.1 seconds)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-an <nnn></td>
|
||||
<td>set the maximum number of simultaneous asynchronous operations
|
||||
(defaults to 1000)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -351,6 +351,13 @@ int epicsShareAPI ca_create_channel (
|
||||
catch ( cacChannel::unsupportedByService & ) {
|
||||
return ECA_UNAVAILINSERV;
|
||||
}
|
||||
catch ( std :: exception & except ) {
|
||||
pcac->printFormated (
|
||||
"ca_create_channel: "
|
||||
"unexpected exception was \"%s\"",
|
||||
except.what () );
|
||||
return ECA_INTERNAL;
|
||||
}
|
||||
catch ( ... ) {
|
||||
return ECA_INTERNAL;
|
||||
}
|
||||
|
||||
@@ -1439,7 +1439,8 @@ void singleSubscriptionDeleteTest ( chid chan, unsigned interestLevel )
|
||||
unsigned j = 0;
|
||||
while ( j < i ) {
|
||||
temp = (float) j++;
|
||||
SEVCHK ( ca_put (DBR_FLOAT, chan, &temp), NULL);
|
||||
SEVCHK ( ca_put (DBR_FLOAT, chan, &temp),
|
||||
"singleSubscriptionDeleteTest - one of multiple puts" );
|
||||
}
|
||||
ca_flush_io ();
|
||||
}
|
||||
@@ -2138,7 +2139,6 @@ void clearChannelInPutCallbackTest ( const char *pName, unsigned level )
|
||||
void clearChannelInSubscrCallbackTest ( const char *pName, unsigned level )
|
||||
{
|
||||
unsigned i;
|
||||
const dbr_double_t value = 1.1;
|
||||
chid chan;
|
||||
int status;
|
||||
|
||||
@@ -2339,7 +2339,10 @@ void monitorUpdateTest ( chid chan, unsigned interestLevel )
|
||||
SEVCHK ( ca_get ( DBR_FLOAT, chan, &getResp ), NULL );
|
||||
SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL );
|
||||
|
||||
assert ( getResp == temp );
|
||||
if ( getResp != temp ) {
|
||||
printf ( "getResp=%f, temp=%f\n", getResp, temp );
|
||||
assert ( getResp == temp );
|
||||
}
|
||||
|
||||
/*
|
||||
* wait for all of the monitors to have correct values
|
||||
@@ -2737,6 +2740,8 @@ void fdManagerVerify ( const char * pName, unsigned interestLevel )
|
||||
assert ( status >= 0 );
|
||||
}
|
||||
|
||||
showProgress ( interestLevel );
|
||||
|
||||
status = ca_add_event ( DBR_FLOAT, newChan,
|
||||
nUpdatesTester, & eventCount, & subscription );
|
||||
assert ( status == ECA_NORMAL );
|
||||
@@ -2751,6 +2756,8 @@ void fdManagerVerify ( const char * pName, unsigned interestLevel )
|
||||
assert ( status >= 0 );
|
||||
}
|
||||
|
||||
showProgress ( interestLevel );
|
||||
|
||||
status = ca_clear_event ( subscription );
|
||||
assert ( status == ECA_NORMAL );
|
||||
|
||||
@@ -2774,6 +2781,8 @@ void fdManagerVerify ( const char * pName, unsigned interestLevel )
|
||||
assert ( eventCount++ < 100 );
|
||||
}
|
||||
|
||||
showProgress ( interestLevel );
|
||||
|
||||
status = ca_clear_channel ( newChan );
|
||||
assert ( status == ECA_NORMAL );
|
||||
|
||||
@@ -2798,12 +2807,12 @@ void verifyConnectWithDisconnectedChannels (
|
||||
|
||||
for ( i= 0u; i < NELEMENTS ( bogusChan ); i++ ) {
|
||||
char buf[256];
|
||||
sprintf ( buf, "aChannelThatShouldNeverNeverNeverExit%u", i );
|
||||
sprintf ( buf, "aChannelThatShouldNeverNeverNeverExist%u", i );
|
||||
status = ca_create_channel ( buf, 0, 0, 0, & bogusChan[i] );
|
||||
assert ( status == ECA_NORMAL );
|
||||
}
|
||||
|
||||
status = ca_pend_io ( timeoutToPendIO );
|
||||
status = ca_pend_io ( 0.001 );
|
||||
assert ( status == ECA_TIMEOUT );
|
||||
|
||||
/* wait a long time for the search interval to increase */
|
||||
@@ -2989,6 +2998,10 @@ void verifyContextRundownFlush ( const char * pName, unsigned interestLevel )
|
||||
|
||||
ca_context_destroy ();
|
||||
}
|
||||
|
||||
if ( i % 100 == 0 ) {
|
||||
showProgress ( interestLevel );
|
||||
}
|
||||
}
|
||||
|
||||
showProgressEnd ( interestLevel );
|
||||
|
||||
@@ -253,6 +253,11 @@ void ca_client_context::registerForFileDescriptorCallBack (
|
||||
this->fdRegFunc = pFunc;
|
||||
this->fdRegArg = pArg;
|
||||
this->fdRegFuncNeedsToBeCalled = true;
|
||||
if ( pFunc ) {
|
||||
// the receive thread might already be blocking
|
||||
// w/o having sent the wakeup message
|
||||
this->_sendWakeupMsg ();
|
||||
}
|
||||
// should block here until releated callback in progress completes
|
||||
}
|
||||
|
||||
@@ -557,11 +562,11 @@ int ca_client_context::pendEvent ( const double & timeout )
|
||||
0, & tmpAddr.sa, & addrSize );
|
||||
} while ( status > 0 );
|
||||
}
|
||||
this->noWakeupSincePend = true;
|
||||
while ( this->callbackThreadsPending > 0 ) {
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
this->callbackThreadActivityComplete.wait ( 30.0 );
|
||||
}
|
||||
this->noWakeupSincePend = true;
|
||||
}
|
||||
|
||||
double elapsed = epicsTime::getCurrent() - current;
|
||||
@@ -613,19 +618,24 @@ void ca_client_context::callbackProcessingInitiateNotify ()
|
||||
}
|
||||
}
|
||||
if ( sendNeeded ) {
|
||||
// send short udp message to wake up a file descriptor manager
|
||||
// when a message arrives
|
||||
osiSockAddr tmpAddr;
|
||||
tmpAddr.ia.sin_family = AF_INET;
|
||||
tmpAddr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK );
|
||||
tmpAddr.ia.sin_port = htons ( this->localPort );
|
||||
char buf = 0;
|
||||
sendto ( this->sock, & buf, sizeof ( buf ),
|
||||
0, & tmpAddr.sa, sizeof ( tmpAddr.sa ) );
|
||||
_sendWakeupMsg ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ca_client_context :: _sendWakeupMsg ()
|
||||
{
|
||||
// send short udp message to wake up a file descriptor manager
|
||||
// when a message arrives
|
||||
osiSockAddr tmpAddr;
|
||||
tmpAddr.ia.sin_family = AF_INET;
|
||||
tmpAddr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK );
|
||||
tmpAddr.ia.sin_port = htons ( this->localPort );
|
||||
char buf = 0;
|
||||
sendto ( this->sock, & buf, sizeof ( buf ),
|
||||
0, & tmpAddr.sa, sizeof ( tmpAddr.sa ) );
|
||||
}
|
||||
|
||||
void ca_client_context::callbackProcessingCompleteNotify ()
|
||||
{
|
||||
// if preemptive callback is enabled then this is a noop
|
||||
|
||||
@@ -143,6 +143,7 @@ cac::cac (
|
||||
initializingThreadsId ( epicsThreadGetIdSelf() ),
|
||||
initializingThreadsPriority ( epicsThreadGetPrioritySelf() ),
|
||||
maxRecvBytesTCP ( MAX_TCP ),
|
||||
maxContigFrames ( contiguousMsgCountWhichTriggersFlowControl ),
|
||||
beaconAnomalyCount ( 0u ),
|
||||
iiuExistenceCount ( 0u )
|
||||
{
|
||||
@@ -215,6 +216,11 @@ cac::cac (
|
||||
if ( ! this->tcpLargeRecvBufFreeList ) {
|
||||
throw std::bad_alloc ();
|
||||
}
|
||||
unsigned bufsPerArray = this->maxRecvBytesTCP / comBuf::capacityBytes ();
|
||||
if ( bufsPerArray > 1u ) {
|
||||
maxContigFrames = bufsPerArray *
|
||||
contiguousMsgCountWhichTriggersFlowControl;
|
||||
}
|
||||
}
|
||||
catch ( ... ) {
|
||||
osiSockRelease ();
|
||||
@@ -547,12 +553,15 @@ void cac::transferChanToVirtCircuit (
|
||||
piiu = pnewiiu.release ();
|
||||
newIIU = true;
|
||||
}
|
||||
catch ( std::bad_alloc & ) {
|
||||
catch ( std :: exception & except ) {
|
||||
errlogPrintf (
|
||||
"CAC: exception during virtual circuit creation \"%s\"\n",
|
||||
except.what () );
|
||||
return;
|
||||
}
|
||||
catch ( ... ) {
|
||||
errlogPrintf (
|
||||
"CAC: Unexpected exception during virtual circuit creation\n" );
|
||||
"CAC: nonstandard exception during virtual circuit creation\n" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +198,7 @@ public:
|
||||
unsigned largeBufferSizeTCP () const;
|
||||
char * allocateLargeBufferTCP ();
|
||||
void releaseLargeBufferTCP ( char * );
|
||||
unsigned maxContiguousFrames ( epicsGuard < epicsMutex > & ) const;
|
||||
|
||||
// misc
|
||||
const char * userNamePointer () const;
|
||||
@@ -272,6 +273,7 @@ private:
|
||||
epicsThreadId initializingThreadsId;
|
||||
unsigned initializingThreadsPriority;
|
||||
unsigned maxRecvBytesTCP;
|
||||
unsigned maxContigFrames;
|
||||
unsigned beaconAnomalyCount;
|
||||
unsigned iiuExistenceCount;
|
||||
|
||||
@@ -442,5 +444,11 @@ inline const char * cac :: pLocalHostName ()
|
||||
return _refLocalHostName->pointer ();
|
||||
}
|
||||
|
||||
inline unsigned cac ::
|
||||
maxContiguousFrames ( epicsGuard < epicsMutex > & ) const
|
||||
{
|
||||
return maxContigFrames;
|
||||
}
|
||||
|
||||
#endif // ifdef cach
|
||||
|
||||
|
||||
273
src/ca/catime.c
273
src/ca/catime.c
@@ -42,15 +42,10 @@
|
||||
|
||||
typedef struct testItem {
|
||||
chid chix;
|
||||
char name[40];
|
||||
char name[128];
|
||||
int type;
|
||||
int count;
|
||||
union {
|
||||
dbr_double_t doubleval;
|
||||
dbr_float_t fltval;
|
||||
dbr_short_t intval;
|
||||
dbr_string_t strval;
|
||||
} val;
|
||||
void * pValue;
|
||||
} ti;
|
||||
|
||||
typedef void tf ( ti *pItems, unsigned iterations, unsigned *pInlineIter );
|
||||
@@ -172,66 +167,66 @@ unsigned *pInlineIter
|
||||
int status;
|
||||
dbr_int_t val;
|
||||
|
||||
for (pi=pItems; pi<&pItems[iterations]; pi++) {
|
||||
for (pi=pItems; pi < &pItems[iterations]; pi++) {
|
||||
status = ca_array_put(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_put(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_put(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_put(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_put(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_put(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_put(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_put(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_put(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_put(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
}
|
||||
#ifdef WAIT_FOR_ACK
|
||||
@@ -243,7 +238,7 @@ unsigned *pInlineIter
|
||||
pItems[0].type,
|
||||
pItems[0].count,
|
||||
pItems[0].chix,
|
||||
&pItems[0].val);
|
||||
pItems[0].pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_flush_io();
|
||||
SEVCHK (status, NULL);
|
||||
@@ -255,7 +250,7 @@ unsigned *pInlineIter
|
||||
* test_get ()
|
||||
*/
|
||||
static void test_get(
|
||||
ti *pItems,
|
||||
ti *pItems,
|
||||
unsigned iterations,
|
||||
unsigned *pInlineIter
|
||||
)
|
||||
@@ -268,64 +263,64 @@ unsigned *pInlineIter
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_get(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_get(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_get(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_get(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_get(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_get(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_get(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_get(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_get(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_get(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_array_get(
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
}
|
||||
status = ca_pend_io(100.0);
|
||||
status = ca_pend_io(1e20);
|
||||
SEVCHK (status, NULL);
|
||||
|
||||
*pInlineIter = 10;
|
||||
@@ -348,7 +343,7 @@ unsigned *pInlineIter
|
||||
pi->type,
|
||||
pi->count,
|
||||
pi->chix,
|
||||
&pi->val);
|
||||
pi->pValue);
|
||||
SEVCHK (status, NULL);
|
||||
status = ca_pend_io(100.0);
|
||||
SEVCHK (status, NULL);
|
||||
@@ -377,7 +372,7 @@ static void measure_get_latency (ti *pItems, unsigned iterations)
|
||||
for ( pi = pItems; pi < &pItems[iterations]; pi++ ) {
|
||||
epicsTimeGetCurrent ( &start_time );
|
||||
status = ca_array_get ( pi->type, pi->count,
|
||||
pi->chix, &pi->val );
|
||||
pi->chix, pi->pValue );
|
||||
SEVCHK ( status, NULL );
|
||||
status = ca_pend_io ( 100.0 );
|
||||
SEVCHK ( status, NULL );
|
||||
@@ -400,14 +395,20 @@ static void measure_get_latency (ti *pItems, unsigned iterations)
|
||||
|
||||
mean = X/iterations;
|
||||
stdDev = sqrt ( XX/iterations - mean*mean );
|
||||
printf ( "Round trip get delays - mean=%f sec, std dev=%f sec, min=%f sec max=%f sec\n",
|
||||
mean, stdDev, min, max );
|
||||
printf (
|
||||
"Get Latency - "
|
||||
"mean = %3.1f uS, "
|
||||
"std dev = %3.1f uS, "
|
||||
"min = %3.1f uS "
|
||||
"max = %3.1f uS\n",
|
||||
mean * 1e6, stdDev * 1e6,
|
||||
min * 1e6, max * 1e6 );
|
||||
}
|
||||
|
||||
/*
|
||||
* printSearchStat()
|
||||
*/
|
||||
static void printSearchStat ( const ti *pi, unsigned iterations )
|
||||
static void printSearchStat ( const ti * pi, unsigned iterations )
|
||||
{
|
||||
unsigned i;
|
||||
double X = 0u;
|
||||
@@ -431,40 +432,43 @@ static void printSearchStat ( const ti *pi, unsigned iterations )
|
||||
|
||||
mean = X / iterations;
|
||||
stdDev = sqrt( XX / iterations - mean * mean );
|
||||
printf ( "Search tries per chan - mean=%f std dev=%f min=%f max=%f\n",
|
||||
printf (
|
||||
"Search tries per chan - "
|
||||
"mean = %3.1f "
|
||||
"std dev = %3.1f "
|
||||
"min = %3.1f "
|
||||
"max = %3.1f\n",
|
||||
mean, stdDev, min, max);
|
||||
}
|
||||
|
||||
/*
|
||||
* timeIt ()
|
||||
*/
|
||||
void timeIt ( tf *pfunc, ti *pItems, unsigned iterations, unsigned nBytes )
|
||||
void timeIt ( tf *pfunc, ti *pItems, unsigned iterations,
|
||||
unsigned nBytesSent, unsigned nBytesRecv )
|
||||
{
|
||||
epicsTimeStamp end_time;
|
||||
epicsTimeStamp start_time;
|
||||
double delay;
|
||||
unsigned inlineIter;
|
||||
|
||||
epicsTimeGetCurrent (&start_time);
|
||||
(*pfunc) (pItems, iterations, &inlineIter);
|
||||
epicsTimeGetCurrent (&end_time);
|
||||
delay = epicsTimeDiffInSeconds (&end_time, &start_time);
|
||||
if (delay>0.0) {
|
||||
epicsTimeGetCurrent ( &start_time );
|
||||
(*pfunc) ( pItems, iterations, &inlineIter );
|
||||
epicsTimeGetCurrent ( &end_time );
|
||||
delay = epicsTimeDiffInSeconds ( &end_time, &start_time );
|
||||
if ( delay > 0.0 ) {
|
||||
double freq = ( iterations * inlineIter ) / delay;
|
||||
printf ( "Elapsed Per Item = %12.8f sec, %10.1f Items per sec",
|
||||
1.0 / freq, freq );
|
||||
printf ( "Per Op, %8.4f uS ( %8.4f MHz )",
|
||||
1e6 / freq, freq / 1e6 );
|
||||
if ( pItems != NULL ) {
|
||||
printf(", %3.1f Mbps\n",
|
||||
(inlineIter*nBytes*CHAR_BIT)/(delay*1e6));
|
||||
printf(", %8.4f snd Mbps, %8.4f rcv Mbps\n",
|
||||
(inlineIter*nBytesSent*CHAR_BIT)/(delay*1e6),
|
||||
(inlineIter*nBytesRecv*CHAR_BIT)/(delay*1e6) );
|
||||
}
|
||||
else {
|
||||
printf ("\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf ("Elapsed Per Item = %12.8f sec\n",
|
||||
delay/(iterations*inlineIter));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -472,19 +476,53 @@ void timeIt ( tf *pfunc, ti *pItems, unsigned iterations, unsigned nBytes )
|
||||
*/
|
||||
static void test ( ti *pItems, unsigned iterations )
|
||||
{
|
||||
unsigned nBytes;
|
||||
unsigned payloadSize, dblPayloadSize;
|
||||
unsigned nBytesSent, nBytesRecv;
|
||||
|
||||
printf ( "\tasync put test\n");
|
||||
nBytes = sizeof ( caHdr ) + OCT_ROUND( dbr_size[pItems[0].type] );
|
||||
timeIt ( test_put, pItems, iterations, nBytes * iterations );
|
||||
payloadSize =
|
||||
dbr_size_n ( pItems[0].type, pItems[0].count );
|
||||
payloadSize = CA_MESSAGE_ALIGN ( payloadSize );
|
||||
|
||||
printf ( "\tasync get test\n");
|
||||
nBytes = 2 * sizeof ( caHdr ) + OCT_ROUND ( dbr_size[pItems[0].type] );
|
||||
timeIt ( test_get, pItems, iterations/2, nBytes * ( iterations / 2 ) );
|
||||
dblPayloadSize = dbr_size [ DBR_DOUBLE ];
|
||||
dblPayloadSize = CA_MESSAGE_ALIGN ( dblPayloadSize );
|
||||
|
||||
if ( payloadSize > dblPayloadSize ) {
|
||||
unsigned factor = payloadSize / dblPayloadSize;
|
||||
while ( factor ) {
|
||||
if ( iterations > 10 * factor ) {
|
||||
iterations /= factor;
|
||||
break;
|
||||
}
|
||||
factor /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
printf ("\tsynch get test\n");
|
||||
nBytes = 2 * sizeof ( caHdr ) + OCT_ROUND ( dbr_size[pItems[0].type] );
|
||||
timeIt ( test_wait, pItems, iterations/100, nBytes * ( iterations / 100 ) );
|
||||
printf ( "\t### async put test ###\n");
|
||||
nBytesSent = sizeof ( caHdr ) + CA_MESSAGE_ALIGN( payloadSize );
|
||||
nBytesRecv = 0u;
|
||||
timeIt ( test_put, pItems, iterations,
|
||||
nBytesSent * iterations,
|
||||
nBytesRecv * iterations );
|
||||
|
||||
printf ( "\t### async get test ###\n");
|
||||
nBytesSent = sizeof ( caHdr );
|
||||
nBytesRecv = sizeof ( caHdr ) + CA_MESSAGE_ALIGN ( payloadSize );
|
||||
timeIt ( test_get, pItems, iterations,
|
||||
nBytesSent * ( iterations ),
|
||||
nBytesRecv * ( iterations ) );
|
||||
|
||||
printf ("\t### synch get test ###\n");
|
||||
nBytesSent = sizeof ( caHdr );
|
||||
nBytesRecv = sizeof ( caHdr ) + CA_MESSAGE_ALIGN ( payloadSize );
|
||||
if ( iterations > 100 ) {
|
||||
iterations /= 100;
|
||||
}
|
||||
else if ( iterations > 10 ) {
|
||||
iterations /= 10;
|
||||
}
|
||||
timeIt ( test_wait, pItems, iterations,
|
||||
nBytesSent * iterations,
|
||||
nBytesRecv * iterations );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -493,11 +531,12 @@ static void test ( ti *pItems, unsigned iterations )
|
||||
int catime ( const char * channelName,
|
||||
unsigned channelCount, enum appendNumberFlag appNF )
|
||||
{
|
||||
unsigned i;
|
||||
unsigned strsize;
|
||||
unsigned nBytes;
|
||||
ti *pItemList;
|
||||
|
||||
unsigned i;
|
||||
int j;
|
||||
unsigned strsize;
|
||||
unsigned nBytesSent, nBytesRecv;
|
||||
ti *pItemList;
|
||||
|
||||
if ( channelCount == 0 ) {
|
||||
printf ( "channel count was zero\n" );
|
||||
return 0;
|
||||
@@ -521,7 +560,8 @@ int catime ( const char * channelName,
|
||||
}
|
||||
|
||||
strsize = sizeof ( pItemList[0].name ) - 1;
|
||||
nBytes = 0;
|
||||
nBytesSent = 0;
|
||||
nBytesRecv = 0;
|
||||
for ( i=0; i < channelCount; i++ ) {
|
||||
if ( appNF == appendNumber ) {
|
||||
sprintf ( pItemList[i].name,"%.*s%.6u",
|
||||
@@ -531,70 +571,109 @@ int catime ( const char * channelName,
|
||||
strncpy ( pItemList[i].name, channelName, strsize);
|
||||
}
|
||||
pItemList[i].name[strsize]= '\0';
|
||||
pItemList[i].count = 1;
|
||||
nBytes += 2 * ( OCT_ROUND ( strlen ( pItemList[i].name ) ) + 2 * sizeof (caHdr) );
|
||||
pItemList[i].count = 0;
|
||||
pItemList[i].pValue = 0;
|
||||
nBytesSent += 2 * ( CA_MESSAGE_ALIGN ( strlen ( pItemList[i].name ) )
|
||||
+ sizeof (caHdr) );
|
||||
nBytesRecv += 2 * sizeof (caHdr);
|
||||
}
|
||||
|
||||
printf ( "channel connect test\n" );
|
||||
timeIt ( test_search, pItemList, channelCount, nBytes );
|
||||
printf ( "Channel Connect Test\n" );
|
||||
printf ( "--------------------\n" );
|
||||
timeIt ( test_search, pItemList, channelCount, nBytesSent, nBytesRecv );
|
||||
printSearchStat ( pItemList, channelCount );
|
||||
|
||||
for ( i = 0; i < channelCount; i++ ) {
|
||||
size_t count = ca_element_count ( pItemList[i].chix );
|
||||
size_t size = sizeof ( dbr_string_t ) * count;
|
||||
pItemList[i].count = count;
|
||||
pItemList[i].pValue = malloc ( size );
|
||||
assert ( pItemList[i].pValue );
|
||||
}
|
||||
|
||||
printf (
|
||||
"channel name=%s, native type=%d, native count=%lu\n",
|
||||
"channel name=%s, native type=%d, native count=%u\n",
|
||||
ca_name (pItemList[0].chix),
|
||||
ca_field_type (pItemList[0].chix),
|
||||
ca_element_count (pItemList[0].chix));
|
||||
pItemList[0].count );
|
||||
|
||||
printf ("\tpend event test\n");
|
||||
timeIt (test_pend, NULL, 100, 0);
|
||||
printf ("Pend Event Test\n");
|
||||
printf ( "----------------\n" );
|
||||
timeIt ( test_pend, NULL, 100, 0, 0 );
|
||||
|
||||
for ( i = 0; i < channelCount; i++ ) {
|
||||
dbr_float_t * pFltVal = ( dbr_float_t * ) pItemList[i].pValue;
|
||||
double val = i;
|
||||
val /= channelCount;
|
||||
pItemList[i].val.fltval = (dbr_float_t) val;
|
||||
for ( j = 0; j < pItemList[i].count; j++ ) {
|
||||
pFltVal[j] = (dbr_float_t) val;
|
||||
}
|
||||
pItemList[i].type = DBR_FLOAT;
|
||||
}
|
||||
printf ( "float test\n" );
|
||||
printf ( "DBR_FLOAT Test\n" );
|
||||
printf ( "--------------\n" );
|
||||
test ( pItemList, channelCount );
|
||||
|
||||
for ( i = 0; i < channelCount; i++ ) {
|
||||
dbr_double_t * pDblVal = ( dbr_double_t * ) pItemList[i].pValue;
|
||||
double val = i;
|
||||
val /= channelCount;
|
||||
pItemList[i].val.doubleval = (dbr_double_t) val;
|
||||
for ( j = 0; j < pItemList[i].count; j++ ) {
|
||||
pDblVal[j] = (dbr_double_t) val;
|
||||
}
|
||||
pItemList[i].type = DBR_DOUBLE;
|
||||
}
|
||||
printf ( "double test\n" );
|
||||
printf ( "DBR_DOUBLE Test\n" );
|
||||
printf ( "---------------\n" );
|
||||
test ( pItemList, channelCount );
|
||||
|
||||
|
||||
for ( i = 0; i < channelCount; i++ ) {
|
||||
dbr_string_t * pStrVal = ( dbr_string_t * ) pItemList[i].pValue;
|
||||
double val = i;
|
||||
val /= channelCount;
|
||||
sprintf ( pItemList[i].val.strval, "%f", val );
|
||||
for ( j = 0; j < pItemList[i].count; j++ ) {
|
||||
sprintf ( pStrVal[j], "%f", val );
|
||||
}
|
||||
pItemList[i].type = DBR_STRING;
|
||||
}
|
||||
printf ( "string test\n" );
|
||||
printf ( "DBR_STRING Test\n" );
|
||||
printf ( "---------------\n" );
|
||||
test ( pItemList, channelCount );
|
||||
|
||||
for ( i = 0; i < channelCount; i++ ) {
|
||||
dbr_int_t * pIntVal = ( dbr_int_t * ) pItemList[i].pValue;
|
||||
double val = i;
|
||||
val /= channelCount;
|
||||
pItemList[i].val.intval = (dbr_int_t) val;
|
||||
for ( j = 0; j < pItemList[i].count; j++ ) {
|
||||
pIntVal[j] = (dbr_int_t) val;
|
||||
}
|
||||
pItemList[i].type = DBR_INT;
|
||||
}
|
||||
printf ( "integer test\n" );
|
||||
printf ( "DBR_INT Test\n" );
|
||||
printf ( "------------\n" );
|
||||
test ( pItemList, channelCount );
|
||||
|
||||
printf ( "round trip jitter test\n" );
|
||||
printf ( "Get Latency Test\n" );
|
||||
printf ( "----------------\n" );
|
||||
for ( i = 0; i < channelCount; i++ ) {
|
||||
pItemList[i].val.fltval = 0.0f;
|
||||
dbr_double_t * pDblVal = ( dbr_double_t * ) pItemList[i].pValue;
|
||||
for ( j = 0; j < pItemList[i].count; j++ ) {
|
||||
pDblVal[j] = 0;
|
||||
}
|
||||
pItemList[i].type = DBR_DOUBLE;
|
||||
}
|
||||
measure_get_latency ( pItemList, channelCount );
|
||||
|
||||
printf ("free test\n");
|
||||
timeIt ( test_free, pItemList, channelCount, 0 );
|
||||
printf ( "Free Channel Test\n" );
|
||||
printf ( "-----------------\n" );
|
||||
timeIt ( test_free, pItemList, channelCount, 0, 0 );
|
||||
|
||||
SEVCHK ( ca_task_exit (), "Unable to free resources at exit" );
|
||||
|
||||
for ( i = 0; i < channelCount; i++ ) {
|
||||
free ( pItemList[i].pValue );
|
||||
}
|
||||
|
||||
free ( pItemList );
|
||||
|
||||
|
||||
@@ -79,11 +79,14 @@ epicsTimerNotify::expireStatus disconnectGovernorTimer::expire (
|
||||
void disconnectGovernorTimer::show ( unsigned level ) const
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
::printf ( "disconnect governor timer:\n" );
|
||||
tsDLIterConst < nciu > pChan = this->chanList.firstIter ();
|
||||
while ( pChan.valid () ) {
|
||||
pChan->show ( level - 1u );
|
||||
pChan++;
|
||||
::printf ( "disconnect governor timer: with %u channels pending\n",
|
||||
this->chanList.count () );
|
||||
if ( level > 0u ) {
|
||||
tsDLIterConst < nciu > pChan = this->chanList.firstIter ();
|
||||
while ( pChan.valid () ) {
|
||||
pChan->show ( level - 1u );
|
||||
pChan++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,12 +77,17 @@ void getCallback::exception (
|
||||
args.status = status;
|
||||
args.dbr = 0;
|
||||
caEventCallBackFunc * pFuncTmp = this->pFunc;
|
||||
// fetch client context and destroy prior to releasing
|
||||
// the lock and calling cb in case they destroy channel there
|
||||
this->chan.getClientCtx().destroyGetCallback ( guard, *this );
|
||||
{
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
( *pFuncTmp ) ( args );
|
||||
}
|
||||
}
|
||||
this->chan.getClientCtx().destroyGetCallback ( guard, *this );
|
||||
else {
|
||||
this->chan.getClientCtx().destroyGetCallback ( guard, *this );
|
||||
}
|
||||
}
|
||||
|
||||
void * getCallback::operator new ( size_t ) // X aCC 361
|
||||
|
||||
@@ -65,24 +65,30 @@ void getCopy::completion (
|
||||
memcpy ( this->pValue, pDataIn, size );
|
||||
this->cacCtx.decrementOutstandingIO ( guard, this->ioSeqNo );
|
||||
this->cacCtx.destroyGetCopy ( guard, *this );
|
||||
// this object destroyed by preceding function call
|
||||
}
|
||||
else {
|
||||
this->exception ( guard, ECA_INTERNAL,
|
||||
"bad data type match in get copy back response",
|
||||
typeIn, countIn);
|
||||
// this object destroyed by preceding function call
|
||||
}
|
||||
}
|
||||
|
||||
void getCopy::exception (
|
||||
epicsGuard < epicsMutex > & guard,
|
||||
int status, const char *pContext, unsigned /* typeIn */, arrayElementCount /* countIn */ )
|
||||
int status, const char *pContext,
|
||||
unsigned /* typeIn */, arrayElementCount /* countIn */ )
|
||||
{
|
||||
oldChannelNotify & chanTmp ( this->chan );
|
||||
unsigned typeTmp ( this->type );
|
||||
arrayElementCount countTmp ( this->count );
|
||||
ca_client_context & caClientCtx ( this->cacCtx );
|
||||
// fetch client context and destroy prior to releasing
|
||||
// the lock and calling cb in case they destroy channel there
|
||||
this->cacCtx.destroyGetCopy ( guard, *this );
|
||||
if ( status != ECA_CHANDESTROY ) {
|
||||
this->cacCtx.exception ( guard, status, pContext,
|
||||
caClientCtx.exception ( guard, status, pContext,
|
||||
__FILE__, __LINE__, chanTmp, typeTmp,
|
||||
countTmp, CA_OP_GET );
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ extern "C" void epicsShareAPI removeDuplicateAddresses
|
||||
ELLNODE *pRawNode;
|
||||
|
||||
while ( (pRawNode = ellGet ( pSrcList ) ) ) {
|
||||
assert ( offsetof (osiSockAddrNode, node) == 0 );
|
||||
STATIC_ASSERT ( offsetof (osiSockAddrNode, node) == 0 );
|
||||
osiSockAddrNode *pNode = reinterpret_cast <osiSockAddrNode *> ( pRawNode );
|
||||
osiSockAddrNode *pTmpNode;
|
||||
|
||||
|
||||
@@ -404,6 +404,7 @@ private:
|
||||
void callbackProcessingCompleteNotify ();
|
||||
cacContext & createNetworkContext (
|
||||
epicsMutex & mutualExclusion, epicsMutex & callbackControl );
|
||||
void _sendWakeupMsg ();
|
||||
|
||||
ca_client_context ( const ca_client_context & );
|
||||
ca_client_context & operator = ( const ca_client_context & );
|
||||
|
||||
@@ -54,11 +54,13 @@ void putCallback::completion ( epicsGuard < epicsMutex > & guard )
|
||||
args.status = ECA_NORMAL;
|
||||
args.dbr = 0;
|
||||
caEventCallBackFunc * pFuncTmp = this->pFunc;
|
||||
// fetch client context and destroy prior to releasing
|
||||
// the lock and calling cb in case they destroy channel there
|
||||
this->chan.getClientCtx().destroyPutCallback ( guard, *this );
|
||||
{
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
( *pFuncTmp ) ( args );
|
||||
}
|
||||
this->chan.getClientCtx().destroyPutCallback ( guard, *this );
|
||||
}
|
||||
|
||||
void putCallback::exception (
|
||||
@@ -75,12 +77,17 @@ void putCallback::exception (
|
||||
args.status = status;
|
||||
args.dbr = 0;
|
||||
caEventCallBackFunc * pFuncTmp = this->pFunc;
|
||||
// fetch client context and destroy prior to releasing
|
||||
// the lock and calling cb in case they destroy channel there
|
||||
this->chan.getClientCtx().destroyPutCallback ( guard, *this );
|
||||
{
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
( *pFuncTmp ) (args);
|
||||
( *pFuncTmp ) ( args );
|
||||
}
|
||||
}
|
||||
this->chan.getClientCtx().destroyPutCallback ( guard, *this );
|
||||
else {
|
||||
this->chan.getClientCtx().destroyPutCallback ( guard, *this );
|
||||
}
|
||||
}
|
||||
|
||||
void * putCallback::operator new ( size_t ) // X aCC 361
|
||||
|
||||
@@ -289,23 +289,31 @@ epicsTimerNotify::expireStatus searchTimer::expire (
|
||||
return expireStatus ( restart, this->period ( guard ) );
|
||||
}
|
||||
|
||||
void searchTimer::show ( unsigned level ) const
|
||||
void searchTimer :: show ( unsigned level ) const
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
::printf ( "search timer delay %f\n", this->period ( guard ) );
|
||||
::printf ( "%u channels with search request pending\n",
|
||||
this->chanListReqPending.count () );
|
||||
tsDLIterConst < nciu > pChan = this->chanListReqPending.firstIter ();
|
||||
while ( pChan.valid () ) {
|
||||
pChan->show ( level - 1u );
|
||||
pChan++;
|
||||
}
|
||||
::printf ( "%u channels with search response pending\n",
|
||||
this->chanListRespPending.count () );
|
||||
pChan = this->chanListRespPending.firstIter ();
|
||||
while ( pChan.valid () ) {
|
||||
pChan->show ( level - 1u );
|
||||
pChan++;
|
||||
::printf ( "searchTimer with period %f\n", this->period ( guard ) );
|
||||
if ( level > 0 ) {
|
||||
::printf ( "channels with search request pending = %u\n",
|
||||
this->chanListReqPending.count () );
|
||||
if ( level > 1u ) {
|
||||
tsDLIterConst < nciu > pChan =
|
||||
this->chanListReqPending.firstIter ();
|
||||
while ( pChan.valid () ) {
|
||||
pChan->show ( level - 2u );
|
||||
pChan++;
|
||||
}
|
||||
}
|
||||
::printf ( "channels with search response pending = %u\n",
|
||||
this->chanListRespPending.count () );
|
||||
if ( level > 1u ) {
|
||||
tsDLIterConst < nciu > pChan =
|
||||
this->chanListRespPending.firstIter ();
|
||||
while ( pChan.valid () ) {
|
||||
pChan->show ( level - 2u );
|
||||
pChan++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
#define epicsAssertAuthor "Jeff Hill johill@lanl.gov"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include "errlog.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
@@ -42,6 +44,8 @@
|
||||
#include "caerr.h"
|
||||
#include "udpiiu.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
const unsigned mSecPerSec = 1000u;
|
||||
const unsigned uSecPerSec = 1000u * mSecPerSec;
|
||||
|
||||
@@ -468,6 +472,7 @@ void tcpRecvThread::run ()
|
||||
if ( ! pComBuf ) {
|
||||
pComBuf = new ( this->iiu.comBufMemMgr ) comBuf;
|
||||
}
|
||||
|
||||
statusWireIO stat;
|
||||
pComBuf->fillFromWire ( this->iiu, stat );
|
||||
|
||||
@@ -497,7 +502,6 @@ void tcpRecvThread::run ()
|
||||
callbackManager mgr ( this->ctxNotify, this->cbMutex );
|
||||
|
||||
epicsGuard < epicsMutex > guard ( this->iiu.mutex );
|
||||
|
||||
|
||||
// route legacy V42 channel connect through the recv thread -
|
||||
// the only thread that should be taking the callback lock
|
||||
@@ -506,19 +510,6 @@ void tcpRecvThread::run ()
|
||||
pChan->connect ( mgr.cbGuard, guard );
|
||||
}
|
||||
|
||||
if ( stat.bytesCopied == pComBuf->capacityBytes () ) {
|
||||
if ( this->iiu.contigRecvMsgCount >=
|
||||
contiguousMsgCountWhichTriggersFlowControl ) {
|
||||
this->iiu.busyStateDetected = true;
|
||||
}
|
||||
else {
|
||||
this->iiu.contigRecvMsgCount++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->iiu.contigRecvMsgCount = 0u;
|
||||
this->iiu.busyStateDetected = false;
|
||||
}
|
||||
this->iiu.unacknowledgedSendBytes = 0u;
|
||||
|
||||
bool protocolOK = false;
|
||||
@@ -542,6 +533,40 @@ void tcpRecvThread::run ()
|
||||
sendWakeupNeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// we dont feel comfortable calling this with a lock applied
|
||||
// (it might block for longer than we like)
|
||||
//
|
||||
// we would prefer to improve efficency by trying, first, a
|
||||
// recv with the new MSG_DONTWAIT flag set, but there isnt
|
||||
// universal support
|
||||
//
|
||||
bool bytesArePending = this->iiu.bytesArePendingInOS ();
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->iiu.mutex );
|
||||
if ( bytesArePending ) {
|
||||
if ( ! this->iiu.busyStateDetected ) {
|
||||
this->iiu.contigRecvMsgCount++;
|
||||
if ( this->iiu.contigRecvMsgCount >=
|
||||
this->iiu.cacRef.maxContiguousFrames ( guard ) ) {
|
||||
this->iiu.busyStateDetected = true;
|
||||
sendWakeupNeeded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// if no bytes are pending then we must immediately
|
||||
// switch off flow control w/o waiting for more
|
||||
// data to arrive
|
||||
this->iiu.contigRecvMsgCount = 0u;
|
||||
if ( this->iiu.busyStateDetected ) {
|
||||
sendWakeupNeeded = true;
|
||||
this->iiu.busyStateDetected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( sendWakeupNeeded ) {
|
||||
this->iiu.sendThreadFlushEvent.signal ();
|
||||
}
|
||||
@@ -673,13 +698,15 @@ tcpiiu::tcpiiu (
|
||||
{
|
||||
this->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP );
|
||||
if ( this->sock == INVALID_SOCKET ) {
|
||||
cac.releaseSmallBufferTCP ( this->pCurData );
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ( "CAC: unable to create virtual circuit because \"%s\"\n",
|
||||
sockErrBuf );
|
||||
cac.releaseSmallBufferTCP ( this->pCurData );
|
||||
throw std::bad_alloc ();
|
||||
std :: string reason =
|
||||
"CAC: TCP circuit creation failure because \"";
|
||||
reason += sockErrBuf;
|
||||
reason += "\"";
|
||||
throw runtime_error ( reason );
|
||||
}
|
||||
|
||||
int flag = true;
|
||||
@@ -727,7 +754,7 @@ tcpiiu::tcpiiu (
|
||||
if (status < 0) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ("CAC: problems setting socket option SO_SNDBUF = \"%s\"\n",
|
||||
errlogPrintf ( "CAC: problems setting socket option SO_SNDBUF = \"%s\"\n",
|
||||
sockErrBuf );
|
||||
}
|
||||
i = MAX_MSG_SIZE;
|
||||
@@ -1694,7 +1721,7 @@ void tcpiiu::decrementBlockingForFlushCount (
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
assert ( this->blockingForFlush > 0u );
|
||||
this->blockingForFlush--;
|
||||
if ( this->blockingForFlush == 0 ) {
|
||||
if ( this->blockingForFlush > 0 ) {
|
||||
this->flushBlockEvent.signal ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ template class tsFreeList < syncGroupWriteNotify, 128, epicsMutexNOOP >;
|
||||
template class tsFreeList < comBuf, 0x20 >;
|
||||
template class tsFreeList < getCallback, 1024, epicsMutexNOOP >;
|
||||
template class tsFreeList < getCopy, 1024, epicsMutexNOOP >;
|
||||
template class tsFreeList < hostNameCache, 16 >;
|
||||
template class tsFreeList < msgForMultiplyDefinedPV, 16 >;
|
||||
template class tsFreeList < nciu, 1024, epicsMutexNOOP>;
|
||||
template class tsFreeList < oldChannelNotify, 1024, epicsMutexNOOP >;
|
||||
|
||||
@@ -44,7 +44,8 @@ caServerI::caServerI ( caServer & tool ) :
|
||||
beaconAnomalyGov ( * new beaconAnomalyGovernor ( *this ) ),
|
||||
debugLevel ( 0u ),
|
||||
nEventsProcessed ( 0u ),
|
||||
nEventsPosted ( 0u )
|
||||
nEventsPosted ( 0u ),
|
||||
ioInProgressCount ( 0u )
|
||||
{
|
||||
assert ( & adapter != NULL );
|
||||
|
||||
|
||||
@@ -76,6 +76,9 @@ public:
|
||||
const char * pHostName, const char * pUserName,
|
||||
const struct caHdrLargeArray * mp, const void * dp,
|
||||
const char * pFormat, ... );
|
||||
bool ioIsPending () const;
|
||||
void incrementIOInProgCount ();
|
||||
void decrementIOInProgCount ();
|
||||
private:
|
||||
clientBufMemoryManager clientBufMemMgr;
|
||||
tsFreeList < casMonitor, 1024 > casMonitorFreeList;
|
||||
@@ -89,6 +92,7 @@ private:
|
||||
unsigned debugLevel;
|
||||
unsigned nEventsProcessed;
|
||||
unsigned nEventsPosted;
|
||||
unsigned ioInProgressCount;
|
||||
|
||||
casEventMask valueEvent; // DBE_VALUE registerEvent("value")
|
||||
casEventMask logEvent; // DBE_LOG registerEvent("log")
|
||||
@@ -137,4 +141,22 @@ inline casEventMask caServerI::alarmEventMask() const
|
||||
return this->alarmEvent;
|
||||
}
|
||||
|
||||
inline bool caServerI :: ioIsPending () const
|
||||
{
|
||||
return ( ioInProgressCount > 0u );
|
||||
}
|
||||
|
||||
inline void caServerI :: incrementIOInProgCount ()
|
||||
{
|
||||
assert ( ioInProgressCount < UINT_MAX );
|
||||
ioInProgressCount++;
|
||||
}
|
||||
|
||||
inline void caServerI :: decrementIOInProgCount ()
|
||||
{
|
||||
assert ( ioInProgressCount > 0 );
|
||||
ioInProgressCount--;
|
||||
this->ioBlockedList::signal ();
|
||||
}
|
||||
|
||||
#endif // caServerIh
|
||||
|
||||
@@ -100,8 +100,6 @@ caStatus casAsyncIOI::cbFunc (
|
||||
this->ioComplete = true;
|
||||
}
|
||||
|
||||
this->client.getCAS().ioBlockedList::signal ();
|
||||
|
||||
// dont use "this" after destroying the object here
|
||||
delete this;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ casAsyncPVAttachIOI::casAsyncPVAttachIOI (
|
||||
casAsyncIOI ( ctx ), msg ( *ctx.getMsg() ),
|
||||
asyncPVAttachIO ( intf ), retVal ( S_cas_badParameter )
|
||||
{
|
||||
ctx.getServer()->incrementIOInProgCount ();
|
||||
ctx.getClient()->installAsynchIO ( *this );
|
||||
}
|
||||
|
||||
@@ -42,6 +43,7 @@ caStatus casAsyncPVAttachIOI::cbFuncAsyncIO (
|
||||
// uninstall here in case the channel is deleted
|
||||
// further down the call stack
|
||||
this->client.uninstallAsynchIO ( *this );
|
||||
this->client.getCAS().decrementIOInProgCount ();
|
||||
|
||||
if ( this->msg.m_cmmd == CA_PROTO_CREATE_CHAN ) {
|
||||
casCtx tmpCtx;
|
||||
@@ -58,6 +60,7 @@ caStatus casAsyncPVAttachIOI::cbFuncAsyncIO (
|
||||
}
|
||||
|
||||
if ( status == S_cas_sendBlocked ) {
|
||||
this->client.getCAS().incrementIOInProgCount ();
|
||||
this->client.installAsynchIO ( *this );
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ casAsyncPVExistIOI::casAsyncPVExistIOI (
|
||||
protocolRevision ( ctx.getClient()->protocolRevision () ),
|
||||
sequenceNumber ( ctx.getClient()->datagramSequenceNumber () )
|
||||
{
|
||||
ctx.getServer()->incrementIOInProgCount ();
|
||||
ctx.getClient()->installAsynchIO ( *this );
|
||||
}
|
||||
|
||||
@@ -61,6 +62,7 @@ caStatus casAsyncPVExistIOI::cbFuncAsyncIO (
|
||||
|
||||
if ( status != S_cas_sendBlocked ) {
|
||||
this->client.uninstallAsynchIO ( *this );
|
||||
this->client.getCAS().decrementIOInProgCount ();
|
||||
}
|
||||
|
||||
return status;
|
||||
|
||||
@@ -90,6 +90,11 @@ caStatus casChannel::write ( const casCtx & ctx, const gdd & value )
|
||||
return ctx.getPV()->write ( ctx, value );
|
||||
}
|
||||
|
||||
caStatus casChannel::writeNotify ( const casCtx & ctx, const gdd & value )
|
||||
{
|
||||
return ctx.getPV()->writeNotify ( ctx, value );
|
||||
}
|
||||
|
||||
void casChannel::show ( unsigned level ) const
|
||||
{
|
||||
if ( level > 2u ) {
|
||||
|
||||
@@ -101,6 +101,17 @@ caStatus casChannelI::write ( const casCtx & ctx, const gdd & value )
|
||||
return status;
|
||||
}
|
||||
|
||||
caStatus casChannelI::writeNotify ( const casCtx & ctx, const gdd & value )
|
||||
{
|
||||
caStatus status = this->chan.beginTransaction ();
|
||||
if ( status != S_casApp_success ) {
|
||||
return status;
|
||||
}
|
||||
status = this->chan.writeNotify ( ctx, value );
|
||||
this->chan.endTransaction ();
|
||||
return status;
|
||||
}
|
||||
|
||||
void casChannelI::postDestroyEvent ()
|
||||
{
|
||||
if ( ! this->serverDeletePending ) {
|
||||
|
||||
@@ -54,6 +54,7 @@ public:
|
||||
bool confirmationRequested () const;
|
||||
caStatus read ( const casCtx & ctx, gdd & prototype );
|
||||
caStatus write ( const casCtx & ctx, const gdd & value );
|
||||
caStatus writeNotify ( const casCtx & ctx, const gdd & value );
|
||||
void show ( unsigned level ) const;
|
||||
private:
|
||||
chanIntfForPV privateForPV;
|
||||
|
||||
@@ -99,7 +99,7 @@ public:
|
||||
bool okToStartAsynchIO ();
|
||||
void setDestroyPending ();
|
||||
|
||||
casEventSys::processStatus eventSysProcess();
|
||||
casProcCond eventSysProcess();
|
||||
|
||||
caStatus addToEventQueue ( casAsyncIOI &,
|
||||
bool & onTheQueue, bool & posted );
|
||||
@@ -172,7 +172,7 @@ inline void casCoreClient::postEvent (
|
||||
}
|
||||
}
|
||||
|
||||
inline casEventSys::processStatus casCoreClient::eventSysProcess ()
|
||||
inline casProcCond casCoreClient :: eventSysProcess ()
|
||||
{
|
||||
epicsGuard < casClientMutex > guard ( this->mutex );
|
||||
return this->eventSys.process ( guard );
|
||||
|
||||
@@ -408,18 +408,15 @@ void casDGClient::sendBeacon ( ca_uint32_t beaconNumber )
|
||||
//
|
||||
// casDGClient::xSend()
|
||||
//
|
||||
outBufClient::flushCondition casDGClient::xSend ( char *pBufIn, // X aCC 361
|
||||
bufSizeT nBytesAvailableToSend, bufSizeT nBytesNeedToBeSent,
|
||||
bufSizeT &nBytesSent )
|
||||
outBufClient::flushCondition casDGClient::xSend ( char *pBufIn,
|
||||
bufSizeT nBytesToSend, bufSizeT & nBytesSent )
|
||||
{
|
||||
assert ( nBytesAvailableToSend >= nBytesNeedToBeSent );
|
||||
|
||||
bufSizeT totalBytes = 0;
|
||||
while ( totalBytes < nBytesNeedToBeSent ) {
|
||||
while ( totalBytes < nBytesToSend ) {
|
||||
cadg *pHdr = reinterpret_cast < cadg * > ( & pBufIn[totalBytes] );
|
||||
|
||||
assert ( totalBytes <= bufSizeT_MAX-pHdr->cadg_nBytes );
|
||||
assert ( totalBytes + pHdr->cadg_nBytes <= nBytesAvailableToSend );
|
||||
assert ( totalBytes <= bufSizeT_MAX - pHdr->cadg_nBytes );
|
||||
assert ( totalBytes + pHdr->cadg_nBytes <= nBytesToSend );
|
||||
|
||||
char * pDG = reinterpret_cast < char * > ( pHdr + 1 );
|
||||
unsigned sizeDG = pHdr->cadg_nBytes - sizeof ( *pHdr );
|
||||
@@ -710,13 +707,15 @@ void casDGClient::inBufFill ( inBufClient::fillParameter parm )
|
||||
this->in.fill ( parm );
|
||||
}
|
||||
|
||||
bufSizeT casDGClient::inBufBytesAvailable () const
|
||||
bufSizeT casDGClient ::
|
||||
inBufBytesPending () const
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
return this->in.bytesAvailable ();
|
||||
return this->in.bytesPresent ();
|
||||
}
|
||||
|
||||
bufSizeT casDGClient::outBufBytesPresent () const
|
||||
bufSizeT casDGClient ::
|
||||
outBufBytesPending () const
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
return this->out.bytesPresent ();
|
||||
|
||||
@@ -48,12 +48,12 @@ public:
|
||||
caStatus sendErr ( const caHdrLargeArray * curp,
|
||||
ca_uint32_t cid, const int reportedStatus,
|
||||
const char *pformat, ... );
|
||||
protected:
|
||||
caStatus processDG ();
|
||||
protected:
|
||||
bool inBufFull () const;
|
||||
void inBufFill ( inBufClient::fillParameter );
|
||||
bufSizeT inBufBytesAvailable () const;
|
||||
bufSizeT outBufBytesPresent () const;
|
||||
bufSizeT inBufBytesPending () const;
|
||||
bufSizeT outBufBytesPending () const;
|
||||
outBufClient::flushCondition flush ();
|
||||
private:
|
||||
inBuf in;
|
||||
@@ -80,8 +80,8 @@ private:
|
||||
const caHdrLargeArray &, const pvExistReturn &,
|
||||
ca_uint16_t protocolRevision, ca_uint32_t sequenceNumber );
|
||||
void sendVersion ();
|
||||
outBufClient::flushCondition xSend ( char *pBufIn, bufSizeT nBytesAvailableToSend,
|
||||
bufSizeT nBytesNeedToBeSent, bufSizeT &nBytesSent );
|
||||
outBufClient::flushCondition xSend ( char *pBufIn, bufSizeT nBytesToSend,
|
||||
bufSizeT &nBytesSent );
|
||||
inBufClient::fillCondition xRecv ( char * pBufIn, bufSizeT nBytesToRecv,
|
||||
fillParameter parm, bufSizeT & nByesRecv );
|
||||
virtual outBufClient::flushCondition osdSend (
|
||||
|
||||
@@ -33,11 +33,13 @@ void casEventSys::show ( unsigned level ) const
|
||||
if (level>=1u) {
|
||||
printf ( "\numSubscriptions = %u, maxLogEntries = %u\n",
|
||||
this->numSubscriptions, this->maxLogEntries );
|
||||
printf ( "\tthere are %d events in the queue\n",
|
||||
printf ( "\tthere are %d items in the event queue\n",
|
||||
this->eventLogQue.count() );
|
||||
printf ( "Replace events flag = %d, dontProcess flag = %d\n",
|
||||
printf ( "\tthere are %d items in the io queue\n",
|
||||
this->ioQue.count() );
|
||||
printf ( "Replace events flag = %d, dontProcessSubscr flag = %d\n",
|
||||
static_cast < int > ( this->replaceEvents ),
|
||||
static_cast < int > ( this->dontProcess ) );
|
||||
static_cast < int > ( this->dontProcessSubscr ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,13 +51,14 @@ casEventSys::~casEventSys()
|
||||
}
|
||||
|
||||
// at this point:
|
||||
// o all channels delete
|
||||
// o all IO deleted
|
||||
// o all channels have been deleted
|
||||
// o all IO has been deleted
|
||||
// o any subscription events remaining on the queue
|
||||
// are pending destroy
|
||||
|
||||
// verify above assertion is true
|
||||
casVerify ( this->eventLogQue.count() == 0 );
|
||||
casVerify ( this->ioQue.count() == 0 );
|
||||
|
||||
// all active subscriptions should also have been
|
||||
// uninstalled
|
||||
@@ -81,20 +84,18 @@ void casEventSys::removeMonitor ()
|
||||
this->maxLogEntries -= averageEventEntries;
|
||||
}
|
||||
|
||||
casEventSys::processStatus casEventSys::process (
|
||||
casProcCond casEventSys :: process (
|
||||
epicsGuard < casClientMutex > & casClientGuard )
|
||||
{
|
||||
casEventSys::processStatus ps;
|
||||
ps.cond = casProcOk;
|
||||
ps.nAccepted = 0u;
|
||||
casProcCond cond = casProcOk;
|
||||
|
||||
epicsGuard < evSysMutex > evGuard ( this->mutex );
|
||||
|
||||
while ( ! this->dontProcess ) {
|
||||
casEvent * pEvent;
|
||||
|
||||
pEvent = this->eventLogQue.get ();
|
||||
|
||||
// we need two queues, one for io and one for subscriptions,
|
||||
// so that we dont hang up the server when in an IO postponed
|
||||
// state simultaneouly with a flow control active state
|
||||
while ( true ) {
|
||||
casEvent * pEvent = this->ioQue.get ();
|
||||
if ( pEvent == NULL ) {
|
||||
break;
|
||||
}
|
||||
@@ -102,27 +103,59 @@ casEventSys::processStatus casEventSys::process (
|
||||
caStatus status = pEvent->cbFunc (
|
||||
this->client, casClientGuard, evGuard );
|
||||
if ( status == S_cas_success ) {
|
||||
ps.nAccepted++;
|
||||
cond = casProcOk;
|
||||
}
|
||||
else if ( status == S_cas_sendBlocked ) {
|
||||
// not accepted so return to the head of the list
|
||||
// (we will try again later)
|
||||
this->eventLogQue.push ( *pEvent );
|
||||
ps.cond = casProcOk;
|
||||
this->ioQue.push ( *pEvent );
|
||||
cond = casProcOk;
|
||||
break;
|
||||
}
|
||||
else if ( status == S_cas_disconnect ) {
|
||||
ps.cond = casProcDisconnect;
|
||||
cond = casProcDisconnect;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
errMessage ( status,
|
||||
"- unexpected error processing event" );
|
||||
ps.cond = casProcDisconnect;
|
||||
"- unexpected error, processing io queue" );
|
||||
cond = casProcDisconnect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( cond == casProcOk ) {
|
||||
while ( ! this->dontProcessSubscr ) {
|
||||
casEvent * pEvent = this->eventLogQue.get ();
|
||||
if ( pEvent == NULL ) {
|
||||
break;
|
||||
}
|
||||
|
||||
caStatus status = pEvent->cbFunc (
|
||||
this->client, casClientGuard, evGuard );
|
||||
if ( status == S_cas_success ) {
|
||||
cond = casProcOk;
|
||||
}
|
||||
else if ( status == S_cas_sendBlocked ) {
|
||||
// not accepted so return to the head of the list
|
||||
// (we will try again later)
|
||||
this->eventLogQue.push ( *pEvent );
|
||||
cond = casProcOk;
|
||||
break;
|
||||
}
|
||||
else if ( status == S_cas_disconnect ) {
|
||||
cond = casProcDisconnect;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
errMessage ( status,
|
||||
"- unexpected error, processing event queue" );
|
||||
cond = casProcDisconnect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// allows the derived class to be informed that it
|
||||
// needs to delete itself via the event system
|
||||
@@ -133,10 +166,10 @@ casEventSys::processStatus casEventSys::process (
|
||||
// pointer.
|
||||
//
|
||||
if ( this->destroyPending ) {
|
||||
ps.cond = casProcDisconnect;
|
||||
cond = casProcDisconnect;
|
||||
}
|
||||
|
||||
return ps;
|
||||
return cond;
|
||||
}
|
||||
|
||||
void casEventSys::eventsOn ()
|
||||
@@ -151,7 +184,7 @@ void casEventSys::eventsOn ()
|
||||
//
|
||||
// allow the event queue to be processed
|
||||
//
|
||||
this->dontProcess = false;
|
||||
this->dontProcessSubscr = false;
|
||||
|
||||
//
|
||||
// remove purge event if it is still pending
|
||||
@@ -188,7 +221,7 @@ bool casEventSys::eventsOff ()
|
||||
// stop processing and sending events to the client
|
||||
// until we exit flow control
|
||||
//
|
||||
this->dontProcess = true;
|
||||
this->dontProcessSubscr = true;
|
||||
}
|
||||
else {
|
||||
if ( this->eventLogQue.count() == 0 ) {
|
||||
@@ -211,7 +244,7 @@ caStatus casEventPurgeEv::cbFunc (
|
||||
epicsGuard < casClientMutex > &,
|
||||
epicsGuard < evSysMutex > & )
|
||||
{
|
||||
this->evSys.dontProcess = true;
|
||||
this->evSys.dontProcessSubscr = true;
|
||||
this->evSys.pPurgeEvent = NULL;
|
||||
delete this;
|
||||
return S_cas_success;
|
||||
@@ -220,60 +253,66 @@ caStatus casEventPurgeEv::cbFunc (
|
||||
caStatus casEventSys::addToEventQueue ( casAsyncIOI & event,
|
||||
bool & onTheQueue, bool & posted, bool & wakeupNeeded )
|
||||
{
|
||||
wakeupNeeded = false;
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
// dont allow them to post completion more than once
|
||||
if ( posted || onTheQueue ) {
|
||||
wakeupNeeded = false;
|
||||
return S_cas_redundantPost;
|
||||
}
|
||||
posted = true;
|
||||
onTheQueue = true;
|
||||
wakeupNeeded = ! this->dontProcess && this->eventLogQue.count() == 0;
|
||||
this->eventLogQue.add ( event );
|
||||
wakeupNeeded =
|
||||
( this->dontProcessSubscr || this->eventLogQue.count() == 0 ) &&
|
||||
this->ioQue.count() == 0;
|
||||
this->ioQue.add ( event );
|
||||
}
|
||||
return S_cas_success;
|
||||
}
|
||||
|
||||
void casEventSys::removeFromEventQueue ( casAsyncIOI & io, bool & onTheEventQueue )
|
||||
void casEventSys::removeFromEventQueue ( casAsyncIOI & io, bool & onTheIOQueue )
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
if ( onTheEventQueue ) {
|
||||
onTheEventQueue = false;
|
||||
this->eventLogQue.remove ( io );
|
||||
if ( onTheIOQueue ) {
|
||||
onTheIOQueue = false;
|
||||
this->ioQue.remove ( io );
|
||||
}
|
||||
}
|
||||
|
||||
bool casEventSys::addToEventQueue ( casChannelI & event,
|
||||
bool & inTheEventQueue )
|
||||
bool & onTheIOQueue )
|
||||
{
|
||||
bool wakeupRequired = false;
|
||||
bool wakeupNeeded = false;
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
if ( ! inTheEventQueue ) {
|
||||
inTheEventQueue = true;
|
||||
wakeupRequired = ! this->dontProcess && this->eventLogQue.count()==0;
|
||||
this->eventLogQue.add ( event );
|
||||
if ( ! onTheIOQueue ) {
|
||||
onTheIOQueue = true;
|
||||
wakeupNeeded =
|
||||
( this->dontProcessSubscr || this->eventLogQue.count() == 0 ) &&
|
||||
this->ioQue.count() == 0;
|
||||
this->ioQue.add ( event );
|
||||
}
|
||||
}
|
||||
return wakeupRequired;
|
||||
return wakeupNeeded;
|
||||
}
|
||||
|
||||
void casEventSys::removeFromEventQueue ( class casChannelI & io,
|
||||
bool & inTheEventQueue )
|
||||
bool & onTheIOQueue )
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
if ( inTheEventQueue ) {
|
||||
inTheEventQueue = false;
|
||||
this->eventLogQue.remove ( io );
|
||||
if ( onTheIOQueue ) {
|
||||
onTheIOQueue = false;
|
||||
this->ioQue.remove ( io );
|
||||
}
|
||||
}
|
||||
|
||||
bool casEventSys::addToEventQueue ( channelDestroyEvent & event )
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
bool wakeupRequired = ! this->dontProcess && this->eventLogQue.count()==0;
|
||||
this->eventLogQue.add ( event );
|
||||
bool wakeupRequired =
|
||||
( this->dontProcessSubscr || this->eventLogQue.count() == 0 ) &&
|
||||
this->ioQue.count() == 0;
|
||||
this->ioQue.add ( event );
|
||||
return wakeupRequired;
|
||||
}
|
||||
|
||||
@@ -283,14 +322,10 @@ void casEventSys::setDestroyPending ()
|
||||
this->destroyPending = true;
|
||||
}
|
||||
|
||||
inline bool casEventSys::full () const // X aCC 361
|
||||
inline bool casEventSys::full () const
|
||||
{
|
||||
if ( this->replaceEvents || this->eventLogQue.count() >= this->maxLogEntries ) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return this->replaceEvents ||
|
||||
this->eventLogQue.count() >= this->maxLogEntries;
|
||||
}
|
||||
|
||||
bool casEventSys::postEvent ( tsDLList < casMonitor > & monitorList,
|
||||
@@ -323,9 +358,11 @@ bool casEventSys::postEvent ( tsDLList < casMonitor > & monitorList,
|
||||
pLog = 0;
|
||||
}
|
||||
|
||||
if ( this->eventLogQue.count() == 0 ) {
|
||||
signalNeeded = true;
|
||||
}
|
||||
signalNeeded |=
|
||||
!this->dontProcessSubscr &&
|
||||
this->eventLogQue.count() == 0 &&
|
||||
this->ioQue.count() == 0;
|
||||
|
||||
iter->installNewEventLog (
|
||||
this->eventLogQue, pLog, event );
|
||||
}
|
||||
|
||||
@@ -68,12 +68,7 @@ public:
|
||||
casEventSys ( casCoreClient & );
|
||||
~casEventSys ();
|
||||
void show ( unsigned level ) const;
|
||||
struct processStatus {
|
||||
casProcCond cond;
|
||||
unsigned nAccepted;
|
||||
};
|
||||
processStatus process (
|
||||
epicsGuard < casClientMutex > & guard );
|
||||
casProcCond process ( epicsGuard < casClientMutex > & guard );
|
||||
void installMonitor ();
|
||||
void removeMonitor ();
|
||||
void prepareMonitorForDestroy ( casMonitor & mon );
|
||||
@@ -97,6 +92,7 @@ public:
|
||||
private:
|
||||
mutable evSysMutex mutex;
|
||||
tsDLList < casEvent > eventLogQue;
|
||||
tsDLList < casEvent > ioQue;
|
||||
tsFreeList < casMonEvent, 1024, epicsMutexNOOP > casMonEventFreeList;
|
||||
casCoreClient & client;
|
||||
class casEventPurgeEv * pPurgeEvent; // flow control purge complete event
|
||||
@@ -104,7 +100,7 @@ private:
|
||||
unsigned maxLogEntries; // max log entries
|
||||
bool destroyPending;
|
||||
bool replaceEvents; // replace last existing event on queue
|
||||
bool dontProcess; // flow ctl is on - dont process event queue
|
||||
bool dontProcessSubscr; // flow ctl is on - dont process subscr event queue
|
||||
|
||||
bool full () const;
|
||||
casEventSys ( const casEventSys & );
|
||||
@@ -141,7 +137,7 @@ inline casEventSys::casEventSys ( casCoreClient & clientIn ) :
|
||||
maxLogEntries ( individualEventEntries ),
|
||||
destroyPending ( false ),
|
||||
replaceEvents ( false ),
|
||||
dontProcess ( false )
|
||||
dontProcessSubscr ( false )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,18 @@ caStatus casPV::write (const casCtx &, const gdd &)
|
||||
return S_casApp_noSupport;
|
||||
}
|
||||
|
||||
//
|
||||
// casPV::writeNotify()
|
||||
//
|
||||
caStatus casPV :: writeNotify (
|
||||
const casCtx & ctx, const gdd & val )
|
||||
{
|
||||
// plumbed this way to preserve backwards
|
||||
// compatibility with the old interface which
|
||||
// did not include a writeNotify interface
|
||||
return this->write ( ctx, val );
|
||||
}
|
||||
|
||||
//
|
||||
// casPV::bestExternalType()
|
||||
//
|
||||
|
||||
@@ -464,6 +464,23 @@ caStatus casPVI::write ( const casCtx & ctx, const gdd & value )
|
||||
}
|
||||
}
|
||||
|
||||
caStatus casPVI::writeNotify ( const casCtx & ctx, const gdd & value )
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
if ( this->pPV ) {
|
||||
caStatus status = this->pPV->beginTransaction ();
|
||||
if ( status != S_casApp_success ) {
|
||||
return status;
|
||||
}
|
||||
status = this->pPV->writeNotify ( ctx, value );
|
||||
this->pPV->endTransaction ();
|
||||
return status;
|
||||
}
|
||||
else {
|
||||
return S_cas_disconnect;
|
||||
}
|
||||
}
|
||||
|
||||
casChannel * casPVI::createChannel ( const casCtx & ctx,
|
||||
const char * const pUserName, const char * const pHostName )
|
||||
{
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
caServerI * getPCAS () const;
|
||||
caStatus attachToServer ( caServerI & cas );
|
||||
aitIndex nativeCount ();
|
||||
bool ioIsPending () const;
|
||||
void clearOutstandingReads ( tsDLList < class casAsyncIOI > &);
|
||||
void destroyAllIO (
|
||||
tsDLList < casAsyncIOI > & );
|
||||
@@ -77,6 +78,7 @@ public:
|
||||
void show ( unsigned level ) const;
|
||||
caStatus read ( const casCtx & ctx, gdd & prototype );
|
||||
caStatus write ( const casCtx & ctx, const gdd & value );
|
||||
caStatus writeNotify ( const casCtx & ctx, const gdd & value );
|
||||
casChannel * createChannel ( const casCtx & ctx,
|
||||
const char * const pUserName, const char * const pHostName );
|
||||
aitEnum bestExternalType () const;
|
||||
@@ -112,5 +114,10 @@ inline casPV * casPVI::apiPointer ()
|
||||
return this->pPV;
|
||||
}
|
||||
|
||||
inline bool casPVI :: ioIsPending () const
|
||||
{
|
||||
return this->nIOAttached > 0u;
|
||||
}
|
||||
|
||||
#endif // casPVIh
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -49,9 +49,9 @@ public:
|
||||
protected:
|
||||
caStatus processMsg ();
|
||||
bool inBufFull () const;
|
||||
bufSizeT inBufBytesAvailable () const;
|
||||
inBufClient::fillCondition inBufFill ();
|
||||
bufSizeT outBufBytesPresent () const;
|
||||
bufSizeT inBufBytesPending () const;
|
||||
bufSizeT outBufBytesPending () const;
|
||||
private:
|
||||
char hostNameStr [32];
|
||||
inBuf in;
|
||||
@@ -62,10 +62,11 @@ private:
|
||||
epicsTime lastRecvTS;
|
||||
char * pUserName;
|
||||
char * pHostName;
|
||||
smartGDDPointer pValueRead;
|
||||
unsigned incommingBytesToDrain;
|
||||
int pendingResponseStatus;
|
||||
caStatus pendingResponseStatus;
|
||||
ca_uint16_t minor_version_number;
|
||||
bool payloadNeedsByteSwap;
|
||||
bool reqPayloadNeedsByteSwap;
|
||||
bool responseIsPending;
|
||||
|
||||
caStatus createChannel ( const char * pName );
|
||||
@@ -142,15 +143,16 @@ private:
|
||||
caStatus accessRightsResponse (
|
||||
epicsGuard < casClientMutex > &, casChannelI * pciu );
|
||||
|
||||
caStatus read ( const gdd * & pDesc );
|
||||
caStatus write ();
|
||||
|
||||
caStatus writeArrayData();
|
||||
caStatus writeScalarData();
|
||||
caStatus writeString();
|
||||
typedef caStatus ( casChannelI :: * PWriteMethod ) (
|
||||
const casCtx &, const gdd & );
|
||||
caStatus read ();
|
||||
caStatus write ( PWriteMethod );
|
||||
caStatus writeArrayData( PWriteMethod );
|
||||
caStatus writeScalarData( PWriteMethod );
|
||||
|
||||
outBufClient::flushCondition xSend ( char * pBuf, bufSizeT nBytesAvailableToSend,
|
||||
bufSizeT nBytesNeedToBeSent, bufSizeT & nBytesSent );
|
||||
outBufClient::flushCondition xSend ( char * pBuf, bufSizeT nBytesToSend,
|
||||
bufSizeT & nBytesSent );
|
||||
inBufClient::fillCondition xRecv ( char * pBuf, bufSizeT nBytesToRecv,
|
||||
inBufClient::fillParameter parm, bufSizeT & nByesRecv );
|
||||
|
||||
@@ -169,6 +171,7 @@ private:
|
||||
const unsigned lineno, const unsigned idIn );
|
||||
void casChannelDestroyFromInterfaceNotify ( casChannelI & chan,
|
||||
bool immediatedSestroyNeeded );
|
||||
static void issuePosponeWhenNonePendingWarning ( const char * pReqTypeStr );
|
||||
|
||||
casStrmClient ( const casStrmClient & );
|
||||
casStrmClient & operator = ( const casStrmClient & );
|
||||
|
||||
@@ -78,6 +78,7 @@ typedef aitUint32 caStatus;
|
||||
#define S_cas_pvAlreadyAttached (M_cas | 31) /*PV attached to another server*/
|
||||
#define S_cas_badRequest (M_cas | 32) /*client's request was invalid*/
|
||||
#define S_cas_invalidAsynchIO (M_cas | 33) /*inappropriate asynchronous IO type*/
|
||||
#define S_cas_posponeWhenNonePending (M_cas | 34) /*request postponement, none pending*/
|
||||
|
||||
/*
|
||||
* ===========================================================
|
||||
@@ -392,16 +393,31 @@ public:
|
||||
// asynchronous IO operation (read or write) completes
|
||||
// against the PV.
|
||||
//
|
||||
// NOTE:
|
||||
// NOTES:
|
||||
// o The incoming GDD with application type "value" is always
|
||||
// converted to the PV.bestExternalType() primitive type.
|
||||
// o The time stamp in the incoming GDD is set to the time that
|
||||
// the last message was received from the client.
|
||||
// o Currently, no container type GDD's are passed here and
|
||||
// the application type is always "value". This may change.
|
||||
// o The write interface is called when the server receives
|
||||
// ca_put request and the writeNotify interface is called
|
||||
// when the server receives ca_put_callback request.
|
||||
// o A writeNotify request is considered complete and therefore
|
||||
// ready for asynchronous completion notification when any
|
||||
// action that it initiates, and any cascaded actions, complete.
|
||||
// o In an IOC context intermediate write requets can be discarded
|
||||
// as long as the final writeRequest is always executed. In an
|
||||
// IOC context intermediate writeNotify requests are never discarded.
|
||||
// o If the service does not implement writeNotify then
|
||||
// the base implementation of casPV :: writeNotify calls
|
||||
// casPV :: write thereby preserving backwards compatibility
|
||||
// with the original interface which included a virtual write
|
||||
// method but not a virtual writeNotify method.
|
||||
//
|
||||
epicsShareFunc virtual caStatus write (const casCtx &ctx, const gdd &value);
|
||||
|
||||
epicsShareFunc virtual caStatus writeNotify (const casCtx &ctx, const gdd &value);
|
||||
|
||||
//
|
||||
// chCreate() is called each time that a PV is attached to
|
||||
// by a client. The server tool may choose not to
|
||||
@@ -600,6 +616,14 @@ public:
|
||||
//
|
||||
epicsShareFunc virtual caStatus write (const casCtx &ctx, const gdd &value);
|
||||
|
||||
//
|
||||
// writeNotify
|
||||
//
|
||||
// If this function is not provided in the derived class then casPV::writeNotify()
|
||||
// is called - see casPV::writeNotify() for additional comments.
|
||||
//
|
||||
epicsShareFunc virtual caStatus writeNotify (const casCtx &ctx, const gdd &value);
|
||||
|
||||
//
|
||||
// This is called for each channel in the server if
|
||||
// caServer::show() is called and the level is high
|
||||
|
||||
@@ -51,12 +51,12 @@ inBuf::~inBuf ()
|
||||
//
|
||||
// inBuf::show()
|
||||
//
|
||||
void inBuf::show (unsigned level) const
|
||||
void inBuf :: show (unsigned level) const
|
||||
{
|
||||
if ( level > 1u ) {
|
||||
printf (
|
||||
"\tUnprocessed request bytes = %d\n",
|
||||
this->bytesAvailable());
|
||||
this->bytesPresent () );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +73,9 @@ inBufClient::fillCondition inBuf::fill ( inBufClient::fillParameter parm )
|
||||
// move back any prexisting data to the start of the buffer
|
||||
//
|
||||
if ( this->nextReadIndex > 0 ) {
|
||||
bufSizeT unprocessedBytes;
|
||||
unprocessedBytes = this->bytesInBuffer - this->nextReadIndex;
|
||||
assert ( this->bytesInBuffer >= this->nextReadIndex );
|
||||
bufSizeT unprocessedBytes =
|
||||
this->bytesInBuffer - this->nextReadIndex;
|
||||
//
|
||||
// memmove() handles overlapping buffers
|
||||
//
|
||||
|
||||
@@ -51,7 +51,6 @@ public:
|
||||
// this is a hack for a Solaris IP kernel feature
|
||||
enum fillParameter { fpNone, fpUseBroadcastInterface };
|
||||
virtual unsigned getDebugLevel () const = 0;
|
||||
virtual bufSizeT incomingBytesPresent () const = 0;
|
||||
virtual fillCondition xRecv ( char *pBuf, bufSizeT nBytesToRecv,
|
||||
enum fillParameter parm, bufSizeT &nByesRecv ) = 0;
|
||||
virtual void hostName ( char *pBuf, unsigned bufSize ) const = 0;
|
||||
@@ -66,7 +65,6 @@ public:
|
||||
bufSizeT ioMinSizeIn );
|
||||
virtual ~inBuf ();
|
||||
bufSizeT bytesPresent () const;
|
||||
bufSizeT bytesAvailable () const;
|
||||
bool full () const;
|
||||
inBufClient::fillCondition fill (
|
||||
inBufClient::fillParameter parm = inBufClient::fpNone );
|
||||
@@ -107,17 +105,6 @@ inline bufSizeT inBuf::bytesPresent () const
|
||||
return this->bytesInBuffer - this->nextReadIndex;
|
||||
}
|
||||
|
||||
//
|
||||
// inBuf::bytesAvailable()
|
||||
//
|
||||
inline bufSizeT inBuf::bytesAvailable () const
|
||||
{
|
||||
bufSizeT bp;
|
||||
bp = this->bytesPresent ();
|
||||
bp += this->client.incomingBytesPresent ();
|
||||
return bp;
|
||||
}
|
||||
|
||||
//
|
||||
// inBuf::full()
|
||||
//
|
||||
|
||||
@@ -15,11 +15,19 @@
|
||||
*/
|
||||
|
||||
#include "errlog.h"
|
||||
#include "epicsTime.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "outBuf.h"
|
||||
#include "osiWireFormat.h"
|
||||
|
||||
const char * outBufClient :: ppFlushCondText[3] =
|
||||
{
|
||||
"flushNone",
|
||||
"flushProgress",
|
||||
"flushDisconnect"
|
||||
};
|
||||
|
||||
//
|
||||
// outBuf::outBuf()
|
||||
//
|
||||
@@ -62,11 +70,10 @@ caStatus outBuf::allocRawMsg ( bufSizeT msgsize, void **ppMsg )
|
||||
stackNeeded = this->bufSize - msgsize;
|
||||
|
||||
if ( this->stack > stackNeeded ) {
|
||||
|
||||
//
|
||||
// Try to flush the output queue
|
||||
//
|
||||
this->flush ( this->stack - stackNeeded );
|
||||
this->flush ();
|
||||
|
||||
//
|
||||
// If this failed then the fd is nonblocking
|
||||
@@ -216,45 +223,32 @@ void outBuf::commitMsg ( ca_uint32_t reducedPayloadSize )
|
||||
//
|
||||
// outBuf::flush ()
|
||||
//
|
||||
outBufClient::flushCondition outBuf::flush ( bufSizeT spaceRequired )
|
||||
outBufClient::flushCondition outBuf :: flush ()
|
||||
{
|
||||
bufSizeT nBytes;
|
||||
bufSizeT nBytesRequired;
|
||||
outBufClient::flushCondition cond;
|
||||
|
||||
if ( this->ctxRecursCount > 0 ) {
|
||||
return outBufClient::flushNone;
|
||||
}
|
||||
|
||||
if ( spaceRequired > this->bufSize ) {
|
||||
nBytesRequired = this->stack;
|
||||
}
|
||||
else {
|
||||
bufSizeT stackPermitted;
|
||||
|
||||
stackPermitted = this->bufSize - spaceRequired;
|
||||
if ( this->stack > stackPermitted ) {
|
||||
nBytesRequired = this->stack - stackPermitted;
|
||||
}
|
||||
else {
|
||||
nBytesRequired = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
cond = this->client.xSend ( this->pBuf, this->stack,
|
||||
nBytesRequired, nBytes );
|
||||
bufSizeT nBytesSent;
|
||||
//epicsTime beg = epicsTime::getCurrent ();
|
||||
outBufClient :: flushCondition cond =
|
||||
this->client.xSend ( this->pBuf, this->stack, nBytesSent );
|
||||
//epicsTime end = epicsTime::getCurrent ();
|
||||
//printf ( "send of %u bytes, stat =%s, cost us %f u sec\n",
|
||||
// this->stack, this->client.ppFlushCondText[cond], ( end - beg ) * 1e6 );
|
||||
if ( cond == outBufClient::flushProgress ) {
|
||||
bufSizeT len;
|
||||
|
||||
if ( nBytes >= this->stack ) {
|
||||
if ( nBytesSent >= this->stack ) {
|
||||
this->stack = 0u;
|
||||
}
|
||||
else {
|
||||
len = this->stack-nBytes;
|
||||
bufSizeT len = this->stack - nBytesSent;
|
||||
//
|
||||
// memmove() is ok with overlapping buffers
|
||||
//
|
||||
memmove ( this->pBuf, &this->pBuf[nBytes], len );
|
||||
//epicsTime beg = epicsTime::getCurrent ();
|
||||
memmove ( this->pBuf, &this->pBuf[nBytesSent], len );
|
||||
//epicsTime end = epicsTime::getCurrent ();
|
||||
//printf ( "mem move cost us %f nano sec\n", ( end - beg ) * 1e9 );
|
||||
this->stack = len;
|
||||
}
|
||||
|
||||
@@ -262,7 +256,7 @@ outBufClient::flushCondition outBuf::flush ( bufSizeT spaceRequired )
|
||||
char buf[64];
|
||||
this->client.hostName ( buf, sizeof ( buf ) );
|
||||
fprintf ( stderr, "CAS outgoing: %u byte reply to %s\n",
|
||||
nBytes, buf );
|
||||
nBytesSent, buf );
|
||||
}
|
||||
}
|
||||
return cond;
|
||||
|
||||
@@ -47,12 +47,18 @@ private:
|
||||
|
||||
class outBufClient { // X aCC 655
|
||||
public:
|
||||
enum flushCondition { flushNone, flushProgress, flushDisconnect };
|
||||
enum flushCondition {
|
||||
flushNone = 0,
|
||||
flushProgress = 1,
|
||||
flushDisconnect = 2
|
||||
};
|
||||
static const char * ppFlushCondText[3];
|
||||
virtual unsigned getDebugLevel () const = 0;
|
||||
virtual void sendBlockSignal () = 0;
|
||||
virtual flushCondition xSend ( char *pBuf, bufSizeT nBytesAvailableToSend,
|
||||
bufSizeT nBytesNeedToBeSent, bufSizeT &nBytesSent ) = 0;
|
||||
virtual flushCondition xSend ( char *pBuf, bufSizeT nBytesToSend,
|
||||
bufSizeT & nBytesSent ) = 0;
|
||||
virtual void hostName ( char *pBuf, unsigned bufSize ) const = 0;
|
||||
virtual bufSizeT osSendBufferSize () const = 0;
|
||||
protected:
|
||||
virtual ~outBufClient() {}
|
||||
};
|
||||
@@ -72,7 +78,7 @@ public:
|
||||
// flush output queue
|
||||
// (returns the number of bytes sent)
|
||||
//
|
||||
outBufClient::flushCondition flush ( bufSizeT spaceRequired=bufSizeT_MAX );
|
||||
outBufClient::flushCondition flush ();
|
||||
|
||||
void show (unsigned level) const;
|
||||
|
||||
@@ -128,13 +134,7 @@ private:
|
||||
//
|
||||
inline bufSizeT outBuf::bytesPresent () const
|
||||
{
|
||||
//
|
||||
// Note on use of lock here:
|
||||
// This guarantees that any pushCtx() operation
|
||||
// in progress completes before another thread checks.
|
||||
//
|
||||
bufSizeT result = this->stack;
|
||||
return result;
|
||||
return this->stack;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
// $Id$
|
||||
//
|
||||
|
||||
|
||||
#include "fdManager.h"
|
||||
#include "errlog.h"
|
||||
|
||||
@@ -27,7 +26,6 @@ public:
|
||||
casDGReadReg ( casDGIntfOS & osIn ) :
|
||||
fdReg (osIn.getFD(), fdrRead), os (osIn) {}
|
||||
~casDGReadReg ();
|
||||
|
||||
void show (unsigned level) const;
|
||||
private:
|
||||
casDGIntfOS &os;
|
||||
@@ -81,8 +79,7 @@ casDGIntfOS::casDGIntfOS (
|
||||
autoBeaconAddr, addConfigBeaconAddr ),
|
||||
pRdReg ( 0 ),
|
||||
pBCastRdReg ( 0 ),
|
||||
pWtReg ( 0 ),
|
||||
sendBlocked ( false )
|
||||
pWtReg ( 0 )
|
||||
{
|
||||
this->xSetNonBlocking();
|
||||
this->armRecv();
|
||||
@@ -140,10 +137,18 @@ void casDGEvWakeup::show ( unsigned level ) const
|
||||
//
|
||||
epicsTimerNotify::expireStatus casDGEvWakeup::expire ( const epicsTime & /* currentTime */ )
|
||||
{
|
||||
casEventSys::processStatus ps = this->pOS->eventSysProcess ();
|
||||
if ( ps.nAccepted > 0u ) {
|
||||
this->pOS->eventFlush ();
|
||||
}
|
||||
this->pOS->eventSysProcess ();
|
||||
// We do not wait for any impartial, or complete,
|
||||
// messages in the input queue to be processed
|
||||
// because.
|
||||
// A) IO postponement might be preventing the
|
||||
// input queue processing from proceeding.
|
||||
// B) Since both reads and events get processed
|
||||
// before going back to select to find out if we
|
||||
// can do a write then we naturally tend to
|
||||
// combine get responses and subscription responses
|
||||
// into one write.
|
||||
this->pOS->armSend ();
|
||||
this->pOS = 0;
|
||||
return noRestart;
|
||||
}
|
||||
@@ -168,21 +173,22 @@ casDGIOWakeup::~casDGIOWakeup()
|
||||
// casDGIOWakeup::expire()
|
||||
//
|
||||
// Running this indirectly off of the timer queue
|
||||
// guarantees that we will not call processInput()
|
||||
// guarantees that we will not call processDG()
|
||||
// recursively
|
||||
//
|
||||
epicsTimerNotify::expireStatus casDGIOWakeup::expire( const epicsTime & /* currentTime */ )
|
||||
epicsTimerNotify :: expireStatus
|
||||
casDGIOWakeup :: expire( const epicsTime & /* currentTime */ )
|
||||
{
|
||||
//
|
||||
// in case there is something in the input buffer
|
||||
// and currently nothing to be read from UDP
|
||||
//
|
||||
// processInput() does an armRecv() so
|
||||
// if recv is not armed, there is space in
|
||||
// the input buffer, and there eventually will
|
||||
// be something to read from TCP this works
|
||||
//
|
||||
this->pOS->processInput ();
|
||||
caStatus status = this->pOS->processDG ();
|
||||
if ( status != S_cas_success &&
|
||||
status != S_cas_sendBlocked ) {
|
||||
char pName[64u];
|
||||
this->pOS->hostName (pName, sizeof (pName));
|
||||
errPrintf (status, __FILE__, __LINE__,
|
||||
"unexpected problem with UDP input from \"%s\"", pName);
|
||||
}
|
||||
this->pOS->armRecv ();
|
||||
this->pOS->armSend ();
|
||||
this->pOS = 0;
|
||||
return noRestart;
|
||||
}
|
||||
@@ -243,7 +249,7 @@ void casDGIntfOS::disarmRecv()
|
||||
//
|
||||
void casDGIntfOS::armSend()
|
||||
{
|
||||
if ( this->outBufBytesPresent () == 0u ) {
|
||||
if ( this->outBufBytesPending () == 0u ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -277,20 +283,6 @@ void casDGIntfOS::eventSignal()
|
||||
this->evWk.start ( *this );
|
||||
}
|
||||
|
||||
//
|
||||
// casDGIntfOS::eventFlush()
|
||||
//
|
||||
void casDGIntfOS::eventFlush()
|
||||
{
|
||||
//
|
||||
// if there is nothing pending in the input
|
||||
// queue, then flush the output queue
|
||||
//
|
||||
if ( this->inBufBytesAvailable() == 0u ) {
|
||||
this->armSend ();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// casDGIntfOS::show()
|
||||
//
|
||||
@@ -396,7 +388,6 @@ void casDGWriteReg::show(unsigned level) const
|
||||
//
|
||||
void casDGIntfOS::sendBlockSignal()
|
||||
{
|
||||
this->sendBlocked = true;
|
||||
this->armSend();
|
||||
}
|
||||
|
||||
@@ -412,47 +403,53 @@ void casDGIntfOS::sendCB()
|
||||
// attempt to flush the output buffer
|
||||
//
|
||||
outBufClient::flushCondition flushCond = this->flush ();
|
||||
if ( flushCond != flushProgress ) {
|
||||
return;
|
||||
}
|
||||
if ( flushCond == flushProgress ) {
|
||||
//
|
||||
// If we are unable to flush out all of the events
|
||||
// in casDgEvWakeup::expire() because the
|
||||
// client is slow then we must check again here when
|
||||
// we _are_ able to write to see if additional events
|
||||
// can be sent to the slow client.
|
||||
//
|
||||
this->eventSysProcess ();
|
||||
|
||||
if ( this->sendBlocked ) {
|
||||
this->sendBlocked = false;
|
||||
}
|
||||
//
|
||||
// If we were able to send something then we need
|
||||
// to process the input queue in case we were send
|
||||
// blocked.
|
||||
//
|
||||
caStatus status = this->processDG ();
|
||||
if ( status != S_cas_success &&
|
||||
status != S_cas_sendBlocked ) {
|
||||
char pName[64u];
|
||||
this->hostName (pName, sizeof (pName));
|
||||
errPrintf ( status, __FILE__, __LINE__,
|
||||
"unexpected problem with UDP input from \"%s\"", pName);
|
||||
}
|
||||
}
|
||||
|
||||
# if defined(DEBUG)
|
||||
printf ("write attempted on %d result was %d\n", this->getFD(), flushCond);
|
||||
printf ("write attempted on %d result was %d\n", this->getFD(), flushCond );
|
||||
printf ("Recv backlog %u\n", this->inBuf::bytesPresent());
|
||||
printf ("Send backlog %u\n", this->outBuf::bytesPresent());
|
||||
# endif
|
||||
|
||||
//
|
||||
// If we are unable to flush out all of the events
|
||||
// in casDgEvWakeup::expire() because the
|
||||
// client is slow then we must check again here when
|
||||
// we _are_ able to write to see if additional events
|
||||
// can be sent to the slow client.
|
||||
//
|
||||
this->eventSysProcess ();
|
||||
|
||||
//
|
||||
// If we were able to send something then we need
|
||||
// to process the input queue in case we were send
|
||||
// blocked.
|
||||
//
|
||||
this->processInput ();
|
||||
|
||||
//
|
||||
// this reenables receipt of incoming frames once
|
||||
// the output has been flushed
|
||||
// the output has been flushed in case the receive
|
||||
// side is blocked due to lack of buffer space
|
||||
//
|
||||
this->armRecv ();
|
||||
|
||||
|
||||
// once we start sending we continue until done
|
||||
this->armSend ();
|
||||
}
|
||||
|
||||
//
|
||||
// casDGIntfOS::recvCB()
|
||||
//
|
||||
void casDGIntfOS::recvCB ( inBufClient::fillParameter parm )
|
||||
void casDGIntfOS :: recvCB ( inBufClient::fillParameter parm )
|
||||
{
|
||||
assert ( this->pRdReg );
|
||||
|
||||
@@ -460,7 +457,16 @@ void casDGIntfOS::recvCB ( inBufClient::fillParameter parm )
|
||||
// copy in new messages
|
||||
//
|
||||
this->inBufFill ( parm );
|
||||
this->processInput ();
|
||||
caStatus status = this->processDG ();
|
||||
if ( status != S_cas_success &&
|
||||
status != S_cas_sendBlocked ) {
|
||||
char pName[64u];
|
||||
this->hostName (pName, sizeof (pName));
|
||||
errPrintf (status, __FILE__, __LINE__,
|
||||
"unexpected problem with UDP input from \"%s\"", pName);
|
||||
}
|
||||
|
||||
this->armSend ();
|
||||
|
||||
//
|
||||
// If there isnt any space then temporarily
|
||||
@@ -477,38 +483,3 @@ void casDGIntfOS::recvCB ( inBufClient::fillParameter parm )
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// casDGIntfOS::processInput()
|
||||
//
|
||||
void casDGIntfOS::processInput()
|
||||
{
|
||||
caStatus status;
|
||||
|
||||
//
|
||||
// attempt to process the datagram in the input
|
||||
// buffer
|
||||
//
|
||||
status = this->processDG ();
|
||||
if (status!=S_cas_success &&
|
||||
status!=S_cas_sendBlocked &&
|
||||
status!=S_casApp_postponeAsyncIO) {
|
||||
char pName[64u];
|
||||
|
||||
this->hostName (pName, sizeof (pName));
|
||||
errPrintf (status, __FILE__, __LINE__,
|
||||
"unexpected problem with UDP input from \"%s\"", pName);
|
||||
}
|
||||
|
||||
//
|
||||
// if anything is in the send buffer
|
||||
// and there are not requests pending in the
|
||||
// input buffer then keep sending the output
|
||||
// buffer until it is empty
|
||||
//
|
||||
if ( this->outBufBytesPresent() > 0u ) {
|
||||
if ( this->inBufBytesAvailable () == 0 ) {
|
||||
this->armSend ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,26 +23,21 @@ class casDGIntfOS : public casDGIntfIO {
|
||||
friend class casDGReadReg;
|
||||
friend class casDGBCastReadReg;
|
||||
friend class casDGWriteReg;
|
||||
friend class casDGEvWakeup;
|
||||
friend class casDGIOWakeup;
|
||||
friend class casStreamEvWakeup;
|
||||
public:
|
||||
casDGIntfOS ( caServerI &, clientBufMemoryManager &,
|
||||
const caNetAddr & addr, bool autoBeaconAddr = true,
|
||||
bool addConfigBeaconAddr = false);
|
||||
|
||||
virtual ~casDGIntfOS ();
|
||||
|
||||
virtual void show (unsigned level) const;
|
||||
|
||||
void processInput();
|
||||
|
||||
void eventFlush ();
|
||||
|
||||
private:
|
||||
casDGIOWakeup ioWk;
|
||||
casDGEvWakeup evWk;
|
||||
class casDGReadReg * pRdReg;
|
||||
class casDGBCastReadReg * pBCastRdReg; // fix for solaris bug
|
||||
class casDGWriteReg * pWtReg;
|
||||
bool sendBlocked;
|
||||
|
||||
void armRecv ();
|
||||
void armSend ();
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
// casStreamOS.cc
|
||||
// $Id$
|
||||
//
|
||||
//
|
||||
//
|
||||
// TO DO:
|
||||
// o armRecv() and armSend() should return bad status when
|
||||
// there isnt enough memory
|
||||
@@ -23,6 +21,34 @@
|
||||
#define epicsExportSharedFunc
|
||||
#include "casStreamOS.h"
|
||||
|
||||
#if 0
|
||||
#define DEBUG
|
||||
#endif
|
||||
|
||||
//
|
||||
// printStatus ()
|
||||
//
|
||||
#if defined(DEBUG)
|
||||
void casStreamOS :: printStatus ( const char * pCtx ) const
|
||||
{
|
||||
static epicsTime beginTime = epicsTime :: getCurrent ();
|
||||
epicsTime current = epicsTime :: getCurrent ();
|
||||
printf (
|
||||
"%03.3f, "
|
||||
"Sock %d, %s, "
|
||||
"RecvBuf %u, "
|
||||
"SendBuf %u\n",
|
||||
current - beginTime,
|
||||
this->getFD(),
|
||||
pCtx,
|
||||
this->inBufBytesPending (),
|
||||
this->outBufBytesPending () );
|
||||
fflush ( stdout );
|
||||
}
|
||||
#else
|
||||
inline void casStreamOS :: printStatus ( const char * ) const {}
|
||||
#endif
|
||||
|
||||
//
|
||||
// casStreamReadReg
|
||||
//
|
||||
@@ -44,13 +70,7 @@ private:
|
||||
inline casStreamReadReg::casStreamReadReg (casStreamOS &osIn) :
|
||||
fdReg (osIn.getFD(), fdrRead), os (osIn)
|
||||
{
|
||||
# if defined(DEBUG)
|
||||
printf ("Read on %d\n", this->os.getFD());
|
||||
printf ("Recv backlog %u\n",
|
||||
this->os.in.bytesPresent());
|
||||
printf ("Send backlog %u\n",
|
||||
this->os.out.bytesPresent());
|
||||
# endif
|
||||
this->os.printStatus ( "read schedualed" );
|
||||
}
|
||||
|
||||
//
|
||||
@@ -58,13 +78,7 @@ inline casStreamReadReg::casStreamReadReg (casStreamOS &osIn) :
|
||||
//
|
||||
inline casStreamReadReg::~casStreamReadReg ()
|
||||
{
|
||||
# if defined(DEBUG)
|
||||
printf ("Read off %d\n", this->os.getFD());
|
||||
printf ("Recv backlog %u\n",
|
||||
this->os.in.bytesPresent());
|
||||
printf ("Send backlog %u\n",
|
||||
this->os.out.bytesPresent());
|
||||
# endif
|
||||
this->os.printStatus ( "read unschedualed" );
|
||||
}
|
||||
|
||||
//
|
||||
@@ -89,13 +103,7 @@ private:
|
||||
inline casStreamWriteReg::casStreamWriteReg (casStreamOS &osIn) :
|
||||
fdReg (osIn.getFD(), fdrWrite, true), os (osIn)
|
||||
{
|
||||
# if defined(DEBUG)
|
||||
printf ("Write on %d\n", this->os.getFD());
|
||||
printf ("Recv backlog %u\n",
|
||||
this->os.in.bytesPresent());
|
||||
printf ("Send backlog %u\n",
|
||||
this->os.out.bytesPresent());
|
||||
# endif
|
||||
this->os.printStatus ( "write schedualed" );
|
||||
}
|
||||
|
||||
//
|
||||
@@ -103,13 +111,7 @@ inline casStreamWriteReg::casStreamWriteReg (casStreamOS &osIn) :
|
||||
//
|
||||
inline casStreamWriteReg::~casStreamWriteReg ()
|
||||
{
|
||||
# if defined(DEBUG)
|
||||
printf ("Write off %d\n", this->os.getFD());
|
||||
printf ("Recv backlog %u\n",
|
||||
this->os.in.bytesPresent());
|
||||
printf ("Send backlog %u\n",
|
||||
this->os.out.bytesPresent());
|
||||
# endif
|
||||
this->os.printStatus ( "write unschedualed" );
|
||||
}
|
||||
|
||||
//
|
||||
@@ -142,13 +144,31 @@ void casStreamEvWakeup::show(unsigned level) const
|
||||
//
|
||||
// casStreamEvWakeup::expire()
|
||||
//
|
||||
epicsTimerNotify::expireStatus casStreamEvWakeup::expire ( const epicsTime & /* currentTime */ )
|
||||
epicsTimerNotify::expireStatus casStreamEvWakeup::
|
||||
expire ( const epicsTime & /* currentTime */ )
|
||||
{
|
||||
casEventSys::processStatus ps = os.eventSysProcess ();
|
||||
if ( ps.nAccepted > 0u ) {
|
||||
this->os.eventFlush ();
|
||||
this->os.printStatus ( "casStreamEvWakeup tmr expire" );
|
||||
casProcCond pc = os.eventSysProcess ();
|
||||
if ( pc == casProcOk ) {
|
||||
// We do not wait for any impartial, or complete,
|
||||
// messages in the input queue to be processed
|
||||
// because.
|
||||
// A) IO postponement might be preventing the
|
||||
// input queue processing from proceeding.
|
||||
// B) We dont want to interrupt subscription
|
||||
// updates while waiting for very large arrays
|
||||
// to be read in a packet at a time.
|
||||
// C) Since both reads and events get processed
|
||||
// before going back to select to find out if we
|
||||
// can do a write then we naturally tend to
|
||||
// combine get responses and subscription responses
|
||||
// into one write.
|
||||
// D) Its probably questionable to hold up event
|
||||
// traffic (introduce latency) because a partial
|
||||
// message is pending in the input queue.
|
||||
this->os.armSend ();
|
||||
}
|
||||
if ( ps.cond != casProcOk ) {
|
||||
else {
|
||||
//
|
||||
// ok to delete the client here
|
||||
// because casStreamEvWakeup::expire()
|
||||
@@ -170,14 +190,16 @@ epicsTimerNotify::expireStatus casStreamEvWakeup::expire ( const epicsTime & /*
|
||||
//
|
||||
// casStreamEvWakeup::start()
|
||||
//
|
||||
// care is needed here because this is called
|
||||
// asynchronously by postEvent
|
||||
//
|
||||
// there is some overhead here but care is taken
|
||||
// in the caller of this routine to call this
|
||||
// only when its the 2nd event on the queue
|
||||
//
|
||||
void casStreamEvWakeup::start( casStreamOS & )
|
||||
{
|
||||
// care is needed here because this is called
|
||||
// asynchronously by postEvent
|
||||
//
|
||||
// there is some overhead here but care is taken
|
||||
// in the caller of this routine to call this
|
||||
// only when its the 2nd event on the queue
|
||||
this->os.printStatus ( "casStreamEvWakeup tmr start" );
|
||||
this->timer.start ( *this, 0.0 );
|
||||
}
|
||||
|
||||
@@ -211,16 +233,52 @@ void casStreamIOWakeup::show ( unsigned level ) const
|
||||
//
|
||||
// casStreamIOWakeup::expire()
|
||||
//
|
||||
// This is called whenever asynchronous IO completes
|
||||
//
|
||||
// Running this indirectly off of the timer queue
|
||||
// guarantees that we will not call processInput()
|
||||
// guarantees that we will not call processMsg()
|
||||
// recursively
|
||||
//
|
||||
epicsTimerNotify::expireStatus casStreamIOWakeup::expire ( const epicsTime & /* currentTime */ )
|
||||
epicsTimerNotify::expireStatus casStreamIOWakeup ::
|
||||
expire ( const epicsTime & /* currentTime */ )
|
||||
{
|
||||
assert ( this->pOS );
|
||||
this->pOS->printStatus ( "casStreamIOWakeup tmr expire" );
|
||||
casStreamOS & tmpOS = *this->pOS;
|
||||
this->pOS = 0;
|
||||
tmpOS.processInput();
|
||||
caStatus status = tmpOS.processMsg ();
|
||||
if ( status == S_cas_success ) {
|
||||
tmpOS.armRecv ();
|
||||
if ( tmpOS._sendNeeded () ) {
|
||||
tmpOS.armSend ();
|
||||
}
|
||||
}
|
||||
else if ( status == S_cas_sendBlocked ) {
|
||||
tmpOS.armSend ();
|
||||
// always activate receives if space is available
|
||||
// in the in buf
|
||||
tmpOS.armRecv ();
|
||||
}
|
||||
else if ( status == S_casApp_postponeAsyncIO ) {
|
||||
// we should be back on the IO blocked list
|
||||
// if S_casApp_postponeAsyncIO was returned
|
||||
// so this function will be called again when
|
||||
// another asynchronous request completes
|
||||
tmpOS.armSend ();
|
||||
// always activate receives if space is available
|
||||
// in the in buf
|
||||
tmpOS.armRecv ();
|
||||
}
|
||||
else {
|
||||
errMessage ( status,
|
||||
"- unexpected problem with client's input - forcing disconnect");
|
||||
tmpOS.getCAS().destroyClient ( tmpOS );
|
||||
//
|
||||
// must _not_ touch "tmpOS" ref
|
||||
// after the destroy
|
||||
//
|
||||
return noRestart;
|
||||
}
|
||||
return noRestart;
|
||||
}
|
||||
|
||||
@@ -236,6 +294,7 @@ void casStreamIOWakeup::start ( casStreamOS &os )
|
||||
this->pOS = &os;
|
||||
this->timer.start ( *this, 0.0 );
|
||||
}
|
||||
this->pOS->printStatus ( "casStreamIOWakeup tmr start" );
|
||||
}
|
||||
|
||||
//
|
||||
@@ -264,7 +323,7 @@ inline void casStreamOS::disarmRecv ()
|
||||
//
|
||||
inline void casStreamOS::armSend()
|
||||
{
|
||||
if ( this->outBufBytesPresent() == 0u ) {
|
||||
if ( this->outBufBytesPending() == 0u ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -284,6 +343,7 @@ inline void casStreamOS::disarmSend ()
|
||||
|
||||
//
|
||||
// casStreamOS::ioBlockedSignal()
|
||||
// (called by main thread when lock is applied)
|
||||
//
|
||||
void casStreamOS::ioBlockedSignal()
|
||||
{
|
||||
@@ -292,26 +352,14 @@ void casStreamOS::ioBlockedSignal()
|
||||
|
||||
//
|
||||
// casStreamOS::eventSignal()
|
||||
// (called by any thread asynchronously
|
||||
// when an event is posted)
|
||||
//
|
||||
void casStreamOS::eventSignal()
|
||||
{
|
||||
this->evWk.start ( *this );
|
||||
}
|
||||
|
||||
//
|
||||
// casStreamOS::eventFlush()
|
||||
//
|
||||
void casStreamOS::eventFlush()
|
||||
{
|
||||
//
|
||||
// if there is nothing pending in the input
|
||||
// queue, then flush the output queue
|
||||
//
|
||||
if ( this->inBufBytesAvailable() == 0u ) {
|
||||
this->armSend ();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// casStreamOS::casStreamOS()
|
||||
//
|
||||
@@ -319,9 +367,14 @@ casStreamOS::casStreamOS (
|
||||
caServerI & cas, clientBufMemoryManager & bufMgrIn,
|
||||
const ioArgsToNewStreamIO & ioArgs ) :
|
||||
casStreamIO ( cas, bufMgrIn, ioArgs ),
|
||||
evWk ( *this ), pWtReg ( 0 ), pRdReg ( 0 ),
|
||||
sendBlocked ( false )
|
||||
evWk ( *this ),
|
||||
pWtReg ( 0 ),
|
||||
pRdReg ( 0 ),
|
||||
_sendBacklogThresh ( osSendBufferSize () / 2u )
|
||||
{
|
||||
if ( _sendBacklogThresh < MAX_TCP / 2 ) {
|
||||
_sendBacklogThresh = MAX_TCP / 2;
|
||||
}
|
||||
this->xSetNonBlocking ();
|
||||
this->armRecv ();
|
||||
}
|
||||
@@ -348,7 +401,6 @@ void casStreamOS::show ( unsigned level ) const
|
||||
this->casStrmClient::show ( level );
|
||||
printf ( "casStreamOS at %p\n",
|
||||
static_cast <const void *> ( this ) );
|
||||
printf ( "\tsendBlocked = %d\n", this->sendBlocked );
|
||||
if ( this->pWtReg ) {
|
||||
this->pWtReg->show ( level );
|
||||
}
|
||||
@@ -384,9 +436,11 @@ void casStreamReadReg::callBack ()
|
||||
//
|
||||
// casStreamOS::recvCB()
|
||||
//
|
||||
void casStreamOS::recvCB ()
|
||||
void casStreamOS :: recvCB ()
|
||||
{
|
||||
assert ( this->pRdReg );
|
||||
|
||||
printStatus ( "receiving" );
|
||||
|
||||
//
|
||||
// copy in new messages
|
||||
@@ -394,38 +448,50 @@ void casStreamOS::recvCB ()
|
||||
inBufClient::fillCondition fillCond = this->inBufFill ();
|
||||
if ( fillCond == casFillDisconnect ) {
|
||||
this->getCAS().destroyClient ( *this );
|
||||
//
|
||||
// must _not_ touch "this" pointer
|
||||
// after the destroy
|
||||
//
|
||||
return;
|
||||
}
|
||||
else {
|
||||
casProcCond procCond = this->processInput ();
|
||||
if ( procCond == casProcDisconnect ) {
|
||||
this->getCAS().destroyClient ( *this );
|
||||
}
|
||||
else if ( this->inBufFull() ) {
|
||||
//
|
||||
// If there isnt any space then temporarily
|
||||
// stop calling this routine until problem is resolved
|
||||
// either by:
|
||||
// (1) sending or
|
||||
// (2) a blocked IO op unblocks
|
||||
//
|
||||
// (casStreamReadReg is _not_ a onceOnly fdReg -
|
||||
// therefore an explicit delete is required here)
|
||||
//
|
||||
this->disarmRecv (); // this deletes the casStreamReadReg object
|
||||
}
|
||||
else if ( fillCond == casFillNone ) {
|
||||
if ( this->inBufFull() ) {
|
||||
this->disarmRecv ();
|
||||
}
|
||||
}
|
||||
else {
|
||||
printStatus ( "recv CB req proc" );
|
||||
caStatus status = this->processMsg ();
|
||||
if ( status == S_cas_success ) {
|
||||
this->armRecv ();
|
||||
if ( _sendNeeded () ) {
|
||||
this->armSend ();
|
||||
}
|
||||
}
|
||||
else if ( status == S_cas_sendBlocked ) {
|
||||
this->armSend ();
|
||||
}
|
||||
else if ( status == S_casApp_postponeAsyncIO ) {
|
||||
this->armSend ();
|
||||
}
|
||||
else {
|
||||
errMessage ( status,
|
||||
"- unexpected problem with client's input - forcing disconnect");
|
||||
this->getCAS().destroyClient ( *this );
|
||||
//
|
||||
// must _not_ touch "this" pointer
|
||||
// after the destroy
|
||||
//
|
||||
return;
|
||||
}
|
||||
}
|
||||
//
|
||||
// NO CODE HERE
|
||||
// (see delete above)
|
||||
//
|
||||
}
|
||||
|
||||
//
|
||||
// casStreamOS::sendBlockSignal()
|
||||
// casStreamOS :: sendBlockSignal()
|
||||
//
|
||||
void casStreamOS::sendBlockSignal ()
|
||||
void casStreamOS :: sendBlockSignal ()
|
||||
{
|
||||
this->sendBlocked = true;
|
||||
this->armSend ();
|
||||
}
|
||||
|
||||
@@ -456,18 +522,21 @@ void casStreamWriteReg::callBack()
|
||||
//
|
||||
void casStreamOS::sendCB ()
|
||||
{
|
||||
// we know that the fdManager will destroy the write
|
||||
// registration after this function returns, and that
|
||||
// it is robust in situations where the callback
|
||||
// deletes its fdReg derived object so delete it now,
|
||||
// because we can now reschedule a send as needed
|
||||
//
|
||||
this->disarmSend ();
|
||||
|
||||
printStatus ( "writing" );
|
||||
|
||||
//
|
||||
// attempt to flush the output buffer
|
||||
//
|
||||
outBufClient::flushCondition flushCond = this->flush ();
|
||||
if ( flushCond == flushProgress ) {
|
||||
if ( this->sendBlocked ) {
|
||||
this->sendBlocked = false;
|
||||
}
|
||||
}
|
||||
else if ( flushCond == outBufClient::flushDisconnect ) {
|
||||
if ( flushCond == outBufClient::flushDisconnect ) {
|
||||
//
|
||||
// ok to delete the client here
|
||||
// because casStreamWriteReg::callBack()
|
||||
@@ -491,8 +560,8 @@ void casStreamOS::sendCB ()
|
||||
// we _are_ able to write to see if additional events
|
||||
// can be sent to the slow client.
|
||||
//
|
||||
casEventSys::processStatus ps = this->eventSysProcess ();
|
||||
if ( ps.cond != casProcOk ) {
|
||||
casProcCond pc = this->eventSysProcess ();
|
||||
if ( pc != casProcOk ) {
|
||||
//
|
||||
// ok to delete the client here
|
||||
// because casStreamWriteReg::callBack()
|
||||
@@ -508,80 +577,57 @@ void casStreamOS::sendCB ()
|
||||
//
|
||||
return;
|
||||
}
|
||||
|
||||
# if defined(DEBUG)
|
||||
printf ( "write attempted on %d result was %d\n",
|
||||
this->getFD(), flushCond );
|
||||
printf ( "Recv backlog %u\n", this->in.bytesPresent() );
|
||||
printf ( "Send backlog %u\n", this->out.bytesPresent() );
|
||||
# endif
|
||||
|
||||
printStatus ( ppFlushCondText [ flushCond ] );
|
||||
|
||||
//
|
||||
// If we were able to send something then we need
|
||||
// to process the input queue in case we were send
|
||||
// blocked.
|
||||
//
|
||||
casProcCond procCond = this->processInput ();
|
||||
if ( procCond == casProcDisconnect ) {
|
||||
this->getCAS().destroyClient ( *this );
|
||||
}
|
||||
else {
|
||||
//
|
||||
// if anything is left in the send buffer that
|
||||
// still needs to be sent and there are not
|
||||
// requests pending in the input buffer then
|
||||
// keep sending the output buffer until it is
|
||||
// empty
|
||||
//
|
||||
// do not test for this with flushCond since
|
||||
// additional bytes may have been added since
|
||||
// we flushed the out buffer
|
||||
//
|
||||
if ( this->outBufBytesPresent() > 0u &&
|
||||
this->inBufBytesAvailable() == 0u ) {
|
||||
this->armSend();
|
||||
}
|
||||
}
|
||||
//
|
||||
// NO CODE HERE
|
||||
// (see deletes above)
|
||||
//
|
||||
|
||||
bufSizeT inBufBytesPend = this->inBufBytesPending ();
|
||||
if ( flushCond == flushProgress && inBufBytesPend ) {
|
||||
printStatus ( "send CB req proc" );
|
||||
caStatus status = this->processMsg ();
|
||||
if ( status == S_cas_success ) {
|
||||
this->armRecv ();
|
||||
}
|
||||
else if ( status == S_cas_sendBlocked
|
||||
|| status == S_casApp_postponeAsyncIO ) {
|
||||
bufSizeT inBufBytesPendNew = this->inBufBytesPending ();
|
||||
if ( inBufBytesPendNew < inBufBytesPend ) {
|
||||
this->armRecv ();
|
||||
}
|
||||
}
|
||||
else {
|
||||
errMessage ( status,
|
||||
"- unexpected problem with client's input - forcing disconnect");
|
||||
this->getCAS().destroyClient ( *this );
|
||||
//
|
||||
// must _not_ touch "this" pointer
|
||||
// after the destroy
|
||||
//
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Once a send starts we will keep going with it until
|
||||
// it flushes all of the way out. Its important to
|
||||
// perform this step only after processMsg so that we
|
||||
// flush out any send blocks detected by processMsg.
|
||||
this->armSend ();
|
||||
}
|
||||
|
||||
//
|
||||
// casStreamOS::processInput()
|
||||
// casStreamOS :: sendNeeded ()
|
||||
//
|
||||
casProcCond casStreamOS::processInput() // X aCC 361
|
||||
bool casStreamOS ::
|
||||
_sendNeeded () const
|
||||
{
|
||||
caStatus status;
|
||||
|
||||
# ifdef DEBUG
|
||||
printf(
|
||||
"Resp bytes to send=%d, Req bytes pending %d\n",
|
||||
this->out.bytesPresent(),
|
||||
this->in.bytesPresent());
|
||||
# endif
|
||||
|
||||
status = this->processMsg();
|
||||
if (status==S_cas_success ||
|
||||
status==S_cas_sendBlocked ||
|
||||
status==S_casApp_postponeAsyncIO) {
|
||||
|
||||
//
|
||||
// if there is nothing pending in the input
|
||||
// queue, then flush the output queue
|
||||
//
|
||||
if ( this->inBufBytesAvailable() == 0u ) {
|
||||
this->armSend ();
|
||||
}
|
||||
this->armRecv ();
|
||||
|
||||
return casProcOk;
|
||||
}
|
||||
else {
|
||||
errMessage ( status,
|
||||
"- unexpected problem with client's input - forcing disconnect");
|
||||
return casProcDisconnect;
|
||||
}
|
||||
}
|
||||
bool sn = this->outBufBytesPending() >= this->_sendBacklogThresh;
|
||||
bufSizeT inBytesPending = this->inBufBytesPending ();
|
||||
return sn || ( inBytesPending == 0u );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -66,14 +66,13 @@ public:
|
||||
const ioArgsToNewStreamIO & );
|
||||
~casStreamOS ();
|
||||
void show ( unsigned level ) const;
|
||||
casProcCond processInput ();
|
||||
void eventFlush ();
|
||||
void printStatus ( const char * pCtx ) const;
|
||||
private:
|
||||
casStreamEvWakeup evWk;
|
||||
casStreamIOWakeup ioWk;
|
||||
class casStreamWriteReg * pWtReg;
|
||||
class casStreamReadReg * pRdReg;
|
||||
bool sendBlocked;
|
||||
bufSizeT _sendBacklogThresh;
|
||||
void armSend ();
|
||||
void armRecv ();
|
||||
void disarmSend();
|
||||
@@ -83,10 +82,13 @@ private:
|
||||
void sendBlockSignal ();
|
||||
void ioBlockedSignal ();
|
||||
void eventSignal ();
|
||||
bool _sendNeeded () const;
|
||||
casStreamOS ( const casStreamOS & );
|
||||
casStreamOS & operator = ( const casStreamOS & );
|
||||
friend class casStreamWriteReg;
|
||||
friend class casStreamReadReg;
|
||||
friend class casStreamIOWakeup;
|
||||
friend class casStreamEvWakeup;
|
||||
};
|
||||
|
||||
#endif // casStreamOSh
|
||||
|
||||
@@ -181,7 +181,7 @@ casDGIntfIO::casDGIntfIO ( caServerI & serverIn, clientBufMemoryManager & memMgr
|
||||
removeDuplicateAddresses ( & filtered, & parsed, true );
|
||||
|
||||
while ( ELLNODE * pRawNode = ellGet ( & filtered ) ) {
|
||||
assert ( offsetof (osiSockAddrNode, node) == 0 );
|
||||
STATIC_ASSERT ( offsetof (osiSockAddrNode, node) == 0 );
|
||||
osiSockAddrNode * pNode = reinterpret_cast < osiSockAddrNode * > ( pRawNode );
|
||||
if ( pNode->addr.sa.sa_family == AF_INET ) {
|
||||
ipIgnoreEntry * pIPI = new ( this->ipIgnoreEntryFreeList )
|
||||
@@ -394,13 +394,14 @@ casDGIntfIO::osdSend ( const char * pBufIn, bufSizeT size, // X aCC 361
|
||||
return outBufClient::flushNone;
|
||||
}
|
||||
}
|
||||
|
||||
bufSizeT casDGIntfIO::incomingBytesPresent () const // X aCC 361
|
||||
|
||||
bufSizeT casDGIntfIO ::
|
||||
dgInBytesPending () const
|
||||
{
|
||||
int status;
|
||||
osiSockIoctl_t nchars = 0;
|
||||
|
||||
status = socket_ioctl ( this->sock, FIONREAD, & nchars ); // X aCC 392
|
||||
status = socket_ioctl ( this->sock, FIONREAD, & nchars );
|
||||
if ( status < 0 ) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
@@ -500,38 +501,21 @@ bufSizeT casDGIntfIO::optimumInBufferSize ()
|
||||
#endif
|
||||
}
|
||||
|
||||
bufSizeT casDGIntfIO::optimumOutBufferSize ()
|
||||
bufSizeT casDGIntfIO ::
|
||||
osSendBufferSize () const
|
||||
{
|
||||
|
||||
#if 1
|
||||
//
|
||||
// must update client before the message size can be
|
||||
// increased here
|
||||
//
|
||||
return MAX_UDP_SEND;
|
||||
#else
|
||||
int n;
|
||||
int size;
|
||||
int status;
|
||||
|
||||
|
||||
/* fetch the TCP send buffer size */
|
||||
n = sizeof(size);
|
||||
status = getsockopt(
|
||||
this->sock,
|
||||
SOL_SOCKET,
|
||||
SO_SNDBUF,
|
||||
(char *)&size,
|
||||
&n);
|
||||
if(status < 0 || n != sizeof(size)){
|
||||
int size = MAX_UDP_SEND;
|
||||
osiSocklen_t n = sizeof ( size );
|
||||
int status = getsockopt( this->sock, SOL_SOCKET, SO_SNDBUF,
|
||||
reinterpret_cast < char * > ( & size ), & n );
|
||||
if ( status < 0 || n != sizeof(size) ) {
|
||||
size = MAX_UDP_SEND;
|
||||
}
|
||||
|
||||
if (size<=0) {
|
||||
if ( size <= MAX_UDP_SEND ) {
|
||||
size = MAX_UDP_SEND;
|
||||
}
|
||||
return (bufSizeT) size;
|
||||
#endif
|
||||
return static_cast < bufSizeT > ( size );
|
||||
}
|
||||
|
||||
SOCKET casDGIntfIO::makeSockDG ()
|
||||
|
||||
@@ -39,10 +39,10 @@ public:
|
||||
inBufClient::fillParameter parm, bufSizeT & nBytesActual, caNetAddr & addr );
|
||||
virtual void show ( unsigned level ) const;
|
||||
|
||||
static bufSizeT optimumOutBufferSize ();
|
||||
static bufSizeT optimumInBufferSize ();
|
||||
|
||||
bufSizeT incomingBytesPresent () const;
|
||||
bufSizeT dgInBytesPending () const;
|
||||
bufSizeT osSendBufferSize () const ;
|
||||
|
||||
private:
|
||||
tsFreeList < ipIgnoreEntry, 128 > ipIgnoreEntryFreeList;
|
||||
|
||||
@@ -236,4 +236,3 @@ caNetAddr casIntfIO::serverAddress () const
|
||||
{
|
||||
return caNetAddr (this->addr);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
clientBufMemoryManager & ) const;
|
||||
|
||||
caNetAddr serverAddress () const;
|
||||
|
||||
|
||||
private:
|
||||
SOCKET sock;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
casStreamIO::casStreamIO ( caServerI & cas, clientBufMemoryManager & bufMgr,
|
||||
const ioArgsToNewStreamIO & args ) :
|
||||
casStrmClient ( cas, bufMgr ), sock ( args.sock ), addr ( args.addr),
|
||||
blockingFlag ( xIsBlocking ), sockHasBeenShutdown ( false )
|
||||
_osSendBufferSize ( MAX_TCP ), blockingFlag ( xIsBlocking ),
|
||||
sockHasBeenShutdown ( false )
|
||||
{
|
||||
assert ( sock >= 0 );
|
||||
int yes = true;
|
||||
@@ -91,7 +92,19 @@ casStreamIO::casStreamIO ( caServerI & cas, clientBufMemoryManager & bufMgr,
|
||||
throw S_cas_internal;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* cache the TCP send buffer size */
|
||||
int size = MAX_TCP;
|
||||
osiSocklen_t n = sizeof ( size ) ;
|
||||
status = getsockopt ( this->sock, SOL_SOCKET,
|
||||
SO_SNDBUF, reinterpret_cast < char * > ( & size ), & n );
|
||||
if ( status < 0 || n != sizeof ( size ) ) {
|
||||
size = MAX_TCP;
|
||||
}
|
||||
if ( size <= MAX_TCP ) {
|
||||
size = MAX_TCP;
|
||||
}
|
||||
_osSendBufferSize = static_cast < bufSizeT > ( size );
|
||||
}
|
||||
|
||||
// casStreamIO::~casStreamIO()
|
||||
@@ -254,14 +267,14 @@ xBlockingStatus casStreamIO::blockingState() const
|
||||
{
|
||||
return this->blockingFlag;
|
||||
}
|
||||
|
||||
// casStreamIO::incomingBytesPresent()
|
||||
bufSizeT casStreamIO::incomingBytesPresent () const // X aCC 361
|
||||
|
||||
// casStreamIO :: inCircuitBytesPending()
|
||||
bufSizeT casStreamIO :: inCircuitBytesPending () const
|
||||
{
|
||||
int status;
|
||||
osiSockIoctl_t nchars = 0;
|
||||
|
||||
status = socket_ioctl ( this->sock, FIONREAD, &nchars ); // X aCC 392
|
||||
status = socket_ioctl ( this->sock, FIONREAD, &nchars );
|
||||
if ( status < 0 ) {
|
||||
int localError = SOCKERRNO;
|
||||
if (
|
||||
@@ -293,32 +306,10 @@ void casStreamIO::hostName ( char * pInBuf, unsigned bufSizeIn ) const
|
||||
ipAddrToA ( & this->addr, pInBuf, bufSizeIn );
|
||||
}
|
||||
|
||||
// casStreamIO:::optimumBufferSize()
|
||||
bufSizeT casStreamIO::optimumBufferSize ()
|
||||
// casStreamIO :: osSendBufferSize ()
|
||||
bufSizeT casStreamIO :: osSendBufferSize () const
|
||||
{
|
||||
|
||||
#if 0
|
||||
int n;
|
||||
int size;
|
||||
int status;
|
||||
|
||||
/* fetch the TCP send buffer size */
|
||||
n = sizeof ( size) ;
|
||||
status = getsockopt ( this->sock, SOL_SOCKET,
|
||||
SO_SNDBUF, (char *) & size, & n );
|
||||
if ( status < 0 || n != sizeof ( size ) ) {
|
||||
size = 0x400;
|
||||
}
|
||||
|
||||
if (size<=0) {
|
||||
size = 0x400;
|
||||
}
|
||||
printf("the tcp buf size is %d\n", size);
|
||||
return (bufSizeT) size;
|
||||
#else
|
||||
// this needs to be MAX_TCP (until we fix the array problem)
|
||||
return (bufSizeT) MAX_TCP; // X aCC 392
|
||||
#endif
|
||||
return _osSendBufferSize;
|
||||
}
|
||||
|
||||
// casStreamIO::getFD()
|
||||
|
||||
@@ -31,16 +31,16 @@ public:
|
||||
void xSetNonBlocking ();
|
||||
const caNetAddr getAddr() const;
|
||||
void hostName ( char *pBuf, unsigned bufSize ) const;
|
||||
|
||||
bufSizeT inCircuitBytesPending () const;
|
||||
bufSizeT osSendBufferSize () const;
|
||||
private:
|
||||
SOCKET sock;
|
||||
struct sockaddr_in addr;
|
||||
bufSizeT _osSendBufferSize;
|
||||
xBlockingStatus blockingFlag;
|
||||
|
||||
bool sockHasBeenShutdown;
|
||||
xBlockingStatus blockingState() const;
|
||||
bufSizeT incomingBytesPresent() const;
|
||||
static bufSizeT optimumBufferSize ();
|
||||
void osdShow ( unsigned level ) const;
|
||||
outBufClient::flushCondition osdSend ( const char *pBuf, bufSizeT nBytesReq,
|
||||
bufSizeT & nBytesActual );
|
||||
|
||||
@@ -872,10 +872,14 @@ static void dbBreakBody(void)
|
||||
double slope =
|
||||
(paBrkInt[i+1].eng - paBrkInt[i].eng)/
|
||||
(paBrkInt[i+1].raw - paBrkInt[i].raw);
|
||||
if (!dbBptNotMonotonic && slope == 0) {
|
||||
yyerrorAbort("breaktable slope is zero");
|
||||
return;
|
||||
}
|
||||
if (i == 0) {
|
||||
down = (slope < 0);
|
||||
} else if (!dbBptNotMonotonic && down != (slope < 0)) {
|
||||
yyerrorAbort("breaktable: curve slope changes sign");
|
||||
yyerrorAbort("breaktable slope changes sign");
|
||||
return;
|
||||
}
|
||||
paBrkInt[i].slope = slope;
|
||||
|
||||
@@ -235,53 +235,55 @@ static long setLinkType(DBENTRY *pdbentry)
|
||||
DBLINK *plink;
|
||||
long status=0;
|
||||
int link_type,ind,type;
|
||||
int isSoftLink;
|
||||
|
||||
dbCopyEntryContents(pdbentry,&dbEntry);
|
||||
status = dbFindField(&dbEntry,"DTYP");
|
||||
if(status) {
|
||||
dbCopyEntryContents(pdbentry, &dbEntry);
|
||||
status = dbFindField(&dbEntry, "DTYP");
|
||||
if (status) {
|
||||
epicsPrintf("field DTYP does not exist for recordtype %s\n",
|
||||
dbGetRecordTypeName(&dbEntry));
|
||||
status = S_dbLib_fieldNotFound;
|
||||
goto done;
|
||||
}
|
||||
|
||||
precordType = dbEntry.precordType;
|
||||
if(!precordType) {
|
||||
if (!precordType) {
|
||||
status = S_dbLib_badField;
|
||||
goto done;
|
||||
}
|
||||
if(ellCount(&precordType->devList)==0) goto done;
|
||||
|
||||
if (ellCount(&precordType->devList) == 0) goto done;
|
||||
|
||||
ind = dbGetMenuIndex(&dbEntry);
|
||||
if(ind==-1) {
|
||||
if (ind == -1) {
|
||||
char *pstring;
|
||||
|
||||
pstring = dbGetString(&dbEntry);
|
||||
if(strstr(pstring,"$(") || strstr(pstring,"${")) {
|
||||
if (strstr(pstring, "$(") || strstr(pstring, "${")) {
|
||||
link_type = MACRO_LINK;
|
||||
} else {
|
||||
status = S_dbLib_badField;
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
pdevSup = (devSup *)ellNth(&precordType->devList,ind+1);
|
||||
if(!pdevSup) {
|
||||
pdevSup = (devSup *)ellNth(&precordType->devList, ind + 1);
|
||||
if (!pdevSup) {
|
||||
status = S_dbLib_badField;
|
||||
goto done;
|
||||
}
|
||||
link_type = pdevSup->link_type;
|
||||
}
|
||||
|
||||
pflddes = pdbentry->pflddes;
|
||||
plink = (DBLINK *)(pdbentry->pfield);
|
||||
if(plink->type == link_type) goto done;
|
||||
plink = (DBLINK *)pdbentry->pfield;
|
||||
if (plink->type == link_type) goto done;
|
||||
|
||||
type = plink->type;
|
||||
if(type==CONSTANT || type==PV_LINK || type==DB_LINK || type==CA_LINK)
|
||||
isSoftLink = TRUE;
|
||||
else
|
||||
isSoftLink = FALSE;
|
||||
if(isSoftLink && (link_type==CONSTANT || link_type==PV_LINK)) goto done;
|
||||
if ((type == CONSTANT || type == PV_LINK || type == DB_LINK || type == CA_LINK) &&
|
||||
(link_type == CONSTANT || link_type == PV_LINK)) goto done;
|
||||
|
||||
dbFreeLinkContents(plink);
|
||||
plink->type = link_type;
|
||||
switch(plink->type) {
|
||||
switch (plink->type) {
|
||||
case VME_IO: plink->value.vmeio.parm = pNullString; break;
|
||||
case CAMAC_IO: plink->value.camacio.parm = pNullString; break;
|
||||
case AB_IO: plink->value.abio.parm = pNullString; break;
|
||||
@@ -2164,8 +2166,9 @@ long epicsShareAPI dbPutString(DBENTRY *pdbentry,const char *pstring)
|
||||
}
|
||||
if((short)strlen(pstring) >= pflddes->size) status = S_dbLib_strLen;
|
||||
break;
|
||||
case DBF_CHAR :
|
||||
case DBF_SHORT :
|
||||
|
||||
case DBF_CHAR:
|
||||
case DBF_SHORT:
|
||||
case DBF_LONG:
|
||||
case DBF_UCHAR:
|
||||
case DBF_USHORT:
|
||||
@@ -2176,68 +2179,75 @@ long epicsShareAPI dbPutString(DBENTRY *pdbentry,const char *pstring)
|
||||
case DBF_MENU:
|
||||
status = dbPutStringNum(pdbentry,pstring);
|
||||
break;
|
||||
|
||||
case DBF_DEVICE: {
|
||||
DBENTRY dbEntry;
|
||||
char *name;
|
||||
|
||||
status = dbPutStringNum(pdbentry,pstring);
|
||||
if(status) return(status);
|
||||
status = dbPutStringNum(pdbentry, pstring);
|
||||
if (status) return status;
|
||||
|
||||
name = dbGetRelatedField(pdbentry);
|
||||
if(!name) return(0);
|
||||
dbCopyEntryContents(pdbentry,&dbEntry);
|
||||
status = dbFindField(&dbEntry,name);
|
||||
if(!status) status = setLinkType(&dbEntry);
|
||||
if (!name) return 0;
|
||||
|
||||
dbCopyEntryContents(pdbentry, &dbEntry);
|
||||
status = dbFindField(&dbEntry, name);
|
||||
if (!status)
|
||||
status = setLinkType(&dbEntry);
|
||||
dbFinishEntry(&dbEntry);
|
||||
return(status);
|
||||
return status;
|
||||
}
|
||||
|
||||
case DBF_INLINK:
|
||||
case DBF_OUTLINK:
|
||||
case DBF_FWDLINK: {
|
||||
DBLINK *plink;
|
||||
char string[80];
|
||||
char *pstr=&string[0];
|
||||
char *pstr = string;
|
||||
int ind;
|
||||
|
||||
if(!pfield) return(S_dbLib_fieldNotFound);
|
||||
if (!pfield)
|
||||
return S_dbLib_fieldNotFound;
|
||||
|
||||
plink = (DBLINK *)pfield;
|
||||
dbFreeLinkContents(plink);
|
||||
if(!stringHasMacro &&(strcmp(pflddes->name,"INP")==0
|
||||
|| strcmp(pflddes->name,"OUT")==0)){
|
||||
status = setLinkType(pdbentry);
|
||||
if(status) {
|
||||
errMessage(status,"in dbPutString from setLinkType");
|
||||
return(status);
|
||||
}
|
||||
}
|
||||
if(stringHasMacro) {
|
||||
if (stringHasMacro) {
|
||||
plink->type = MACRO_LINK;
|
||||
plink->value.macro_link.macroStr =
|
||||
dbCalloc(strlen(pstring)+1,sizeof(char));
|
||||
strcpy(plink->value.macro_link.macroStr,pstring);
|
||||
plink->value.macro_link.macroStr = epicsStrDup(pstring);
|
||||
goto done;
|
||||
}
|
||||
if(strlen(pstring)>=sizeof(string)) {
|
||||
|
||||
if (strcmp(pflddes->name, "INP") == 0 ||
|
||||
strcmp(pflddes->name, "OUT") == 0) {
|
||||
status = setLinkType(pdbentry); /* This uses DTYP to look up and set plink->type, necessary for default DTYP */
|
||||
if (status) {
|
||||
errMessage(status,"in dbPutString from setLinkType");
|
||||
return status;
|
||||
}
|
||||
}
|
||||
if (strlen(pstring) >= sizeof(string)) {
|
||||
status = S_dbLib_badField;
|
||||
errMessage(status,
|
||||
"dbPutString received a string that is too long");
|
||||
return(status);
|
||||
return status;
|
||||
}
|
||||
strcpy(pstr,pstring);
|
||||
/*strip off leading blanks and tabs*/
|
||||
while(*pstr && (*pstr==' ' || *pstr=='\t')) pstr++;
|
||||
/*strip off trailing blanks and tabs*/
|
||||
if(pstr) for(ind = strlen(pstr)-1; ind>=0; ind--) {
|
||||
if(pstr[ind]!=' ' && pstr[ind]!='\t') break;
|
||||
pstr[ind] = '\0';
|
||||
}
|
||||
if(!pstr || (int)strlen(pstr)<=0 ) {
|
||||
if(plink->type==PV_LINK) dbCvtLinkToConstant(pdbentry);
|
||||
if(plink->type!=CONSTANT) return(S_dbLib_badField);
|
||||
strcpy(pstr, pstring);
|
||||
/* Strip leading blanks and tabs */
|
||||
while (*pstr && (*pstr == ' ' || *pstr == '\t')) pstr++;
|
||||
/* Strip trailing blanks and tabs */
|
||||
if (pstr)
|
||||
for (ind = strlen(pstr) - 1; ind >= 0; ind--) {
|
||||
if (pstr[ind] != ' ' && pstr[ind] != '\t') break;
|
||||
pstr[ind] = '\0';
|
||||
}
|
||||
if (!pstr || !*pstr) {
|
||||
if (plink->type == PV_LINK) dbCvtLinkToConstant(pdbentry);
|
||||
if (plink->type != CONSTANT) return S_dbLib_badField;
|
||||
free((void *)plink->value.constantStr);
|
||||
plink->value.constantStr = NULL;
|
||||
goto done;
|
||||
}
|
||||
switch(plink->type) {
|
||||
switch (plink->type) {
|
||||
case CONSTANT:
|
||||
case PV_LINK: {
|
||||
short ppOpt = 0;
|
||||
@@ -2251,60 +2261,64 @@ long epicsShareAPI dbPutString(DBENTRY *pdbentry,const char *pstring)
|
||||
/* Check first to see if string is a constant*/
|
||||
/*It is a string if epicsStrtod or strtol eats entire string*/
|
||||
/*leading and trailing blanks have already been stripped*/
|
||||
tempdouble = epicsStrtod(pstr,&enddouble);
|
||||
templong = strtol(pstr,&endlong,0);
|
||||
if((*enddouble == 0) || (*endlong == 0)) {
|
||||
if(plink->type==PV_LINK) dbCvtLinkToConstant(pdbentry);
|
||||
if((!plink->value.constantStr) ||
|
||||
((int)strlen(plink->value.constantStr)<(int)strlen(pstr)
|
||||
)) {
|
||||
tempdouble = epicsStrtod(pstr, &enddouble);
|
||||
templong = strtol(pstr, &endlong, 0);
|
||||
|
||||
if (*enddouble == 0 || *endlong == 0) {
|
||||
if (plink->type == PV_LINK) dbCvtLinkToConstant(pdbentry);
|
||||
if (!plink->value.constantStr ||
|
||||
strlen(plink->value.constantStr) < strlen(pstr)) {
|
||||
free(plink->value.constantStr);
|
||||
plink->value.constantStr =
|
||||
dbCalloc(strlen(pstr)+1,sizeof(char));
|
||||
dbCalloc(strlen(pstr) + 1, sizeof(char));
|
||||
}
|
||||
strcpy(plink->value.constantStr,pstr);
|
||||
strcpy(plink->value.constantStr, pstr);
|
||||
goto done;
|
||||
}
|
||||
if(plink->type==CONSTANT) dbCvtLinkToPvlink(pdbentry);
|
||||
|
||||
if (plink->type==CONSTANT) dbCvtLinkToPvlink(pdbentry);
|
||||
end = strchr(pstr,' ');
|
||||
if(end) {
|
||||
if (end) {
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_INLINK: {
|
||||
if(strstr(end,"NPP")) ppOpt = 0;
|
||||
else if(strstr(end,"CPP")) ppOpt = pvlOptCPP;
|
||||
else if(strstr(end,"PP")) ppOpt = pvlOptPP;
|
||||
else if(strstr(end,"CA")) ppOpt = pvlOptCA;
|
||||
else if(strstr(end,"CP")) ppOpt = pvlOptCP;
|
||||
if (strstr(end, "NPP")) ppOpt = 0;
|
||||
else if (strstr(end, "CPP")) ppOpt = pvlOptCPP;
|
||||
else if (strstr(end, "PP")) ppOpt = pvlOptPP;
|
||||
else if (strstr(end, "CA")) ppOpt = pvlOptCA;
|
||||
else if (strstr(end, "CP")) ppOpt = pvlOptCP;
|
||||
else ppOpt = 0;
|
||||
if(strstr(end,"NMS")) msOpt = pvlOptNMS;
|
||||
else if(strstr(end,"MSI")) msOpt = pvlOptMSI;
|
||||
else if(strstr(end,"MSS")) msOpt = pvlOptMSS;
|
||||
/*must be the last one:*/ else if(strstr(end,"MS")) msOpt = pvlOptMS;
|
||||
if (strstr(end, "NMS")) msOpt = pvlOptNMS;
|
||||
else if (strstr(end, "MSI")) msOpt = pvlOptMSI;
|
||||
else if (strstr(end, "MSS")) msOpt = pvlOptMSS;
|
||||
/*must be the last one:*/ else if (strstr(end, "MS")) msOpt = pvlOptMS;
|
||||
else msOpt = 0;
|
||||
*end = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case DBF_OUTLINK: {
|
||||
if(strstr(end,"NPP")) ppOpt = 0;
|
||||
else if(strstr(end,"PP")) ppOpt = pvlOptPP;
|
||||
else if(strstr(end,"CA")) ppOpt = pvlOptCA;
|
||||
if (strstr(end, "NPP")) ppOpt = 0;
|
||||
else if(strstr(end, "PP")) ppOpt = pvlOptPP;
|
||||
else if(strstr(end, "CA")) ppOpt = pvlOptCA;
|
||||
else ppOpt = 0;
|
||||
if(strstr(end,"NMS")) msOpt = pvlOptNMS;
|
||||
else if(strstr(end,"MSI")) msOpt = pvlOptMSI;
|
||||
else if(strstr(end,"MSS")) msOpt = pvlOptMSS;
|
||||
/*must be the last one:*/ else if(strstr(end,"MS")) msOpt = pvlOptMS;
|
||||
if (strstr(end, "NMS")) msOpt = pvlOptNMS;
|
||||
else if(strstr(end, "MSI")) msOpt = pvlOptMSI;
|
||||
else if(strstr(end, "MSS")) msOpt = pvlOptMSS;
|
||||
/*must be the last one:*/ else if(strstr(end, "MS")) msOpt = pvlOptMS;
|
||||
else msOpt = 0;
|
||||
*end = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case DBF_FWDLINK: {
|
||||
if(strstr(end,"NPP")) ppOpt = 0;
|
||||
else if(strstr(end,"CA")) ppOpt = pvlOptCA;
|
||||
if (strstr(end, "NPP")) ppOpt = 0;
|
||||
else if (strstr(end, "CA")) ppOpt = pvlOptCA;
|
||||
else ppOpt = 0;
|
||||
msOpt = 0;
|
||||
*end = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
epicsPrintf("dbPutString: Logic Error\n");
|
||||
}
|
||||
@@ -2316,38 +2330,39 @@ long epicsShareAPI dbPutString(DBENTRY *pdbentry,const char *pstring)
|
||||
case VME_IO: {
|
||||
char *end;
|
||||
|
||||
if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField);
|
||||
if (!(end = strchr(pstr,'#'))) return S_dbLib_badField;
|
||||
pstr = end + 1;
|
||||
if(!(end = strchr(pstr,'C'))) return (S_dbLib_badField);
|
||||
if (!(end = strchr(pstr,'C'))) return S_dbLib_badField;
|
||||
pstr = end + 1;
|
||||
cvtDecimalOrHexToShort(pstr,&plink->value.vmeio.card);
|
||||
if(!(end = strchr(pstr,'S'))) return (S_dbLib_badField);
|
||||
if (!(end = strchr(pstr,'S'))) return S_dbLib_badField;
|
||||
pstr = end + 1;
|
||||
cvtDecimalOrHexToShort(pstr,&plink->value.vmeio.signal);
|
||||
status = putParmString(&plink->value.vmeio.parm,pstr);
|
||||
cvtDecimalOrHexToShort(pstr, &plink->value.vmeio.signal);
|
||||
status = putParmString(&plink->value.vmeio.parm, pstr);
|
||||
}
|
||||
break;
|
||||
|
||||
case CAMAC_IO: {
|
||||
char *end;
|
||||
|
||||
if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField);
|
||||
if (!(end = strchr(pstr,'#'))) return (S_dbLib_badField);
|
||||
pstr = end + 1;
|
||||
if(!(end = strchr(pstr,'B'))) return (S_dbLib_badField);
|
||||
if (!(end = strchr(pstr,'B'))) return (S_dbLib_badField);
|
||||
pstr = end + 1;
|
||||
cvtDecimalOrHexToShort(pstr,&plink->value.camacio.b);
|
||||
if(!(end = strchr(pstr,'C'))) return (S_dbLib_badField);
|
||||
if (!(end = strchr(pstr,'C'))) return (S_dbLib_badField);
|
||||
pstr = end + 1;
|
||||
cvtDecimalOrHexToShort(pstr,&plink->value.camacio.c);
|
||||
if(!(end = strchr(pstr,'N'))) return (S_dbLib_badField);
|
||||
if (!(end = strchr(pstr,'N'))) return (S_dbLib_badField);
|
||||
pstr = end + 1;
|
||||
cvtDecimalOrHexToShort(pstr,&plink->value.camacio.n);
|
||||
if(!(end = strchr(pstr,'A'))) {
|
||||
if (!(end = strchr(pstr,'A'))) {
|
||||
plink->value.camacio.a = 0;
|
||||
} else {
|
||||
pstr = end + 1;
|
||||
cvtDecimalOrHexToShort(pstr,&plink->value.camacio.a);
|
||||
}
|
||||
if(!(end = strchr(pstr,'F'))) {
|
||||
if (!(end = strchr(pstr,'F'))) {
|
||||
plink->value.camacio.f = 0;
|
||||
} else {
|
||||
pstr = end + 1;
|
||||
@@ -2356,6 +2371,7 @@ long epicsShareAPI dbPutString(DBENTRY *pdbentry,const char *pstring)
|
||||
status = putParmString(&plink->value.camacio.parm,pstr);
|
||||
}
|
||||
break;
|
||||
|
||||
case RF_IO: {
|
||||
char *end;
|
||||
|
||||
@@ -2492,22 +2508,22 @@ long epicsShareAPI dbPutString(DBENTRY *pdbentry,const char *pstring)
|
||||
}
|
||||
break;
|
||||
case INST_IO: {
|
||||
status = putParmString(&plink->value.instio.string,pstr);
|
||||
status = putParmString(&plink->value.instio.string, pstr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return (S_dbLib_badField);
|
||||
return S_dbLib_badField;
|
||||
}
|
||||
done:
|
||||
if(!status && strcmp(pflddes->name,"VAL")==0) {
|
||||
if (!status && strcmp(pflddes->name, "VAL") == 0) {
|
||||
DBENTRY dbentry;
|
||||
|
||||
dbCopyEntryContents(pdbentry,&dbentry);
|
||||
if(!dbFindField(&dbentry,"UDF")) {
|
||||
dbPutString(&dbentry,"0");
|
||||
dbCopyEntryContents(pdbentry, &dbentry);
|
||||
if (!dbFindField(&dbentry, "UDF")) {
|
||||
dbPutString(&dbentry, "0");
|
||||
}
|
||||
dbFinishEntry(&dbentry);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <epicsStdlib.h>
|
||||
#include <limits.h>
|
||||
#include "epicsStdio.h"
|
||||
#include "cvtFast.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "aitConvert.h"
|
||||
|
||||
@@ -69,7 +71,7 @@ bool putDoubleToString (
|
||||
const double in, const gddEnumStringTable * pEST,
|
||||
char * pString, size_t strSize )
|
||||
{
|
||||
if ( strSize == 0u ) {
|
||||
if ( strSize <= 1u ) {
|
||||
return false;
|
||||
}
|
||||
if ( pEST && in >= 0 && in <= UINT_MAX ) {
|
||||
@@ -79,11 +81,33 @@ bool putDoubleToString (
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#if 1
|
||||
bool cvtDoubleToStringInRange =
|
||||
( in < 1.e4 && in > 1.e-4 ) ||
|
||||
( in > -1.e4 && in < -1.e-4 ) ||
|
||||
in == 0.0;
|
||||
// conservative
|
||||
static const unsigned cvtDoubleToStringSizeMax = 15;
|
||||
int nChar;
|
||||
if ( cvtDoubleToStringInRange &&
|
||||
strSize > cvtDoubleToStringSizeMax ) {
|
||||
nChar = cvtDoubleToString ( in, pString, 4 );
|
||||
}
|
||||
else {
|
||||
nChar = epicsSnprintf (
|
||||
pString, strSize-1, "%g", in );
|
||||
}
|
||||
if ( nChar < 1 ) {
|
||||
return false;
|
||||
}
|
||||
assert ( nChar < strSize );
|
||||
#else
|
||||
int nChar = epicsSnprintf (
|
||||
pString, strSize-1, "%g", in );
|
||||
if ( nChar <= 0 ) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
size_t nCharU = static_cast < size_t > ( nChar );
|
||||
nChar = epicsMin ( nCharU, strSize-1 ) + 1;
|
||||
memset ( &pString[nChar], '\0', strSize - nChar );
|
||||
|
||||
@@ -70,76 +70,6 @@ static void *bucketLookupItem(BUCKET *pb, bucketSET *pBSET, const void *pId);
|
||||
*/
|
||||
#define BUCKET_MAX_WIDTH 12
|
||||
|
||||
#ifdef DEBUG
|
||||
main()
|
||||
{
|
||||
unsigned id1;
|
||||
unsigned id2;
|
||||
char *pValSave1;
|
||||
char *pValSave2;
|
||||
int s;
|
||||
BUCKET *pb;
|
||||
char *pVal;
|
||||
unsigned i;
|
||||
clock_t start, finish;
|
||||
double duration;
|
||||
const int LOOPS = 500000;
|
||||
|
||||
pb = bucketCreate(8);
|
||||
if(!pb){
|
||||
return -1;
|
||||
}
|
||||
|
||||
id1 = 0x1000a432;
|
||||
pValSave1 = "fred";
|
||||
s = bucketAddItemUnsignedId(pb, &id1, pValSave1);
|
||||
assert (s == S_bucket_success);
|
||||
|
||||
pValSave2 = "jane";
|
||||
id2 = 0x0000a432;
|
||||
s = bucketAddItemUnsignedId(pb, &id2, pValSave2);
|
||||
assert (s == S_bucket_success);
|
||||
|
||||
start = clock();
|
||||
for(i=0; i<LOOPS; i++){
|
||||
pVal = bucketLookupItemUnsignedId(pb, &id1);
|
||||
assert(pVal == pValSave1);
|
||||
pVal = bucketLookupItemUnsignedId(pb, &id1);
|
||||
assert(pVal == pValSave1);
|
||||
pVal = bucketLookupItemUnsignedId(pb, &id1);
|
||||
assert(pVal == pValSave1);
|
||||
pVal = bucketLookupItemUnsignedId(pb, &id1);
|
||||
assert(pVal == pValSave1);
|
||||
pVal = bucketLookupItemUnsignedId(pb, &id1);
|
||||
assert(pVal == pValSave1);
|
||||
pVal = bucketLookupItemUnsignedId(pb, &id1);
|
||||
assert(pVal == pValSave1);
|
||||
pVal = bucketLookupItemUnsignedId(pb, &id1);
|
||||
assert(pVal == pValSave1);
|
||||
pVal = bucketLookupItemUnsignedId(pb, &id1);
|
||||
assert(pVal == pValSave1);
|
||||
pVal = bucketLookupItemUnsignedId(pb, &id1);
|
||||
assert(pVal == pValSave1);
|
||||
pVal = bucketLookupItemUnsignedId(pb, &id2);
|
||||
assert(pVal == pValSave2);
|
||||
}
|
||||
finish = clock();
|
||||
|
||||
duration = finish-start;
|
||||
duration = duration/CLOCKS_PER_SEC;
|
||||
printf("It took %15.10f total sec\n", duration);
|
||||
duration = duration/LOOPS;
|
||||
duration = duration/10;
|
||||
duration = duration * 1e6;
|
||||
printf("It took %15.10f u sec per hash lookup\n", duration);
|
||||
|
||||
bucketShow(pb);
|
||||
|
||||
return S_bucket_success;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* bucketUnsignedCompare()
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
// 1) This library is not thread safe
|
||||
//
|
||||
|
||||
#undef FD_SETSIZE
|
||||
#define FD_SETSIZE 4096
|
||||
|
||||
//
|
||||
@@ -51,15 +50,15 @@ const unsigned uSecPerSec = 1000u * mSecPerSec;
|
||||
//
|
||||
epicsShareFunc fdManager::fdManager () :
|
||||
sleepQuantum ( epicsThreadSleepQuantum () ),
|
||||
fdSetsPtr ( new fd_set [fdrNEnums+1] ),
|
||||
fdSetsPtr ( new fd_set [fdrNEnums] ),
|
||||
pTimerQueue ( 0 ), maxFD ( 0 ), processInProg ( false ),
|
||||
pCBReg ( 0 )
|
||||
{
|
||||
int status = osiSockAttach ();
|
||||
assert (status);
|
||||
|
||||
for ( size_t i = 0u; i <= fdrNEnums; i++ ) {
|
||||
FD_ZERO ( &fdSetsPtr[i] ); // X aCC 392
|
||||
for ( size_t i = 0u; i < fdrNEnums; i++ ) {
|
||||
FD_ZERO ( &fdSetsPtr[i] );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,8 +123,10 @@ epicsShareFunc void fdManager::process (double delay)
|
||||
tv.tv_sec = static_cast<long> ( minDelay );
|
||||
tv.tv_usec = static_cast<long> ( (minDelay-tv.tv_sec) * uSecPerSec );
|
||||
|
||||
int status = select (this->maxFD, &this->fdSetsPtr[fdrRead],
|
||||
&this->fdSetsPtr[fdrWrite], &this->fdSetsPtr[fdrException], &tv);
|
||||
fd_set * pReadSet = & this->fdSetsPtr[fdrRead];
|
||||
fd_set * pWriteSet = & this->fdSetsPtr[fdrWrite];
|
||||
fd_set * pExceptSet = & this->fdSetsPtr[fdrException];
|
||||
int status = select (this->maxFD, pReadSet, pWriteSet, pExceptSet, &tv);
|
||||
|
||||
this->pTimerQueue->process(epicsTime::getCurrent());
|
||||
|
||||
@@ -135,16 +136,17 @@ epicsShareFunc void fdManager::process (double delay)
|
||||
// Look for activity
|
||||
//
|
||||
iter=this->regList.firstIter ();
|
||||
while ( iter.valid () ) {
|
||||
tsDLIter<fdReg> tmp = iter;
|
||||
while ( iter.valid () && status > 0 ) {
|
||||
tsDLIter < fdReg > tmp = iter;
|
||||
tmp++;
|
||||
if (FD_ISSET(iter->getFD(), &this->fdSetsPtr[iter->getType()])) {
|
||||
FD_CLR(iter->getFD(), &this->fdSetsPtr[iter->getType()]);
|
||||
this->regList.remove(*iter);
|
||||
this->activeList.add(*iter);
|
||||
iter->state = fdReg::active;
|
||||
status--;
|
||||
}
|
||||
iter=tmp;
|
||||
iter = tmp;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -182,6 +184,12 @@ epicsShareFunc void fdManager::process (double delay)
|
||||
}
|
||||
else if ( status < 0 ) {
|
||||
int errnoCpy = SOCKERRNO;
|
||||
|
||||
// dont depend on flags being properly set if
|
||||
// an error is retuned from select
|
||||
for ( size_t i = 0u; i < fdrNEnums; i++ ) {
|
||||
FD_ZERO ( &fdSetsPtr[i] );
|
||||
}
|
||||
|
||||
//
|
||||
// print a message if its an unexpected error
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
#endif
|
||||
#define FALSE 0
|
||||
|
||||
#ifndef LOCAL
|
||||
/* deprecated, use static */
|
||||
#ifndef LOCAL
|
||||
# define LOCAL static
|
||||
#endif
|
||||
|
||||
@@ -36,11 +37,24 @@
|
||||
# define NELEMENTS(array) (sizeof (array) / sizeof ((array) [0]))
|
||||
#endif
|
||||
|
||||
/* byte offset of member in structure*/
|
||||
/* byte offset of member in structure - deprecated, use offsetof */
|
||||
#ifndef OFFSET
|
||||
# define OFFSET(structure, member) offsetof(structure, member)
|
||||
#endif
|
||||
|
||||
/* Subtract member byte offset, returning pointer to parent object */
|
||||
#ifndef CONTAINER
|
||||
# ifdef __GNUC__
|
||||
# define CONTAINER(ptr, structure, member) ({ \
|
||||
const __typeof(((structure*)0)->member) *_ptr = (ptr); \
|
||||
(structure*)((char*)_ptr - offsetof(structure, member)); \
|
||||
})
|
||||
# else
|
||||
# define CONTAINER(ptr, structure, member) \
|
||||
((structure*)((char*)(ptr) - offsetof(structure, member)))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*Process Variable Name Size */
|
||||
/* PVNAME_STRINGSZ includes the nil terminator */
|
||||
#define PVNAME_STRINGSZ 61
|
||||
|
||||
@@ -31,25 +31,26 @@ extern "C" {
|
||||
#undef assert
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define assert(ignore) ((void) 0)
|
||||
# define assert(ignore) ((void) 0)
|
||||
#else /* NDEBUG */
|
||||
|
||||
epicsShareFunc void epicsAssert (const char *pFile, const unsigned line,
|
||||
const char *pExp, const char *pAuthorName);
|
||||
|
||||
#if (defined(__STDC__) || defined(__cplusplus)) && !defined(VAXC)
|
||||
# define assert(exp) \
|
||||
( (exp) ? (void) 0 : \
|
||||
epicsAssert( __FILE__, __LINE__, #exp, epicsAssertAuthor ) )
|
||||
#else /* __STDC__ or __cplusplus */
|
||||
# define assert(exp) \
|
||||
( (exp) ? (void) 0 : \
|
||||
epicsAssert( __FILE__, __LINE__, "", epicsAssertAuthor ) )
|
||||
#endif /* (__STDC__ or __cplusplus) and not VAXC */
|
||||
# define assert(exp) ((exp) ? (void)0 : \
|
||||
epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor))
|
||||
|
||||
#endif /* NDEBUG */
|
||||
|
||||
|
||||
/* Compile-time checks */
|
||||
#define STATIC_JOIN(x, y) STATIC_JOIN2(x, y)
|
||||
#define STATIC_JOIN2(x, y) x ## y
|
||||
#define STATIC_ASSERT(expr) \
|
||||
typedef int STATIC_JOIN(static_assert_failed_at_line_, __LINE__) \
|
||||
[ (expr) ? 1 : -1 ]
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#include <float.h>
|
||||
#include <string.h>
|
||||
|
||||
// The following is required for Solaris builds
|
||||
#undef __EXTENSIONS__
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "epicsAlgorithm.h"
|
||||
#include "epicsTime.h"
|
||||
@@ -28,10 +31,24 @@
|
||||
#include "epicsGuard.h"
|
||||
#include "errlog.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
epicsThreadRunable::~epicsThreadRunable () {}
|
||||
void epicsThreadRunable::run () {}
|
||||
void epicsThreadRunable::show ( unsigned int ) const {}
|
||||
|
||||
|
||||
class epicsThread :: unableToCreateThread :
|
||||
public exception {
|
||||
public:
|
||||
const char * what () const throw ();
|
||||
};
|
||||
|
||||
const char * epicsThread ::
|
||||
unableToCreateThread :: what () const throw ()
|
||||
{
|
||||
return "unable to create thread";
|
||||
}
|
||||
|
||||
void epicsThread :: printLastChanceExceptionMessage (
|
||||
const char * pExceptionTypeName,
|
||||
const char * pExceptionContext )
|
||||
@@ -47,7 +64,8 @@ void epicsThread :: printLastChanceExceptionMessage (
|
||||
char name [128];
|
||||
epicsThreadGetName ( this->id, name, sizeof ( name ) );
|
||||
errlogPrintf (
|
||||
"epicsThread: Unexpected C++ exception \"%s\" with type \"%s\" in thread \"%s\" at %s\n",
|
||||
"epicsThread: Unexpected C++ exception \"%s\" "
|
||||
"with type \"%s\" in thread \"%s\" at %s\n",
|
||||
pExceptionContext, pExceptionTypeName, name, date );
|
||||
errlogFlush ();
|
||||
// this should behave as the C++ implementation intends when an
|
||||
@@ -276,6 +294,27 @@ void epicsThreadPrivateBase::throwUnableToCreateThreadPrivate ()
|
||||
throw epicsThreadPrivateBase::unableToCreateThreadPrivate ();
|
||||
}
|
||||
|
||||
void epicsThread :: show ( unsigned level ) const throw ()
|
||||
{
|
||||
::printf ( "epicsThread at %p\n", this->id );
|
||||
if ( level > 0u ) {
|
||||
epicsThreadShow ( this->id, level - 1 );
|
||||
if ( level > 1u ) {
|
||||
::printf ( "pWaitReleaseFlag = %p\n", this->pWaitReleaseFlag );
|
||||
::printf ( "begin = %c, cancel = %c, terminated = %c\n",
|
||||
this->begin ? 'T' : 'F',
|
||||
this->cancel ? 'T' : 'F',
|
||||
this->terminated ? 'T' : 'F' );
|
||||
this->runable.show ( level - 2u );
|
||||
this->mutex.show ( level - 2u );
|
||||
::printf ( "general purpose event\n" );
|
||||
this->event.show ( level - 2u );
|
||||
::printf ( "exit event\n" );
|
||||
this->exitEvent.show ( level - 2u );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
static epicsThreadOnceId okToBlockOnce = EPICS_THREAD_ONCE_INIT;
|
||||
epicsThreadPrivateId okToBlockPrivate;
|
||||
|
||||
@@ -149,6 +149,7 @@ public:
|
||||
bool isSuspended () const throw ();
|
||||
bool isCurrentThread () const throw ();
|
||||
bool operator == ( const epicsThread & ) const throw ();
|
||||
void show ( unsigned level ) const throw ();
|
||||
/* these operate on the current thread */
|
||||
static void suspendSelf () throw ();
|
||||
static void sleep (double seconds) throw ();
|
||||
@@ -158,7 +159,7 @@ public:
|
||||
static void setOkToBlock ( bool isOkToBlock ) throw ();
|
||||
|
||||
/* exceptions */
|
||||
class unableToCreateThread {};
|
||||
class unableToCreateThread;
|
||||
private:
|
||||
epicsThreadRunable & runable;
|
||||
epicsThreadId id;
|
||||
|
||||
@@ -59,7 +59,7 @@ static struct taskVar *taskVarHead;
|
||||
/*
|
||||
* Support for `once-only' execution
|
||||
*/
|
||||
static int initialized;
|
||||
static int initialized = 0;
|
||||
static epicsMutexId onceMutex;
|
||||
|
||||
/*
|
||||
|
||||
@@ -165,6 +165,9 @@ fdmgrTest_SRCS += fdmgrTest.c
|
||||
fdmgrTest_LIBS += ca
|
||||
# FIXME: program never exits.
|
||||
|
||||
TESTPROD_HOST += cvtFastPerform
|
||||
cvtFastPerform_SRCS += cvtFastPerform.cpp
|
||||
testHarness_SRCS += cvtFastPerform.cpp
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
|
||||
143
src/libCom/test/cvtFastPerform.cpp
Normal file
143
src/libCom/test/cvtFastPerform.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
|
||||
// Author: Jeff Hill, LANL
|
||||
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
#include <cstdlib>
|
||||
#include <typeinfo>
|
||||
|
||||
#include "epicsStdio.h"
|
||||
#include "cvtFast.h"
|
||||
#include "epicsTime.h"
|
||||
#include "testMain.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef void ( * PTestFunc ) ( const double &, char * pBug, size_t bufSize );
|
||||
|
||||
class Test {
|
||||
public:
|
||||
Test ();
|
||||
void execute ();
|
||||
protected:
|
||||
char _pDst[128];
|
||||
double _srcVal;
|
||||
unsigned short _prec;
|
||||
static unsigned const _nUnrolled = 10;
|
||||
static const unsigned _uSecPerSec = 1000000;
|
||||
static unsigned const _nIterations = 10000;
|
||||
virtual void _target () = 0;
|
||||
void _measure ();
|
||||
Test ( const Test & );
|
||||
Test & operator = ( Test & );
|
||||
};
|
||||
|
||||
class TestCvtFastDouble : public Test {
|
||||
protected:
|
||||
void _target ();
|
||||
};
|
||||
|
||||
class TestSNPrintf : public Test {
|
||||
protected:
|
||||
void _target ();
|
||||
};
|
||||
|
||||
Test ::
|
||||
Test () :
|
||||
_srcVal ( 0.0 ), _prec ( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
void Test :: execute ()
|
||||
{
|
||||
static const unsigned lowPrecision = 2;
|
||||
static const unsigned highPrecision = DBL_MANT_DIG;
|
||||
|
||||
for ( unsigned i = 0; i < 3; i++ ) {
|
||||
double mVal = rand ();
|
||||
mVal /= (RAND_MAX + 1);
|
||||
double fEVal = rand ();
|
||||
fEVal /= (RAND_MAX + 1);
|
||||
fEVal *= DBL_MAX_EXP - DBL_MIN_EXP;
|
||||
fEVal += DBL_MIN_EXP;
|
||||
int eVal = static_cast < int > ( fEVal + 0.5 );
|
||||
_srcVal = ldexp ( mVal, eVal );
|
||||
for ( _prec = lowPrecision;
|
||||
_prec <= highPrecision; _prec += 4u ) {
|
||||
_measure ();
|
||||
}
|
||||
_srcVal = rand ();
|
||||
_srcVal /= (RAND_MAX + 1);
|
||||
_srcVal *= 10.0;
|
||||
_srcVal -= 5.0;
|
||||
for ( _prec = lowPrecision;
|
||||
_prec <= highPrecision; _prec += 4u ) {
|
||||
_measure ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Test :: _measure ()
|
||||
{
|
||||
epicsTime beg = epicsTime :: getCurrent ();
|
||||
for ( unsigned i = 0; i < _nIterations; i++ ) {
|
||||
_target ();
|
||||
}
|
||||
epicsTime end = epicsTime :: getCurrent ();
|
||||
double elapsed = end - beg;
|
||||
elapsed /= _nIterations * _nUnrolled;
|
||||
elapsed *= _uSecPerSec;
|
||||
printf ( " %4.4f usec, prec=%i, val=%4.4g, for %s\n",
|
||||
elapsed, _prec, _srcVal, typeid ( *this ).name () );
|
||||
}
|
||||
|
||||
void TestCvtFastDouble :: _target ()
|
||||
{
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
}
|
||||
|
||||
void TestSNPrintf :: _target ()
|
||||
{
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
}
|
||||
|
||||
MAIN(cvtFastPerform)
|
||||
{
|
||||
TestCvtFastDouble testCvtFastDouble;
|
||||
TestSNPrintf testSNPrintf;
|
||||
|
||||
testCvtFastDouble.execute ();
|
||||
testSNPrintf.execute ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -281,7 +281,7 @@ MAIN(epicsCalcTest)
|
||||
testCalc("finite(-Inf,1,2)", 0);
|
||||
testExpr(isinf(0));
|
||||
testExpr(isinf(Inf));
|
||||
testExpr(isinf(-Inf));
|
||||
testExpr(!!isinf(-Inf)); // Some GCCs return -1/0/+1 rather than 0/+1
|
||||
testExpr(isinf(NaN));
|
||||
testExpr(isnan(0));
|
||||
testExpr(isnan(Inf));
|
||||
|
||||
@@ -36,6 +36,7 @@ int ringPointerTest(void);
|
||||
int ringBytesTest(void);
|
||||
int blockingSockTest(void);
|
||||
int taskwdTest(void);
|
||||
int cvtFastPerform(void);
|
||||
int epicsExitTest(void);
|
||||
|
||||
void epicsRunLibComTests(void)
|
||||
@@ -90,6 +91,8 @@ void epicsRunLibComTests(void)
|
||||
|
||||
runTest(taskwdTest);
|
||||
|
||||
runTest(cvtFastPerform);
|
||||
|
||||
/*
|
||||
* Exit must come last as it never returns
|
||||
*/
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
\*************************************************************************/
|
||||
|
||||
#include "epicsExit.h"
|
||||
#include "epicsGeneralTime.h"
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
extern void epicsRunLibComTests(void);
|
||||
generalTimeReport(1);
|
||||
epicsRunLibComTests();
|
||||
epicsExit(0);
|
||||
return 0;
|
||||
|
||||
@@ -189,12 +189,6 @@ epicsTimer::expireInfo timer::getExpireInfo () const
|
||||
void timer::show ( unsigned int level ) const
|
||||
{
|
||||
epicsGuard < epicsMutex > locker ( this->queue.mutex );
|
||||
const char * pName = "<no notify attached>";
|
||||
const char *pStateName;
|
||||
|
||||
if ( this->pNotify ) {
|
||||
pName = typeid ( *this->pNotify ).name ();
|
||||
}
|
||||
double delay;
|
||||
if ( this->curState == statePending || this->curState == stateActive ) {
|
||||
try {
|
||||
@@ -207,6 +201,7 @@ void timer::show ( unsigned int level ) const
|
||||
else {
|
||||
delay = -DBL_MAX;
|
||||
}
|
||||
const char *pStateName;
|
||||
if ( this->curState == statePending ) {
|
||||
pStateName = "pending";
|
||||
}
|
||||
@@ -219,8 +214,8 @@ void timer::show ( unsigned int level ) const
|
||||
else {
|
||||
pStateName = "corrupt";
|
||||
}
|
||||
printf ( "%s, state = %s, delay = %f\n",
|
||||
pName, pStateName, delay );
|
||||
printf ( "timer, state = %s, delay = %f\n",
|
||||
pStateName, delay );
|
||||
if ( level >= 1u && this->pNotify ) {
|
||||
this->pNotify->show ( level - 1u );
|
||||
}
|
||||
|
||||
@@ -131,7 +131,10 @@ void timerQueueActive::show ( unsigned int level ) const
|
||||
{
|
||||
printf ( "EPICS threaded timer queue at %p\n",
|
||||
static_cast <const void *> ( this ) );
|
||||
if ( level >=1u ) {
|
||||
if ( level > 0u ) {
|
||||
// specifying level one here avoids recursive
|
||||
// show callback
|
||||
this->thread.show ( 1u );
|
||||
this->queue.show ( level - 1u );
|
||||
printf ( "reschedule event\n" );
|
||||
this->rescheduleEvent.show ( level - 1u );
|
||||
|
||||
@@ -15,59 +15,145 @@
|
||||
|
||||
#include "exServer.h"
|
||||
|
||||
exAsyncPV::exAsyncPV ( exServer & cas, pvInfo & setup,
|
||||
bool preCreateFlag, bool scanOnIn,
|
||||
double asyncDelayIn ) :
|
||||
exScalarPV ( cas, setup, preCreateFlag, scanOnIn ),
|
||||
asyncDelay ( asyncDelayIn ),
|
||||
simultAsychReadIOCount ( 0u ),
|
||||
simultAsychWriteIOCount ( 0u )
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncPV::read()
|
||||
// (virtual replacement for the default)
|
||||
//
|
||||
caStatus exAsyncPV::read (const casCtx &ctx, gdd &valueIn)
|
||||
{
|
||||
exAsyncReadIO *pIO;
|
||||
|
||||
if (this->simultAsychIOCount>=maxSimultAsyncIO) {
|
||||
if ( this->simultAsychReadIOCount >= this->cas.maxSimultAsyncIO () ) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
|
||||
this->simultAsychIOCount++;
|
||||
|
||||
pIO = new exAsyncReadIO ( this->cas, ctx, *this, valueIn );
|
||||
if (!pIO) {
|
||||
return S_casApp_noMemory;
|
||||
pIO = new exAsyncReadIO ( this->cas, ctx,
|
||||
*this, valueIn, this->asyncDelay );
|
||||
if ( ! pIO ) {
|
||||
if ( this->simultAsychReadIOCount > 0 ) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
else {
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
}
|
||||
|
||||
this->simultAsychReadIOCount++;
|
||||
return S_casApp_asyncCompletion;
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncPV::write()
|
||||
// (virtual replacement for the default)
|
||||
// exAsyncPV::writeNotify()
|
||||
//
|
||||
caStatus exAsyncPV::write ( const casCtx &ctx, const gdd &valueIn )
|
||||
{
|
||||
exAsyncWriteIO *pIO;
|
||||
|
||||
if ( this->simultAsychIOCount >= maxSimultAsyncIO ) {
|
||||
caStatus exAsyncPV::writeNotify ( const casCtx &ctx, const gdd &valueIn )
|
||||
{
|
||||
if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
|
||||
this->simultAsychIOCount++;
|
||||
|
||||
pIO = new exAsyncWriteIO ( this->cas, ctx, *this, valueIn );
|
||||
exAsyncWriteIO * pIO = new
|
||||
exAsyncWriteIO ( this->cas, ctx, *this,
|
||||
valueIn, this->asyncDelay );
|
||||
if ( ! pIO ) {
|
||||
return S_casApp_noMemory;
|
||||
if ( this->simultAsychReadIOCount > 0 ) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
else {
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
}
|
||||
this->simultAsychWriteIOCount++;
|
||||
return S_casApp_asyncCompletion;
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncPV::write()
|
||||
//
|
||||
caStatus exAsyncPV::write ( const casCtx &ctx, const gdd &valueIn )
|
||||
{
|
||||
// implement the discard intermediate values, but last value
|
||||
// sent always applied behavior that IOCs provide excepting
|
||||
// that we will alow N requests to pend instead of a limit
|
||||
// of only one imposed in the IOC
|
||||
if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) {
|
||||
pStandbyValue.set ( & valueIn );
|
||||
return S_casApp_success;
|
||||
}
|
||||
|
||||
exAsyncWriteIO * pIO = new
|
||||
exAsyncWriteIO ( this->cas, ctx, *this,
|
||||
valueIn, this->asyncDelay );
|
||||
if ( ! pIO ) {
|
||||
pStandbyValue.set ( & valueIn );
|
||||
return S_casApp_success;
|
||||
}
|
||||
this->simultAsychWriteIOCount++;
|
||||
return S_casApp_asyncCompletion;
|
||||
}
|
||||
|
||||
// Implementing a specialized update for exAsyncPV
|
||||
// allows standby value to update when we update
|
||||
// the PV from an asynchronous write timer expiration
|
||||
// which is a better time compared to removeIO below
|
||||
// which, if used, gets the reads and writes out of
|
||||
// order. This type of reordering can cause the
|
||||
// regression tests to fail.
|
||||
caStatus exAsyncPV :: updateFromAsyncWrite ( const gdd & src )
|
||||
{
|
||||
caStatus stat = this->update ( src );
|
||||
if ( this->simultAsychWriteIOCount <=1 &&
|
||||
pStandbyValue.valid () ) {
|
||||
//printf("updateFromAsyncWrite: write standby\n");
|
||||
stat = this->update ( *this->pStandbyValue );
|
||||
this->pStandbyValue.set ( 0 );
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
void exAsyncPV::removeReadIO ()
|
||||
{
|
||||
if ( this->simultAsychReadIOCount > 0u ) {
|
||||
this->simultAsychReadIOCount--;
|
||||
}
|
||||
else {
|
||||
fprintf ( stderr, "inconsistent simultAsychReadIOCount?\n" );
|
||||
}
|
||||
}
|
||||
|
||||
void exAsyncPV::removeWriteIO ()
|
||||
{
|
||||
if ( this->simultAsychWriteIOCount > 0u ) {
|
||||
this->simultAsychWriteIOCount--;
|
||||
if ( this->simultAsychWriteIOCount == 0 &&
|
||||
pStandbyValue.valid () ) {
|
||||
//printf("removeIO: write standby\n");
|
||||
this->update ( *this->pStandbyValue );
|
||||
this->pStandbyValue.set ( 0 );
|
||||
}
|
||||
}
|
||||
else {
|
||||
fprintf ( stderr, "inconsistent simultAsychWriteIOCount?\n" );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncWriteIO::exAsyncWriteIO()
|
||||
//
|
||||
exAsyncWriteIO::exAsyncWriteIO ( exServer & cas,
|
||||
const casCtx & ctxIn, exAsyncPV & pvIn, const gdd & valueIn ) :
|
||||
const casCtx & ctxIn, exAsyncPV & pvIn,
|
||||
const gdd & valueIn, double asyncDelay ) :
|
||||
casAsyncWriteIO ( ctxIn ), pv ( pvIn ),
|
||||
timer ( cas.createTimer () ), pValue(valueIn)
|
||||
{
|
||||
this->timer.start ( *this, 0.1 );
|
||||
this->timer.start ( *this, asyncDelay );
|
||||
}
|
||||
|
||||
//
|
||||
@@ -75,23 +161,25 @@ exAsyncWriteIO::exAsyncWriteIO ( exServer & cas,
|
||||
//
|
||||
exAsyncWriteIO::~exAsyncWriteIO()
|
||||
{
|
||||
this->pv.removeIO();
|
||||
this->timer.destroy ();
|
||||
// if the timer hasnt expired, and the value
|
||||
// hasnt been written then force it to happen
|
||||
// now so that regression testing works
|
||||
if ( this->pValue.valid () ) {
|
||||
this->pv.update ( *this->pValue );
|
||||
this->pv.updateFromAsyncWrite ( *this->pValue );
|
||||
}
|
||||
this->pv.removeWriteIO();
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncWriteIO::expire()
|
||||
// (a virtual function that runs when the base timer expires)
|
||||
//
|
||||
epicsTimerNotify::expireStatus exAsyncWriteIO::expire ( const epicsTime & /* currentTime */ )
|
||||
epicsTimerNotify::expireStatus exAsyncWriteIO::
|
||||
expire ( const epicsTime & /* currentTime */ )
|
||||
{
|
||||
caStatus status = this->pv.update ( *this->pValue );
|
||||
assert ( this->pValue.valid () );
|
||||
caStatus status = this->pv.updateFromAsyncWrite ( *this->pValue );
|
||||
this->pValue.set ( 0 );
|
||||
this->postIOCompletion ( status );
|
||||
return noRestart;
|
||||
@@ -101,11 +189,12 @@ epicsTimerNotify::expireStatus exAsyncWriteIO::expire ( const epicsTime & /* cur
|
||||
// exAsyncReadIO::exAsyncReadIO()
|
||||
//
|
||||
exAsyncReadIO::exAsyncReadIO ( exServer & cas, const casCtx & ctxIn,
|
||||
exAsyncPV & pvIn, gdd & protoIn ) :
|
||||
exAsyncPV & pvIn, gdd & protoIn,
|
||||
double asyncDelay ) :
|
||||
casAsyncReadIO ( ctxIn ), pv ( pvIn ),
|
||||
timer ( cas.createTimer() ), pProto ( protoIn )
|
||||
{
|
||||
this->timer.start ( *this, 0.1 );
|
||||
this->timer.start ( *this, asyncDelay );
|
||||
}
|
||||
|
||||
//
|
||||
@@ -113,24 +202,22 @@ exAsyncReadIO::exAsyncReadIO ( exServer & cas, const casCtx & ctxIn,
|
||||
//
|
||||
exAsyncReadIO::~exAsyncReadIO()
|
||||
{
|
||||
this->pv.removeIO ();
|
||||
this->pv.removeReadIO ();
|
||||
this->timer.destroy ();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// exAsyncReadIO::expire()
|
||||
// (a virtual function that runs when the base timer expires)
|
||||
//
|
||||
epicsTimerNotify::expireStatus exAsyncReadIO::expire ( const epicsTime & /* currentTime */ )
|
||||
epicsTimerNotify::expireStatus
|
||||
exAsyncReadIO::expire ( const epicsTime & /* currentTime */ )
|
||||
{
|
||||
caStatus status;
|
||||
|
||||
//
|
||||
// map between the prototype in and the
|
||||
// current value
|
||||
//
|
||||
status = this->pv.exPV::readNoCtx ( this->pProto );
|
||||
caStatus status = this->pv.exPV::readNoCtx ( this->pProto );
|
||||
|
||||
//
|
||||
// post IO completion
|
||||
|
||||
@@ -39,16 +39,20 @@ const unsigned exServer::pvListNElem = NELEMENTS (exServer::pvList);
|
||||
// static on-the-fly PVs
|
||||
//
|
||||
pvInfo exServer::billy (-1.0, "billybob", 10.0f, -10.0f, aitEnumFloat64, excasIoAsync, 1u);
|
||||
pvInfo exServer::bloaty (-1.0, "bloaty", 10.0f, -10.0f, aitEnumFloat64, excasIoSync, 100000u);
|
||||
pvInfo exServer::bloater (.010, "bloater", 10.0f, -10.0f, aitEnumFloat64, excasIoSync, 10000u);
|
||||
pvInfo exServer::bloaty (.010, "bloaty", 10.0f, -10.0f, aitEnumFloat64, excasIoSync, 100000u);
|
||||
|
||||
|
||||
//
|
||||
// exServer::exServer()
|
||||
//
|
||||
exServer::exServer ( const char * const pvPrefix,
|
||||
unsigned aliasCount, bool scanOnIn,
|
||||
bool asyncScan ) :
|
||||
pTimerQueue ( 0 ), simultAsychIOCount ( 0u ),
|
||||
scanOn ( scanOnIn )
|
||||
bool asyncScan, double asyncDelayIn,
|
||||
unsigned maxSimultAsyncIOIn ) :
|
||||
pTimerQueue ( 0 ), simultAsychIOCount ( 0u ),
|
||||
_maxSimultAsyncIO ( maxSimultAsyncIOIn ),
|
||||
asyncDelay ( asyncDelayIn ), scanOn ( scanOnIn )
|
||||
{
|
||||
unsigned i;
|
||||
exPV *pPV;
|
||||
@@ -74,7 +78,7 @@ exServer::exServer ( const char * const pvPrefix,
|
||||
// pre-create all of the simple PVs that this server will export
|
||||
//
|
||||
for (pPVI = exServer::pvList; pPVI < pPVAfter; pPVI++) {
|
||||
pPV = pPVI->createPV (*this, true, scanOnIn);
|
||||
pPV = pPVI->createPV (*this, true, scanOnIn, this->asyncDelay );
|
||||
if (!pPV) {
|
||||
fprintf(stderr, "Unable to create new PV \"%s\"\n",
|
||||
pPVI->getName());
|
||||
@@ -103,6 +107,8 @@ exServer::exServer ( const char * const pvPrefix,
|
||||
//
|
||||
sprintf ( pvAlias, pNameFmtStr, pvPrefix, billy.getName() );
|
||||
this->installAliasName ( billy, pvAlias );
|
||||
sprintf ( pvAlias, pNameFmtStr, pvPrefix, bloater.getName() );
|
||||
this->installAliasName ( bloater, pvAlias );
|
||||
sprintf ( pvAlias, pNameFmtStr, pvPrefix, bloaty.getName() );
|
||||
this->installAliasName ( bloaty, pvAlias );
|
||||
}
|
||||
@@ -187,18 +193,19 @@ pvExistReturn exServer::pvExistTest // X aCC 361
|
||||
return pverExistsHere;
|
||||
}
|
||||
else {
|
||||
if ( this->simultAsychIOCount >= maxSimultAsyncIO ) {
|
||||
if ( this->simultAsychIOCount >= this->_maxSimultAsyncIO ) {
|
||||
return pverDoesNotExistHere;
|
||||
}
|
||||
|
||||
this->simultAsychIOCount++;
|
||||
|
||||
exAsyncExistIO *pIO;
|
||||
pIO = new exAsyncExistIO ( pvi, ctxIn, *this );
|
||||
if (pIO) {
|
||||
exAsyncExistIO * pIO =
|
||||
new exAsyncExistIO ( pvi, ctxIn, *this );
|
||||
if ( pIO ) {
|
||||
return pverAsyncCompletion;
|
||||
}
|
||||
else {
|
||||
this->simultAsychIOCount--;
|
||||
return pverDoesNotExistHere;
|
||||
}
|
||||
}
|
||||
@@ -228,7 +235,7 @@ pvAttachReturn exServer::pvAttach // X aCC 361
|
||||
// If this is a synchronous PV create the PV now
|
||||
//
|
||||
if (pvi.getIOType() == excasIoSync) {
|
||||
pPV = pvi.createPV(*this, false, this->scanOn);
|
||||
pPV = pvi.createPV(*this, false, this->scanOn, this->asyncDelay );
|
||||
if (pPV) {
|
||||
return *pPV;
|
||||
}
|
||||
@@ -240,18 +247,20 @@ pvAttachReturn exServer::pvAttach // X aCC 361
|
||||
// Initiate async IO if this is an async PV
|
||||
//
|
||||
else {
|
||||
if (this->simultAsychIOCount>=maxSimultAsyncIO) {
|
||||
if (this->simultAsychIOCount>=this->_maxSimultAsyncIO) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
|
||||
this->simultAsychIOCount++;
|
||||
|
||||
exAsyncCreateIO *pIO =
|
||||
new exAsyncCreateIO(pvi, *this, ctx, this->scanOn);
|
||||
new exAsyncCreateIO ( pvi, *this, ctx,
|
||||
this->scanOn, this->asyncDelay );
|
||||
if (pIO) {
|
||||
return S_casApp_asyncCompletion;
|
||||
}
|
||||
else {
|
||||
this->simultAsychIOCount--;
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
}
|
||||
@@ -281,8 +290,8 @@ class epicsTimer & exServer::createTimer ()
|
||||
//
|
||||
// pvInfo::createPV()
|
||||
//
|
||||
exPV *pvInfo::createPV ( exServer & cas,
|
||||
bool preCreateFlag, bool scanOn )
|
||||
exPV *pvInfo::createPV ( exServer & cas, bool preCreateFlag,
|
||||
bool scanOn, double asyncDelay )
|
||||
{
|
||||
if (this->pPV) {
|
||||
return this->pPV;
|
||||
@@ -301,7 +310,8 @@ exPV *pvInfo::createPV ( exServer & cas,
|
||||
pNewPV = new exScalarPV ( cas, *this, preCreateFlag, scanOn );
|
||||
break;
|
||||
case excasIoAsync:
|
||||
pNewPV = new exAsyncPV ( cas, *this, preCreateFlag, scanOn );
|
||||
pNewPV = new exAsyncPV ( cas, *this,
|
||||
preCreateFlag, scanOn, asyncDelay );
|
||||
break;
|
||||
default:
|
||||
pNewPV = NULL;
|
||||
@@ -388,11 +398,12 @@ epicsTimerNotify::expireStatus exAsyncExistIO::expire ( const epicsTime & /*curr
|
||||
//
|
||||
// exAsyncCreateIO::exAsyncCreateIO()
|
||||
//
|
||||
exAsyncCreateIO::exAsyncCreateIO ( pvInfo &pviIn, exServer &casIn,
|
||||
const casCtx &ctxIn, bool scanOnIn ) :
|
||||
exAsyncCreateIO ::
|
||||
exAsyncCreateIO ( pvInfo &pviIn, exServer &casIn,
|
||||
const casCtx &ctxIn, bool scanOnIn, double asyncDelayIn ) :
|
||||
casAsyncPVAttachIO ( ctxIn ), pvi ( pviIn ),
|
||||
timer ( casIn.createTimer () ),
|
||||
cas ( casIn ), scanOn ( scanOnIn )
|
||||
cas ( casIn ), asyncDelay ( asyncDelayIn ), scanOn ( scanOnIn )
|
||||
{
|
||||
this->timer.start ( *this, 0.00001 );
|
||||
}
|
||||
@@ -412,9 +423,8 @@ exAsyncCreateIO::~exAsyncCreateIO()
|
||||
//
|
||||
epicsTimerNotify::expireStatus exAsyncCreateIO::expire ( const epicsTime & /*currentTime*/ )
|
||||
{
|
||||
exPV *pPV;
|
||||
|
||||
pPV = this->pvi.createPV ( this->cas, false, this->scanOn );
|
||||
exPV * pPV = this->pvi.createPV ( this->cas, false,
|
||||
this->scanOn, this->asyncDelay );
|
||||
if ( pPV ) {
|
||||
this->postIOCompletion ( pvAttachReturn ( *pPV ) );
|
||||
}
|
||||
|
||||
@@ -51,8 +51,6 @@
|
||||
# define NELEMENTS(A) (sizeof(A)/sizeof(A[0]))
|
||||
#endif
|
||||
|
||||
#define maxSimultAsyncIO 1000u
|
||||
|
||||
//
|
||||
// info about all pv in this server
|
||||
//
|
||||
@@ -62,28 +60,27 @@ class exPV;
|
||||
class exServer;
|
||||
|
||||
//
|
||||
// pvInfo
|
||||
//
|
||||
// pvInfo
|
||||
//
|
||||
class pvInfo {
|
||||
public:
|
||||
pvInfo ( double scanPeriodIn, const char *pNameIn,
|
||||
aitFloat32 hoprIn, aitFloat32 loprIn,
|
||||
aitEnum typeIn, excasIoType ioTypeIn,
|
||||
unsigned countIn );
|
||||
pvInfo ( const pvInfo & copyIn );
|
||||
public:
|
||||
|
||||
pvInfo ( double scanPeriodIn, const char * pNameIn,
|
||||
aitFloat32 hoprIn, aitFloat32 loprIn, aitEnum typeIn,
|
||||
excasIoType ioTypeIn, unsigned countIn );
|
||||
pvInfo ( const pvInfo & copyIn );
|
||||
~pvInfo ();
|
||||
double getScanPeriod () const;
|
||||
const char *getName () const;
|
||||
double getHopr () const;
|
||||
double getLopr () const;
|
||||
aitEnum getType () const;
|
||||
excasIoType getIOType () const;
|
||||
unsigned getElementCount () const;
|
||||
void unlinkPV ();
|
||||
exPV *createPV ( exServer & exCAS,
|
||||
bool preCreateFlag, bool scanOn );
|
||||
double getScanPeriod () const;
|
||||
const char * getName ()
|
||||
const; double getHopr () const;
|
||||
double getLopr () const;
|
||||
aitEnum getType () const;
|
||||
excasIoType getIOType () const;
|
||||
unsigned getElementCount () const;
|
||||
void unlinkPV ();
|
||||
exPV *createPV ( exServer & exCAS, bool preCreateFlag,
|
||||
bool scanOn, double asyncDelay );
|
||||
void deletePV ();
|
||||
|
||||
private:
|
||||
const double scanPeriod;
|
||||
const char * pName;
|
||||
@@ -277,7 +274,8 @@ class exServer : private caServer {
|
||||
public:
|
||||
exServer ( const char * const pvPrefix,
|
||||
unsigned aliasCount, bool scanOn,
|
||||
bool asyncScan );
|
||||
bool asyncScan, double asyncDelay,
|
||||
unsigned maxSimultAsyncIO );
|
||||
~exServer ();
|
||||
void show ( unsigned level ) const;
|
||||
void removeIO ();
|
||||
@@ -287,11 +285,15 @@ public:
|
||||
void setDebugLevel ( unsigned level );
|
||||
|
||||
void destroyAllPV ();
|
||||
|
||||
unsigned maxSimultAsyncIO () const;
|
||||
|
||||
private:
|
||||
resTable < pvEntry, stringId > stringResTbl;
|
||||
epicsTimerQueueActive * pTimerQueue;
|
||||
unsigned simultAsychIOCount;
|
||||
const unsigned _maxSimultAsyncIO;
|
||||
double asyncDelay;
|
||||
bool scanOn;
|
||||
|
||||
void installAliasName ( pvInfo & info, const char * pAliasName );
|
||||
@@ -310,12 +312,13 @@ private:
|
||||
//
|
||||
static pvInfo pvList[];
|
||||
static const unsigned pvListNElem;
|
||||
|
||||
|
||||
//
|
||||
// on-the-fly PVs
|
||||
//
|
||||
static pvInfo bill;
|
||||
static pvInfo billy;
|
||||
static pvInfo bloater;
|
||||
static pvInfo bloaty;
|
||||
static pvInfo boot;
|
||||
static pvInfo booty;
|
||||
@@ -327,12 +330,18 @@ private:
|
||||
class exAsyncPV : public exScalarPV {
|
||||
public:
|
||||
exAsyncPV ( exServer & cas, pvInfo &setup,
|
||||
bool preCreateFlag, bool scanOnIn );
|
||||
bool preCreateFlag, bool scanOnIn, double asyncDelay );
|
||||
caStatus read ( const casCtx & ctxIn, gdd & protoIn );
|
||||
caStatus write ( const casCtx & ctxIn, const gdd & value );
|
||||
void removeIO();
|
||||
caStatus writeNotify ( const casCtx & ctxIn, const gdd & value );
|
||||
void removeReadIO();
|
||||
void removeWriteIO();
|
||||
caStatus updateFromAsyncWrite ( const gdd & );
|
||||
private:
|
||||
unsigned simultAsychIOCount;
|
||||
double asyncDelay;
|
||||
smartConstGDDPointer pStandbyValue;
|
||||
unsigned simultAsychReadIOCount;
|
||||
unsigned simultAsychWriteIOCount;
|
||||
exAsyncPV & operator = ( const exAsyncPV & );
|
||||
exAsyncPV ( const exAsyncPV & );
|
||||
};
|
||||
@@ -357,7 +366,8 @@ private:
|
||||
//
|
||||
class exAsyncWriteIO : public casAsyncWriteIO, public epicsTimerNotify {
|
||||
public:
|
||||
exAsyncWriteIO ( exServer &, const casCtx & ctxIn, exAsyncPV &, const gdd & );
|
||||
exAsyncWriteIO ( exServer &, const casCtx & ctxIn,
|
||||
exAsyncPV &, const gdd &, double asyncDelay );
|
||||
~exAsyncWriteIO ();
|
||||
private:
|
||||
exAsyncPV & pv;
|
||||
@@ -373,7 +383,8 @@ private:
|
||||
//
|
||||
class exAsyncReadIO : public casAsyncReadIO, public epicsTimerNotify {
|
||||
public:
|
||||
exAsyncReadIO ( exServer &, const casCtx &, exAsyncPV &, gdd & );
|
||||
exAsyncReadIO ( exServer &, const casCtx &,
|
||||
exAsyncPV &, gdd &, double asyncDelay );
|
||||
virtual ~exAsyncReadIO ();
|
||||
private:
|
||||
exAsyncPV & pv;
|
||||
@@ -410,12 +421,13 @@ private:
|
||||
class exAsyncCreateIO : public casAsyncPVAttachIO, public epicsTimerNotify {
|
||||
public:
|
||||
exAsyncCreateIO ( pvInfo & pviIn, exServer & casIn,
|
||||
const casCtx & ctxIn, bool scanOnIn );
|
||||
const casCtx & ctxIn, bool scanOnIn, double asyncDelay );
|
||||
virtual ~exAsyncCreateIO ();
|
||||
private:
|
||||
pvInfo & pvi;
|
||||
epicsTimer & timer;
|
||||
exServer & cas;
|
||||
double asyncDelay;
|
||||
bool scanOn;
|
||||
expireStatus expire ( const epicsTime & currentTime );
|
||||
exAsyncCreateIO & operator = ( const exAsyncCreateIO & );
|
||||
@@ -562,25 +574,13 @@ inline void exServer::removeIO()
|
||||
}
|
||||
else {
|
||||
fprintf ( stderr,
|
||||
"simultAsychIOCount underflow?\n" );
|
||||
"simultAsychIOCount underflow?\n" );
|
||||
}
|
||||
}
|
||||
|
||||
inline exAsyncPV::exAsyncPV ( exServer & cas, pvInfo & setup,
|
||||
bool preCreateFlag, bool scanOnIn ) :
|
||||
exScalarPV ( cas, setup, preCreateFlag, scanOnIn ),
|
||||
simultAsychIOCount ( 0u )
|
||||
inline unsigned exServer :: maxSimultAsyncIO () const
|
||||
{
|
||||
}
|
||||
|
||||
inline void exAsyncPV::removeIO ()
|
||||
{
|
||||
if ( this->simultAsychIOCount > 0u ) {
|
||||
this->simultAsychIOCount--;
|
||||
}
|
||||
else {
|
||||
fprintf ( stderr, "inconsistent simultAsychIOCount?\n" );
|
||||
}
|
||||
return this->_maxSimultAsyncIO;
|
||||
}
|
||||
|
||||
inline exChannel::exChannel ( const casCtx & ctxIn ) :
|
||||
|
||||
@@ -24,12 +24,14 @@ extern int main ( int argc, const char **argv )
|
||||
exServer *pCAS;
|
||||
unsigned debugLevel = 0u;
|
||||
double executionTime = 0.0;
|
||||
double asyncDelay = 0.1;
|
||||
char pvPrefix[128] = "";
|
||||
unsigned aliasCount = 1u;
|
||||
unsigned scanOn = true;
|
||||
unsigned syncScan = true;
|
||||
char arraySize[64] = "";
|
||||
bool forever = true;
|
||||
unsigned maxSimultAsyncIO = 1000u;
|
||||
int i;
|
||||
|
||||
i = 1;
|
||||
@@ -77,6 +79,18 @@ extern int main ( int argc, const char **argv )
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ( strcmp ( argv[i],"-ad" ) == 0 ) {
|
||||
if ( i+1 < argc && sscanf ( argv[i+1], "%lf", & asyncDelay ) == 1 ) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ( strcmp ( argv[i],"-an" ) == 0 ) {
|
||||
if ( i+1 < argc && sscanf ( argv[i+1], "%u", & maxSimultAsyncIO ) == 1 ) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
printf ( "\"%s\"?\n", argv[i] );
|
||||
if ( i + 1 < argc ) {
|
||||
printf ( "\"%s\"?\n", argv[i+1] );
|
||||
@@ -85,7 +99,8 @@ extern int main ( int argc, const char **argv )
|
||||
"usage: %s [-d <debug level> -t <execution time> -p <PV name prefix> "
|
||||
"-c <numbered alias count> -s <1=scan on (default), 0=scan off> "
|
||||
"-ss <1=synchronous scan (default), 0=asynchronous scan> "
|
||||
"-a <max array size>\n",
|
||||
"-a <max array size> -ad <async delay> "
|
||||
"-an <max simultaneous async>\n",
|
||||
argv[0]);
|
||||
|
||||
return (1);
|
||||
@@ -97,7 +112,8 @@ extern int main ( int argc, const char **argv )
|
||||
|
||||
try {
|
||||
pCAS = new exServer ( pvPrefix, aliasCount,
|
||||
scanOn != 0, syncScan == 0 );
|
||||
scanOn != 0, syncScan == 0, asyncDelay,
|
||||
maxSimultAsyncIO );
|
||||
}
|
||||
catch ( ... ) {
|
||||
errlogPrintf ( "Server initialization error\n" );
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# CONFIG
|
||||
# CONFIG - Load build configuration data
|
||||
#
|
||||
# Do not make changes to this file!
|
||||
|
||||
# Allow user to override where the build rules come from
|
||||
RULES = $(EPICS_BASE)
|
||||
|
||||
# RELEASE files point to other application tops
|
||||
include $(TOP)/configure/RELEASE
|
||||
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common
|
||||
|
||||
ifdef T_A
|
||||
-include $(TOP)/configure/RELEASE.Common.$(T_A)
|
||||
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
@@ -13,11 +16,12 @@ endif
|
||||
CONFIG = $(RULES)/configure
|
||||
include $(CONFIG)/CONFIG
|
||||
|
||||
# Override for definition in base
|
||||
# Override the Base definition:
|
||||
INSTALL_LOCATION = $(TOP)
|
||||
|
||||
# CONFIG_SITE files contain other build configuration settings
|
||||
include $(TOP)/configure/CONFIG_SITE
|
||||
-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common
|
||||
|
||||
ifdef T_A
|
||||
-include $(TOP)/configure/CONFIG_SITE.Common.$(T_A)
|
||||
-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
#RELEASE Location of external support modules
|
||||
# RELEASE - Location of external support modules
|
||||
#
|
||||
# IF YOU MAKE ANY CHANGES to this file you MUST at least run
|
||||
# "gnumake" in this directory afterwards; you usually need
|
||||
# to run "gnumake rebuild" in the application's top level
|
||||
# directory each time this file is changed.
|
||||
# IF YOU MAKE ANY CHANGES to this file you must subsequently
|
||||
# do a "gnumake rebuild" in this application's top level
|
||||
# directory.
|
||||
#
|
||||
# NOTE: The build does not check dependencies against files
|
||||
# that are outside this application, thus you should run
|
||||
# "gnumake distclean install" in the top directory each time
|
||||
# EPICS_BASE, SNCSEQ, or any other external module defined
|
||||
# in the RELEASE file is rebuilt.
|
||||
# The build process does not check dependencies against files
|
||||
# that are outside this application, thus you should do a
|
||||
# "gnumake rebuild" in the top level directory after EPICS_BASE
|
||||
# or any other external module pointed to below is rebuilt.
|
||||
#
|
||||
# Host/target specific settings can be specified in files named
|
||||
# Host- or target-specific settings can be given in files named
|
||||
# RELEASE.$(EPICS_HOST_ARCH).Common
|
||||
# RELEASE.Common.$(T_A)
|
||||
# RELEASE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
#
|
||||
# This file should ONLY define paths to other support modules,
|
||||
# or include statements that pull in similar RELEASE files.
|
||||
# Build settings that are NOT module paths should appear in a
|
||||
# CONFIG_SITE file.
|
||||
|
||||
TEMPLATE_TOP=_TEMPLATE_TOP_
|
||||
|
||||
@@ -24,5 +27,6 @@ TEMPLATE_TOP=_TEMPLATE_TOP_
|
||||
# EPICS_BASE usually appears last so other apps can override stuff:
|
||||
EPICS_BASE=_EPICS_BASE_
|
||||
|
||||
# Set RULES to use build rules from somewhere other than EPICS_BASE:
|
||||
# Set RULES here if you want to take build rules from somewhere
|
||||
# other than EPICS_BASE:
|
||||
#RULES=/path/to/epics/support/module/rules/x-y
|
||||
|
||||
@@ -15,5 +15,8 @@
|
||||
|
||||
# Location of external products
|
||||
EPICS_BASE=_EPICS_BASE_
|
||||
EPICS_EXTENSIONS = $(EPICS_BASE)/../extensions
|
||||
EPICS_EXTENSIONS = $(TOP)
|
||||
|
||||
# OAG_APPS may be needed by extension SDDS
|
||||
#OAG_APPS=$(TOP)/../../oag/apps
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ IDL = /usr/local/idl
|
||||
# IDL=$(IDL)/external/rpc is the sun4 version
|
||||
IDLRPC = $(IDL)/external/rpc.solaris
|
||||
|
||||
X11_LIB = d:/cygwin/usr/X11R6/lib
|
||||
X11_INC = d:/cygwin/usr/X11R6/include
|
||||
X11_LIB = c:/cygwin/lib
|
||||
X11_INC = c:/cygwin/include
|
||||
|
||||
MOTIF_LIB = $(X11_LIB)
|
||||
MOTIF_INC = $(X11_INC)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
-include $(TOP)/configure/os/CONFIG_SITE.linux-x86.linux-x86
|
||||
|
||||
X11_LIB=/usr/lib64
|
||||
X11_INC=/usr/include/X11
|
||||
X11_INC=/usr/include
|
||||
|
||||
# OpenMotif location
|
||||
MOTIF_LIB=/usr/lib64
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
-include $(TOP)/configure/os/CONFIG_SITE.solaris-sparc.solaris-sparc
|
||||
|
||||
XRTGRAPH_EXTENSIONS=
|
||||
XRTGRAPH=
|
||||
#XRTGRAPH_EXTENSIONS=
|
||||
#XRTGRAPH=
|
||||
SCIPLOT=YES
|
||||
|
||||
X11_LIB = /usr/lib
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# Site Specific Configuration Information
|
||||
# Only the local epics system manager should modify this file
|
||||
|
||||
# Where to find utilities/libraries
|
||||
# If you do not have a certain product,
|
||||
# leave the line empty.
|
||||
#
|
||||
|
||||
-include $(TOP)/configure/os/CONFIG_SITE.solaris-x86.solaris-x86
|
||||
|
||||
XRTGRAPH=
|
||||
SCIPLOT=YES
|
||||
|
||||
@@ -181,7 +181,7 @@ ifeq ($(EXCEED),Exceed10.0)
|
||||
EXCEED_CFLAGS=/DXMSTATIC /DMOTIFAPP
|
||||
endif
|
||||
ifeq ($(EXCEED),Exceed12.0)
|
||||
XDK=C:/Exceed10.0/XDK
|
||||
XDK=C:/Exceed12.0/XDK
|
||||
X11_LIB = $(XDK)/lib
|
||||
X11_INC = $(XDK)/include
|
||||
EXCEED_XLIBS=XmStatic XmStatXt Xlib HCLXmu
|
||||
|
||||
@@ -146,7 +146,7 @@ static long process(subRecord *prec)
|
||||
if (!pact && prec->pact) return 0;
|
||||
prec->pact = TRUE;
|
||||
|
||||
/* Old async signal, deprecated */
|
||||
/* Asynchronous function (documented API!) */
|
||||
if (status == 1) return 0;
|
||||
|
||||
recGblGetTimeStamp(prec);
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
|
||||
if $running_under_some_shell; # registerRecordDeviceDriver
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
# Copyright (c) 2009 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 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.
|
||||
#*************************************************************************
|
||||
|
||||
@@ -152,7 +151,14 @@ if (@variables) {
|
||||
|
||||
#Now actual registration code.
|
||||
|
||||
print "int $subname(DBBASE *pbase)\n{\n";
|
||||
print << "END" ;
|
||||
int $subname(DBBASE *pbase)
|
||||
{
|
||||
if (!pbase) {
|
||||
printf("pdbbase is NULL; you must load a DBD file first.\\n");
|
||||
return -1;
|
||||
}
|
||||
END
|
||||
|
||||
if($numberRecordType>0) {
|
||||
print " registerRecordTypes(pbase, $numberRecordType, ",
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
##########################################################################
|
||||
# Copyright (c) 2009 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 a Software License Agreement found
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
##########################################################################
|
||||
@@ -13,14 +11,13 @@ include $(TOP)/configure/CONFIG
|
||||
|
||||
PROD_IOC = softIoc
|
||||
|
||||
INC += epicsInstallDir.h
|
||||
|
||||
DBD += softIoc.dbd
|
||||
softIoc_DBD += base.dbd
|
||||
softIoc_DBD += dlload.dbd
|
||||
softIoc_DBD += system.dbd
|
||||
|
||||
ABS_INSTALL_DIR = $(shell $(PERL) $(TOOLS)/fullPathName.pl $(INSTALL_LOCATION))
|
||||
USR_CXXFLAGS += -DEPICS_BASE=$(ABS_INSTALL_DIR)
|
||||
|
||||
softIoc_SRCS += softIoc_registerRecordDeviceDriver.cpp
|
||||
softIoc_SRCS_DEFAULT += softMain.cpp
|
||||
softIoc_SRCS_vxWorks = -nil-
|
||||
@@ -30,3 +27,6 @@ softIoc_LIBS = $(EPICS_BASE_IOC_LIBS)
|
||||
DB += softIocExit.db
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
$(COMMON_DIR)/epicsInstallDir.h:
|
||||
$(PERL) ../makeInstallDir.pl $(INSTALL_LOCATION) > $@
|
||||
|
||||
31
src/softIoc/makeInstallDir.pl
Normal file
31
src/softIoc/makeInstallDir.pl
Normal file
@@ -0,0 +1,31 @@
|
||||
eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
|
||||
if $running_under_some_shell; # registerRecordDeviceDriver
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
use strict;
|
||||
|
||||
use FindBin qw($Bin);
|
||||
use lib "$Bin/../../lib/perl";
|
||||
|
||||
use EPICS::Path;
|
||||
|
||||
die "Path to INSTALL_LOCATION missing\n" unless @ARGV == 1;
|
||||
|
||||
my $path = AbsPath(shift);
|
||||
|
||||
$path =~ s/\\/\\\\/gx;
|
||||
|
||||
print "/* THIS IS A GENERATED FILE. DO NOT EDIT! */\n",
|
||||
"\n",
|
||||
"#ifndef INC_epicsInstallDir_H\n",
|
||||
"#define INC_epicsInstallDir_H\n",
|
||||
"\n",
|
||||
"#define EPICS_BASE \"$path\"\n",
|
||||
"\n",
|
||||
"#endif /* INC_epicsInstallDir_H */\n";
|
||||
|
||||
@@ -70,16 +70,16 @@
|
||||
#include "asDbLib.h"
|
||||
#include "iocInit.h"
|
||||
#include "iocsh.h"
|
||||
#include "epicsInstallDir.h"
|
||||
|
||||
extern "C" int softIoc_registerRecordDeviceDriver(struct dbBase *pdbbase);
|
||||
|
||||
#define QUOTE(x) #x
|
||||
#define DBD_FILE(top) QUOTE(top) "/dbd/softIoc.dbd"
|
||||
#define EXIT_FILE(top) QUOTE(top) "/db/softIocExit.db"
|
||||
#define DBD_FILE EPICS_BASE "/dbd/softIoc.dbd"
|
||||
#define EXIT_FILE EPICS_BASE "/db/softIocExit.db"
|
||||
|
||||
const char *arg0;
|
||||
const char *base_dbd = DBD_FILE(EPICS_BASE);
|
||||
const char *exit_db = EXIT_FILE(EPICS_BASE);
|
||||
const char *base_dbd = DBD_FILE;
|
||||
const char *exit_db = EXIT_FILE;
|
||||
|
||||
|
||||
static void exitSubroutine(subRecord *precord) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
eval 'exec perl -S -w $0 ${1+"$@"}' # -*- Mode: perl -*-
|
||||
if 0;
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2008 The University of Chicago, as Operator of Argonne
|
||||
# Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
@@ -11,14 +11,27 @@ eval 'exec perl -S -w $0 ${1+"$@"}' # -*- Mode: perl -*-
|
||||
|
||||
# Determines an absolute pathname for its argument,
|
||||
# which may be either a relative or absolute path and
|
||||
# might have trailing parts that don't exist yet.
|
||||
# might have trailing directory names that don't exist yet.
|
||||
|
||||
use strict;
|
||||
|
||||
use FindBin qw($Bin);
|
||||
use lib "$Bin/../../lib/perl";
|
||||
|
||||
use Getopt::Std;
|
||||
use EPICS::Path;
|
||||
|
||||
print AbsPath(shift), "\n";
|
||||
our ($opt_h);
|
||||
|
||||
$Getopt::Std::OUTPUT_HELP_VERSION = 1;
|
||||
&HELP_MESSAGE if !getopts('h') || $opt_h || @ARGV != 1;
|
||||
|
||||
my $path = AbsPath(shift);
|
||||
|
||||
print "$path\n";
|
||||
|
||||
|
||||
sub HELP_MESSAGE {
|
||||
print STDERR "Usage: fullPathName.pl [-h] pathname\n";
|
||||
exit 2;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ YACCOPT = -l -d
|
||||
|
||||
SKELETON_FILE = include/flex.skel.static
|
||||
|
||||
USR_CPPFLAGS = -DDEFAULT_SKELETON_FILE="\"$(SKELETON_FILE)\""
|
||||
USR_CPPFLAGS = -DDEFAULT_SKELETON_FILE=$(SKELETON_FILE)
|
||||
|
||||
INC += flex.skel.static
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#define ENQUOTE(path) #path
|
||||
|
||||
#ifndef lint
|
||||
char copyright[] =
|
||||
"@(#) Copyright (c) 1990 The Regents of the University of California.\n\
|
||||
@@ -294,7 +296,7 @@ void flexend(int status)
|
||||
if ( usemecs )
|
||||
putc( 'm', stderr );
|
||||
|
||||
if ( strcmp( skelname, DEFAULT_SKELETON_FILE ) )
|
||||
if ( strcmp( skelname, ENQUOTE(DEFAULT_SKELETON_FILE) ) )
|
||||
fprintf( stderr, " -S%s", skelname );
|
||||
|
||||
putc( '\n', stderr );
|
||||
@@ -557,7 +559,7 @@ get_next_arg: /* used by -C and -S flags in lieu of a "continue 2" control */
|
||||
static char skeleton_name_storage[400];
|
||||
|
||||
skelname = skeleton_name_storage;
|
||||
(void) strcpy( skelname, DEFAULT_SKELETON_FILE );
|
||||
(void) strcpy( skelname, ENQUOTE(DEFAULT_SKELETON_FILE) );
|
||||
}
|
||||
|
||||
if ( ! use_stdout )
|
||||
|
||||
Reference in New Issue
Block a user