Giter Club home page Giter Club logo

asyn's People

Contributors

anjohnson avatar apanna avatar bhill-slac avatar bl0x avatar daykin avatar dirk-zimoch avatar ericonr avatar exzombie avatar freddieakeroyd avatar gdyendell avatar hinxx avatar insomnia1437 avatar jevarlec avatar jphammonds avatar jtagger avatar krisztianloki avatar lrossa avatar mark0n avatar markrivers avatar mdavidsaver avatar mrkraimer avatar norumwe12 avatar ralphlange avatar sbaily avatar shadowguy avatar simon-ess avatar tboegi avatar timmmooney avatar ulrikpedersen avatar xiaoqiangwang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

asyn's Issues

Finish documentation of test programs

Some of the test applications are now documented, including screen shots. But some still need to be documented.

The test applications only have medm screens, they should have edm, caqtdm, and boy screens as well.

Uninitialized value in paramVal

When parameters get set for the first time like in

paramVal parVal("myParam", asynParamUInt32Digital);
parVal.setUInt32(42, 0, 0);

this results in the following issues being reported by Valgrind:

==7929== Conditional jump or move depends on uninitialised value(s)
==7929==    at 0x10AE88: paramVal::setUInt32(unsigned int, unsigned int, unsigned int) (paramVal.cpp:171)
==7929==    by 0x10AF8E: (anonymous namespace)::testA() (asynPortDriverTest.cpp:57)
==7929==    by 0x10A7E9: main (asynPortDriverTest.cpp:171)
==7929== 
==7929== Invalid read of size 8
==7929==    at 0x5F4B2A7: create_thread (createthread.c:116)
==7929==    by 0x5F4CFC7: pthread_create@@GLIBC_2.2.5 (pthread_create.c:822)
==7929==    by 0x5112F2E: epicsThreadCreate (osdThread.c:516)
==7929==    by 0x4E79F66: asynPortDriver::initialize(char const*, int, int, int, int, int, int, int) (asynPortDriver.cpp:3384)
==7929==    by 0x4E7ABCD: asynPortDriver::asynPortDriver(char const*, int, int, int, int, int, int, int) (asynPortDriver.cpp:3249)
==7929==    by 0x10AFCE: (anonymous namespace)::testA() (asynPortDriverTest.cpp:61)
==7929==    by 0x10A7E9: main (asynPortDriverTest.cpp:171)
==7929==  Address 0x7200028 is 72 bytes inside a block of size 166 free'd
==7929==    at 0x4C30D18: free (vg_replace_malloc.c:530)
==7929==    by 0x51125D8: start_routine (osdThread.c:406)
==7929==    by 0x5F4C7FB: start_thread (pthread_create.c:465)
==7929==    by 0x59F6B5E: clone (clone.S:95)
==7929==  Block was alloc'd at
==7929==    at 0x4C31A1E: calloc (vg_replace_malloc.c:711)
==7929==    by 0x5111EF2: create_threadInfo (osdThread.c:161)
==7929==    by 0x5111EF2: init_threadInfo (osdThread.c:180)
==7929==    by 0x5112EE1: epicsThreadCreate (osdThread.c:511)
==7929==    by 0x4E79F66: asynPortDriver::initialize(char const*, int, int, int, int, int, int, int) (asynPortDriver.cpp:3384)
==7929==    by 0x4E7ABCD: asynPortDriver::asynPortDriver(char const*, int, int, int, int, int, int, int) (asynPortDriver.cpp:3249)
==7929==    by 0x10AFCE: (anonymous namespace)::testA() (asynPortDriverTest.cpp:61)
==7929==    by 0x10A7E9: main (asynPortDriverTest.cpp:171)
==7929== 
==7929== Invalid read of size 1
==7929==    at 0x5F4B2D4: create_thread (createthread.c:139)
==7929==    by 0x5F4CFC7: pthread_create@@GLIBC_2.2.5 (pthread_create.c:822)
==7929==    by 0x5112F2E: epicsThreadCreate (osdThread.c:516)
==7929==    by 0x4E79F66: asynPortDriver::initialize(char const*, int, int, int, int, int, int, int) (asynPortDriver.cpp:3384)
==7929==    by 0x4E7ABCD: asynPortDriver::asynPortDriver(char const*, int, int, int, int, int, int, int) (asynPortDriver.cpp:3249)
==7929==    by 0x10AFCE: (anonymous namespace)::testA() (asynPortDriverTest.cpp:61)
==7929==    by 0x10A7E9: main (asynPortDriverTest.cpp:171)
==7929==  Address 0x7200008 is 40 bytes inside a block of size 166 free'd
==7929==    at 0x4C30D18: free (vg_replace_malloc.c:530)
==7929==    by 0x51125D8: start_routine (osdThread.c:406)
==7929==    by 0x5F4C7FB: start_thread (pthread_create.c:465)
==7929==    by 0x59F6B5E: clone (clone.S:95)
==7929==  Block was alloc'd at
==7929==    at 0x4C31A1E: calloc (vg_replace_malloc.c:711)
==7929==    by 0x5111EF2: create_threadInfo (osdThread.c:161)
==7929==    by 0x5111EF2: init_threadInfo (osdThread.c:180)
==7929==    by 0x5112EE1: epicsThreadCreate (osdThread.c:511)
==7929==    by 0x4E79F66: asynPortDriver::initialize(char const*, int, int, int, int, int, int, int) (asynPortDriver.cpp:3384)
==7929==    by 0x4E7ABCD: asynPortDriver::asynPortDriver(char const*, int, int, int, int, int, int, int) (asynPortDriver.cpp:3249)
==7929==    by 0x10AFCE: (anonymous namespace)::testA() (asynPortDriverTest.cpp:61)
==7929==    by 0x10A7E9: main (asynPortDriverTest.cpp:171)
==7929== 

The issue seems to be that the data member of paramVal doesn't get initialized when the object is constructed. When setting some bits of the value for the first time the uninitialized value is being used resulting in undefined behavior.

"main" blocking for thread "asynPortDriverCallback" to exit

I have an asynPortDriver based asyn driver that reports messages below when IOC shell 'exit' is requested. The operation would hang (forever?) until Ctrl+C is pressed to actually exit to shell. This is using asyn master branch code at commit 2eb4218.

epics> exit
[INF] AKI2C_TCA9555::~AKI2C_TCA9555: shut down ..
[INF] AKI2C::~AKI2C: shut down ..
[INF] AKBase::~AKBase: shut down ..
epicsThread::~epicsThread(): "main" blocking for thread "asynPortDriverCallback" to exit
was epicsThread object destroyed before thread exit ?
^C

In the more recent asyn master commit 9f4bcbd the output is a bit different, but still hangs (without changes to module code, just recompiled asyn is used now).

epics> exit
[INF] AKI2C_TCA9555::~AKI2C_TCA9555: shut down ..
[INF] AKI2C::~AKI2C: shut down ..
[INF] AKBase::~AKBase: shut down ..
epicsThread::~epicsThread(): "_main_" blocking for thread "!" to exit
was epicsThread object destroyed before thread exit ?
epicsThread::~epicsThread(): "_main_" blocking for thread "!" to exit
was epicsThread object destroyed before thread exit ?
^C

Improve documentation for asynTrace

From an e-mail exchange between Mark Rivers and Ben Franksen.

Hi Mark

ah, thanks, I didn't know that (nor is it obvious, at least not by
looking at the docs or the header file).

BTW, it would help a lot if this and also the possible constants to use
for asynSetTraceMask and asynSetTraceIOMask would be listed together
with these functions in the asynDriver docs.

Cheers
Ben

On 11/20/2017 05:17 PM, Mark Rivers wrote:
> Hi Ben,
> 
> "reason" is the asyn traceMask bit mask.  The bits are:
> 
> /* traceMask definitions*/
> #define ASYN_TRACE_ERROR     0x0001
> #define ASYN_TRACEIO_DEVICE  0x0002
> #define ASYN_TRACEIO_FILTER  0x0004
> #define ASYN_TRACEIO_DRIVER  0x0008
> #define ASYN_TRACE_FLOW      0x0010
> #define ASYN_TRACE_WARNING   0x0020
> 
> I suggest calling asynPrint as follows:
> ASYN_TRACE_ERROR for errors
> ASYN_TRACE_WARNING for warnings
> ASYN_TRACE_FLOW for debugging of flow.
> 
> Since streamDevice is device support I suggest calling asynPrintIO in this case:
> ASYN_TRACEIO_DEVICE for debugging output that contains data
> 
> Mark
> 
> -----Original Message-----
> From: Benjamin Franksen [mailto:[email protected]] 
> Sent: Monday, November 20, 2017 9:48 AM
> To: Mark Rivers
> Subject: Re: How to turn off spewing messaging from asyn/stream devicse.
> 
> Hi Mark
> 
> On 11/09/2017 11:34 PM, Mark Rivers wrote:
>> However, because those messages are coming from streamDevice and not
>> asyn that does not work.  I have asked Dirk to consider changing the
>> streamDevice error reporting so that if asyn is being used (which it
>> almost always is) then it uses the asynTrace mechanism for control
>> messages.  That would allow controlling them as above.
> 
> I plan to make this change in StreamDevice. The fact that the asyn
> backend is confined to a single module actually makes things easier. I
> am planning to change only those calls to the 'error' that result from
> asyn calls indicating failure, since these are the ones that we want to
> filter.
> 
> The problem I have is this: using asynTrace requires to provide the
> print method with an 'int reason' argument. I don't know what is
> expected here. How do I get hold of a suitable 'reason'?
> 
> Cheers
> Ben

The asynDriver.html documentation needs to be improved to explain this better.

Buffer overflow when using vxi11 and streamdevice

Possibly related to #28

  • Architecture: linux-x86_64
  • Asyn version: 4-38
  • Streamdevice version: 2.8.10
  • EPICS base version: 7.0.2.2

Streamdevice calls pasynOctet->read with bytesToRead=63, read returns asynSuccess and 78 bytes, eomReason=ASYN_EOM_EOS. Calling read again results in segfault.

What was expected:
Streamdevice calls pasynOctet->read with bytesToRead=63, read returns asynSuccess and 63 bytes, eomReason=ASYN_EOM_CNT. Streamdevice calls read again to retrieve the remaining bytes.

The issue can be reproduced every time the device returns message longer than 63 bytes.

Streamdevice debug log:

