Merge commit '10d472202dc2bf1fa5c569d2a14d460e95030564' into PSI-7.0
This is before PVA is modified
This commit is contained in:
@@ -37,6 +37,8 @@ skip_commits:
|
||||
# build matrix configuration #
|
||||
#---------------------------------#
|
||||
|
||||
image: Visual Studio 2015
|
||||
|
||||
# Build Configurations: dll/static, regular/debug
|
||||
configuration:
|
||||
- dynamic
|
||||
@@ -49,6 +51,7 @@ environment:
|
||||
# common / default variables for all jobs
|
||||
SETUP_PATH: .ci-local:.ci
|
||||
BASE: SELF
|
||||
EPICS_TEST_IMPRECISE_TIMING: YES
|
||||
|
||||
matrix:
|
||||
- CMP: vs2019
|
||||
@@ -59,7 +62,7 @@ environment:
|
||||
- CMP: vs2013
|
||||
- CMP: vs2012
|
||||
- CMP: vs2010
|
||||
- CMP: mingw
|
||||
- CMP: gcc
|
||||
|
||||
# Platform: processor architecture
|
||||
platform:
|
||||
@@ -89,17 +92,17 @@ matrix:
|
||||
|
||||
install:
|
||||
- cmd: git submodule update --init --recursive
|
||||
- cmd: python .ci/appveyor/do.py prepare
|
||||
- cmd: python .ci/cue.py prepare
|
||||
|
||||
build_script:
|
||||
- cmd: python .ci/appveyor/do.py build
|
||||
- cmd: python .ci/cue.py build
|
||||
|
||||
test_script:
|
||||
- cmd: python .ci/appveyor/do.py test
|
||||
- cmd: python .ci/cue.py test
|
||||
|
||||
on_finish:
|
||||
- ps: Get-ChildItem *.tap -Recurse -Force | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
- cmd: python .ci/appveyor/do.py build test-results -s
|
||||
- cmd: python .ci/cue.py build test-results -s
|
||||
|
||||
#---------------------------------#
|
||||
# debugging #
|
||||
|
||||
19
.ci-local/travis-fixup.sh
Executable file
19
.ci-local/travis-fixup.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
set -e -u -x
|
||||
|
||||
env|grep TRAVIS
|
||||
|
||||
[ "$TRAVIS_OS_NAME" = "linux" ] || exit 0
|
||||
|
||||
# Ensure there is an interface with a (correct) broadcast address
|
||||
# eg. 'trusty' VMs have interface broadcast address mis-configured
|
||||
# (why oh why do people insist on setting this explicitly?)
|
||||
|
||||
sudo ip tuntap add dev tap42 mode tap
|
||||
|
||||
sudo ip addr add 192.168.240.1/24 broadcast + dev tap42
|
||||
|
||||
sudo ip link set dev tap42 up
|
||||
|
||||
# note that this device will be UP but not RUNNING
|
||||
# so java will see it as not UP since java confuses UP and RUNNING
|
||||
18
.travis.yml
18
.travis.yml
@@ -13,6 +13,7 @@ env:
|
||||
global:
|
||||
- SETUP_PATH=.ci-local:.ci
|
||||
- BASE=SELF
|
||||
- EPICS_TEST_IMPRECISE_TIMING=YES
|
||||
|
||||
addons:
|
||||
apt:
|
||||
@@ -35,10 +36,13 @@ addons:
|
||||
update: true
|
||||
|
||||
install:
|
||||
- ./.ci/travis/prepare.sh
|
||||
- ./.ci-local/travis-fixup.sh
|
||||
- python .ci/cue.py prepare
|
||||
|
||||
script:
|
||||
- ./.ci/travis/build.sh
|
||||
- python .ci/cue.py build
|
||||
- python .ci/cue.py test
|
||||
- python .ci/cue.py test-results
|
||||
|
||||
# Define build jobs
|
||||
|
||||
@@ -51,7 +55,7 @@ jobs:
|
||||
- dist: xenial
|
||||
|
||||
- dist: bionic
|
||||
env: STATIC=YES EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
env: BCFG=static EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
|
||||
- dist: trusty
|
||||
env: EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
@@ -63,15 +67,13 @@ jobs:
|
||||
|
||||
- dist: trusty
|
||||
compiler: clang
|
||||
env: STATIC=YES
|
||||
env: BCFG=static
|
||||
|
||||
# Cross-compilations to Windows using MinGW and WINE
|
||||
|
||||
- env: WINE=32 TEST=NO STATIC=YES
|
||||
compiler: mingw
|
||||
- env: WINE=32 TEST=NO BCFG=static
|
||||
|
||||
- env: WINE=32 TEST=NO STATIC=NO
|
||||
compiler: mingw
|
||||
- env: WINE=32 TEST=NO
|
||||
|
||||
# Cross-compilation to RTEMS
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ ifneq (,$(filter $(T_A), $(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS)))
|
||||
RUNTESTS_ENABLED = YES
|
||||
TESTSCRIPTS.t = $(filter %.t, $(TESTSCRIPTS))
|
||||
TAPFILES.t += $(TESTSCRIPTS.t:.t=.tap)
|
||||
JUNITFILES.t += $(TESTSCRIPTS.t:.t=.xml)
|
||||
JUNITFILES.t += $(TESTSCRIPTS.t:.t=-results.xml)
|
||||
TAPFILES += $(TAPFILES.t)
|
||||
JUNITFILES += $(JUNITFILES.t)
|
||||
endif
|
||||
@@ -403,7 +403,7 @@ ifdef RUNTESTS_ENABLED
|
||||
$(PERL) $< -tap > $@
|
||||
endif
|
||||
|
||||
$(JUNITFILES.t): %.xml: %.tap
|
||||
$(JUNITFILES.t): %-results.xml: %.tap
|
||||
$(TAPTOJUNIT) --puretap --output $@ --input $< $*
|
||||
|
||||
# If there's a perl test script (.plt) available, use it
|
||||
|
||||
@@ -1329,6 +1329,15 @@ header and removed the need for dbScan.c to reach into the internals of its
|
||||
# Changes incorporated from the 3.15 branch
|
||||
|
||||
|
||||
## Changes made on the 3.15 branch since 3.15.8
|
||||
|
||||
### Change to the `junitfiles` self-test build target
|
||||
|
||||
The names of the generated junit xml test output files have been changed
|
||||
from `<testname>.xml` to `<testname>-results.xml`, to allow better
|
||||
distinction from other xml files. (I.e., for easy wildcard matching.)
|
||||
|
||||
|
||||
## Changes made between 3.15.7 and 3.15.8
|
||||
|
||||
### Bug fixes
|
||||
@@ -1352,7 +1361,6 @@ The following launchpad bugs have fixes included in this release:
|
||||
- [lp: 1868486](https://bugs.launchpad.net/epics-base/+bug/1868486),
|
||||
epicsMessageQueue lost messages
|
||||
|
||||
|
||||
### Improvements to the self-test build targets
|
||||
|
||||
This release contains changes that make it possible to integrate another test
|
||||
@@ -1368,7 +1376,7 @@ results; previously the `-k` flag to make was needed and even that didn't always
|
||||
work.
|
||||
|
||||
Continuous Integration systems are recommended to run `make tapfiles` (or if
|
||||
they can read junittest output instead of TAP `make junitests`) followed by
|
||||
they can read junittest output instead of TAP `make junitfiles`) followed by
|
||||
`make -s test-results` to display the results of the tests. If multiple CPUs are
|
||||
available the `-j` flag can be used to run tests in parallel, giving the maximum
|
||||
jobs that should be allowed so `make -j4 tapfiles` for a system with 4 CPUs say.
|
||||
@@ -1616,8 +1624,8 @@ cases. This fixes
|
||||
Some documentation has been added to the `dbdToHtml.pl` script
|
||||
explaining how Perl POD (Plain Old Documentation) markup can be added to
|
||||
`.dbd` files to generate HTML documentation for the record types. To see
|
||||
these instructions, run `perl bin/<host>/dbdToHtml.pl -H`
|
||||
or `perldoc bin/<host>/dbdToHtml.pl`.
|
||||
these instructions, run `perl bin/<host>/dbdToHtml.pl -H`
|
||||
or `perldoc bin/<host>/dbdToHtml.pl`.
|
||||
|
||||
### Fix problem with numeric soft events
|
||||
|
||||
|
||||
@@ -603,7 +603,7 @@ void epicsStdCall ca_signal_formated ( long ca_status, const char *pfilenm,
|
||||
}
|
||||
else {
|
||||
fprintf ( stderr, "CA exception in thread w/o CA ctx: status=%s file=%s line=%d: \n",
|
||||
ca_message ( ca_status ), pfilenm, lineno );
|
||||
ca_message ( ca_status ), pfilenm ? pfilenm : "<null>", lineno );
|
||||
if ( pFormat ) {
|
||||
vfprintf ( stderr, pFormat, theArgs );
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ ifdef T_A
|
||||
PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname)
|
||||
PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME)
|
||||
|
||||
PERL_ARCHLIB := $(shell $(PERL) ../perlConfig.pl archlib)
|
||||
PERL_ARCHLIB := $(shell $(PERL) ../perlConfig.pl archlibexp)
|
||||
PERL_h = $(PERL_ARCHLIB)/CORE/perl.h
|
||||
|
||||
EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils
|
||||
PERLBIN := $(shell $(PERL) ../perlConfig.pl bin)
|
||||
EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlibexp)/ExtUtils
|
||||
PERLBIN := $(shell $(PERL) ../perlConfig.pl binexp)
|
||||
XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp))
|
||||
|
||||
# Special settings for Darwin:
|
||||
|
||||
@@ -35,9 +35,11 @@ my %fieldType = (
|
||||
DBF_DOUBLE => 'DBF_FLOAT',
|
||||
DBF_FLOAT => 'DBF_FLOAT',
|
||||
DBF_LONG => 'DBF_LONG',
|
||||
DBF_INT64 => 'DBF_FLOAT',
|
||||
DBF_SHORT => 'DBF_LONG',
|
||||
DBF_ULONG => 'DBF_LONG',
|
||||
DBF_USHORT => 'DBF_LONG',
|
||||
DBF_UINT64 => 'DBF_FLOAT',
|
||||
DBF_DEVICE => 'DBF_STRING',
|
||||
DBF_ENUM => 'DBF_STRING',
|
||||
DBF_FWDLINK => 'DBF_STRING',
|
||||
@@ -86,7 +88,7 @@ if (@ARGV) {
|
||||
printRecord($_, @ARGV);
|
||||
} else {
|
||||
if (m/^ \s* ([]+:;<>0-9A-Za-z[-]+) (?:\. \w+)? \s* , \s* (\d+) \s* $/x) {
|
||||
# Recognizes ",n" as an interest leve, drops any ".FIELD" part
|
||||
# Recognizes ",n" as an interest level, drops any ".FIELD" part
|
||||
printRecord($1, $2);
|
||||
} else {
|
||||
# Drop any ".FIELD" part
|
||||
@@ -235,8 +237,9 @@ sub printField {
|
||||
$outStr = sprintf('%-5s %.8f', $field, $fieldData);
|
||||
} elsif ( $dataType eq 'DBF_CHAR' ) {
|
||||
$outStr = sprintf('%-5s %d', $field, ord($fieldData));
|
||||
}else {
|
||||
# DBF_LONG, DBF_SHORT, DBF_UCHAR, DBF_ULONG, DBF_USHORT
|
||||
} else {
|
||||
# DBF_INT64, DBF_LONG, DBF_SHORT,
|
||||
# DBF_UINT64, DBF_ULONG, DBF_USHORT, DBF_UCHAR,
|
||||
$outStr = sprintf('%-5s %d', $field, $fieldData);
|
||||
}
|
||||
|
||||
@@ -270,17 +273,18 @@ sub printField {
|
||||
sub caget {
|
||||
my @chans = map { CA->new($_); } @_;
|
||||
|
||||
#clear results;
|
||||
#clear any previous results;
|
||||
%callback_data = ();
|
||||
%timed_out = ();
|
||||
|
||||
eval { CA->pend_io($opt_w); };
|
||||
if ($@) {
|
||||
if ($@ =~ m/^ECA_TIMEOUT/) {
|
||||
my $err = (@chans > 1) ? 'some PV(s)' : '"' . $chans[0]->name . '"';
|
||||
my $name = $chans[0]->name;
|
||||
my $err = (@chans > 1) ? 'some fields' : "'$name'";
|
||||
print "Channel connect timed out: $err not found.\n";
|
||||
foreach my $chan (@chans) {
|
||||
$timed_out{$chan->name} = $chan->is_connected;
|
||||
$timed_out{$chan->name} = !$chan->is_connected;
|
||||
}
|
||||
@chans = grep { $_->is_connected } @chans;
|
||||
} else {
|
||||
@@ -289,14 +293,12 @@ sub caget {
|
||||
}
|
||||
|
||||
map {
|
||||
my $type;
|
||||
$type = $_->field_type;
|
||||
$_->get_callback(\&caget_callback, $type);
|
||||
$_->get_callback(\&caget_callback, $_->field_type);
|
||||
} @chans;
|
||||
|
||||
my $fields_read = @chans;
|
||||
$callback_incomplete = @chans;
|
||||
CA->pend_event(0.1) while $callback_incomplete;
|
||||
my $fields_read = $callback_incomplete = @chans;
|
||||
CA->pend_event(0.1)
|
||||
while $callback_incomplete;
|
||||
return $fields_read;
|
||||
}
|
||||
|
||||
@@ -325,9 +327,10 @@ sub printRecord {
|
||||
my @bases = (); #bases, from parser
|
||||
foreach my $field (sort keys %{$record{$recType}}) {
|
||||
# Skip DTYP field if this rec type doesn't have device support defined
|
||||
if ($field eq 'DTYP' && !(exists($device{$recType}))) { next; }
|
||||
next if $field eq 'DTYP' && !exists($device{$recType});
|
||||
|
||||
my ($fType, $fInterest, $base) = getFieldParams($recType, $field);
|
||||
# FIXME: Support waveform.VAL fields etc.
|
||||
unless( $fType eq 'DBF_NOACCESS' ) {
|
||||
if ($interest >= $fInterest ) {
|
||||
my $fToGet = "$name.$field";
|
||||
@@ -346,15 +349,10 @@ sub printRecord {
|
||||
my $field = $fields_pr[$i];
|
||||
my $fToGet = $readlist[$i];
|
||||
my ($fType, $data, $base);
|
||||
if ($timed_out{$fToGet}) {
|
||||
$fType = $fieldType{DBF_STRING};
|
||||
$data = '<timeout>';
|
||||
}
|
||||
else {
|
||||
$fType = $ftypes[$i];
|
||||
$base = $bases[$i];
|
||||
$data = $callback_data{$fToGet};
|
||||
}
|
||||
next if $timed_out{$fToGet};
|
||||
$fType = $ftypes[$i];
|
||||
$base = $bases[$i];
|
||||
$data = $callback_data{$fToGet};
|
||||
$col = printField($field, $data, $fType, $base, $col);
|
||||
}
|
||||
print("\n"); # Final newline
|
||||
@@ -387,8 +385,8 @@ sub printRecordList {
|
||||
if (exists($record{$type}) ) {
|
||||
print("Record type - $type\n");
|
||||
foreach my $fkey (sort keys %{$record{$type}}) {
|
||||
printf('%-4s', $fkey);
|
||||
printf(" interest = $record{$type}{$fkey}[$iIdx]");
|
||||
printf('%-8s', $fkey);
|
||||
printf(" interest = $record{$type}{$fkey}[$iIdx]");
|
||||
printf(" type = %-12s ",$record{$type}{$fkey}[$tIdx]);
|
||||
print (" base = $record{$type}{$fkey}[$bIdx]\n");
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ int main (int argc, char *argv[])
|
||||
}
|
||||
break;
|
||||
case 's': /* ca_client_status interest level */
|
||||
if (sscanf(optarg,"%du", &statLevel) != 1)
|
||||
if (sscanf(optarg,"%u", &statLevel) != 1)
|
||||
{
|
||||
fprintf(stderr, "'%s' is not a valid interest level "
|
||||
"- ignored. ('cainfo -h' for help.)\n", optarg);
|
||||
|
||||
@@ -258,7 +258,7 @@ int main (int argc, char *argv[])
|
||||
}
|
||||
break;
|
||||
case '#': /* Array count */
|
||||
if (sscanf(optarg,"%ld", &reqElems) != 1)
|
||||
if (sscanf(optarg,"%lu", &reqElems) != 1)
|
||||
{
|
||||
fprintf(stderr, "'%s' is not a valid array element count "
|
||||
"- ignored. ('camonitor -h' for help.)\n", optarg);
|
||||
|
||||
@@ -437,6 +437,7 @@ int main (int argc, char *argv[])
|
||||
dbuf = calloc (count, sizeof(double));
|
||||
if(!sbuf || !dbuf) {
|
||||
fprintf(stderr, "Memory allocation failed\n");
|
||||
free(sbuf); free(dbuf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -450,6 +451,7 @@ int main (int argc, char *argv[])
|
||||
result = ca_pend_io(caTimeout);
|
||||
if (result == ECA_TIMEOUT) {
|
||||
fprintf(stderr, "Read operation timed out: ENUM data was not read.\n");
|
||||
free(sbuf); free(dbuf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -460,6 +462,7 @@ int main (int argc, char *argv[])
|
||||
if (*(argv+optind+i) == pend) { /* Conversion didn't work */
|
||||
fprintf(stderr, "Enum index value '%s' is not a number.\n",
|
||||
*(argv+optind+i));
|
||||
free(sbuf); free(dbuf);
|
||||
return 1;
|
||||
}
|
||||
if (dbuf[i] >= bufGrEnum.no_str) {
|
||||
@@ -486,6 +489,7 @@ int main (int argc, char *argv[])
|
||||
dbuf[i] = epicsStrtod(sbuf[i], &pend);
|
||||
if (sbuf[i] == pend || enumAsString) {
|
||||
fprintf(stderr, "Enum string value '%s' invalid.\n", sbuf[i]);
|
||||
free(sbuf); free(dbuf);
|
||||
return 1;
|
||||
}
|
||||
if (dbuf[i] >= bufGrEnum.no_str) {
|
||||
@@ -503,6 +507,7 @@ int main (int argc, char *argv[])
|
||||
ebuf = calloc(len, sizeof(char));
|
||||
if(!ebuf) {
|
||||
fprintf(stderr, "Memory allocation failed\n");
|
||||
free(sbuf); free(dbuf); free(ebuf);
|
||||
return 1;
|
||||
}
|
||||
count = epicsStrnRawFromEscaped(ebuf, len, cbuf, len-1) + 1;
|
||||
@@ -537,12 +542,14 @@ int main (int argc, char *argv[])
|
||||
}
|
||||
if (result != ECA_NORMAL) {
|
||||
fprintf(stderr, "Error from put operation: %s\n", ca_message(result));
|
||||
free(sbuf); free(dbuf); free(ebuf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
result = ca_pend_io(caTimeout);
|
||||
if (result == ECA_TIMEOUT) {
|
||||
fprintf(stderr, "Write operation timed out: Data was not written.\n");
|
||||
free(sbuf); free(dbuf); free(ebuf);
|
||||
return 1;
|
||||
}
|
||||
if (request == callback) { /* Also wait for callbacks */
|
||||
@@ -556,6 +563,7 @@ int main (int argc, char *argv[])
|
||||
|
||||
if (result != ECA_NORMAL) {
|
||||
fprintf(stderr, "Error occured writing data: %s\n", ca_message(result));
|
||||
free(sbuf); free(dbuf); free(ebuf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -567,6 +575,7 @@ int main (int argc, char *argv[])
|
||||
|
||||
/* Shut down Channel Access */
|
||||
ca_context_destroy();
|
||||
free(sbuf); free(dbuf); free(ebuf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -603,6 +603,10 @@ long dbChannelOpen(dbChannel *chan)
|
||||
probe.no_elements = dbChannelElements(chan);
|
||||
probe.field_size = dbChannelFieldSize(chan);
|
||||
probe.sevr = NO_ALARM;
|
||||
probe.stat = NO_ALARM;
|
||||
probe.time.secPastEpoch = 0;
|
||||
probe.time.nsec = 0;
|
||||
|
||||
p = probe;
|
||||
|
||||
/*
|
||||
|
||||
@@ -69,7 +69,7 @@ void recGblDbaddrError(long status, const struct dbAddr *paddr,
|
||||
errPrintf(status,0,0,
|
||||
"PV: %s.%s "
|
||||
"error detected in routine: %s\n",
|
||||
(paddr ? precord->name : "Unknown"),
|
||||
(precord ? precord->name : "Unknown"),
|
||||
(pdbFldDes ? pdbFldDes->name : ""),
|
||||
(pmessage ? pmessage : "Unknown"));
|
||||
return;
|
||||
@@ -106,7 +106,7 @@ void recGblRecSupError(long status, const struct dbAddr *paddr,
|
||||
" %s\n",
|
||||
(psupport_name ? psupport_name : "Unknown"),
|
||||
(pdbRecordType ? pdbRecordType->name : "Unknown"),
|
||||
(paddr ? precord->name : "Unknown"),
|
||||
(precord ? precord->name : "Unknown"),
|
||||
(pdbFldDes ? pdbFldDes->name : ""),
|
||||
(pmessage ? pmessage : ""));
|
||||
return;
|
||||
|
||||
@@ -136,12 +136,14 @@ static void allocTemp(void *pvoid)
|
||||
static void *popFirstTemp(void)
|
||||
{
|
||||
tempListNode *ptempListNode;
|
||||
void *ptemp;
|
||||
void *ptemp = NULL;
|
||||
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
ptemp = ptempListNode->item;
|
||||
ellDelete(&tempList,(ELLNODE *)ptempListNode);
|
||||
freeListFree(freeListPvt,ptempListNode);
|
||||
if(ptempListNode) {
|
||||
ptemp = ptempListNode->item;
|
||||
ellDelete(&tempList,(ELLNODE *)ptempListNode);
|
||||
freeListFree(freeListPvt,ptempListNode);
|
||||
}
|
||||
return(ptemp);
|
||||
}
|
||||
|
||||
@@ -477,12 +479,16 @@ static void dbMenuBody(void)
|
||||
return;
|
||||
}
|
||||
pnewMenu = (dbMenu *)popFirstTemp();
|
||||
if(!pnewMenu)
|
||||
return;
|
||||
pnewMenu->nChoice = nChoice = ellCount(&tempList)/2;
|
||||
pnewMenu->papChoiceName = dbCalloc(pnewMenu->nChoice,sizeof(char *));
|
||||
pnewMenu->papChoiceValue = dbCalloc(pnewMenu->nChoice,sizeof(char *));
|
||||
for(i=0; i<nChoice; i++) {
|
||||
pnewMenu->papChoiceName[i] = (char *)popFirstTemp();
|
||||
pnewMenu->papChoiceValue[i] = (char *)popFirstTemp();
|
||||
if(!pnewMenu->papChoiceName[i] || !pnewMenu->papChoiceValue[i])
|
||||
return;
|
||||
}
|
||||
if(ellCount(&tempList)) yyerrorAbort("dbMenuBody: tempList not empty");
|
||||
/* Add menu in sorted order */
|
||||
@@ -703,6 +709,8 @@ static void dbRecordtypeBody(void)
|
||||
return;
|
||||
}
|
||||
pdbRecordType= (dbRecordType *)popFirstTemp();
|
||||
if(!pdbRecordType)
|
||||
return;
|
||||
pdbRecordType->no_fields = no_fields = ellCount(&tempList);
|
||||
pdbRecordType->papFldDes = dbCalloc(no_fields,sizeof(dbFldDes *));
|
||||
pdbRecordType->papsortFldName = dbCalloc(no_fields,sizeof(char *));
|
||||
@@ -710,6 +718,8 @@ static void dbRecordtypeBody(void)
|
||||
no_prompt = no_links = 0;
|
||||
for(i=0; i<no_fields; i++) {
|
||||
pdbFldDes = (dbFldDes *)popFirstTemp();
|
||||
if(!pdbFldDes)
|
||||
return;
|
||||
pdbFldDes->pdbRecordType = pdbRecordType;
|
||||
pdbFldDes->indRecordType = i;
|
||||
pdbRecordType->papFldDes[i] = pdbFldDes;
|
||||
@@ -974,6 +984,8 @@ static void dbBreakBody(void)
|
||||
return;
|
||||
}
|
||||
pnewbrkTable = (brkTable *)popFirstTemp();
|
||||
if(!pnewbrkTable)
|
||||
return;
|
||||
number = ellCount(&tempList);
|
||||
if (number % 2) {
|
||||
yyerrorAbort("breaktable: Raw value missing");
|
||||
@@ -990,10 +1002,14 @@ static void dbBreakBody(void)
|
||||
char *str;
|
||||
|
||||
str = (char *)popFirstTemp();
|
||||
if(!str)
|
||||
return;
|
||||
(void) epicsScanDouble(str, &paBrkInt[i].raw);
|
||||
free(str);
|
||||
|
||||
str = (char *)popFirstTemp();
|
||||
if(!str)
|
||||
return;
|
||||
(void) epicsScanDouble(str, &paBrkInt[i].eng);
|
||||
free(str);
|
||||
}
|
||||
@@ -1034,22 +1050,49 @@ static void dbBreakBody(void)
|
||||
}
|
||||
pgphentry->userPvt = pnewbrkTable;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
int dbRecordNameValidate(const char *name)
|
||||
{
|
||||
size_t i=0u;
|
||||
const char *pos = name;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("Error: Record/Alias name can't be empty");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for(; *pos; i++, pos++) {
|
||||
char c = *pos;
|
||||
if(i==0) {
|
||||
/* first character restrictions */
|
||||
if(c=='-' || c=='+' || c=='[' || c=='{') {
|
||||
errlogPrintf("Warning: Record/Alias name '%s' should not begin with '%c'\n", name, c);
|
||||
}
|
||||
}
|
||||
/* any character restrictions */
|
||||
if(c < ' ') {
|
||||
errlogPrintf("Warning: Record/Alias name '%s' should not contain non-printable 0x%02u\n",
|
||||
name, (unsigned)c);
|
||||
|
||||
} else if(c==' ' || c=='\t' || c=='"' || c=='\'' || c=='.' || c=='$') {
|
||||
epicsPrintf("Error: Bad character '%c' in Record/Alias name \"%s\"\n",
|
||||
c, name);
|
||||
yyerrorAbort(NULL);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dbRecordHead(char *recordType, char *name, int visible)
|
||||
{
|
||||
char *badch;
|
||||
DBENTRY *pdbentry;
|
||||
long status;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRecordHead: Record name can't be empty");
|
||||
if(dbRecordNameValidate(name))
|
||||
return;
|
||||
}
|
||||
badch = strpbrk(name, " \"'.$");
|
||||
if (badch) {
|
||||
epicsPrintf("Bad character '%c' in record name \"%s\"\n",
|
||||
*badch, name);
|
||||
}
|
||||
|
||||
pdbentry = dbAllocEntry(pdbbase);
|
||||
if (ellCount(&tempList))
|
||||
@@ -1180,10 +1223,9 @@ static void dbRecordAlias(char *name)
|
||||
tempListNode *ptempListNode;
|
||||
long status;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRecordAlias: Alias name can't be empty");
|
||||
if(dbRecordNameValidate(name))
|
||||
return;
|
||||
}
|
||||
|
||||
if (duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbentry = ptempListNode->item;
|
||||
@@ -1201,10 +1243,9 @@ static void dbAlias(char *name, char *alias)
|
||||
DBENTRY dbEntry;
|
||||
DBENTRY *pdbEntry = &dbEntry;
|
||||
|
||||
if (!*alias) {
|
||||
yyerrorAbort("dbAlias: Alias name can't be empty");
|
||||
if(dbRecordNameValidate(alias))
|
||||
return;
|
||||
}
|
||||
|
||||
dbInitEntry(pdbbase, pdbEntry);
|
||||
if (dbFindRecord(pdbEntry, name)) {
|
||||
epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n",
|
||||
|
||||
@@ -438,7 +438,7 @@ int htoi(unsigned char *str)
|
||||
{
|
||||
int result;
|
||||
|
||||
(void) sscanf( (char *) str, "%x", &result );
|
||||
(void) sscanf( (char *) str, "%x", (unsigned *) &result );
|
||||
|
||||
return ( result );
|
||||
}
|
||||
@@ -653,7 +653,7 @@ int otoi(Char *str)
|
||||
{
|
||||
int result;
|
||||
|
||||
(void) sscanf( (char *) str, "%o", &result );
|
||||
(void) sscanf( (char *) str, "%o", (unsigned *) &result );
|
||||
|
||||
return ( result );
|
||||
}
|
||||
|
||||
@@ -1,250 +0,0 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Author W. Eric Norum
|
||||
* norume@aps.anl.gov
|
||||
* 630 252 4793
|
||||
*/
|
||||
|
||||
/*
|
||||
* We want to access information which is
|
||||
* normally hidden from application programs.
|
||||
*/
|
||||
#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <rtems.h>
|
||||
#include <rtems/error.h>
|
||||
#include "epicsMessageQueue.h"
|
||||
#include "errlog.h"
|
||||
|
||||
LIBCOM_API epicsMessageQueueId epicsStdCall
|
||||
epicsMessageQueueCreate(unsigned int capacity, unsigned int maximumMessageSize)
|
||||
{
|
||||
rtems_status_code sc;
|
||||
epicsMessageQueueId id = calloc(1, sizeof(*id));
|
||||
rtems_interrupt_level level;
|
||||
static char c1 = 'a';
|
||||
static char c2 = 'a';
|
||||
static char c3 = 'a';
|
||||
|
||||
if(!id)
|
||||
return NULL;
|
||||
|
||||
sc = rtems_message_queue_create (rtems_build_name ('Q', c3, c2, c1),
|
||||
capacity,
|
||||
maximumMessageSize,
|
||||
RTEMS_FIFO|RTEMS_LOCAL,
|
||||
&id->id);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
free(id);
|
||||
errlogPrintf ("Can't create message queue: %s\n", rtems_status_text (sc));
|
||||
return NULL;
|
||||
}
|
||||
id->maxSize = maximumMessageSize;
|
||||
id->localBuf = NULL;
|
||||
rtems_interrupt_disable (level);
|
||||
if (c1 == 'z') {
|
||||
if (c2 == 'z') {
|
||||
if (c3 == 'z') {
|
||||
c3 = 'a';
|
||||
}
|
||||
else {
|
||||
c3++;
|
||||
}
|
||||
c2 = 'a';
|
||||
}
|
||||
else {
|
||||
c2++;
|
||||
}
|
||||
c1 = 'a';
|
||||
}
|
||||
else {
|
||||
c1++;
|
||||
}
|
||||
rtems_interrupt_enable (level);
|
||||
return id;
|
||||
}
|
||||
|
||||
static rtems_status_code rtems_message_queue_send_timeout(
|
||||
rtems_id id,
|
||||
void *buffer,
|
||||
uint32_t size,
|
||||
rtems_interval timeout)
|
||||
{
|
||||
Message_queue_Control *the_message_queue;
|
||||
Objects_Locations location;
|
||||
CORE_message_queue_Status msg_status;
|
||||
|
||||
the_message_queue = _Message_queue_Get( id, &location );
|
||||
switch ( location )
|
||||
{
|
||||
case OBJECTS_ERROR:
|
||||
return RTEMS_INVALID_ID;
|
||||
|
||||
case OBJECTS_LOCAL:
|
||||
msg_status = _CORE_message_queue_Send(
|
||||
&the_message_queue->message_queue,
|
||||
buffer,
|
||||
size,
|
||||
id,
|
||||
NULL,
|
||||
1,
|
||||
timeout
|
||||
);
|
||||
|
||||
_Thread_Enable_dispatch();
|
||||
|
||||
/*
|
||||
* If we had to block, then this is where the task returns
|
||||
* after it wakes up. The returned status is correct for
|
||||
* non-blocking operations but if we blocked, then we need
|
||||
* to look at the status in our TCB.
|
||||
*/
|
||||
|
||||
if ( msg_status == CORE_MESSAGE_QUEUE_STATUS_UNSATISFIED_WAIT )
|
||||
msg_status = _Thread_Executing->Wait.return_code;
|
||||
return _Message_queue_Translate_core_message_queue_return_code( msg_status );
|
||||
}
|
||||
return RTEMS_INTERNAL_ERROR; /* unreached - only to remove warnings */
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueueSend(
|
||||
epicsMessageQueueId id,
|
||||
void *message,
|
||||
unsigned int messageSize)
|
||||
{
|
||||
if (rtems_message_queue_send_timeout(id->id, message, messageSize, RTEMS_NO_TIMEOUT) == RTEMS_SUCCESSFUL)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueueSendWithTimeout(
|
||||
epicsMessageQueueId id,
|
||||
void *message,
|
||||
unsigned int messageSize,
|
||||
double timeout)
|
||||
{
|
||||
rtems_interval delay;
|
||||
extern double rtemsTicksPerSecond_double;
|
||||
|
||||
/*
|
||||
* Convert time to ticks
|
||||
*/
|
||||
if (timeout <= 0.0)
|
||||
return epicsMessageQueueTrySend(id, message, messageSize);
|
||||
delay = (int)(timeout * rtemsTicksPerSecond_double);
|
||||
if (delay == 0)
|
||||
delay++;
|
||||
if (rtems_message_queue_send_timeout(id->id, message, messageSize, delay) == RTEMS_SUCCESSFUL)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int receiveMessage(
|
||||
epicsMessageQueueId id,
|
||||
void *buffer,
|
||||
uint32_t size,
|
||||
uint32_t wait,
|
||||
rtems_interval delay)
|
||||
{
|
||||
size_t rsize;
|
||||
rtems_status_code sc;
|
||||
|
||||
if (size < id->maxSize) {
|
||||
if (id->localBuf == NULL) {
|
||||
id->localBuf = malloc(id->maxSize);
|
||||
if (id->localBuf == NULL)
|
||||
return -1;
|
||||
}
|
||||
rsize = receiveMessage(id, id->localBuf, id->maxSize, wait, delay);
|
||||
if (rsize > size)
|
||||
return -1;
|
||||
memcpy(buffer, id->localBuf, rsize);
|
||||
}
|
||||
else {
|
||||
sc = rtems_message_queue_receive(id->id, buffer, &rsize, wait, delay);
|
||||
if (sc != RTEMS_SUCCESSFUL)
|
||||
return -1;
|
||||
}
|
||||
return rsize;
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueueTryReceive(
|
||||
epicsMessageQueueId id,
|
||||
void *message,
|
||||
unsigned int size)
|
||||
{
|
||||
return receiveMessage(id, message, size, RTEMS_NO_WAIT, 0);
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueueReceive(
|
||||
epicsMessageQueueId id,
|
||||
void *message,
|
||||
unsigned int size)
|
||||
{
|
||||
return receiveMessage(id, message, size, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueueReceiveWithTimeout(
|
||||
epicsMessageQueueId id,
|
||||
void *message,
|
||||
unsigned int size,
|
||||
double timeout)
|
||||
{
|
||||
rtems_interval delay;
|
||||
uint32_t wait;
|
||||
extern double rtemsTicksPerSecond_double;
|
||||
|
||||
/*
|
||||
* Convert time to ticks
|
||||
*/
|
||||
if (timeout <= 0.0) {
|
||||
wait = RTEMS_NO_WAIT;
|
||||
delay = 0;
|
||||
}
|
||||
else {
|
||||
wait = RTEMS_WAIT;
|
||||
delay = (int)(timeout * rtemsTicksPerSecond_double);
|
||||
if (delay == 0)
|
||||
delay++;
|
||||
}
|
||||
return receiveMessage(id, message, size, wait, delay);
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueuePending(
|
||||
epicsMessageQueueId id)
|
||||
{
|
||||
uint32_t count;
|
||||
rtems_status_code sc;
|
||||
|
||||
sc = rtems_message_queue_get_number_pending(id->id, &count);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
errlogPrintf("Message queue %x get number pending failed: %s\n",
|
||||
(unsigned int)id,
|
||||
rtems_status_text(sc));
|
||||
return -1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
LIBCOM_API void epicsStdCall epicsMessageQueueShow(
|
||||
epicsMessageQueueId id,
|
||||
int level)
|
||||
{
|
||||
int pending = epicsMessageQueuePending(id);
|
||||
if (pending >= 0)
|
||||
printf ("Message queue %lx -- Pending: %d\n", (unsigned long)id, pending);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Author W. Eric Norum
|
||||
* norume@aps.anl.gov
|
||||
* 630 252 4793
|
||||
*/
|
||||
|
||||
/*
|
||||
* Very thin shims around RTEMS routines
|
||||
*/
|
||||
#include <rtems.h>
|
||||
|
||||
struct epicsMessageQueueOSD {
|
||||
rtems_id id;
|
||||
unsigned int maxSize;
|
||||
void *localBuf;
|
||||
|
||||
};
|
||||
#define epicsMessageQueueDestroy(q) (rtems_message_queue_delete((q)->id))
|
||||
|
||||
#define epicsMessageQueueTrySend(q,m,l) (rtems_message_queue_send((q)->id, (m), (l)) == RTEMS_SUCCESSFUL ? 0 : -1)
|
||||
@@ -211,7 +211,7 @@ LIBCOM_API void epicsStdCall osiSockDiscoverBroadcastAddresses
|
||||
pNewNode->addr.sa = pIfreqList->ifr_broadaddr;
|
||||
ifDepenDebugPrintf ( ( "found broadcast addr = %x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
|
||||
} else {
|
||||
ifDepenDebugPrintf ( ( "Ignoring broadcast addr = \n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
|
||||
ifDepenDebugPrintf ( ( "Ignoring broadcast addr = %x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
|
||||
free ( pNewNode );
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ LIBCOM_API int epicsThreadHookAdd(EPICS_THREAD_HOOK_ROUTINE hook)
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "epicsThreadHookAdd: Locking problem\n");
|
||||
free(pHook);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -473,8 +473,9 @@ LIBCOM_API void epicsThreadMap ( EPICS_THREAD_HOOK_ROUTINE func )
|
||||
while (noTasks == 0) {
|
||||
noTasks = taskIdListGet(taskIdList, taskIdListSize);
|
||||
if (noTasks == taskIdListSize) {
|
||||
taskIdList = realloc(taskIdList, (taskIdListSize+ID_LIST_CHUNK)*sizeof(int));
|
||||
assert(taskIdList);
|
||||
int *newlist = realloc(taskIdList, (taskIdListSize+ID_LIST_CHUNK)*sizeof(int));
|
||||
assert(newlist);
|
||||
taskIdList = newlist;
|
||||
taskIdListSize += ID_LIST_CHUNK;
|
||||
noTasks = 0;
|
||||
}
|
||||
|
||||
@@ -25,12 +25,9 @@
|
||||
static const char *msg1 = "1234567890This is a very long message.";
|
||||
static volatile int sendExit = 0;
|
||||
static volatile int recvExit = 0;
|
||||
static epicsEventId finished;
|
||||
static unsigned int mediumStack;
|
||||
|
||||
#define SLEEPY_TESTS 500
|
||||
static int numSent, numReceived;
|
||||
static epicsEventId complete;
|
||||
|
||||
/*
|
||||
* In Numerical Recipes in C: The Art of Scientific Computing (William H.
|
||||
@@ -116,7 +113,6 @@ receiver(void *arg)
|
||||
if (!testOk1(errors == 0))
|
||||
testDiag("Error count was %d", errors);
|
||||
testDiag("%s exiting", myName);
|
||||
epicsEventSignal(finished);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
@@ -133,15 +129,18 @@ fastReceiver(void *arg)
|
||||
}
|
||||
}
|
||||
recvExit = 0;
|
||||
epicsEventSignal(complete);
|
||||
}
|
||||
|
||||
void sleepySender(double delay)
|
||||
{
|
||||
epicsThreadOpts opts = {epicsThreadPriorityMedium, epicsThreadStackMedium, 1};
|
||||
epicsThreadId rxThread;
|
||||
|
||||
testDiag("sleepySender: sending every %.3f seconds", delay);
|
||||
epicsMessageQueue q(4, 20);
|
||||
epicsThreadCreate("Fast Receiver", epicsThreadPriorityMedium,
|
||||
mediumStack, fastReceiver, &q);
|
||||
rxThread = epicsThreadCreateOpt("Fast Receiver", fastReceiver, &q, &opts);
|
||||
if (!rxThread)
|
||||
testAbort("Task create failed");
|
||||
|
||||
numSent = 0;
|
||||
for (int i = 0 ; i < SLEEPY_TESTS ; i++) {
|
||||
@@ -159,7 +158,7 @@ void sleepySender(double delay)
|
||||
recvExit = 1;
|
||||
while (q.send((void *)msg1, 4) != 0)
|
||||
epicsThreadSleep(0.01);
|
||||
epicsEventMustWait(complete);
|
||||
epicsThreadMustJoin(rxThread);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
@@ -179,11 +178,14 @@ fastSender(void *arg)
|
||||
}
|
||||
}
|
||||
sendExit = 0;
|
||||
epicsEventSignal(complete);
|
||||
}
|
||||
|
||||
void sleepyReceiver(double delay)
|
||||
{
|
||||
epicsThreadOpts opts = {epicsThreadPriorityMedium,
|
||||
epicsThreadStackMedium, 1};
|
||||
epicsThreadId txThread;
|
||||
|
||||
testDiag("sleepyReceiver: acquiring every %.3f seconds", delay);
|
||||
epicsMessageQueue q(4, 20);
|
||||
|
||||
@@ -192,8 +194,9 @@ void sleepyReceiver(double delay)
|
||||
q.send((void *)msg1, 4);
|
||||
}
|
||||
|
||||
epicsThreadCreate("Fast Sender", epicsThreadPriorityMedium,
|
||||
mediumStack, fastSender, &q);
|
||||
txThread = epicsThreadCreateOpt("Fast Sender", fastSender, &q, &opts);
|
||||
if (!txThread)
|
||||
testAbort("Task create failed");
|
||||
epicsThreadSleep(0.5);
|
||||
|
||||
char cbuf[80];
|
||||
@@ -208,21 +211,15 @@ void sleepyReceiver(double delay)
|
||||
epicsThreadSleep(delay);
|
||||
}
|
||||
|
||||
#ifdef __rtems__
|
||||
testTodoBegin("RTEMS failure expected");
|
||||
#endif
|
||||
testOk(numSent == SLEEPY_TESTS, "Sent %d (should be %d)",
|
||||
numSent, SLEEPY_TESTS);
|
||||
#ifdef __rtems__
|
||||
testTodoEnd();
|
||||
#endif
|
||||
testOk(numReceived == SLEEPY_TESTS, "Received %d (should be %d)",
|
||||
numReceived, SLEEPY_TESTS);
|
||||
|
||||
sendExit = 1;
|
||||
while (q.receive(cbuf, sizeof cbuf) <= 0)
|
||||
epicsThreadSleep(0.01);
|
||||
epicsEventMustWait(complete);
|
||||
epicsThreadMustJoin(txThread);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
@@ -242,130 +239,136 @@ sender(void *arg)
|
||||
testDiag("%s exiting, sent %d messages", epicsThreadGetNameSelf(), i);
|
||||
}
|
||||
|
||||
#define NUM_SENDERS 4
|
||||
extern "C" void messageQueueTest(void *parm)
|
||||
{
|
||||
epicsThreadId myThreadId = epicsThreadGetIdSelf();
|
||||
epicsThreadId rxThread;
|
||||
epicsThreadId senderId[NUM_SENDERS];
|
||||
epicsThreadOpts opts = {epicsThreadPriorityMedium,
|
||||
epicsThreadStackMedium, 1};
|
||||
|
||||
unsigned int i;
|
||||
char cbuf[80];
|
||||
int len;
|
||||
int pass;
|
||||
int want;
|
||||
|
||||
epicsMessageQueue *q1 = new epicsMessageQueue(4, 20);
|
||||
epicsMessageQueue q1(4, 20);
|
||||
|
||||
testDiag("Simple single-thread tests:");
|
||||
i = 0;
|
||||
testOk1(q1->pending() == 0);
|
||||
while (q1->trySend((void *)msg1, i ) == 0) {
|
||||
i++;
|
||||
testOk(q1->pending() == i, "q1->pending() == %d", i);
|
||||
testOk1(q1.pending() == 0);
|
||||
for (i = 0; i < 4;) {
|
||||
int ret = q1.trySend((void *)msg1, i++);
|
||||
testOk(ret == 0, "trySend succeeded (%d == 0)", ret);
|
||||
testOk(q1.pending() == i, "loop: q1.pending() == %d", i);
|
||||
}
|
||||
testOk1(q1->pending() == 4);
|
||||
testOk1(q1.pending() == 4);
|
||||
|
||||
want = 0;
|
||||
len = q1->receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1->pending() == 3);
|
||||
len = q1.receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1.pending() == 3);
|
||||
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
|
||||
want++;
|
||||
len = q1->receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1->pending() == 2);
|
||||
len = q1.receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1.pending() == 2);
|
||||
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
q1->trySend((void *)msg1, i++);
|
||||
q1.trySend((void *)msg1, i++);
|
||||
|
||||
want++;
|
||||
len = q1->receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1->pending() == 2);
|
||||
len = q1.receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1.pending() == 2);
|
||||
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
q1->trySend((void *)msg1, i++);
|
||||
testOk1(q1->pending() == 3);
|
||||
q1.trySend((void *)msg1, i++);
|
||||
testOk1(q1.pending() == 3);
|
||||
|
||||
i = 3;
|
||||
while ((len = q1->receive(cbuf, sizeof cbuf, 1.0)) >= 0) {
|
||||
while ((len = q1.receive(cbuf, sizeof cbuf, 1.0)) >= 0) {
|
||||
--i;
|
||||
testOk(q1->pending() == i, "q1->pending() == %d", i);
|
||||
testOk(q1.pending() == i, "loop: q1.pending() == %d", i);
|
||||
want++;
|
||||
if (!testOk1((len == want) & (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
}
|
||||
testOk1(q1->pending() == 0);
|
||||
testOk1(q1.pending() == 0);
|
||||
|
||||
testDiag("Test sender timeout:");
|
||||
i = 0;
|
||||
testOk1(q1->pending() == 0);
|
||||
while (q1->send((void *)msg1, i, 1.0 ) == 0) {
|
||||
testOk1(q1.pending() == 0);
|
||||
while (q1.send((void *)msg1, i, 1.0 ) == 0) {
|
||||
i++;
|
||||
testOk(q1->pending() == i, "q1->pending() == %d", i);
|
||||
testOk(q1.pending() == i, "loop: q1.pending() == %d", i);
|
||||
}
|
||||
testOk1(q1->pending() == 4);
|
||||
testOk1(q1.pending() == 4);
|
||||
|
||||
want = 0;
|
||||
len = q1->receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1->pending() == 3);
|
||||
len = q1.receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1.pending() == 3);
|
||||
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
|
||||
want++;
|
||||
len = q1->receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1->pending() == 2);
|
||||
len = q1.receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1.pending() == 2);
|
||||
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
q1->send((void *)msg1, i++, 1.0);
|
||||
q1.send((void *)msg1, i++, 1.0);
|
||||
|
||||
want++;
|
||||
len = q1->receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1->pending() == 2);
|
||||
len = q1.receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1.pending() == 2);
|
||||
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
q1->send((void *)msg1, i++, 1.0);
|
||||
testOk1(q1->pending() == 3);
|
||||
q1.send((void *)msg1, i++, 1.0);
|
||||
testOk1(q1.pending() == 3);
|
||||
|
||||
i = 3;
|
||||
while ((len = q1->receive(cbuf, sizeof cbuf, 1.0)) >= 0) {
|
||||
while ((len = q1.receive(cbuf, sizeof cbuf, 1.0)) >= 0) {
|
||||
--i;
|
||||
testOk(q1->pending() == i, "q1->pending() == %d", i);
|
||||
testOk(q1.pending() == i, "loop: q1.pending() == %d", i);
|
||||
want++;
|
||||
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
}
|
||||
testOk1(q1->pending() == 0);
|
||||
testOk1(q1.pending() == 0);
|
||||
|
||||
testDiag("Test receiver with timeout:");
|
||||
for (i = 0 ; i < 4 ; i++)
|
||||
testOk1 (q1->send((void *)msg1, i, 1.0) == 0);
|
||||
testOk1(q1->pending() == 4);
|
||||
testOk1 (q1.send((void *)msg1, i, 1.0) == 0);
|
||||
testOk1(q1.pending() == 4);
|
||||
for (i = 0 ; i < 4 ; i++)
|
||||
testOk(q1->receive((void *)cbuf, sizeof cbuf, 1.0) == (int)i,
|
||||
"q1->receive(...) == %d", i);
|
||||
testOk1(q1->pending() == 0);
|
||||
testOk1(q1->receive((void *)cbuf, sizeof cbuf, 1.0) < 0);
|
||||
testOk1(q1->pending() == 0);
|
||||
testOk(q1.receive((void *)cbuf, sizeof cbuf, 1.0) == (int)i,
|
||||
"q1.receive(...) == %d", i);
|
||||
testOk1(q1.pending() == 0);
|
||||
testOk1(q1.receive((void *)cbuf, sizeof cbuf, 1.0) < 0);
|
||||
testOk1(q1.pending() == 0);
|
||||
|
||||
testDiag("Single receiver with invalid size, single sender tests:");
|
||||
epicsThreadCreate("Bad Receiver", epicsThreadPriorityMedium,
|
||||
mediumStack, badReceiver, q1);
|
||||
rxThread = epicsThreadCreateOpt("Bad Receiver", badReceiver, &q1, &opts);
|
||||
if (!rxThread)
|
||||
testAbort("epicsThreadCreate failed");
|
||||
epicsThreadSleep(1.0);
|
||||
testOk(q1->send((void *)msg1, 10) == 0, "Send with waiting receiver");
|
||||
epicsThreadSleep(2.0);
|
||||
testOk(q1->send((void *)msg1, 10) == 0, "Send with no receiver");
|
||||
testOk(q1.send((void *)msg1, 10) == 0, "Send with waiting receiver");
|
||||
epicsThreadSleep(2.0);
|
||||
testOk(q1.send((void *)msg1, 10) == 0, "Send with no receiver");
|
||||
epicsThreadMustJoin(rxThread);
|
||||
|
||||
testDiag("6 Single receiver single sender 'Sleepy timeout' tests,");
|
||||
testDiag(" these should take about %.2f seconds each:",
|
||||
SLEEPY_TESTS * 0.010);
|
||||
|
||||
complete = epicsEventMustCreate(epicsEventEmpty);
|
||||
sleepySender(0.009);
|
||||
sleepySender(0.010);
|
||||
sleepySender(0.011);
|
||||
@@ -375,11 +378,12 @@ extern "C" void messageQueueTest(void *parm)
|
||||
|
||||
testDiag("Single receiver, single sender tests:");
|
||||
epicsThreadSetPriority(myThreadId, epicsThreadPriorityHigh);
|
||||
epicsThreadCreate("Receiver one", epicsThreadPriorityMedium,
|
||||
mediumStack, receiver, q1);
|
||||
rxThread = epicsThreadCreateOpt("Receiver one", receiver, &q1, &opts);
|
||||
if (!rxThread)
|
||||
testAbort("epicsThreadCreate failed");
|
||||
for (pass = 1 ; pass <= 3 ; pass++) {
|
||||
for (i = 0 ; i < 10 ; i++) {
|
||||
if (q1->trySend((void *)msg1, i) < 0)
|
||||
if (q1.trySend((void *)msg1, i) < 0)
|
||||
break;
|
||||
if (pass >= 3)
|
||||
epicsThreadSleep(0.5);
|
||||
@@ -408,18 +412,20 @@ extern "C" void messageQueueTest(void *parm)
|
||||
*/
|
||||
testDiag("Single receiver, multiple sender tests:");
|
||||
testDiag("This test lasts 30 seconds...");
|
||||
testOk(!!epicsThreadCreate("Sender 1", epicsThreadPriorityLow,
|
||||
mediumStack, sender, q1),
|
||||
"Created Sender 1");
|
||||
testOk(!!epicsThreadCreate("Sender 2", epicsThreadPriorityMedium,
|
||||
mediumStack, sender, q1),
|
||||
"Created Sender 2");
|
||||
testOk(!!epicsThreadCreate("Sender 3", epicsThreadPriorityHigh,
|
||||
mediumStack, sender, q1),
|
||||
"Created Sender 3");
|
||||
testOk(!!epicsThreadCreate("Sender 4", epicsThreadPriorityHigh,
|
||||
mediumStack, sender, q1),
|
||||
"Created Sender 4");
|
||||
for (i=0; i<NUM_SENDERS; i++) {
|
||||
char name[16];
|
||||
const int pri[NUM_SENDERS] = {
|
||||
epicsThreadPriorityLow,
|
||||
epicsThreadPriorityMedium,
|
||||
epicsThreadPriorityHigh,
|
||||
epicsThreadPriorityHigh
|
||||
};
|
||||
sprintf(name, "Sender %d", i+1);
|
||||
opts.priority = pri[i];
|
||||
senderId[i] = epicsThreadCreateOpt(name, sender, &q1, &opts);
|
||||
if (!senderId[i])
|
||||
testAbort("epicsThreadCreate failed");
|
||||
}
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
testDiag("... %2d", 6 - i);
|
||||
@@ -428,23 +434,30 @@ extern "C" void messageQueueTest(void *parm)
|
||||
|
||||
sendExit = 1;
|
||||
epicsThreadSleep(1.0);
|
||||
for (i=0; i<NUM_SENDERS; i++) {
|
||||
epicsThreadMustJoin(senderId[i]);
|
||||
}
|
||||
recvExit = 1;
|
||||
testDiag("Scheduler exiting");
|
||||
epicsThreadMustJoin(rxThread);
|
||||
}
|
||||
|
||||
MAIN(epicsMessageQueueTest)
|
||||
{
|
||||
testPlan(74);
|
||||
epicsThreadOpts opts = {
|
||||
epicsThreadPriorityMedium,
|
||||
epicsThreadStackMedium,
|
||||
1
|
||||
};
|
||||
epicsThreadId testThread;
|
||||
|
||||
finished = epicsEventMustCreate(epicsEventEmpty);
|
||||
mediumStack = epicsThreadGetStackSize(epicsThreadStackMedium);
|
||||
testPlan(70 + NUM_SENDERS);
|
||||
|
||||
epicsThreadCreate("messageQueueTest", epicsThreadPriorityMedium,
|
||||
mediumStack, messageQueueTest, NULL);
|
||||
testThread = epicsThreadCreateOpt("messageQueueTest",
|
||||
messageQueueTest, NULL, &opts);
|
||||
if (!testThread)
|
||||
testAbort("epicsThreadCreate failed");
|
||||
|
||||
epicsEventMustWait(finished);
|
||||
testDiag("Main thread signalled");
|
||||
epicsThreadSleep(1.0);
|
||||
epicsThreadMustJoin(testThread);
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -7,12 +7,18 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "epicsAssert.h"
|
||||
#include "dbDefs.h"
|
||||
#include "osiSock.h"
|
||||
#include "epicsTime.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
/* This could easily be generalized to test more options */
|
||||
static
|
||||
void udpBroadcast(SOCKET s, int put)
|
||||
{
|
||||
int status;
|
||||
@@ -27,6 +33,7 @@ void udpBroadcast(SOCKET s, int put)
|
||||
"getsockopt BROADCAST => %d", flag);
|
||||
}
|
||||
|
||||
static
|
||||
void multiCastLoop(SOCKET s, int put)
|
||||
{
|
||||
int status;
|
||||
@@ -42,6 +49,7 @@ void multiCastLoop(SOCKET s, int put)
|
||||
"getsockopt MULTICAST_LOOP => %d", (int) flag);
|
||||
}
|
||||
|
||||
static
|
||||
void multiCastTTL(SOCKET s, int put)
|
||||
{
|
||||
int status;
|
||||
@@ -57,10 +65,13 @@ void multiCastTTL(SOCKET s, int put)
|
||||
"getsockopt IP_MULTICAST_TTL => %d", (int) flag);
|
||||
}
|
||||
|
||||
static
|
||||
void udpSockTest(void)
|
||||
{
|
||||
SOCKET s;
|
||||
|
||||
testDiag("udpSockTest()");
|
||||
|
||||
s = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
testOk(s != INVALID_SOCKET, "epicsSocketCreate INET, DGRAM, 0");
|
||||
|
||||
@@ -107,11 +118,43 @@ int doBind(int expect, SOCKET S, unsigned* port)
|
||||
}
|
||||
}
|
||||
|
||||
void udpSockFanoutTest(void)
|
||||
static
|
||||
void tcpSockReuseBindTest(int reuse)
|
||||
{
|
||||
SOCKET A, B;
|
||||
unsigned port=0; /* choose random port */
|
||||
|
||||
testDiag("tcpSockReuseBindTest(%d)", reuse);
|
||||
|
||||
A = epicsSocketCreate(AF_INET, SOCK_STREAM, 0);
|
||||
B = epicsSocketCreate(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if(A==INVALID_SOCKET || B==INVALID_SOCKET)
|
||||
testAbort("Insufficient sockets");
|
||||
|
||||
if(reuse) {
|
||||
testDiag("epicsSocketEnableAddressReuseDuringTimeWaitState");
|
||||
epicsSocketEnableAddressReuseDuringTimeWaitState(A);
|
||||
epicsSocketEnableAddressReuseDuringTimeWaitState(B);
|
||||
}
|
||||
|
||||
doBind(0, A, &port);
|
||||
if(listen(A, 4))
|
||||
testFail("listen(A) -> %d", (int)SOCKERRNO);
|
||||
doBind(1, B, &port);
|
||||
|
||||
epicsSocketDestroy(A);
|
||||
epicsSocketDestroy(B);
|
||||
}
|
||||
|
||||
static
|
||||
void udpSockFanoutBindTest(void)
|
||||
{
|
||||
SOCKET A, B, C;
|
||||
unsigned port=0; /* choose random port */
|
||||
|
||||
testDiag("udpSockFanoutBindTest()");
|
||||
|
||||
A = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
B = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
C = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
@@ -138,16 +181,233 @@ void udpSockFanoutTest(void)
|
||||
epicsSocketDestroy(C);
|
||||
}
|
||||
|
||||
struct CASearch {
|
||||
epicsUInt16 cmd, size, dtype, dcnt;
|
||||
epicsUInt32 p1, p2;
|
||||
char body[16];
|
||||
};
|
||||
|
||||
STATIC_ASSERT(sizeof(struct CASearch)==32);
|
||||
|
||||
union CASearchU {
|
||||
struct CASearch msg;
|
||||
char bytes[sizeof(struct CASearch)];
|
||||
};
|
||||
|
||||
static
|
||||
unsigned nsuccess;
|
||||
|
||||
static
|
||||
const unsigned nrepeat = 6u;
|
||||
|
||||
struct TInfo {
|
||||
SOCKET sock;
|
||||
unsigned id;
|
||||
epicsUInt32 key;
|
||||
epicsUInt8 rxmask;
|
||||
epicsUInt8 dupmask;
|
||||
};
|
||||
|
||||
static
|
||||
void udpSockFanoutTestRx(void* raw)
|
||||
{
|
||||
struct TInfo *info = raw;
|
||||
epicsTimeStamp start, now;
|
||||
#ifdef _WIN32
|
||||
/* ms */
|
||||
DWORD timeout = 10000;
|
||||
#else
|
||||
struct timeval timeout;
|
||||
memset(&timeout, 0, sizeof(struct timeval));
|
||||
timeout.tv_sec = 10;
|
||||
timeout.tv_usec = 0;
|
||||
#endif
|
||||
unsigned nremain = nrepeat;
|
||||
|
||||
(void)epicsTimeGetCurrent(&start);
|
||||
now = start;
|
||||
testDiag("RX%u start", info->id);
|
||||
|
||||
if(setsockopt(info->sock, SOL_SOCKET, SO_RCVTIMEO, (void*)&timeout, sizeof(timeout))) {
|
||||
testFail("Unable to set socket timeout");
|
||||
return;
|
||||
}
|
||||
|
||||
while(epicsTimeDiffInSeconds(&now, &start)<=5.0) {
|
||||
union CASearchU buf;
|
||||
osiSockAddr src;
|
||||
osiSocklen_t srclen = sizeof(src);
|
||||
|
||||
int n = recvfrom(info->sock, buf.bytes, sizeof(buf.bytes), 0, &src.sa, &srclen);
|
||||
buf.bytes[sizeof(buf.bytes)-1] = '\0';
|
||||
|
||||
if(n<0) {
|
||||
testDiag("recvfrom error (%d)", (int)SOCKERRNO);
|
||||
break;
|
||||
} else if((n==sizeof(buf.bytes)) && buf.msg.cmd==htons(6) && buf.msg.size==htons(16)
|
||||
&&buf.msg.dtype==htons(5) && buf.msg.dcnt==0 && strcmp(buf.msg.body, "totallyinvalid")==0)
|
||||
{
|
||||
unsigned ord = ntohl(buf.msg.p1)-info->key;
|
||||
testDiag("RX%u success %u", info->id, ord);
|
||||
if(ord<8) {
|
||||
const epicsUInt8 mask = 1u<<ord;
|
||||
if(info->rxmask&mask)
|
||||
info->dupmask|=mask;
|
||||
info->rxmask|=mask;
|
||||
}
|
||||
if(0==--nremain)
|
||||
break;
|
||||
} else {
|
||||
testDiag("RX ignore");
|
||||
}
|
||||
}
|
||||
testDiag("RX%u end", info->id);
|
||||
}
|
||||
|
||||
static
|
||||
void udpSockFanoutTestIface(const osiSockAddr* addr)
|
||||
{
|
||||
SOCKET sender;
|
||||
struct TInfo rx1, rx2;
|
||||
epicsThreadId trx1, trx2;
|
||||
epicsThreadOpts topts = EPICS_THREAD_OPTS_INIT;
|
||||
int opt = 1;
|
||||
unsigned i;
|
||||
osiSockAddr any;
|
||||
epicsUInt32 key = 0xdeadbeef ^ ntohl(addr->ia.sin_addr.s_addr);
|
||||
union CASearchU buf;
|
||||
|
||||
topts.joinable = 1;
|
||||
|
||||
/* we bind to any for lack of a portable way to find the
|
||||
* interface address from the interface broadcast address
|
||||
*/
|
||||
memset(&any, 0, sizeof(any));
|
||||
any.ia.sin_family = AF_INET;
|
||||
any.ia.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
any.ia.sin_port = addr->ia.sin_port;
|
||||
|
||||
buf.msg.cmd = htons(6);
|
||||
buf.msg.size = htons(16);
|
||||
buf.msg.dtype = htons(5);
|
||||
buf.msg.dcnt = htons(0); /* version 0, which newer servers should ignore */
|
||||
/* .p1 and .p2 set below */
|
||||
memcpy(buf.msg.body, "tota" "llyi" "nval" "id\0\0", 16);
|
||||
|
||||
sender = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
rx1.sock = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
rx2.sock = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
if((sender==INVALID_SOCKET) || (rx1.sock==INVALID_SOCKET) || (rx2.sock==INVALID_SOCKET))
|
||||
testAbort("Unable to allocate test socket(s)");
|
||||
|
||||
rx1.id = 1;
|
||||
rx2.id = 2;
|
||||
rx1.key = rx2.key = key;
|
||||
rx1.rxmask = rx2.rxmask = 0u;
|
||||
rx1.dupmask = rx2.dupmask = 0u;
|
||||
|
||||
if(setsockopt(sender, SOL_SOCKET, SO_BROADCAST, (void*)&opt, sizeof(opt))!=0) {
|
||||
testFail("setsockopt SOL_SOCKET, SO_BROADCAST error -> %d", (int)SOCKERRNO);
|
||||
}
|
||||
|
||||
epicsSocketEnableAddressUseForDatagramFanout(rx1.sock);
|
||||
epicsSocketEnableAddressUseForDatagramFanout(rx2.sock);
|
||||
|
||||
if(bind(rx1.sock, &any.sa, sizeof(any)))
|
||||
testFail("Can't bind test socket rx1 %d", (int)SOCKERRNO);
|
||||
if(bind(rx2.sock, &any.sa, sizeof(any)))
|
||||
testFail("Can't bind test socket rx2 %d", (int)SOCKERRNO);
|
||||
|
||||
trx1 = epicsThreadCreateOpt("rx1", &udpSockFanoutTestRx, &rx1, &topts);
|
||||
trx2 = epicsThreadCreateOpt("rx2", &udpSockFanoutTestRx, &rx2, &topts);
|
||||
|
||||
for(i=0; i<nrepeat; i++) {
|
||||
int ret;
|
||||
|
||||
/* don't spam */
|
||||
epicsThreadSleep(0.5);
|
||||
|
||||
buf.msg.p1 = buf.msg.p2 = htonl(key + i);
|
||||
ret = sendto(sender, buf.bytes, sizeof(buf.bytes), 0, &addr->sa, sizeof(*addr));
|
||||
if(ret!=(int)sizeof(buf.bytes))
|
||||
testDiag("sendto() error %d (%d)", ret, (int)SOCKERRNO);
|
||||
}
|
||||
|
||||
epicsThreadMustJoin(trx1);
|
||||
epicsThreadMustJoin(trx2);
|
||||
|
||||
testDiag("Result: RX1 %x:%x RX2 %x:%x",
|
||||
rx1.rxmask, rx1.dupmask, rx2.rxmask, rx2.dupmask);
|
||||
/* success if any one packet was seen by both sockets */
|
||||
if(rx1.rxmask & rx2.rxmask)
|
||||
nsuccess++;
|
||||
|
||||
epicsSocketDestroy(sender);
|
||||
epicsSocketDestroy(rx1.sock);
|
||||
epicsSocketDestroy(rx2.sock);
|
||||
}
|
||||
|
||||
/* This test violates the principle of unittest isolation by broadcasting
|
||||
* on the well known CA search port on all interfaces. There is no
|
||||
* portable way to avoid this. (eg. 127.255.255.255 is Linux specific)
|
||||
*/
|
||||
static
|
||||
void udpSockFanoutTest()
|
||||
{
|
||||
ELLLIST ifaces = ELLLIST_INIT;
|
||||
ELLNODE *cur;
|
||||
SOCKET dummy;
|
||||
osiSockAddr match;
|
||||
int foundNotLo = 0;
|
||||
|
||||
testDiag("udpSockFanoutTest()");
|
||||
|
||||
memset(&match, 0, sizeof(match));
|
||||
match.ia.sin_family = AF_INET;
|
||||
match.ia.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
if((dummy = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0))==INVALID_SOCKET)
|
||||
testAbort("Unable to allocate discovery socket");
|
||||
|
||||
osiSockDiscoverBroadcastAddresses(&ifaces, dummy, &match);
|
||||
|
||||
for(cur = ellFirst(&ifaces); cur; cur = ellNext(cur)) {
|
||||
char name[64];
|
||||
osiSockAddrNode* node = CONTAINER(cur, osiSockAddrNode, node);
|
||||
|
||||
node->addr.ia.sin_port = htons(5064);
|
||||
(void)sockAddrToDottedIP(&node->addr.sa, name, sizeof(name));
|
||||
|
||||
testDiag("Interface %s", name);
|
||||
if(node->addr.ia.sin_addr.s_addr!=htonl(INADDR_LOOPBACK)) {
|
||||
testDiag("Not LO");
|
||||
foundNotLo = 1;
|
||||
}
|
||||
|
||||
udpSockFanoutTestIface(&node->addr);
|
||||
}
|
||||
|
||||
ellFree(&ifaces);
|
||||
|
||||
testOk(foundNotLo, "Found non-loopback interface");
|
||||
testOk(nsuccess>0, "Successes %u", nsuccess);
|
||||
|
||||
epicsSocketDestroy(dummy);
|
||||
}
|
||||
|
||||
MAIN(osiSockTest)
|
||||
{
|
||||
int status;
|
||||
testPlan(18);
|
||||
testPlan(24);
|
||||
|
||||
status = osiSockAttach();
|
||||
testOk(status, "osiSockAttach");
|
||||
|
||||
udpSockTest();
|
||||
udpSockFanoutBindTest();
|
||||
udpSockFanoutTest();
|
||||
tcpSockReuseBindTest(0);
|
||||
tcpSockReuseBindTest(1);
|
||||
|
||||
osiSockRelease();
|
||||
return testDone();
|
||||
|
||||
Reference in New Issue
Block a user