First a question:
Does anyone know of any code which manually post monitors for the VAL
field of a waveformRecord (or aai and aao)? Something like
"db_post_events(prec, prec->bptr, DBE_VALUE)". Such code would be
broken (become a no-op) by the change I am proposing for Base 3.15.
https://code.launchpad.net/~epics-core/epics-base/array-opt
This change would make it possible to safely replace the array buffer
pointer for the waveform, aai, and aao recordtypes. This is effectively
generalizing the stratagy used by the sscanRecord (and perhaps others)
and making it easier to use.
http://www.aps.anl.gov/epics/tech-talk/2013/msg00037.php
Please consider the following as a draft of the documentation describing
how to use this feature. Suggestions or additions and clarifications
are welcomed.
The basic rules are:
1) BPTR and the memory it is currently pointing to can only be accessed
while the record is locked.
2) BPTR must always point to a piece of memory large enough to
accommodate the maximum number of elements.
#1 means that it is only save to read, write, or de-reference the BPTR
field from a device support function, or after manually calling
dbScanLock(). #2 means that BPTR can never be set to NULL, and when
replacing BPTR, the replacement must be allocated large enough for the
worst case.
It is feasible to use this feature to avoid one array copy when moving
array data into, or out of one of these recordtypes.
Note: The following examples make inefficient use of malloc() and
free(). This is done to make clear where new memory appears. In
reality a free list should be used. Also, since free() is used there no
need for special handling of the initial buffer allocated by the
waveformRecord, in other cases care must be taken to cleanup and replace
it in init_record().
For example, a waveformRecord device support for a digitizer might look like
struct devicePvt {
epicsMutexId lock;
void *nextBuffer;
IOSCANPVT scan;
epicsUInt32 maxbytes, numbytes;
};
We create a structure to communicate between a worker thread and our
device support function. This structure must be guarded with a Mutex.
We assume that member 'maxbytes' is initialized to
"dbValueSize(prec->ftvl)*prec->nelm" in the init_record() device support
function, and never changed. If is also assumed that init_record()
checks that maxbytes >= 1024.
static void internalThread(void *raw) {
struct devicePvt *pvt=raw;
while(1) {
int wake = 1;
void * temp=mallocMustSucceed(pvt->maxbytes);
fillFromDevice(temp); /* copy data from device to temp buffer (eg.
DMA) */
epicsMutexMustLock(pvt->lock);
if(pvt->nextBuffer) {
free(pvt->nextBuffer);
wake = 0;
}
pvt->nextBuffer = temp;
pvt->numbytes = 1024;
epicsMutexUnlock(pvt->lock);
if(wake)
scanIoRequest(pvt->scan);
}
The worker thread fills an array with a fictional function named
fillFromDevice() which will write all 1024 bytes of the temporary
buffer. The protocol for communicating with device support to place the
temporary pointer into the shared structure, after checking to see if
the previous pointer was removed. If the device support did consume the
previous value, then a 'I/O Intr' scan request is queued to wake it up
again.
static void read_wf(waveformRecord *prec) {
struct devicePvt *pvt=prec->dpvt;
epicsUInt32 nbytes;
void *buf;
epicsMutexMustLock(pvt->lock);
buf = pvt->nextBuffer;
pvt->nextBuffer = NULL;
nbytes = pvt->numbytes;
epicsMutexUnlock(pvt->lock);
if(buf) {
if(prec->bptr)
free(prec->bptr);
prec->bptr = buf;
prec->nord = nbytes/dbValueSize(prec->ftvl);
}
return 0;
}
All that the device support does is to cleanup, and replace, the pointer
in BPTR. Remember to write NORD each time to handle changing array
size, and to protect against someone issuing a caput (waveform VAL field
is writable).
It is also possible to move data in the other direction, from a record
into a driver.
struct devicePvt {
epicsMutexId lock;
void *nextBuffer;
epicsEventId wakeup;
epicsUInt32 numbytes;
SOCKET sd;
};
Here we have a similar shared structure with the IOSCAN replaced with an
Event which will wakeup up the worker. Here we imagine that our device
is a socket.
static void write_wf(waveformRecord *prec) {
struct devicePvt *pvt=prec->dpvt;
void *temp = mallocMustSucceed(dbValueSize(prec->ftvl)*prec->nelm);
epicsMutexMustLock(pvt->lock);
if(!pvt->nextBuffer) {
pvt->nextBuffer = prec->bptr;
prec->bptr = temp;
temp = NULL;
pvt->numbytes = prec->nord*dbValueSize(prec->ftvl);
prec->nord = 0;
} else
free(temp);
epicsMutexUnlock(pvt->lock);
epicsEventMustTrigger(pvt->wakeup);
return 0;
}
If the previous buffer has been consumed, the device support write
function will move BPTR to the shared structure and replace it with a
pointer to newly allocated (uninitialized) memory. The fact that BPTR
now points to uninitialized memory is acceptable because NORD is set to 0.
static void internalThread(void *raw) {
struct devicePvt *pvt=raw;
while(1) {
int ret;
void *temp;
epicsUInt32 count;
epicsEventMustWait(pvt->wakeup);
epicsMutexMustLock(pvt->lock);
temp = pvt->nextBuffer;
pvt->nextBuffer = NULL;
count = pvt->numbytes;
epicsMutexUnlock(pvt->lock);
if(!temp)
continue;
ret = send(pvt->sd, temp, count);
assert(ret==count);
free(temp);
}
}
The worker takes a pointer from the shared structure and passes it to
the send() syscall.
- Navigate by Date:
- Prev:
RTEMS driver for CAEN V6533N Hu, Yong
- Next:
copying archived data from 32-bit to 64-bit machine Pierrick Hanlet
- Index:
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
<2013>
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
- Navigate by Thread:
- Prev:
RTEMS driver for CAEN V6533N Hu, Yong
- Next:
copying archived data from 32-bit to 64-bit machine Pierrick Hanlet
- Index:
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
<2013>
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
|