iocRun: All initialization complete
# IOC initialization done
epics> var streamDebug 1
epics> dbpf SIG-GEN-01:BSWV_C1_STAT.PROC 1
epics> 2020/02/17 11:16:42.401400 CAS-client StreamEpics.cc:843: Stream::process(SIG-GEN-01:BSWV_C1_STAT)
2020/02/17 11:16:42.401446 CAS-client StreamEpics.cc:869: Stream::process(SIG-GEN-01:BSWV_C1_STAT) start
2020/02/17 11:16:42.401451 CAS-client StreamCore.cc:411: StreamCore::startProtocol(SIG-GEN-01:BSWV_C1_STAT, startMode=StartNormal)
2020/02/17 11:16:42.401456 CAS-client StreamCore.cc:544: StreamCore::evalCommand(SIG-GEN-01:BSWV_C1_STAT): activeCommand = out
2020/02/17 11:16:42.401472 CAS-client StreamCore.cc:592: StreamCore::evalOut: outputLine = "C1:BSWV?<0a>"
2020/02/17 11:16:42.401477 CAS-client StreamCore.cc:603: StreamCore::evalOut(SIG-GEN-01:BSWV_C1_STAT): lockRequest(5000)
2020/02/17 11:16:42.401482 CAS-client AsynDriverInterface.cc:548: AsynDriverInterface::lockRequest(SIG-GEN-01:BSWV_C1_STAT, 5000 msec)
2020/02/17 11:16:42.401499 CAS-client StreamEpics.cc:883: Stream::process(SIG-GEN-01:BSWV_C1_STAT): protocol started
2020/02/17 11:16:42.401585 sigGen01 AsynDriverInterface.cc:1493: AsynDriverInterface::handleRequest(SIG-GEN-01:BSWV_C1_STAT) Lock
2020/02/17 11:16:42.401619 sigGen01 AsynDriverInterface.cc:589: AsynDriverInterface::lockHandler(SIG-GEN-01:BSWV_C1_STAT)
2020/02/17 11:16:42.401642 sigGen01 StreamCore.cc:812: StreamCore::lockCallback(SIG-GEN-01:BSWV_C1_STAT, StreamIoSuccess)
2020/02/17 11:16:42.401646 sigGen01 AsynDriverInterface.cc:626: AsynDriverInterface::writeRequest(SIG-GEN-01:BSWV_C1_STAT, "C1:BSWV?<0a>", 100 msec)
2020/02/17 11:16:42.401655 sigGen01 AsynDriverInterface.cc:1493: AsynDriverInterface::handleRequest(SIG-GEN-01:BSWV_C1_STAT) Write
2020/02/17 11:16:42.401659 sigGen01 AsynDriverInterface.cc:648: AsynDriverInterface::writeHandler(SIG-GEN-01:BSWV_C1_STAT)
2020/02/17 11:16:42.401661 sigGen01 AsynDriverInterface.cc:678: AsynDriverInterface::writeHandler(SIG-GEN-01:BSWV_C1_STAT): flushing old input
2020/02/17 11:16:42.574 sigGen01 -1 vxiWrite
C1:BSWV?\n
2020/02/17 11:16:42.573685 sigGen01 AsynDriverInterface.cc:714: AsynDriverInterface::writeHandler(SIG-GEN-01:BSWV_C1_STAT): write(..., "C1:BSWV?<0a>", outputSize=9, written=9) [timeout=0.1 sec] = asynSuccess 
2020/02/17 11:16:42.573698 sigGen01 StreamCore.cc:857: StreamCore::writeCallback(SIG-GEN-01:BSWV_C1_STAT, StreamIoSuccess)
2020/02/17 11:16:42.573702 sigGen01 StreamCore.cc:544: StreamCore::evalCommand(SIG-GEN-01:BSWV_C1_STAT): activeCommand = in
2020/02/17 11:16:42.573707 sigGen01 AsynDriverInterface.cc:805: AsynDriverInterface::readRequest(SIG-GEN-01:BSWV_C1_STAT, 1000 msec reply, 100 msec read, expect 0 bytes, async=no)
2020/02/17 11:16:42.573837 sigGen01 AsynDriverInterface.cc:830: AsynDriverInterface::readRequest SIG-GEN-01:BSWV_C1_STAT: queueRequest(..., priority=0, queueTimeout=1 sec) = asynSuccess [async=false] 
2020/02/17 11:16:42.573888 sigGen01 AsynDriverInterface.cc:1493: AsynDriverInterface::handleRequest(SIG-GEN-01:BSWV_C1_STAT) Read
2020/02/17 11:16:42.573928 sigGen01 AsynDriverInterface.cc:889: AsynDriverInterface::readHandler(SIG-GEN-01:BSWV_C1_STAT) input EOS changed from "" to "<0a>"
2020/02/17 11:16:42.573947 sigGen01 AsynDriverInterface.cc:948: AsynDriverInterface::readHandler(SIG-GEN-01:BSWV_C1_STAT): ioAction=Read read(..., bytesToRead=63, ...) [timeout=1 sec]
2020/02/17 11:16:42.744 sigGen01 -1 vxiRead
C1:BSWV WVTP,ARB,FRQ,1000HZ,PERI,0.001S,AMP,4V,OFST,0V,HLEV,2V,LLEV,-2V,PHSE,0\n
2020/02/17 11:16:42.743865 sigGen01 AsynDriverInterface.cc:956: AsynDriverInterface::readHandler(SIG-GEN-01:BSWV_C1_STAT): read returned asynSuccess: ioAction=Read received=78, eomReason=EOS+END, buffer="C1:BSWV WVTP,ARB,FRQ,1000HZ,PERI,0.001S,AMP,4V,OFST,0V,HLEV,2V,LLEV,-2V,PHSE,0"
2020/02/17 11:16:42.743881 sigGen01 AsynDriverInterface.cc:994: AsynDriverInterface::readHandler(SIG-GEN-01:BSWV_C1_STAT): received 78 of 63 bytes "C1:BSWV WVTP,ARB,FRQ,1000HZ,PERI,0.001S,AMP,4V,OFST,0V,HLEV,2V,LLEV,-2V,PHSE,0" eomReason=EOS+END
2020/02/17 11:16:42.743888 sigGen01 StreamCore.cc:943: StreamCore::readCallback(SIG-GEN-01:BSWV_C1_STAT, StreamIoEnd input="C1:BSWV WVTP,ARB,FRQ,1000HZ,PERI,0.001S,AMP,4V,OFST,0V,HLEV,2V,LLEV,-2V,PHSE,0", size=78)
2020/02/17 11:16:42.743893 sigGen01 StreamCore.cc:993: StreamCore::readCallback(SIG-GEN-01:BSWV_C1_STAT) inputBuffer="C1:BSWV WVTP,ARB,FRQ,1000HZ,PERI,0.001S,AMP,4V,OFST,0V,HLEV,2V,LLEV,-2V,PHSE,0", size 78
2020/02/17 11:16:42.743900 sigGen01 StreamCore.cc:1038: StreamCore::readCallback(SIG-GEN-01:BSWV_C1_STAT) inTerminator <0a> not found
2020/02/17 11:16:42.743906 sigGen01 StreamCore.cc:1045: StreamCore::readCallback(SIG-GEN-01:BSWV_C1_STAT) end flag received
2020/02/17 11:16:42.743911 sigGen01 StreamCore.cc:1103: StreamCore::readCallback(SIG-GEN-01:BSWV_C1_STAT) input line: "C1:BSWV WVTP,ARB,FRQ,1000HZ,PERI,0.001S,AMP,4V,OFST,0V,HLEV,2V,LLEV,-2V,PHSE,0"
2020/02/17 11:16:42.743922 sigGen01 StreamCore.cc:1192: StreamCore::matchInput(SIG-GEN-01:BSWV_C1_STAT): format = "%{SINE|SQUARE|RAMP|PULSE|NOISE|ARB|DC}"
2020/02/17 11:16:42.743942 sigGen01 EnumConverter.cc:154: EnumConverter::scanLong(%{, "ARB,FRQ,1000HZ,PERI,0.001S,AMP,4V,OFST,0V,HLEV,2V,LLEV,-2V,PHSE,0")
2020/02/17 11:16:42.743951 sigGen01 EnumConverter.cc:165: EnumConverter::scanLong: check #0 "SINE"
2020/02/17 11:16:42.743954 sigGen01 EnumConverter.cc:165: EnumConverter::scanLong: check #1 "SQUARE"
2020/02/17 11:16:42.743957 sigGen01 EnumConverter.cc:165: EnumConverter::scanLong: check #2 "RAMP"
2020/02/17 11:16:42.743964 sigGen01 EnumConverter.cc:165: EnumConverter::scanLong: check #3 "PULSE"
2020/02/17 11:16:42.743967 sigGen01 EnumConverter.cc:165: EnumConverter::scanLong: check #4 "NOISE"
2020/02/17 11:16:42.743970 sigGen01 EnumConverter.cc:165: EnumConverter::scanLong: check #5 "ARB"
2020/02/17 11:16:42.743973 sigGen01 EnumConverter.cc:181: EnumConverter::scanLong: value 5 matches
2020/02/17 11:16:42.743977 sigGen01 StreamCore.cc:1470: StreamCore::scanValue(SIG-GEN-01:BSWV_C1_STAT, format=%{, long) input="ARB" value=5
2020/02/17 11:16:42.743981 sigGen01 StreamEpics.cc:935: Stream::scan() currentValueLength=3
2020/02/17 11:16:42.743984 sigGen01 StreamEpics.cc:1511: Stream::matchValue(SIG-GEN-01:BSWV_C1_STAT): success, 3 bytes consumed
2020/02/17 11:16:42.743989 sigGen01 StreamCore.cc:544: StreamCore::evalCommand(SIG-GEN-01:BSWV_C1_STAT): activeCommand = end
2020/02/17 11:16:42.743995 sigGen01 StreamCore.cc:447: StreamCore::finishProtocol(SIG-GEN-01:BSWV_C1_STAT, Success) bus owner
2020/02/17 11:16:42.744001 sigGen01 AsynDriverInterface.cc:609: AsynDriverInterface::unlock(SIG-GEN-01:BSWV_C1_STAT)
2020/02/17 11:16:42.744006 sigGen01 AsynDriverInterface.cc:1473: AsynDriverInterface::finish(SIG-GEN-01:BSWV_C1_STAT) start
2020/02/17 11:16:42.744011 sigGen01 AsynDriverInterface.cc:1483: AsynDriverInterface::finish(SIG-GEN-01:BSWV_C1_STAT) done
2020/02/17 11:16:42.744015 sigGen01 StreamEpics.cc:969: Stream::protocolFinishHook(SIG-GEN-01:BSWV_C1_STAT, Success)
2020/02/17 11:16:42.744029 sigGen01 AsynDriverInterface.cc:1140: AsynDriverInterface::readHandler(SIG-GEN-01:BSWV_C1_STAT) input EOS restored to ""
2020/02/17 11:16:42.744052 cbLow StreamEpics.cc:1047: recordProcessCallback(SIG-GEN-01:BSWV_C1_STAT) processing record
2020/02/17 11:16:42.744097 cbLow StreamEpics.cc:843: Stream::process(SIG-GEN-01:BSWV_C1_STAT)
2020/02/17 11:16:42.744113 cbLow StreamEpics.cc:857: Stream::process(SIG-GEN-01:BSWV_C1_STAT) ready. convert
2020/02/17 11:16:42.744138 cbLow StreamEpics.cc:1051: recordProcessCallback(SIG-GEN-01:BSWV_C1_STAT) processing record done

Streamdevice protocol used:

Terminator = LF;

getBSWV_WVTP {
    ExtraInput = Ignore;
    out "C\$1:BSWV?";
    in "C\$1:BSWV WVTP,%{SINE|SQUARE|RAMP|PULSE|NOISE|ARB|DC}";
}

Record used:

record (mbbi, "$(DEVICE):BSWV_C$(CHAN)_STAT")
{
  field(DESC, "Query for the type of basic wave")
  field(DTYP, "stream")
  field(INP, "@sigGen.proto getBSWV_WVTP($(CHAN)) $(PORT)")
  field(ZRST, "SINE")
  field(ONST, "SQUARE")
  field(TWST, "RAMP")
  field(THST, "PULSE")
  field(FRST, "NOISE")
  field(FVST, "ARB")
  field(SXST, "DC")
  field(SCAN, "$(SCAN)")
}

It was also tested with the following protocol and waveform record:

Terminator = LF;

getBSWV_WVTP {
    out "C\$1:BSWV?";
    in "%#s";
}

Improve documentation

Rod Rod Nussbaumer suggested that the contents of this e-mail should be added to the asyn documentation. Need to find a way to do that. Perhaps an Overview and Introduction section to asynDriver.html that lays out the structure?

Hi Mark,

	When you say "it will buffer array callbacks", I assume you are referring to the asyn module (for the record to do so doesn't make a lot of sense, and it has no knowledge that this tag even exists, so I will go from there).

For this discussion it is useful to think of 3 components of asyn.

1)	asynManager.  This is the core part of asyn.  It knows nothing about EPICS records.  In fact it is completely independent of EPICS except that it uses libCom for OS-independent things like mutexes, message queues, events, etc.  The only queuing it provides is for callback requests to communicate with asynchronous drivers (ASYN_CANBLOCK) via pasynManager->queueRequest().

2)	Standard asyn device support (devEpics directory).  This is the only part of asyn that knows about EPICS records and depends on EPICS components other than libCom.  It supports callbacks from the driver under 2 conditions:
a.	Input records with SCAN=I/O Intr
b.	Output records with asyn:READBACK=1.
The callback values can be placed in a ring buffer so that values are not lost if the callbacks happen faster than the record can process.  The size of the ring buffer can be controlled with the asyn:FIFO info tag.  The default is 10 for scalar records.  The default is 0 for waveform records, and for stringout and stringin records.  If the ring buffer is in use then each driver callback results in pushing a new value into the buffer and a request to process the record in a separate callback thread.  The driver callbacks do not block waiting for the record to process.

3)	asynPortDriver.  asynPortDriver does not support queueing.  It does have a parameter library that stores the most recent value of scalar parameters.  It does not store values for array parameters.


	So what does it mean to "buffer array callbacks"?  

It is the ring buffer in devEpics described above.

	If this tag is used, does the doCallbacksXxxArray() functions return an error if the queue is full?

If the queue is full then the oldest value in the queue is discarded and the new value is added.  This guarantees that the record will eventually have the value of the most recent callback, but it may skip some before this.  If ASYN_TRACE_WARNING is set then a warning message is printed on the IOC console such as this one:

devAsynInt32.c-        if (pPvt->ringBufferOverflows > 0) {
devAsynInt32.c:            asynPrint(pPvt->pasynUser, ASYN_TRACE_WARNING,
devAsynInt32.c-                "%s %s::%s warning, %d ring buffer overflows\n",
devAsynInt32.c-                                    pPvt->pr->name, driverName, functionName, pPvt->ringBufferOverflows);
devAsynInt32.c-            pPvt->ringBufferOverflows = 0;
devAsynInt32.c-        }

	Of course that issue of a driver-launched thread blocking when performing callbacks would not be unique to waveform records.  
	Does this tag apply only to waveform records, or does it work for scalar values as well?  

The asyn:FIFO tag applies to both scalar and waveform records.  If not present it defaults to 10 for scalar records and 0 for waveform records.

	Or does it not apply to scalar values (i.e. when ASYN_CANBLOCK is used, writes to and callbacks for scalar values are simply added to the end of the Work Queue, and the only limitation in how many values are buffered is how big the queue can get (?))
The Work Queue is not involved in callbacks from the driver.  It is only involved in callbacks from pasynManager->queueRequest, i.e. callbacks that occur when device support has exclusive access to the asyn port driver and can make calls like pasynInt32->write(), pasynFloat64->read(), etc.

Mark

Please may I query the logic of asynPortDriver::callParamCallbacks(int addr)?

params is a 2D array, being a vector[maxAddr] of parameter lists.

I think the logic given below (for the 1D method overload) isn't correct, in that the code accesses the [addr, addr] address of the array.
I believe it should be accssing the [0, addr], the 'normal' case being just one list (i.e. maxAddr=1).

This results in a 'vector subscript out of range' debug assertion, when called with an addr value of >maxAddr-1.

I think the associated comments e.g. "with list=addr, which is normal." and "Typically the same value as list" are also misleading.

