Merge commit '10d472202dc2bf1fa5c569d2a14d460e95030564' into PSI-7.0

This is before PVA is modified
This commit is contained in:
2020-11-16 17:06:22 +01:00
22 changed files with 527 additions and 447 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 );
}

View File

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

View File

@@ -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");
}

View File

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

View File

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

View File

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

View File

@@ -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;
/*

View File

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

View File

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

View File

@@ -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 );
}

View File

@@ -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);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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();
}

View File

@@ -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();