Files
pcas/src/ca/CASG.cpp
2002-04-25 18:13:42 +00:00

400 lines
9.7 KiB
C++

/*
* $Id$
* Author: Jeffrey O. Hill
* hill@luke.lanl.gov
* (505) 665 1831
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
*/
#define epicsAssertAuthor "Jeff Hill johill@lanl.gov"
#define epicsExportSharedSymbols
#include "iocinf.h"
#include "syncGroup.h"
#include "oldAccess.h"
#include "autoPtrDestroy.h"
#include "cac.h"
epicsSingleton < tsFreeList < struct CASG, 128 > > CASG::pFreeList;
CASG::CASG ( oldCAC &cacIn ) :
client ( cacIn ), magic ( CASG_MAGIC )
{
client.installCASG ( *this );
}
void CASG::destroy ()
{
delete this;
}
CASG::~CASG ()
{
if ( this->verify () ) {
this->reset ();
this->client.uninstallCASG ( *this );
this->magic = 0;
}
else {
this->printf ("cac: attempt to destroy invalid sync group ignored\n");
}
}
bool CASG::verify () const
{
return ( this->magic == CASG_MAGIC );
}
/*
* CASG::block ()
*/
int CASG::block ( double timeout )
{
epicsTime cur_time;
epicsTime beg_time;
double delay;
double remaining;
int status;
// prevent recursion nightmares by disabling blocking
// for IO from within a CA callback.
if ( epicsThreadPrivateGet ( caClientCallbackThreadId ) ) {
return ECA_EVDISALLOW;
}
if ( timeout < 0.0 ) {
return ECA_TIMEOUT;
}
cur_time = epicsTime::getCurrent ();
this->client.flushRequest ();
beg_time = cur_time;
delay = 0.0;
while ( 1 ) {
if ( this->ioPendingList.count() == 0u ) {
status = ECA_NORMAL;
break;
}
remaining = timeout - delay;
if ( remaining <= CAC_SIGNIFICANT_DELAY ) {
/*
* Make sure that we take care of
* recv backlog at least once
*/
status = ECA_TIMEOUT;
break;
}
status = this->client.blockForEventAndEnableCallbacks ( this->sem, remaining );
if ( status != ECA_NORMAL ) {
break;
}
/*
* force a time update
*/
cur_time = epicsTime::getCurrent ();
delay = cur_time - beg_time;
}
this->reset ();
return status;
}
void CASG::reset ()
{
epicsGuard < casgMutex > locker ( this->mutex );
this->destroyCompletedIO ();
this->destroyPendingIO ();
}
// lock must be applied
void CASG::destroyCompletedIO ()
{
tsDLList < syncGroupNotify > userStillRequestingList;
syncGroupNotify *pNotify;
while ( ( pNotify = this->ioCompletedList.get () ) ) {
if ( pNotify->ioInitiated() ) {
pNotify->destroy ( * this );
}
else {
userStillRequestingList.add ( *pNotify );
}
}
this->ioCompletedList.add ( userStillRequestingList );
}
// lock must be applied
void CASG::destroyPendingIO ()
{
tsDLList < syncGroupNotify > userStillRequestingList;
syncGroupNotify *pNotify;
while ( ( pNotify = this->ioPendingList.get () ) ) {
if ( pNotify->ioInitiated() ) {
pNotify->destroy ( * this );
}
else {
userStillRequestingList.add ( *pNotify );
}
}
this->ioPendingList.add ( userStillRequestingList );
}
void CASG::show ( unsigned level ) const
{
::printf ( "Sync Group: id=%u, magic=%u, opPend=%u\n",
this->getId (), this->magic, this->ioPendingList.count () );
if ( level ) {
epicsGuard < casgMutex > locker ( this->mutex );
::printf ( "\tPending" );
tsDLIterConstBD < syncGroupNotify > notifyPending = this->ioPendingList.firstIter ();
while ( notifyPending.valid () ) {
notifyPending->show ( level - 1u );
notifyPending++;
}
::printf ( "\tCompleted" );
tsDLIterConstBD < syncGroupNotify > notifyCompleted = this->ioCompletedList.firstIter ();
while ( notifyCompleted.valid () ) {
notifyCompleted->show ( level - 1u );
notifyCompleted++;
}
}
}
bool CASG::ioComplete ()
{
bool isCompleted;
{
epicsGuard < casgMutex > locker ( this->mutex );
this->destroyCompletedIO ();
isCompleted = ( this->ioPendingList.count () == 0u );
}
return isCompleted;
}
int CASG::put ( chid pChan, unsigned type, arrayElementCount count, const void *pValue )
{
syncGroupWriteNotify * pNotify = 0;
try {
{
epicsGuard < casgMutex > locker ( this->mutex );
pNotify = syncGroupWriteNotify::factory (
this->freeListWriteOP, *this, pChan );
if ( pNotify ) {
this->ioPendingList.add ( *pNotify );
}
else {
return ECA_ALLOCMEM;
}
}
pNotify->begin ( type, count, pValue );
return ECA_NORMAL;
}
catch ( cacChannel::badString & )
{
destroyPendingIO ( pNotify );
return ECA_BADSTR;
}
catch ( cacChannel::badType & )
{
destroyPendingIO ( pNotify );
return ECA_BADTYPE;
}
catch ( cacChannel::outOfBounds & )
{
destroyPendingIO ( pNotify );
return ECA_BADCOUNT;
}
catch ( cacChannel::noWriteAccess & )
{
destroyPendingIO ( pNotify );
return ECA_NOWTACCESS;
}
catch ( cacChannel::notConnected & )
{
destroyPendingIO ( pNotify );
return ECA_DISCONN;
}
catch ( cacChannel::unsupportedByService & )
{
destroyPendingIO ( pNotify );
return ECA_NOTINSERVICE;
}
catch ( cacChannel::requestTimedOut & )
{
destroyPendingIO ( pNotify );
return ECA_TIMEOUT;
}
catch ( std::bad_alloc & )
{
destroyPendingIO ( pNotify );
return ECA_ALLOCMEM;
}
catch ( ... )
{
destroyPendingIO ( pNotify );
return ECA_INTERNAL;
}
}
int CASG::get ( chid pChan, unsigned type, arrayElementCount count, void *pValue )
{
syncGroupReadNotify * pNotify = 0;
try {
{
epicsGuard < casgMutex > locker ( this->mutex );
pNotify = syncGroupReadNotify::factory (
this->freeListReadOP, *this, pChan, pValue );
if ( pNotify ) {
this->ioPendingList.add ( *pNotify );
}
else {
return ECA_ALLOCMEM;
}
}
pNotify->begin ( type, count );
return ECA_NORMAL;
}
catch ( cacChannel::badString & )
{
destroyPendingIO ( pNotify );
return ECA_BADSTR;
}
catch ( cacChannel::badType & )
{
destroyPendingIO ( pNotify );
return ECA_BADTYPE;
}
catch ( cacChannel::outOfBounds & )
{
destroyPendingIO ( pNotify );
return ECA_BADCOUNT;
}
catch ( cacChannel::noReadAccess & )
{
destroyPendingIO ( pNotify );
return ECA_NORDACCESS;
}
catch ( cacChannel::notConnected & )
{
destroyPendingIO ( pNotify );
return ECA_DISCONN;
}
catch ( cacChannel::unsupportedByService & )
{
destroyPendingIO ( pNotify );
return ECA_NOTINSERVICE;
}
catch ( std::bad_alloc & )
{
destroyPendingIO ( pNotify );
return ECA_ALLOCMEM;
}
catch ( ... )
{
destroyPendingIO ( pNotify );
return ECA_INTERNAL;
}
}
void CASG::destroyPendingIO ( syncGroupNotify * pNotify )
{
epicsGuard < casgMutex > locker ( this->mutex );
if ( pNotify ) {
this->ioPendingList.remove ( *pNotify );
pNotify->destroy ( *this );
}
}
void CASG::completionNotify ( syncGroupNotify & notify )
{
unsigned requestsIncomplete;
{
epicsGuard < casgMutex > locker ( this->mutex );
this->ioPendingList.remove ( notify );
this->ioCompletedList.add ( notify );
requestsIncomplete = this->ioPendingList.count ();
}
if ( requestsIncomplete == 0u ) {
this->sem.signal ();
}
}
void CASG::recycleSyncGroupWriteNotify ( syncGroupWriteNotify &io )
{
this->freeListWriteOP.release ( &io, sizeof ( io ) );
}
void CASG::recycleSyncGroupReadNotify ( syncGroupReadNotify &io )
{
this->freeListReadOP.release ( &io, sizeof ( io ) );
}
void * CASG::operator new (size_t size)
{
return CASG::pFreeList->allocate ( size );
}
void CASG::operator delete (void *pCadaver, size_t size)
{
CASG::pFreeList->release ( pCadaver, size );
}
int CASG::printf ( const char *pformat, ... )
{
va_list theArgs;
int status;
va_start ( theArgs, pformat );
status = this->client.vPrintf ( pformat, theArgs );
va_end ( theArgs );
return status;
}
void CASG::exception ( int status, const char *pContext,
const char *pFileName, unsigned lineNo )
{
this->client.exception ( status, pContext, pFileName, lineNo );
}
void CASG::exception ( int status, const char *pContext,
const char *pFileName, unsigned lineNo, oldChannelNotify &chan,
unsigned type, arrayElementCount count, unsigned op )
{
this->client.exception ( status, pContext, pFileName,
lineNo, chan, type, count, op );
}