(I usually call the 2D method overload, so I hadn't noticed this before.)

/** Calls callParamCallbacks(addr, addr) i.e. with list=addr, which is normal. */
asynStatus asynPortDriver::callParamCallbacks(int addr)
{
return this->callParamCallbacks(addr, addr);
}

/** Calls paramList::callCallbacks(addr) for a specific parameter list.

  • \param[in] list The parameter list number. Must be < maxAddr passed to asynPortDriver::asynPortDriver.
  • \param[in] addr The asyn address to be used in the callback. Typically the same value as list. */
    asynStatus asynPortDriver::callParamCallbacks(int list, int addr)
    {
    return this->params[list]->callCallbacks(addr);
    }

Possible problem with UNIX sockets

I received the following report of a potential problem with Unix sockets.

Hi, Mark!

My name is Eduardo, I work for the Brazilian Synchrotron Light Laboratory.
A few days ago, while creating an EPICS IOC with StreamDevice that connects to a UNIX socket, I got some error messages from asynDriver and things didn't work.
I never studied the asynDriver code in details. But in a quick trial I added two lines of code in the file <asyn_root>/asyn/drvAsynSerial/drvAsynIPPort.c and now everything works and I don't see any error messages. Here is the diff between my modified file (drvAsynIPPort_.c) and the original version (drvAsynIPPort.c):

$ diff drvAsynIPPort_.c drvAsynIPPort.c
309,310d308
<         tty->isCom = 0;
<         tty->flags &= ~FLAG_SHUTDOWN;

I'm working with asynDriver 4-30.
Am I missing something? Or actually is this a bug?
I put in copy Gustavo, a colleague that is working in the same project.
Thanks in advance.
Eduardo

It does look like a bug to me. In the case of Unix sockets tty->isCom is only ever set to ISCOM_UNKNOWN, and then the test for calling interposeCOM is just for a non-zero value of tty->isCom.

@norumwe12 can you take a look at this?

Reporting that the parameter value has changed to the driver

I have a proposal for an enhancement.

I'm working with hardware that needs to have parameters updated at certain point in time. For example if DSP on FPGA is in 'armed' mode and processing is taking place parameter updates, that involve setting a register value, need to be delayed to the time after the DSP processing has finished.

I was thinking that this might be an addition to the asynPortDriver, more specifically to the paramList class by introducing 'value changed' status. It would function in a similar manner as 'flags' member currently works. Upon PV hence param value change, 'value changed' status is set. Then asynPortDriver derived class can check if param 'value changed' status is set and can act accordingly; use the parameter value when needed (update the FPGA register when safe). Afterwards it would reset the 'value changed' status.

asynPortDriver derived class is in total control of the 'value changed' status, because it needs to knows and might even ignore it if it wants to..

Does this make sense?

support SO_REUSEPORT socket option

The SO_REUSEPORT socket option allows multiple clients on the same user@host to bind to the same UDP port. With this option enabled in asyn, I expect to be able to run multiple IOCs using new support for the Oxford CS800 CryoStream controller (blows cold nitrogen gas at protein sample during crystallography measurement). Currently, when I try to run a second IOC from the same user@host, this error is reported to the console and no communications is the result:

# ### cs800.iocsh ###
# STATUS_ADDR  "255.255.255.255:30304:30304 UDP"
# COMMAND_ADDR "10.0.0.173:30305 UDP*"
# Status Packets
drvAsynIPPortConfigure("OC_SP", "192.168.144.169:30304:30304 UDP*", 0, 0, 0)
2020/04/18 11:16:04.544 OC_SP -1 autoConnect could not connect: unable to bind to local port: Address already in use
# Commands

Is it possible to call setsockopt() (similar to what is shown) from the st.cmd file?

Otherwise, can support for the SO_REUSEPORT socket option be enabled in asyn? The text SO_REUSEPORT does not appear anywhere in the asyn repo so I assume this option is not available yet.

devEpics leaking DBENTRY*

There are many calls to dbAllocEntry() without a corresponding dbFreeEntry(). These calls are found in various initCommon() and createRingBuffer(). I think these are only one-time initialization per record, so the practical effect of this leak is bounded.

FYI, from what I can see it would be better to use dbInitEntry() and dbFinishEntry() and keep the DBENTRY on the stack. Note that dbFinishEntry() is still required to free() any error message string which might be allocated while using the DBENTRY.

Deadlocks with asyn:READBACK on output records

There is a problem with output records with the info tag asyn:READBACK, i.e. output records that update their values from driver callbacks.

The interrupt callback function for output records calls dbScanLock because it is reading the PACT field of the record. This can cause deadlocks because output records with synchronous drivers (ASYN_CANBLOCK=0) take the locks in the order

  1. dbScanLock (taken by EPICS base record processing)
  2. driverLock (taken when device support process function calls the driver)

However, the callback function takes the locks in the order

  1. driverLock (taken by the driver prior to the callback)
  2. dbScanLock (taken in the callback function in device support)

Since the locks are taken in the opposite order deadlocks can occur.

Device support is looking at PACT to determine if the record is currently in the middle of
asynchronous record processing.

A potential fix is to use a private variable to hold the information about whether asynchronous processing is in progress, so only the private mutex is needed, and not dbScanLock.

asynXXXArray should support aai and aao record types

asyn only supports the waveform record for array data.

The aai and aao records have been part of EPICS Base for a long time. They provide proper input and output functionality for array data and are similar enough to the waveform record to be supported without much effort.

records with asyn:READBACK are not always setting STAT and SEVR correctly when UDF=0

This issue is in response to this EPICS tech-talk thread: https://epics.anl.gov/tech-talk/2021/msg01094.php

I have created a new debug_udf branch in asyn to test this, using a modified version of testAsynPortDriver.cpp and its IOC.

I have reproduced the problem.

I added a new bo record called Clutch. This is the definition of that record. Note that it does not set PINI=1, does not set VAL, and sets the asyn:READBACK info tag to 1.

record(bo, "$(P)$(R)Clutch")
{
    field(DTYP, "asynInt32")
    field(OUT,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))CLUTCH")
    field(ZNAM, "Off")
    field(ONAM, "On")
    info(asyn:READBACK, "1")
}

The driver was modified to add an asynInt32 parameter, P_Clutch, corresponding to this record. Its value is not set in the constructor. The following code was added to the poller thread.

+    epicsThreadSleep(2.0);
     lock();
+    setIntegerParam(P_Clutch, 1);
+    callParamCallbacks();
+    unlock();
+    epicsThreadSleep(2.0);
+    lock();
+    setIntegerParam(P_Clutch, 0);
+    callParamCallbacks();

Because the value of the Clutch parameter is not set at all until 2 seconds after the thread is created the initial readback in init_record will return asynUndefined, and UDF should be 1, and STAT=UDF, SEVR=INVALID.

The above code sleeps for 2 seconds and then sets the value of clutch to 1 and calls the callbacks. Because asyn:READBACK=1 this should do the following:

  • Set the record VAL field to 1
  • Set the record UDF field to 0
  • Process the record. Processing the record should set STAT and SEVR to NO_ALARM.

The code then sleeps for another 2 seconds and sets the value back to 0, and again does the callbacks. At this point the record VAL should be 0 and STAT and SEVR should still be NO_ALARM.

iocBoot/ioctestAsynPortDriver/st.cmd was modified as follows:

index cdd5baf..499ce2a 100644
--- a/iocBoot/ioctestAsynPortDriver/st.cmd
+++ b/iocBoot/ioctestAsynPortDriver/st.cmd
@@ -5,9 +5,20 @@ testAsynPortDriver_registerRecordDeviceDriver(pdbbase)
 #asynSetTraceMask("", 0, 17)

 testAsynPortDriverConfigure("testAPD", 1000)
+date

 dbLoadRecords("../../db/testAsynPortDriver.db","P=testAPD:,R=scope1:,PORT=testAPD,ADDR=0,TIMEOUT=1,NPOINTS=1000")
 dbLoadRecords("../../db/asynRecord.db","P=testAPD:,R=asyn1,PORT=testAPD,ADDR=0,OMAX=80,IMAX=80")
 #asynSetTraceMask("testAPD",0,0xff)
 asynSetTraceIOMask("testAPD",0,0x2)
 iocInit()
+
+date
+dbpr testAPD:scope1:Clutch 2
+epicsThreadSleep(3)
+date
+dbpr testAPD:scope1:Clutch 2
+epicsThreadSleep(3)
+date
+dbpr testAPD:scope1:Clutch 2
+

It prints the time right after the driver is created. This is the output, so the driver was created at 09:31:02.84.

testAsynPortDriverConfigure("testAPD", 1000)
date
2021/05/28 09:31:02.849064

It prints the time and then runs dbpr on the Clutch record immediately after iocInit. At this point we expect VAL=0 (record default), UDF=1, STAT=UDF, SEVR=INVALID, TIME=undefined because the record has never processed. This is indeed exactly what we actually see:

iocRun: All initialization complete
date
2021/05/28 09:31:03.353862
dbpr testAPD:scope1:Clutch 2
ACKS: NO_ALARM      ACKT: YES           ASG :               BKPT: 00
COSV: NO_ALARM      DESC:               DISA: 0             DISP: 0
DISS: NO_ALARM      DISV: 1             DOL : CONSTANT      DTYP: asynInt32
EVNT:               FLNK: CONSTANT      HIGH: 0
IVOA: Continue normally                 IVOV: 0             LCNT: 0
MASK: 0             NAME: testAPD:scope1:Clutch             NSEV: NO_ALARM
NSTA: NO_ALARM      OMSL: supervisory   ONAM: On            OSV : NO_ALARM
OUT : INST_IO @asyn(testAPD,0,1)CLUTCH  PACT: 0             PHAS: 0
PINI: NO            PRIO: LOW           PUTF: 0             RBV : 0
RPRO: 0             RVAL: 0             SCAN: Passive       SDIS: CONSTANT
SDLY: -1            SEVR: INVALID       SIML: CONSTANT      SIMM: NO
SIMS: NO_ALARM      SIOL: CONSTANT      SSCN: <nil>         STAT: UDF
TIME: <undefined>   TPRO: 0             TSE : 0             TSEL: CONSTANT
UDF : 1             UDFS: INVALID       VAL : 0             ZNAM: Off
ZSV : NO_ALARM

It then sleeps for 3 seconds, which is long enough for the first setting of the Clutch parameter in the polling thread. At this point we expect VAL=1, UDF=0, STAT=SEVR=NO_ALARM, TIME=about 2 seconds after driver was created. Here is what we actually see.

  • UDF=0 (as expected)
  • VAL=1 (as expected)
  • TIME= 09:31:04.84 (exactly 2 seconds after the driver and the poller thread was created, as expected)
  • STAT=UDF (this is NOT expected)
  • SEVR=INVALID ( this is NOT expected)
epicsThreadSleep(3)
date
2021/05/28 09:31:06.354234
dbpr testAPD:scope1:Clutch 2
ACKS: NO_ALARM      ACKT: YES           ASG :               BKPT: 00
COSV: NO_ALARM      DESC:               DISA: 0             DISP: 0
DISS: NO_ALARM      DISV: 1             DOL : CONSTANT      DTYP: asynInt32
EVNT:               FLNK: CONSTANT      HIGH: 0
IVOA: Continue normally                 IVOV: 0             LCNT: 0
MASK: 0             NAME: testAPD:scope1:Clutch             NSEV: NO_ALARM
NSTA: NO_ALARM      OMSL: supervisory   ONAM: On            OSV : NO_ALARM
OUT : INST_IO @asyn(testAPD,0,1)CLUTCH  PACT: 0             PHAS: 0
PINI: NO            PRIO: LOW           PUTF: 0             RBV : 0
RPRO: 0             RVAL: 1             SCAN: Passive       SDIS: CONSTANT
SDLY: -1            SEVR: INVALID       SIML: CONSTANT      SIMM: NO
SIMS: NO_ALARM      SIOL: CONSTANT      SSCN: <nil>         STAT: UDF
TIME: 2021-05-28 09:31:04.849222534     TPRO: 0             TSE : 0
TSEL: CONSTANT      UDF : 0             UDFS: INVALID       VAL : 1
ZNAM: Off           ZSV : NO_ALARM

It then sleeps for anther 3 seconds, which is long enough for the second setting of the Clutch parameter in the polling thread, which is back to 0. At this point we expect VAL=0, UDF=0, STAT=SEVR=NO_ALARM, TIME=about 2 seconds after the previous time the record was processed=09:31:06.84. That is indeed exactly what we actually see.

epicsThreadSleep(3)
date
2021/05/28 09:31:09.354519
dbpr testAPD:scope1:Clutch 2
ACKS: NO_ALARM      ACKT: YES           ASG :               BKPT: 00
COSV: NO_ALARM      DESC:               DISA: 0             DISP: 0
DISS: NO_ALARM      DISV: 1             DOL : CONSTANT      DTYP: asynInt32
EVNT:               FLNK: CONSTANT      HIGH: 0
IVOA: Continue normally                 IVOV: 0             LCNT: 0
MASK: 0             NAME: testAPD:scope1:Clutch             NSEV: NO_ALARM
NSTA: NO_ALARM      OMSL: supervisory   ONAM: On            OSV : NO_ALARM
OUT : INST_IO @asyn(testAPD,0,1)CLUTCH  PACT: 0             PHAS: 0
PINI: NO            PRIO: LOW           PUTF: 0             RBV : 0
RPRO: 0             RVAL: 0             SCAN: Passive       SDIS: CONSTANT
SDLY: -1            SEVR: NO_ALARM      SIML: CONSTANT      SIMM: NO
SIMS: NO_ALARM      SIOL: CONSTANT      SSCN: <nil>         STAT: NO_ALARM
TIME: 2021-05-28 09:31:06.849333901     TPRO: 0             TSE : 0
TSEL: CONSTANT      UDF : 0             UDFS: INVALID       VAL : 0
ZNAM: Off           ZSV : NO_ALARM

So the problem is that the first time the record processes due to a callback UDF=0 but STAT and SEVR do not get set to NO_ALARM. Why not?

GPIB driver returns EOM_END when it should not

A problem was found when using StreamDevice with the GPIB driver and reading lines longer then 63 characters. StreamDevice reads in chunks of 63 characters, repeatedly until if octet->read returns something other than EOM_CNT, i.e. EOM_END or EOM_EOS. The problem is that is the GPIB driver reads more than 63 characters and gets an END, then it returns EOM_CNT | EOM_END on the read by StreamDevice. This is not correct, it should only return EOM_END when the read includes the last character read.

RS485 support needs work

I have made a new branch, rs485. This branch started with Florian's pull request. I made the following fixes:

  • Made it work on vxWorks. The problem was that ioctl was being illegally redefined in default/serial_rs485.h. I removed the definition of ioctl from that file.
  • Made it work on older Linux systems. These systems don't define the RS485 constants in linux/serial.h, and the ioctl won't work. I test whether RS485 is supported by seeing if serial.h defines SER_RS485_ENABLED. If it does then I assume RS-485 is supported and define the symbol ASYN_RS485_SUPPORTED. In drvAsynSerialPort.c the ioctl for RS485 is only executed if that symbol is defined. It is not defined in default/serial_rs485.h or for older Linux systems.

There is still a problem. I ran the testGpibSerial IOC on a newer Linux system which defines SER_RS485_ENABLED and so I think does support RS485. I get the following errors when I boot the IOC:

drvAsynSerialPortConfigure("L0","/dev/ttyS0",0,0,0)
asynSetOption("L0", -1, "baud", "2400")
setOption failed ioctl TIOCSRS485 failed: Invalid argument
asynSetOption("L0", -1, "bits", "8")
setOption failed ioctl TIOCSRS485 failed: Invalid argument
asynSetOption("L0", -1, "parity", "none")
setOption failed ioctl TIOCSRS485 failed: Invalid argument
asynSetOption("L0", -1, "stop", "1")  
setOption failed ioctl TIOCSRS485 failed: Invalid argument
asynSetOption("L0", -1, "clocal", "Y")
setOption failed ioctl TIOCSRS485 failed: Invalid argument
asynSetOption("L0", -1, "crtscts", "N")
setOption failed ioctl TIOCSRS485 failed: Invalid argument

So every time any option is set it does the ioctl for RS485 and gets an error. Is this because RS485 is only supported on certain types of serial hardware that support RS485? Or is there some other reason for these errors, such as one of the arguments actually being invalid?

server code or client code?

Hi,
is your vx11 code is client code ?
do you have server code ?
possible to use your vxi11 API's to write server code ?

Thanks

typo or wrong calling for outputCallbackCallback in devEpics modules?

asyn4-41, many devSet modules in devEpics , such as devAsynInt32.c, there is one function outputCallbackCallback for activating the second process of a PVRecord (according to the control flow introduction from asyn ducument):

static void outputCallbackCallback(CALLBACK *pcb)
{
static const char *functionName="outputCallbackCallback";
devPvt *pPvt;
callbackGetUser(pPvt, pcb);
{
dbCommon pr = pPvt->pr;
dbScanLock(pr);
epicsMutexLock(pPvt->devPvtLock);
pPvt->newOutputCallbackValue = 1;
dbProcess(pr);
if (pPvt->newOutputCallbackValue != 0) {
/
We called dbProcess but the record did not process, perhaps because PACT was 1
* Need to remove ring buffer element */
asynPrint(pPvt->pasynUser, ASYN_TRACE_ERROR,
"%s %s::%s warning dbProcess did not process record, PACT=%d\n",
pr->name, driverName, functionName,pr->pact);
getCallbackValue(pPvt);
pPvt->newOutputCallbackValue = 0;
}
epicsMutexUnlock(pPvt->devPvtLock);
dbScanUnlock(pr);
}
}

this function is also registered by callbackSetCallback in initCommon function:

static long initCommon(dbCommon *pr, DBLINK *plink,
userCallback processCallback,interruptCallbackInt32 interruptCallback, interruptCallbackEnum callbackEnum,
int maxEnums, char *pFirstString, int *pFirstValue, epicsEnum16 pFirstSeverity)
{
...
/
If the info field "asyn:READBACK" is 1 and interruptCallback is not NULL
* then register for callbacks on output records /
if (interruptCallback) {
int enableCallbacks=0;
...
if (enableCallbacks) {
status = createRingBuffer(pr);
if (status!=asynSuccess) goto bad;
status = pPvt->pint32->registerInterruptUser(
pPvt->int32Pvt,pPvt->pasynUser,
pPvt->interruptCallback,pPvt,&pPvt->registrarPvt);
if (status!=asynSuccess) {
printf("%s %s::%s error calling registerInterruptUser %s\n",
pr->name, driverName, functionName,pPvt->pasynUser->errorMessage);
}
/
Initialize the interrupt callback */
callbackSetCallback(outputCallbackCallback, &pPvt->outputCallback); // here the outputCallbackCallback is registered as callback function and assgined to pPvt->outputCallback
callbackSetPriority(pr->prio, &pPvt->outputCallback);
callbackSetUser(pPvt, &pPvt->outputCallback);
}
}
return INIT_OK;
bad:
recGblSetSevr(pr,LINK_ALARM,INVALID_ALARM);
pr->pact=1;
return INIT_ERROR;
}

so the function is registered and assigned into pPvt->outputCallback. but in the port thread responding to
queueRequest, such as function processCallbackInput and processCallbackOutput, the callbackRequestProcessCallback uses another parameter pPvt->processCallback, which seems never get registered or assignment else where.

static void processCallbackInput(asynUser *pasynUser)
{
...
pPvt->lastStatus = pPvt->result.status;
if(pr->pact) callbackRequestProcessCallback(&pPvt->processCallback,pr->prio,pr);
}

static void processCallbackOutput(asynUser *pasynUser)
{
...
pPvt->lastStatus = pPvt->result.status;
if(pr->pact) callbackRequestProcessCallback(&pPvt->processCallback,pr->prio,pr);
}

should those callbackRequestProcessCallback function take &pPvt->outputCallback as the 1st parameter? or is there other tricks?

Problems with error checking in asynPortDriver functions

In asynPortDriver.cpp most of the asynPortDriver::writeXXX() methods do this:

 /* Set the parameter in the parameter library. */
    status = (asynStatus) setXXXParam(addr, function, ...);

    /* Do callbacks so higher layers see any changes */
    status = (asynStatus) callParamCallbacks(addr, addr);

    if (status)

whereas asynPortDriver::writeFloat64() does this:

    /* Set the parameter and readback in the parameter library. */
    status = setDoubleParam(addr, function, value);

    /* Do callbacks so higher layers see any changes */
    callParamCallbacks(addr, addr);
    if (status)

I see problems in both versions: In the first case the status from the setXXXParam() call is being discarded; in the second the status from callParamCallbacks() is lost. Whatever the correct behavior should be I would expect all the types to implement the same error handling.

Error in drvAsynFTDIPortConfigure

drvAsynFTDIPortConfigure takes 8 arguments, but only 7 are passed in the drvAsynFTDIPortConfigure iocsh command.

All 8 arguments are defined, but only 7 are passed:

static const iocshArg drvAsynFTDIPortConfigureArg0 = { "port name",iocshArgString};
static const iocshArg drvAsynFTDIPortConfigureArg1 = { "vendor ID",iocshArgInt};
static const iocshArg drvAsynFTDIPortConfigureArg2 = { "product ID",iocshArgInt};
static const iocshArg drvAsynFTDIPortConfigureArg3 = { "baudrate",iocshArgInt};
static const iocshArg drvAsynFTDIPortConfigureArg4 = { "latency",iocshArgInt};
static const iocshArg drvAsynFTDIPortConfigureArg5 = { "priority",iocshArgInt};
static const iocshArg drvAsynFTDIPortConfigureArg6 = { "disable auto-connect",iocshArgInt};
static const iocshArg drvAsynFTDIPortConfigureArg7 = { "noProcessEos",iocshArgInt};
static const iocshArg *drvAsynFTDIPortConfigureArgs[] = {
    &drvAsynFTDIPortConfigureArg0, &drvAsynFTDIPortConfigureArg1,
    &drvAsynFTDIPortConfigureArg2, &drvAsynFTDIPortConfigureArg3,
    &drvAsynFTDIPortConfigureArg4, &drvAsynFTDIPortConfigureArg5,
    &drvAsynFTDIPortConfigureArg6};
static const iocshFuncDef drvAsynFTDIPortConfigureFuncDef =
                      {"drvAsynFTDIPortConfigure",7,drvAsynFTDIPortConfigureArgs};
static void drvAsynFTDIPortConfigureCallFunc(const iocshArgBuf *args)
{
    drvAsynFTDIPortConfigure(args[0].sval, args[1].ival, args[2].ival, args[3].ival, args[4].ival, args[5].ival, args[6].ival, args[7].ival);
}

devAsynXxx error message spamming

This is the same problem that I fixed some time ago for the asyn re-connect logic. Device support currently issues a message for every failed asyn read or write, in other words every time the record gets processed. Instead it should remember the last status and print a message only when the status changes. Below is a patch (relative tio asyn-4.35) that fixes this for devAsynInt32 and devAsynFloat64 but I think a similar change should be made to all device supports.

diff -rN -u old-4-35/asyn/devEpics/devAsynFloat64.c new-4-35/asyn/devEpics/devAsynFloat64.c
--- old-4-35/asyn/devEpics/devAsynFloat64.c	2019-10-09 18:38:58.736137891 +0200
+++ new-4-35/asyn/devEpics/devAsynFloat64.c	2019-10-09 18:38:58.736137891 +0200
@@ -74,6 +74,7 @@
     int               ringSize;
     int               ringBufferOverflows;
     ringBufferElement result;
+    asynStatus        lastStatus;
     epicsFloat64      sum;
     interruptCallbackFloat64 interruptCallback;
     int               numAverage;
@@ -333,10 +334,13 @@
         asynPrint(pasynUser, ASYN_TRACEIO_DEVICE,
             "%s %s::%s process value=%f\n",pr->name, driverName, functionName,pPvt->result.value);
     } else {
-        asynPrint(pasynUser, ASYN_TRACE_ERROR,
-              "%s %s::%s process read error %s\n",
-              pr->name, driverName, functionName,pasynUser->errorMessage);
+        if (pPvt->result.status  != pPvt->lastStatus) {
+            asynPrint(pasynUser, ASYN_TRACE_ERROR,
+                  "%s %s::%s process read error %s\n",
+                  pr->name, driverName, functionName, pasynUser->errorMessage);
+        }
     }
+    pPvt->lastStatus = pPvt->result.status;
     if(pr->pact) callbackRequestProcessCallback(&pPvt->processCallback,pr->prio,pr);
 }
 
@@ -354,10 +358,13 @@
         asynPrint(pasynUser, ASYN_TRACEIO_DEVICE,
             "%s %s::%s process val %f\n",pr->name, driverName, functionName,pPvt->result.value);
     } else {
-       asynPrint(pasynUser, ASYN_TRACE_ERROR,
-           "%s %s::%s pPvt->result.status=%d, process error %s\n",
-           pr->name, driverName, functionName,pPvt->result.status, pasynUser->errorMessage);
+        if (pPvt->result.status  != pPvt->lastStatus) {
+            asynPrint(pasynUser, ASYN_TRACE_ERROR,
+                  "%s %s::%s process write error %s\n",
+                  pr->name, driverName, functionName, pasynUser->errorMessage);
+        }
     }
+    pPvt->lastStatus = pPvt->result.status;
     if(pr->pact) callbackRequestProcessCallback(&pPvt->processCallback,pr->prio,pr);
 }
 
diff -rN -u old-4-35/asyn/devEpics/devAsynInt32.c new-4-35/asyn/devEpics/devAsynInt32.c
--- old-4-35/asyn/devEpics/devAsynInt32.c	2019-10-09 18:38:58.736137891 +0200
+++ new-4-35/asyn/devEpics/devAsynInt32.c	2019-10-09 18:38:58.736137891 +0200
@@ -92,6 +92,7 @@
     int               ringSize;
     int               ringBufferOverflows;
     ringBufferElement result;
+    asynStatus        lastStatus;
     interruptCallbackInt32 interruptCallback;
     double            sum;
     int               numAverage;
@@ -502,10 +503,13 @@
         asynPrint(pasynUser, ASYN_TRACEIO_DEVICE,
             "%s %s::%s process value=%d\n",pr->name, driverName, functionName,pPvt->result.value);
     } else {
-        asynPrint(pasynUser, ASYN_TRACE_ERROR,
-              "%s %s::%s process read error %s\n",
-              pr->name, driverName, functionName, pasynUser->errorMessage);
+        if (pPvt->result.status  != pPvt->lastStatus) {
+            asynPrint(pasynUser, ASYN_TRACE_ERROR,
+                  "%s %s::%s process read error %s\n",
+                  pr->name, driverName, functionName, pasynUser->errorMessage);
+        }
     }
+    pPvt->lastStatus = pPvt->result.status;
     if(pr->pact) callbackRequestProcessCallback(&pPvt->processCallback,pr->prio,pr);
 }
 
@@ -523,10 +527,13 @@
         asynPrint(pasynUser, ASYN_TRACEIO_DEVICE,
             "%s %s::%s process value %d\n",pr->name, driverName, functionName,pPvt->result.value);
     } else {
-       asynPrint(pasynUser, ASYN_TRACE_ERROR,
-           "%s %s::%s process error %s\n",
-           pr->name, driverName, functionName, pasynUser->errorMessage);
+        if (pPvt->result.status  != pPvt->lastStatus) {
+            asynPrint(pasynUser, ASYN_TRACE_ERROR,
+                  "%s %s::%s process write error %s\n",
+                  pr->name, driverName, functionName, pasynUser->errorMessage);
+        }
     }
+    pPvt->lastStatus = pPvt->result.status;
     if(pr->pact) callbackRequestProcessCallback(&pPvt->processCallback,pr->prio,pr);
 }

std::vector index overrun issue in asynPortDriver.cpp

There is an issue with paramList::flags, I experience this causing an assertion within a debug build while using VS2017.

The variable is accessed (in a threaded manner) from paramList::setFlag and paramList::callCallbacks.

I understand that the variable is used as a notifier that a particular parameter has been changed, so that callbacks need to be invoked.

There are really 2 issues:
a. I think std::vector is not the optimal STL container for this purpose.
b. More importantly, STL containers are not thread-safe - this usage needs to be protected by a mutex.

I will be able to suggest an alternate solution.

Screen shot attached (while using the simDetector):
simDetectorApp

Probable bug in 'drvAsynIPServerPort', function 'readIt()'

Hello,

I am using 'drvAsynIPServerPort' to read binary data from a UDP/IP packet into a structure, and I noticed that the function 'readIt()' is not reading the last byte.

This may be intentional, but if not, the issue seems to be with lines 195-197 from drvAsynIPServerPort.c:

for (x = 0; x < (int)maxchars - 1; x++) { data[x] = tty->UDPbuffer[x + tty->UDPbufferPos]; }

With this implementation, if I ask it to fill a structure of exactly N bytes, it will only fill N-1.
Considering this, I propose modifying 'maxchar-1' to 'maxchar'.

I don't think it will mess up with string data, because the termination character will always be added later (lines 231-232), if there is enough space.

if (thisRead < (int) maxchars) data[thisRead] = 0;

In addition, I think the remaining size of tty->UDPbuffer[] should be checked before the reading of maxchar characters, otherwise it seems to me that there is nothing stopping a client from reading beyond the end of the array.

Crash when accessing lists above maxAddr in asynPortDriver

Here is the relevant part of the traceback:

#0  paramList::setString (this=0x0, index=index@entry=98, value=0x7ffff77866df "") at ../../asyn/asynPortDriver/asynPortDriver.cpp:265
#1  0x00007ffff6b9d998 in asynPortDriver::setStringParam (this=0x6d07d0, list=10, index=98, value=<optimized out>)
    at ../../asyn/asynPortDriver/asynPortDriver.cpp:1463

It is not entirely obvious what the issue is from this. We had set maxAddr to 10. Perhaps direct accesses to lists this->params[list] could be replaced with a method getParamList(list) that verifies list against maxAddr.

Or, maybe this is just user error and developers should be expected to set maxAddr correctly, as we now have.

DevSup: failed processing sets VAL/RVAL and returns success

The processRrrr() routines of the ASYN Device support for input records always set the VAL (or RVAL) field and return success, even if the call to queueRequest() acquiring the value fails.

In our case, with a non-blocking ASYN driver for a PXIe board, the RVAL field of the mbbi record showing the board status is always set to 0 (= "OK"), even when there is no such board present and the device never connects.
We are pre-setting the database with 4 = "no board", but that gets overwritten with 0 ="OK" at every processing.

That doesn't seem right.
Shouldn't the process routine leave the value unchanged and return an
error when reading fails?

asynOctetSetOutputEos and asynOctetSetInputEos block IOC init and exit if no device connected

Hi,

Setting the OEOS (or IEOS) in the st.cmd file of the testConnect App using an IP that will never connect ends in the IOC blocked a considerable amount of time during init process and at exit.
I am running debian/buster and next configurations:
a) base 3.15.7 + asyn R4-37
b) base 3.15.7 + asyn master (0679e9f3f757...)
c) base 7.0.3.1(epics-base/epics-base@6feb1c788d...) + asyn master (0679e9f...)

The modified st.cmd file of the testConnect App looks as follows:

dbLoadDatabase("../../dbd/testConnect.dbd")
testConnect_registerRecordDeviceDriver(pdbbase)
# This is a bogus IP address that will never connect
drvAsynIPPortConfigure("IPPort", "164.54.160.220:20", 0, 0, 0);
asynSetTraceIOMask("IPPort",0,0xFF)
asynOctetSetOutputEos("IPPort",0,"\r")
iocInit()

No matter what config (a, b or c) i use, the output is very similar, the init takes 2min+10 seconds and the exit between 2 min and 16 min.

Next output belongs to test case c).

vagrant@buster:~/development/asyn/iocBoot/ioctestConnect$ ../../bin/linux-x86_64/testConnect st.cmd 
dbLoadDatabase("../../dbd/testConnect.dbd")
testConnect_registerRecordDeviceDriver(pdbbase)
# This is a bogus IP address that will never connect
drvAsynIPPortConfigure("IPPort", "164.54.160.220:20", 0, 0, 0);
asynSetTraceMask("IPPort",0,0xFF)
asynOctetSetOutputEos("IPPort",0,"\r")
2020/02/13 16:52:04.161 IPPort addr -1 queueRequest priority 3 not lockHolder
2020/02/13 16:54:14.301 asynManager connect queueCallback port:IPPort
2020/02/13 16:54:14.301 IPPort set Eos 1

iocInit()
Starting iocInit
############################################################################
## EPICS R7.0.3.2-DEV
## Rev. R7.0.3.1-78-g6feb1c788d261376d3b1
############################################################################
2020/02/13 16:54:14.302 IPPort -1 autoConnect
2020/02/13 16:54:14.302 Attempting to connect to 164.54.160.220:20  reason:0  fd:-1
iocRun: All initialization complete
epics> exit
2020/02/13 16:54:20.529 IPPort lockPort
2020/02/13 16:54:34.296 IPPort addr -1 queueRequest priority 3 not lockHolder
2020/02/13 16:56:25.374 IPPort -1 autoConnect could not connect: Can't connect to 164.54.160.220:20: Connection timed out
2020/02/13 16:56:25.374 asynManager connect queueCallback port:IPPort
2020/02/13 16:56:25.374 Attempting to connect to 164.54.160.220:20  reason:0  fd:-1
2020/02/13 16:58:36.438 IPPort -1 autoConnect
2020/02/13 16:58:36.438 Attempting to connect to 164.54.160.220:20  reason:0  fd:-1
2020/02/13 16:58:56.434 IPPort addr -1 queueRequest priority 3 not lockHolder
2020/02/13 17:00:47.510 asynManager connect queueCallback port:IPPort
2020/02/13 17:00:47.510 Attempting to connect to 164.54.160.220:20  reason:0  fd:-1
2020/02/13 17:02:58.584 IPPort -1 autoConnect
2020/02/13 17:02:58.584 IPPort unlockPort
2020/02/13 17:02:58.584 Attempting to connect to 164.54.160.220:20  reason:0  fd:-1
vagrant@buster:~/development/asyn/iocBoot/ioctestConnect$

Race condition with info tag asyn:READBACK

@mdavidsaver wrote:

Something doesn't look right in processAo() [1]. To correctly do a
bi-directional (setting and readback) record your process function has
to know whether it's being called with a new setting, or to pull an
updated readback. The fact that record processing (by design) lacks
this contextual information is why the standard device supports can't do
this.
Simply checking the internal readback FIFO allows a race which can
ignore a new value from CA. I'm fairly confident that this is what
Garth is seeing.
I don't immediately see where to fix this in the context of asyn.

and

In interruptCallbackOutput() [1], instead of scanOnce() use a custom
CALLBACK like:

static void ProcessCallback(CALLBACK *pcallback)
 {
     dbCommon *pRec;
     devPvt *pPvt;
 
     callbackGetUser(pRec, pcallback);
     if (!pRec) return;
     pPvt = (devPvt *)pRec->dpvt;
     dbScanLock(pRec);
     pPvt->updateReadback = 1;
     dbProcess(pRec);
     pPvt->updateReadback = 0;
     dbScanUnlock(pRec);
 }

Where 'updateReadback' is a new flag which can be tested in processAo()
to determine why processing has been started: a new setting value
(updateReadback=0) or a new readback (updateReadback=1).

asynPortDriver: data race on mutex in destructor

Every once in a while thread sanitizer reports a data race when running the unit tests for one of our drivers:

WARNING: ThreadSanitizer: data race (pid=7727)
  Write of size 1 at 0x7b0c0018f060 by main thread (mutexes: write M14):
    #0 pthread_mutex_destroy <null> (libtsan.so.0+0x2cd89)
    #1 epicsMutexOsdDestroy <null> (libCom.so.3.15.5+0x43108)
    #2 drvFGPDB::~drvFGPDB() /home/marko/work/RF/LLRF/support/drvfgpdb/drvFGPDBApp/src/drvFGPDB.cpp:147 (libdrvfgpdb.so.1.3+0x28715)
    #3 std::default_delete<drvFGPDB>::operator()(drvFGPDB*) const /usr/include/c++/8/bits/unique_ptr.h:81 (drvFGPDBTests+0x6c878)
    #4 std::unique_ptr<drvFGPDB, std::default_delete<drvFGPDB> >::~unique_ptr() /usr/include/c++/8/bits/unique_ptr.h:274 (drvFGPDBTests+0x6c878)
    #5 AnFGPDBDriver::~AnFGPDBDriver() /home/marko/work/RF/LLRF/support/drvfgpdb/drvFGPDBApp/test/drvFGPDBTestCommon.h:75 (drvFGPDBTests+0x6c878)
    #6 AnFGPDBDriverUsingIOSyncMock::~AnFGPDBDriverUsingIOSyncMock() /home/marko/work/RF/LLRF/support/drvfgpdb/drvFGPDBApp/test/drvFGPDBTests.cpp:69 (drvFGPDBTests+0x6d54e)
    #7 AnFGPDBDriverUsingIOSyncMock_newDriverInstanceContainsDriverParams_Test::~AnFGPDBDriverUsingIOSyncMock_newDriverInstanceContainsDriverParams_Test() /home/marko/work/RF/LLRF/support/drvfgpdb/drvFGPDBApp/test/drvFGPDBTests.cpp:108 (drvFGPDBTests+0x6d54e)
    #8 AnFGPDBDriverUsingIOSyncMock_newDriverInstanceContainsDriverParams_Test::~AnFGPDBDriverUsingIOSyncMock_newDriverInstanceContainsDriverParams_Test() /home/marko/work/RF/LLRF/support/drvfgpdb/drvFGPDBApp/test/drvFGPDBTests.cpp:108 (drvFGPDBTests+0x6d54e)
    #9 testing::Test::DeleteSelf_() /usr/lib/epics/include/gtest/gtest.h:453 (drvFGPDBTests+0xcfaec)
    #10 void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) /usr/src/googletest/googletest/src/gtest.cc:2402 (drvFGPDBTests+0xda048)
    #11 void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) /usr/src/googletest/googletest/src/gtest.cc:2438 (drvFGPDBTests+0xda048)
    #12 testing::TestInfo::Run() /usr/src/googletest/googletest/src/gtest.cc:2662 (drvFGPDBTests+0xcb9fb)
    #13 testing::TestCase::Run() /usr/src/googletest/googletest/src/gtest.cc:2776 (drvFGPDBTests+0xcbf47)
    #14 testing::internal::UnitTestImpl::RunAllTests() /usr/src/googletest/googletest/src/gtest.cc:4651 (drvFGPDBTests+0xcc875)
    #15 bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /usr/src/googletest/googletest/src/gtest.cc:2402 (drvFGPDBTests+0xccfd9)
    #16 bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /usr/src/googletest/googletest/src/gtest.cc:2438 (drvFGPDBTests+0xccfd9)
    #17 testing::UnitTest::Run() /usr/src/googletest/googletest/src/gtest.cc:4259 (drvFGPDBTests+0xccfd9)
    #18 RUN_ALL_TESTS() /usr/lib/epics/include/gtest/gtest.h:2233 (drvFGPDBTests+0x5a812)
    #19 main /usr/src/googletest/googlemock/src/gmock_main.cc:53 (drvFGPDBTests+0x5a812)

  Previous atomic read of size 1 at 0x7b0c0018f060 by thread T589 (mutexes: write M41512113047926320, write M40949163094503520):
    #0 pthread_mutex_unlock <null> (libtsan.so.0+0x402d9)
    #1 epicsMutexOsdUnlock <null> (libCom.so.3.15.5+0x43188)

  Location is heap block of size 48 at 0x7b0c0018f060 allocated by main thread:
    #0 calloc <null> (libtsan.so.0+0x2b653)
    #1 epicsMutexOsdCreate <null> (libCom.so.3.15.5+0x43074)
    #2 std::_MakeUniq<drvFGPDB>::__single_object std::make_unique<drvFGPDB, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::shared_ptr<asynOctetSyncIOInterface>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int const&, ResendMode, std::shared_ptr<logger>&>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::shared_ptr<asynOctetSyncIOInterface>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int const&, ResendMode&&, std::shared_ptr<logger>&) /usr/include/c++/8/bits/unique_ptr.h:831 (drvFGPDBTests+0x82833)
    #3 AnFGPDBDriverUsingIOSyncMock::AnFGPDBDriverUsingIOSyncMock() /home/marko/work/RF/LLRF/support/drvfgpdb/drvFGPDBApp/test/drvFGPDBTests.cpp:79 (drvFGPDBTests+0x82833)
    #4 AnFGPDBDriverUsingIOSyncMock_newDriverInstanceContainsDriverParams_Test::AnFGPDBDriverUsingIOSyncMock_newDriverInstanceContainsDriverParams_Test() /home/marko/work/RF/LLRF/support/drvfgpdb/drvFGPDBApp/test/drvFGPDBTests.cpp:108 (drvFGPDBTests+0x83744)
    #5 testing::internal::TestFactoryImpl<AnFGPDBDriverUsingIOSyncMock_newDriverInstanceContainsDriverParams_Test>::CreateTest() /usr/lib/epics/include/gtest/internal/gtest-internal.h:484 (drvFGPDBTests+0x83744)
    #6 testing::Test* testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::TestFactoryBase, testing::Test*>(testing::internal::TestFactoryBase*, testing::Test* (testing::internal::TestFactoryBase::*)(), char const*) /usr/src/googletest/googletest/src/gtest.cc:2402 (drvFGPDBTests+0xda288)
    #7 testing::Test* testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::TestFactoryBase, testing::Test*>(testing::internal::TestFactoryBase*, testing::Test* (testing::internal::TestFactoryBase::*)(), char const*) /usr/src/googletest/googletest/src/gtest.cc:2438 (drvFGPDBTests+0xda288)
    #8 testing::TestInfo::Run() /usr/src/googletest/googletest/src/gtest.cc:2647 (drvFGPDBTests+0xcb8f9)
    #9 testing::TestCase::Run() /usr/src/googletest/googletest/src/gtest.cc:2776 (drvFGPDBTests+0xcbf47)
    #10 testing::internal::UnitTestImpl::RunAllTests() /usr/src/googletest/googletest/src/gtest.cc:4651 (drvFGPDBTests+0xcc875)
    #11 bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /usr/src/googletest/googletest/src/gtest.cc:2402 (drvFGPDBTests+0xccfd9)
    #12 bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /usr/src/googletest/googletest/src/gtest.cc:2438 (drvFGPDBTests+0xccfd9)
    #13 testing::UnitTest::Run() /usr/src/googletest/googletest/src/gtest.cc:4259 (drvFGPDBTests+0xccfd9)
    #14 RUN_ALL_TESTS() /usr/lib/epics/include/gtest/gtest.h:2233 (drvFGPDBTests+0x5a812)
    #15 main /usr/src/googletest/googlemock/src/gmock_main.cc:53 (drvFGPDBTests+0x5a812)

  Mutex M14 (0x7b0c00000000) created at:
    #0 pthread_mutex_init <null> (libtsan.so.0+0x2cc3d)
    #1 epicsMutexOsdCreate <null> (libCom.so.3.15.5+0x430a8)

  Mutex M41512113047926320 is already destroyed.

  Mutex M40949163094503520 is already destroyed.

  Thread T589 'testDriver301' (tid=9790, running) created by main thread at:
    #0 pthread_create <null> (libtsan.so.0+0x2c37e)
    #1 epicsThreadCreate <null> (libCom.so.3.15.5+0x415c0)
    #2 std::_MakeUniq<drvFGPDB>::__single_object std::make_unique<drvFGPDB, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::shared_ptr<asynOctetSyncIOInterface>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int const&, ResendMode, std::shared_ptr<logger>&>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::shared_ptr<asynOctetSyncIOInterface>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int const&, ResendMode&&, std::shared_ptr<logger>&) /usr/include/c++/8/bits/unique_ptr.h:831 (drvFGPDBTests+0x82833)
    #3 AnFGPDBDriverUsingIOSyncMock::AnFGPDBDriverUsingIOSyncMock() /home/marko/work/RF/LLRF/support/drvfgpdb/drvFGPDBApp/test/drvFGPDBTests.cpp:79 (drvFGPDBTests+0x82833)
    #4 AnFGPDBDriverUsingIOSyncMock_newDriverInstanceContainsDriverParams_Test::AnFGPDBDriverUsingIOSyncMock_newDriverInstanceContainsDriverParams_Test() /home/marko/work/RF/LLRF/support/drvfgpdb/drvFGPDBApp/test/drvFGPDBTests.cpp:108 (drvFGPDBTests+0x83744)
    #5 testing::internal::TestFactoryImpl<AnFGPDBDriverUsingIOSyncMock_newDriverInstanceContainsDriverParams_Test>::CreateTest() /usr/lib/epics/include/gtest/internal/gtest-internal.h:484 (drvFGPDBTests+0x83744)
    #6 testing::Test* testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::TestFactoryBase, testing::Test*>(testing::internal::TestFactoryBase*, testing::Test* (testing::internal::TestFactoryBase::*)(), char const*) /usr/src/googletest/googletest/src/gtest.cc:2402 (drvFGPDBTests+0xda288)
    #7 testing::Test* testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::TestFactoryBase, testing::Test*>(testing::internal::TestFactoryBase*, testing::Test* (testing::internal::TestFactoryBase::*)(), char const*) /usr/src/googletest/googletest/src/gtest.cc:2438 (drvFGPDBTests+0xda288)
    #8 testing::TestInfo::Run() /usr/src/googletest/googletest/src/gtest.cc:2647 (drvFGPDBTests+0xcb8f9)
    #9 testing::TestCase::Run() /usr/src/googletest/googletest/src/gtest.cc:2776 (drvFGPDBTests+0xcbf47)
    #10 testing::internal::UnitTestImpl::RunAllTests() /usr/src/googletest/googletest/src/gtest.cc:4651 (drvFGPDBTests+0xcc875)
    #11 bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /usr/src/googletest/googletest/src/gtest.cc:2402 (drvFGPDBTests+0xccfd9)
    #12 bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /usr/src/googletest/googletest/src/gtest.cc:2438 (drvFGPDBTests+0xccfd9)
    #13 testing::UnitTest::Run() /usr/src/googletest/googletest/src/gtest.cc:4259 (drvFGPDBTests+0xccfd9)
    #14 RUN_ALL_TESTS() /usr/lib/epics/include/gtest/gtest.h:2233 (drvFGPDBTests+0x5a812)
    #15 main /usr/src/googletest/googlemock/src/gmock_main.cc:53 (drvFGPDBTests+0x5a812)

SUMMARY: ThreadSanitizer: data race (/usr/lib/x86_64-linux-gnu/libtsan.so.0+0x2cd89) in pthread_mutex_destroy
==================

~asynPortDriver() destroys the mutex but I don't see how we can be sure that the mutex is unlocked at this time. I'm wondering if the "asynPortDriverCallback" thread could still be holding the lock.

avoid static param table size?

Is there any interest in making redundant the paramTableSize argument of the asynPortDriver ctor? I have found the FIRST/LAST macro pattern used to calculate it is error prone. It seems to me that it would be a simple matter to change paramList::createParam() to resize its internal storage as new parameters are added.

I am not proposing to remove the paramTableSize argument as this would break too much, just to ignore it (or maybe warn). Only private data members should change, so existing code would continue to work as before.

Probable bug in drvVxi11.c

gcc warns me:

../../asyn/vxi11/drvVxi11.c:1714:8: warning: suggest parentheses around operand of ‘!’ or change ‘&’ to ‘&&’ or ‘!’ to ‘~’ [-Wparentheses]
     if(!flags & FLAG_NO_SRQ) pvxiPort->hasSRQ = TRUE;

and I guess it got it right here: if(!(flags & FLAG_NO_SRQ)) is most probably what the author wanted to write. (I have been bitten by the idiotic precedences for bitwise operators in the past...)

Moral of the story: too many warnings, so nobody notices the interesting ones.

4 parameter version of asynPortDriver::createParam should be private

The public interface to asynPortDriver::createParam() should be restricted to the 3 parameter version where the list index is not passed. That version calls the 4 parameter version with the "list" argument for each list (address) in the parameter library. The 4 parameter version should be private. Users who call it are likely to get in trouble because they don't realize they must keep the same parameters in all lists.

Remove paramTableSize argument from asynPortDriver constructor

I like the improvements @mdavidsaver made recently (see #35 and #36) and I would like to propose to take this to the next level by removing the unused paramTableSize argument completely. Of course this means that existing code needs to be converted at some point. I would like to suggest the following approach to make this process as painless as possible:

  1. Add a new constructor without the paramTableSize parameter. This one would be the recommended one for new applications. Mark the old one as deprecated (e.g. using GCC's __attribute__((deprecated))). Thus users that are building their code and have not converted their constructor calls, yet, would see a warning. Document this in the change log for the next release.
  2. At some point in the future remove the deprecated constructor.

New generic testing driver request

Ralph Lange has suggested a new generic driver that can be used for testing databases in the absence of a real device. He proposed:

We are using the standard ASYN EPICS support, so that a dummy emulation port driver might work as an emulation replacement for any asyn based driver, which would make the approach very efficient.

My question:

Can you explain how you see such a generic driver working. How would you set the new value of an input record? How would that message get to the driver?

Ralph's answer:

(In first iteration) I would make that dummy driver as simple as possible:

  • Writes do nothing.
  • Reads do not update the record, set the timestamp from the IOC clock (ignoring TSE) and set STAT/SEVR to SIMM//NO_ALARM.
  • I/O Intr records are processed every dd seconds (per port configuration option).

Aim is that everything is "green" for the user (while the special mode is still visible in the STAT of all records), the IOC console is reasonably quiet, and they can run tests (programmatically or manually) by writing to the VAL fields of input records.

My question:

What value does the driver pass to device support for I/O Intr records?

Ralph's answer:

The driver should not set a value. The user will write their test value from above (CA or DB), the driver must set STAT, the timestamp and trigger processing.

Problem with UDP broadcast sockets

There is a problem with UDP broadcast sockets created with drvAsynIPPortConfigure using the IP protocol string UDP*. Broadcast messages written to that socket work correctly. However, replies from clients to such broadcasts are rejected by the server that sent the broadcast. The problem is that the drvAsynIPPort calls connect(), send(), and recv() on such sockets. This is incorrect, it should not call connect(), and it should call sendto() and recvfrom() rather than send() and recv().

Pull request #10 fixes this problem by doing the following for all UDP ports, not just ports configured for broadcasts:

  • Does not call connect()
  • Calls sendto() rather than send()
  • Calls recvfrom() rather than recv()

Initial value of output records with devAsynOctet.c

devAsynOctet.c does not do a read from the driver in init_record to attempt to get the initial value of output records. The numeric device support (devAsynInt32.c, devAsynUInt32Digital.c, devAsynFloat64.c) does do this. This should probably be added to devAsynOctet.c.

CentOS 8 build fails due to removal of rpc

It seems that as of Fedora 28, rpc is being removed from the glibc-headers, which causes building asyn to fail:

https://fedoraproject.org/wiki/Changes/SunRPCRemoval

I am building asyn in a fresh docker container created with the following:


FROM centos:8
RUN dnf check-update || { rc=$?; [ "$rc" -eq 100 ] && exit 0; exit "$rc"; }
RUN dnf -y update
RUN dnf -y install epel-release
RUN dnf -y install git wget tar make cmake gcc gcc-c++ pkgconfig
RUN dnf -y install libtirpc-devel
RUN dnf -y --enablerepo=PowerTools install re2c libusb-devel rpcgen
RUN dnf -y install libxml2-devel pcre-devel libXext-devel
RUN dnf -y install libjpeg-devel readline-devel libusbx-devel
RUN dnf -y install boost-devel
RUN dnf -y install libraw1394
CMD ["/bin/bash"]

and

docker build -t cos8test .

and

docker run -it cos8test

And I get the following error when running make -sj in the asyn directory:

In file included from vxi11core_xdr.c:6:
vxi11core.h:9:10: fatal error: rpc/rpc.h: No such file or directory
 #include <rpc/rpc.h>
          ^~~~~~~~~~~
compilation terminated.
../../asyn/asynRecord/asynRecord.c: In function ‘getOptions’:
../../asyn/asynRecord/asynRecord.c:1863:66: warning: argument to ‘sizeof’ in ‘strncpy’ call is the same expression as the source; did you mean to use the size of the destination? [-Wsizeof-pointer-memaccess]
     strncpy(pasynRecPvt->old.hostinfo, pasynRec->hostinfo, sizeof(pasynRec->hostinfo));
                                                                  ^
../../asyn/asynRecord/asynRecord.c:1943:70: warning: argument to ‘sizeof’ in ‘strncpy’ call is the same expression as the source; did you mean to use the size of the destination? [-Wsizeof-pointer-memaccess]
         strncpy(pasynRecPvt->old.hostinfo, pasynRec->hostinfo, sizeof(pasynRec->hostinfo));
                                                                      ^
make[3]: *** [/epics/base/configure/RULES_BUILD:234: vxi11core_xdr.o] Error 1
make[3]: *** Waiting for unfinished jobs....
In file included from ../../asyn/vxi11/drvVxi11.c:41:
../../asyn/vxi11/osiRpc.h:12:10: fatal error: rpc/rpc.h: No such file or directory
 #include <rpc/rpc.h>
          ^~~~~~~~~~~
compilation terminated.
make[3]: *** [/epics/base/configure/RULES_BUILD:234: drvVxi11.o] Error 1

From what I could gather, it seems that rpc dependent projects should use tirpc instead, though I haven't looked too much into specifics yet. The same Dockerfile when using CentOS7 as a base does not show these issues.

Building-problems

Trying to "make" asyn on my Beagle Board Black running Debian Stretch, I encounter the following error message, maybe someone has a solution to this:

debian@beaglebone:~/Apps/epics/modules/asyn$ make
Can't open perl script ".": Is a directory
make -C ./configure install
make[1]: Entering directory '/home/debian/Apps/epics/modules/asyn/configure'
Can't open perl script "..": Is a directory
perl -CSD /home/debian/Apps/epics/modules/asyn/configure/~ /Apps/epics/base/bin/linux-arm/makeMakefile.pl O.linux-arm ../..
Can't open perl script "/home/debian/Apps/epics/modules/asyn/configure/~ /Apps/epics/base/bin/linux-arm/makeMakefile.pl": No such file or directory
/home/debian/Apps/epics/base/configure/RULES_ARCHS:67: recipe for target 'O.linux-arm' failed
make[1]: *** [O.linux-arm] Error 2
make[1]: Leaving directory '/home/debian/Apps/epics/modules/asyn/configure'
/home/debian/Apps/epics/base/configure/RULES_DIRS:84: recipe for target 'configure.install' failed
make: *** [configure.install] Error 2

Can't destroy and reconstruct asynPortDriver

When I construct an asynPortDriver with a certain portName, destroy it and then try to re-create it with the same portName (which I think is legitimate) the constructor fails with the following error:

asynPortDriver:asynPortDriver ERROR: Can't register interfaces: Can't register common.

It seems like ~asynPortDriver() is not cleaning up properly.

Enhance support for asynInt32Average and asynFloat64Average device support

Currently the asynInt32Average and asynFloat64Average device support can only be used with Periodically scanned records or Event scanned records. In some use cases it is desirable to scan faster than Periodic scanning allows. It would be nice to be able to add the following feature:

  • Specify a desired averaging time to the device support
  • This could be implemented by adding an info tag asyn:AVERAGING_TIME_LINK. This tag is used to specify a link from which the averaging time is read.
  • Each time the interrupt callback occurs this link is read to get the averaging time.
  • If the averaging time is greater than 0 and the time since the record was last processed is greater than or equal to the averaging time then process the record.
  • Question: how does the SCAN field interact with this mode?
    • We can't really use SCAN=I/O Intr for this because then the interrupt callback will be cancelled when the record is taken out of I/O Intr scanning, which is not what we want.
    • Should it only occur when SCAN=PASSIVE? That is not really what the user expects. If we use a PERIODIC scanning then the record will also process at the specified scan rate, which is not what is expected either.
    • Perhaps use SCAN=I/O Intr but handle it differently for AiAverage device support, don't cancel the interrupt when record is taken out of I/O Intr scanning?

Compilation problem on master 31df0435853d3125bab5

Trying to compile asyn on this commit
commit 31df043 (HEAD, origin/master, origin/HEAD)
Merge: 1371670 158ca6b
Author: Mark Rivers [email protected]
Date: Tue Jun 16 10:49:36 2020 -0500
Merge pull request #114 from krisztianloki/master
Take connect timestamp after connection attempt
Gives the following error message, please see below.

(Probably the commit did not introduce the problem.
I hope, that it is easy to fix by an expert

[snip]
/usr/bin/ar -rc libtestConnectSupport.a testConnect.o
/usr/bin/ranlib libtestConnectSupport.a
c++ -o libtestConnectSupport.dylib -dynamiclib -flat_namespace -undefined dynamic_lookup -install_name /home/epics/modules/asyn/lib/darwin-x86/libtestConnectSupport.dylib -L/home/epics/modules/asyn/lib/darwin-x86 -L/home/epics/base/lib/darwin-x86 -arch x86_64 testConnect.o -lasyn -ldbRecStd -ldbCore -lca -lCom -lreadline -lm
Installing shared library ../../../lib/darwin-x86/libtestConnectSupport.dylib
Installing library ../../../lib/darwin-x86/libtestConnectSupport.a
c++ -DUNIX -Ddarwin -O3 -g -Wall -arch x86_64 -fno-common -I. -I../O.Common -I. -I. -I.. -I../../../include/compiler/clang -I../../../include/os/Darwin -I../../../include -I/home/epics/base/include/compiler/clang -I/home/epics/base/include/os/Darwin -I/home/epics/base/include -I/home/epics/base/../modules/asyn/include -I/home/epics/base/../modules/calc/include -c testConnect_registerRecordDeviceDriver.cpp
c++ -DUNIX -Ddarwin -O3 -g -Wall -arch x86_64 -fno-common -I. -I../O.Common -I. -I. -I.. -I../../../include/compiler/clang -I../../../include/os/Darwin -I../../../include -I/home/epics/base/include/compiler/clang -I/home/epics/base/include/os/Darwin -I/home/epics/base/include -I/home/epics/base/../modules/asyn/include -I/home/epics/base/../modules/calc/include -c ../testConnectMain.cpp
c++ -o testConnect -L/home/epics/modules/asyn/lib/darwin-x86 -L/home/epics/base/lib/darwin-x86 -arch x86_64 testConnect_registerRecordDeviceDriver.o testConnectMain.o -ltestConnectSupport -lasyn -ldbRecStd -ldbCore -lca -lCom
Installing created executable ../../../bin/darwin-x86/testConnect
/Library/Developer/CommandLineTools/usr/bin/make -C ./testEpicsApp install
/Library/Developer/CommandLineTools/usr/bin/make -C ./src install
perl -CSD /home/epics/base/bin/darwin-x86/makeMakefile.pl O.darwin-x86 ../../..
mkdir -p O.Common
/Library/Developer/CommandLineTools/usr/bin/make -C O.darwin-x86 -f ../Makefile TOP=../../..
T_A=darwin-x86 install
perl -CSD /home/epics/base/bin/darwin-x86/mkmf.pl -m uint32DigitalDriver.d -I. -I../O.Common -I. -I. -I.. -I../../../include/compiler/clang -I../../../include/os/Darwin -I../../../include -I/home/epics/base/include/compiler/clang -I/home/epics/base/include/os/Darwin -I/home/epics/base/include -I../../../include -I/home/epics/base/../modules/calc/include uint32DigitalDriver.o ../uint32DigitalDriver.c
perl -CSD /home/epics/base/bin/darwin-x86/mkmf.pl -m int32Driver.d -I. -I../O.Common -I. -I. -I.. -I../../../include/compiler/clang -I../../../include/os/Darwin -I../../../include -I/home/epics/base/include/compiler/clang -I/home/epics/base/include/os/Darwin -I/home/epics/base/include -I../../../include -I/home/epics/base/../modules/calc/include int32Driver.o ../int32Driver.c
perl -CSD /home/epics/base/bin/darwin-x86/mkmf.pl -m testEpicsMain.d -I. -I../O.Common -I. -I. -I.. -I../../../include/compiler/clang -I../../../include/os/Darwin -I../../../include -I/home/epics/base/include/compiler/clang -I/home/epics/base/include/os/Darwin -I/home/epics/base/include -I../../../include -I/home/epics/base/../modules/calc/include testEpicsMain.o ../testEpicsMain.cpp
Creating dbd file testEpics.dbd
perl -CSD /home/epics/base/bin/darwin-x86/dbdExpand.pl -I. -I.. -I../O.Common -I../../../dbd -I/home/epics/base/dbd -I../../../dbd -I/home/epics/base/../modules/calc/dbd -o testEpics.dbd testEpicsCommon.dbd calcSupport.dbd asynCalc.dbd
dbdExpand.pl: Can't find file 'asynCalc.dbd'
while reading 'asynCalc.dbd' to create 'testEpics.dbd'
Your Makefile may need this dependency rule:
$(COMMON_DIR)/testEpics.dbd: $(COMMON_DIR)/asynCalc.dbd
dbdExpand.pl: Exiting due to errors
make[3]: *** [../O.Common/testEpics.dbd] Error 2
make[2]: *** [install.darwin-x86] Error 2
make[1]: *** [src.install] Error 2
make: *** [testEpicsApp.install] Error 2

Implement asynOption in drvAsynIPPort

I want to implement the asynOption interface for drvAsynIPPort. Right now there are 2 options I want to implement:

"closeOnReadTimeout" "Y" or "N"
In R4-27 this was added via the userFlags in the configuration command. I don’t like that implementation because this is something one might to turn off and on at runtime.

"hostInfo" "New IP host info string"
This will allow one to connect the same asyn port to a different IP address at runtime. SLAC has requested this for when a device fails and they want to swap in a new one without rebooting.

This is pretty straightforward except if the hostInfo includes COM. In that case the driver uses an interposeInterface (asynInterposeCom.c) to implement the asynOption interface. I think this is OK, except that when it finds an unknown key it prints an error message. I think all I need to do is to change that code so if it finds an unknown key it calls the underlying drvAsynIPPort->getOption or setOption. Does that seem correct?

Add support for new 64-bit integer types

EPICS 3.16.1 and 7.0 introduced support for epicsInt64 and epicsUInt64 types (Channel Access converts them to DBF_DOUBLE).

We need to add the following to asyn:

  • New asynInt64 and asynUInt64 interfaces
  • New device support for 3 records:
    • epicsInt64in
    • epicsInt64out
    • waveform
  • Support in asynPortDriver

Question: What's the best way to handle building on older versions of base that don't have 64-bit support? Perhaps just use the epicsInt32 and epicsUInt32 types if DBR_INT64 is not defined?

Invalid baudrate error handling

When asyn record posts this error message;
"rls2:asyn_9: Error setting option, SIO_BAUD_SET failed: S_errno_EINVAL"
it does not revert the baudrate in either the BAUD or LBAUD fields to a valid value.
Investigate if asyn record can revert the BAUD and/or LBAUD field(s) to the previous, valid value after detecting this error (and also learn how to use github).

Appveyor build not working

I am trying to add an appveyor builds to the .ci builds for asyn.

When I push to asyn it is triggering a build on appveyor, which is good. However, it is failing with this error that I don't understand. I did edit the .appveyor.yml file, but only to remove some old versions of Visual Studio, I don't think that would have caused this problem?

Error parsing appveyor.yml: (Line: 106, Col: 1, Idx: 2830) - (Line: 106, Col: 8, Idx: 2837): Duplicate key

Unable to compile on Fedora 33 due to

I am getting the following error when building from source (version R4.41).

/usr/bin/gcc  -D_GNU_SOURCE -D_DEFAULT_SOURCE               -DUSE_TYPED_RSET -DUSE_TYPED_DSET -DUSE_TYPED_DRVET -DBUILDING_asyn_API  -D_X86_64_ -DUNIX  -Dlinux      -O3 -g   -Wall -Werror-implicit-function-declaration    -DUSE_TYPED_RSET -DUSE_TYPED_DSET -DUSE_TYPED_DRVET -DHAVE_LSREC -DHAVE_DEVINT64  -mtune=generic     -m64 -fPIC -I. -I../O.Common -I. -I. -I../../asyn/drvAsynSerial/os/Linux -I../../asyn/drvAsynSerial/os/default -I.. -I../../asyn/asynDriver -I../../asyn/asynGpib -I../../asyn/drvAsynSerial -I../../asyn/interfaces -I../../asyn/miscellaneous -I../../asyn/asynPortDriver/exceptions -I../../asyn/asynPortDriver -I../../asyn/asynPortClient -I../../asyn/devEpics -I../../asyn/asynRecord -I../../asyn/vxi11 -I../../asyn/gsIP488 -I../../asyn/ni1014 -I../../asyn/devGpib -I../../include/compiler/gcc -I../../include/os/Linux -I../../include   -I/home/user/Dev/ipac/include   -I/home/user/Dev/epics-sequencer/seq-2.2.6/include -I/home/user/Dev/epics-base/include/compiler/gcc -I/home/user/Dev/epics-base/include/os/Linux -I/home/user/Dev/epics-base/include -I/home/user/Dev/epics-base/include/compiler/gcc -I/home/user/Dev/epics-base/include/os/Linux -I/home/user/Dev/epics-base/include -I/home/user/Dev/epics-base/include/compiler/gcc -I/home/user/Dev/epics-base/include/os/Linux -I/home/user/Dev/epics-base/include -I/home/user/Dev/epics-base/include/compiler/gcc -I/home/user/Dev/epics-base/include/os/Linux -I/home/user/Dev/epics-base/include -I/home/user/Dev/epics-base/include/compiler/gcc -I/home/user/Dev/epics-base/include/os/Linux -I/home/user/Dev/epics-base/include -I/home/user/Dev/epics-base/include/compiler/gcc -I/home/user/Dev/epics-base/include/os/Linux -I/home/user/Dev/epics-base/include -I/home/user/Dev/epics-base/include/compiler/gcc -I/home/user/Dev/epics-base/include/os/Linux -I/home/user/Dev/epics-base/include -I/home/user/Dev/epics-base/include/compiler/gcc -I/home/user/Dev/epics-base/include/os/Linux -I/home/user/Dev/epics-base/include        -c ../../asyn/asynRecord/drvAsyn.c

make[2]: *** No rule to make target 'vxi11core_xdr.c', needed by 'vxi11core_xdr.o'.  Stop.
make[2]: Leaving directory '/home/user/Dev/asyn/asyn/O.linux-x86_64'
make[1]: *** [/home/user/Dev/epics-base/configure/RULES_ARCHS:58: install.linux-x86_64] Error 2
make[1]: Leaving directory '/home/user/Dev/asyn/asyn'
make: *** [/home/user/Dev/epics-base/configure/RULES_DIRS:85: asyn.install] Error 2
# in the async/Makefile
SRC_DIRS += $(ASYN)/vxi11
ifeq ($(OS_CLASS), WIN32)
  asyn_SRCS += drvVxi11Win32.c
else
  asyn_SRCS += vxi11core_xdr.c
  asyn_SRCS += drvVxi11.c
endif

Changing this to the correct path for vxi11core_xdr.c I get a different error to do with rpc/rpc.h:

# in the async/Makefile
SRC_DIRS += $(ASYN)/vxi11
ifeq ($(OS_CLASS), WIN32)
  asyn_SRCS += drvVxi11Win32.c
else
  # this line changed
  asyn_SRCS += rpc/vxi11core_xdr.c
  asyn_SRCS += drvVxi11.c
endif

The error is:

In file included from ../../asyn/vxi11/rpc/vxi11core_xdr.c:6:
../../asyn/vxi11/rpc/vxi11core.h:9:10: fatal error: rpc/rpc.h: No such file or directory
    9 | #include <rpc/rpc.h>
      |          ^~~~~~~~~~~
compilation terminated.

make[2]: *** [/home/user/Dev/epics-base/configure/RULES_BUILD:255: rpc/vxi11core_xdr.o] Error 1
make[2]: Leaving directory '/home/user/Dev/asyn/asyn/O.linux-x86_64'
make[1]: *** [/home/user/Dev/epics-base/configure/RULES_ARCHS:58: install.linux-x86_64] Error 2
make[1]: Leaving directory '/home/user/Dev/asyn/asyn'
make: *** [/home/user/Dev/epics-base/configure/RULES_DIRS:85: asyn.install] Error 2

Any ideas on what is going on here? I am building from source following the steps stated here: https://epics-modules.github.io/master/asyn/

On Fedora 33 using:

  • gcc 10.2.1
  • perl 5.32.1
  • epics-base 7.0.4
  • sequencer 2.2.6
  • ipac 2.16

unittests fail for Windows dynamic builds

Running the CI in appveyor exposed the fact that the unittests fail for dynamic builds on Windows for all compiler versions (VS 2019/2017/2015/2013, mingw).

I have reproduced the problem locally.

The problem is that the int32cb callback function is not being called by asynPortClient when it should be.

This is the output on windows-x64-static:

ok 43 - client.read(&val)==asynParamUndefined
# int32cb() called with 55
ok 44 - client->read(&other)==asynSuccess
ok 45 - data==other
# int32cb() done
ok 46 - portA->callParamCallbacks()==asynSuccess
ok 47 - lastint32==55
ok 48 - cbcount==1
# int32cb() called with 43
ok 49 - client->read(&other)==asynSuccess
ok 50 - data==other
# int32cb() done
ok 51 - client.read(&val)==asynSuccess
ok 52 - val==43
ok 53 - lastint32==43
ok 54 - cbcount==2

    Results
    =======
       Tests: 54
      Passed:  54 = 100.00%

Note the diagnostic messages from int32cb, and all tests passed.

This is the output on windows-x64:

ok 43 - client.read(&val)==asynParamUndefined
ok 44 - portA->callParamCallbacks()==asynSuccess
not ok 45 - lastint32==55
not ok 46 - cbcount==1
ok 47 - client.read(&val)==asynSuccess
ok 48 - val==43
not ok 49 - lastint32==43
not ok 50 - cbcount==2

Planned 54 tests but only ran 50

    Results
    =======
       Tests: 50
      Passed:  46 = 92.00%
      Failed:   4 =  8.00%

Note that there are no diagnostic messages from int32cb and it is only executing 50 tests rather than 54. This is because 2 of the tests are in the callback function, and that should be called twice.

I am not sure what is wrong. I tried changing the code to add epicsShareFunc to the int32cb, but that did not help.

diff --git a/asyn/asynPortDriver/unittest/asynPortDriverTest.cpp b/asyn/asynPortDriver/unittest/asynPortDriverTest.cpp
index a26d79b..64b702d 100644
--- a/asyn/asynPortDriver/unittest/asynPortDriverTest.cpp
+++ b/asyn/asynPortDriver/unittest/asynPortDriverTest.cpp
@@ -16,6 +16,8 @@
 #include <asynPortDriver.h>
 #include <asynPortClient.h>

+#include <epicsExport.h>
+
 // fake out asynPortDriver callback dispatching
 extern "C" int interruptAccept;
 int interruptAccept=1;
@@ -32,7 +34,7 @@ typedef epicsGuard<asynPortDriver> Guard;
 epicsInt32 lastint32;
 size_t cbcount;

-void int32cb(void *userPvt, asynUser *pasynUser,
+epicsShareFunc void int32cb(void *userPvt, asynUser *pasynUser,
                                        epicsInt32 data)
 {
     testDiag("int32cb() called with %d", (int)data);

@mdavidsaver you wrote these tests. Any idea why the int32cb function is not being called, but there are no error messages?

asynPortDriver segfault on duplicate port name

FYI a duplicate port name fails in a messy way. In this case I copy+pasted NDStatsConfigure() twice with the same port name.

NDStatsConfigure("STATS1", 20, 0, "ARV1", 0, 0, 0)
asynCommon:registerDriver STATS1 already registered
asynPortDriver:asynPortDriver: ERROR: Can't register port
interface asynCommon already registered for port STATS1
2016/12/08 18:55:53.729 asynPortDriver:asynPortDriver ERROR: Can't register interfaces: Can't register common.

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff721ef9c in asynPortDriver::createParam (this=0x8eff20, list=0, 
    name=0x7ffff77b3157 "PORT_NAME_SELF", type=asynParamOctet, index=0x8f0174)
    at ../../asyn/asynPortDriver/asynPortDriver.cpp:759
759     ../../asyn/asynPortDriver/asynPortDriver.cpp: No such file or directory.
(gdb) p this
$1 = (asynPortDriver * const) 0x8eff20
(gdb) p *this
$2 = {_vptr.asynPortDriver = 0x7ffff79b73d0 <vtable for asynNDArrayDriver+16>, 
  portName = 0x8efd80 "STATS1", maxAddr = 1, pasynUserSelf = 0x8f0598, 
  asynStdInterfaces = {common = {interfaceType = 0x7ffff7258618 "asynCommon", 
      pinterface = 0x7ffff74707d0 <ifaceCommon>, drvPvt = 0x8eff20}, 
    drvUser = {interfaceType = 0x0, 
      pinterface = 0x7ffff7470a20 <ifaceDrvUser>, drvPvt = 0x0}, option = {
      interfaceType = 0x0, pinterface = 0x0, drvPvt = 0x0}, octet = {
      interfaceType = 0x0, pinterface = 0x7ffff74708c0 <ifaceOctet>, 
      drvPvt = 0x0}, octetProcessEosIn = 0, octetProcessEosOut = 0, 
    octetInterruptProcess = 0, octetCanInterrupt = 1, octetInterruptPvt = 0x0, 
    uInt32Digital = {interfaceType = 0x0, pinterface = 0x0, drvPvt = 0x0}, 
    uInt32DigitalCanInterrupt = 0, uInt32DigitalInterruptPvt = 0x0, int32 = {
      interfaceType = 0x0, pinterface = 0x7ffff7470800 <ifaceInt32>, 
      drvPvt = 0x0}, int32CanInterrupt = 1, int32InterruptPvt = 0x0, 
    float64 = {interfaceType = 0x0, 
      pinterface = 0x7ffff7470880 <ifaceFloat64>, drvPvt = 0x0}, 
    float64CanInterrupt = 1, float64InterruptPvt = 0x0, int8Array = {
      interfaceType = 0x0, pinterface = 0x0, drvPvt = 0x0}, 
    int8ArrayCanInterrupt = 0, int8ArrayInterruptPvt = 0x0, int16Array = {
      interfaceType = 0x0, pinterface = 0x0, drvPvt = 0x0}, 
    int16ArrayCanInterrupt = 0, int16ArrayInterruptPvt = 0x0, int32Array = {
      interfaceType = 0x0, pinterface = 0x7ffff7470960 <ifaceInt32Array>, 
      drvPvt = 0x0}, int32ArrayCanInterrupt = 1, int32ArrayInterruptPvt = 0x0, 
    float32Array = {interfaceType = 0x0, pinterface = 0x0, drvPvt = 0x0}, 
    float32ArrayCanInterrupt = 0, float32ArrayInterruptPvt = 0x0, 
    float64Array = {interfaceType = 0x0, 
      pinterface = 0x7ffff74709a0 <ifaceFloat64Array>, drvPvt = 0x0}, 
    float64ArrayCanInterrupt = 1, float64ArrayInterruptPvt = 0x0,
    genericPointer = {interfaceType = 0x0, 
      pinterface = 0x7ffff74709c0 <ifaceGenericPointer>, drvPvt = 0x0}, 
    genericPointerCanInterrupt = 1, genericPointerInterruptPvt = 0x0, Enum = {
      interfaceType = 0x0, pinterface = 0x0, drvPvt = 0x0}, 
    enumCanInterrupt = 0, enumInterruptPvt = 0x0}, params = 0x0, mutexId = 
    0x8efc60, inputEosOctet = 0x8efc20 "", inputEosLenOctet = 0, 
  outputEosOctet = 0x8efc40 "", outputEosLenOctet = 0}
(gdb) info args
this = 0x8eff20
list = 0
name = 0x7ffff77b3157 "PORT_NAME_SELF"
type = asynParamOctet
index = 0x8f0174
(gdb) bt
#0  0x00007ffff721ef9c in asynPortDriver::createParam (this=0x8eff20, list=0, 
    name=0x7ffff77b3157 "PORT_NAME_SELF", type=asynParamOctet, index=0x8f0174)
    at ../../asyn/asynPortDriver/asynPortDriver.cpp:759
#1  0x00007ffff721ef42 in asynPortDriver::createParam (this=0x8eff20, 
    name=0x7ffff77b3157 "PORT_NAME_SELF", type=asynParamOctet, index=0x8f0174)
    at ../../asyn/asynPortDriver/asynPortDriver.cpp:742
#2  0x00007ffff77aeabd in asynNDArrayDriver::asynNDArrayDriver (this=0x8eff20, 
    portName=0x8efef1 "STATS1", maxAddr=1, numParams=67, maxBuffers=0, 
    maxMemory=0, interfaceMask=6762, interruptMask=6760, asynFlags=0, 
    autoConnect=1, priority=0, stackSize=0) at ../asynNDArrayDriver.cpp:617
#3  0x00007ffff74d6b8b in NDPluginDriver::NDPluginDriver (this=0x8eff20, 
    portName=0x8efef1 "STATS1", queueSize=20, blockingCallbacks=0, 
    NDArrayPort=0x8efefd "ARV1", NDArrayAddr=0, maxAddr=1, numParams=58, 
    maxBuffers=0, maxMemory=0, interfaceMask=6656, interruptMask=6656, 
    asynFlags=0, autoConnect=1, priority=0, stackSize=0)
    at ../NDPluginDriver.cpp:493
#4  0x00007ffff7501c26 in NDPluginStats::NDPluginStats (this=0x8eff20, 
    portName=0x8efef1 "STATS1", queueSize=20, blockingCallbacks=0, 
    NDArrayPort=0x8efefd "ARV1", NDArrayAddr=0, maxBuffers=0, maxMemory=0, 
    priority=0, stackSize=0) at ../NDPluginStats.cpp:749
#5  0x00007ffff7502663 in NDStatsConfigure (portName=0x8efef1 "STATS1", 
    queueSize=20, blockingCallbacks=0, NDArrayPort=0x8efefd "ARV1", 
    NDArrayAddr=0, maxBuffers=0, maxMemory=0, priority=0, stackSize=0)
    at ../NDPluginStats.cpp:848
#6  0x00007ffff7502727 in initCallFunc (args=0x63b0b0)
    at ../NDPluginStats.cpp:876
#7  0x00007ffff66526a4 in iocshBody (pathname=0x7fffffffe849 "st.cmd", 
    commandLine=0x0, macros=0x0) at ../../../src/libCom/iocsh/iocsh.cpp:813
#8  0x00007ffff6652a60 in iocshLoad (pathname=0x7fffffffe849 "st.cmd", 
    macros=0x0) at ../../../src/libCom/iocsh/iocsh.cpp:895
#9  0x00007ffff6652a00 in iocsh (pathname=0x7fffffffe849 "st.cmd")
    at ../../../src/libCom/iocsh/iocsh.cpp:881
#10 0x0000000000406f18 in main (argc=2, argv=0x7fffffffe5f8)
    at ../testMain.cpp:17

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.