Files
epics-base/src/cas/generic/casEventSys.cc
Jeff Hill 8b12a243a2 clean up
2003-05-02 17:40:26 +00:00

351 lines
9.5 KiB
C++

/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* $Id$
*
* Author Jeffrey O. Hill
* johill@lanl.gov
* 505 665 1831
*/
#include <string.h>
#define epicsExportSharedSymbols
#include "caHdrLargeArray.h"
#include "casCoreClient.h"
#include "casAsyncIOI.h"
#include "casChannelI.h"
#include "channelDestroyEvent.h"
void casEventSys::show ( unsigned level ) const
{
epicsGuard < epicsMutex > guard ( this->mutex );
printf ( "casEventSys at %p\n",
static_cast <const void *> ( this ) );
if (level>=1u) {
printf ( "\numSubscriptions = %u, maxLogEntries = %u\n",
this->numSubscriptions, this->maxLogEntries );
printf ( "\tthere are %d events in the queue\n",
this->eventLogQue.count() );
printf ( "Replace events flag = %d, dontProcess flag = %d\n",
static_cast < int > ( this->replaceEvents ),
static_cast < int > ( this->dontProcess ) );
}
}
casEventSys::~casEventSys()
{
if ( this->pPurgeEvent != NULL ) {
this->eventLogQue.remove ( *this->pPurgeEvent );
delete this->pPurgeEvent;
}
// at this point:
// o all channels delete
// o all IO deleted
// o any subscription events remaining on the queue
// are pending destroy
// verify above assertion is true
casVerify ( this->eventLogQue.count() == 0 );
// all active subscriptions should also have been
// uninstalled
casVerify ( this->numSubscriptions == 0 );
if ( this->numSubscriptions != 0 ) {
printf ( "numSubscriptions=%u\n", this->numSubscriptions );
}
}
void casEventSys::installMonitor ()
{
epicsGuard < epicsMutex > guard ( this->mutex );
assert ( this->numSubscriptions < UINT_MAX );
this->numSubscriptions++;
this->maxLogEntries += averageEventEntries;
}
void casEventSys::removeMonitor ()
{
epicsGuard < epicsMutex > guard ( this->mutex );
assert ( this->numSubscriptions >= 1u );
this->numSubscriptions--;
this->maxLogEntries -= averageEventEntries;
}
casEventSys::processStatus casEventSys::process (
epicsGuard < casClientMutex > & casClientGuard )
{
casEventSys::processStatus ps;
ps.cond = casProcOk;
ps.nAccepted = 0u;
epicsGuard < evSysMutex > evGuard ( this->mutex );
while ( ! this->dontProcess ) {
casEvent * pEvent;
pEvent = this->eventLogQue.get ();
if ( pEvent == NULL ) {
break;
}
caStatus status = pEvent->cbFunc (
this->client, casClientGuard, evGuard );
if ( status == S_cas_success ) {
ps.nAccepted++;
}
else if ( status == S_cas_sendBlocked ) {
// not accepted so return to the head of the list
// (we will try again later)
this->eventLogQue.push ( *pEvent );
ps.cond = casProcOk;
break;
}
else if ( status == S_cas_disconnect ) {
ps.cond = casProcDisconnect;
break;
}
else {
errMessage ( status,
"- unexpected error processing event" );
ps.cond = casProcDisconnect;
break;
}
}
//
// allows the derived class to be informed that it
// needs to delete itself via the event system
//
// this gets the server out of nasty situations
// where the client needs to be deleted but
// the caller may be using the client's "this"
// pointer.
//
if ( this->destroyPending ) {
ps.cond = casProcDisconnect;
}
return ps;
}
void casEventSys::eventsOn ()
{
epicsGuard < epicsMutex > guard ( this->mutex );
//
// allow multiple events for each monitor
//
this->replaceEvents = false;
//
// allow the event queue to be processed
//
this->dontProcess = false;
//
// remove purge event if it is still pending
//
if ( this->pPurgeEvent != NULL ) {
this->eventLogQue.remove ( *this->pPurgeEvent );
delete this->pPurgeEvent;
this->pPurgeEvent = NULL;
}
}
bool casEventSys::eventsOff ()
{
bool signalNeeded = false;
{
epicsGuard < epicsMutex > guard ( this->mutex );
//
// new events will replace the last event on
// the queue for a particular monitor
//
this->replaceEvents = true;
//
// suppress the processing and sending of events
// only after we have purged the event queue
// for this particular client
//
if ( this->pPurgeEvent == NULL ) {
this->pPurgeEvent = new casEventPurgeEv ( *this );
if ( this->pPurgeEvent == NULL ) {
//
// if there is no room for the event then immediately
// stop processing and sending events to the client
// until we exit flow control
//
this->dontProcess = true;
}
else {
if ( this->eventLogQue.count() == 0 ) {
signalNeeded = true;
}
this->eventLogQue.add ( *this->pPurgeEvent );
signalNeeded = true;
}
}
}
return signalNeeded;
}
casEventPurgeEv::casEventPurgeEv ( casEventSys & evSysIn ) :
evSys ( evSysIn )
{
}
caStatus casEventPurgeEv::cbFunc (
casCoreClient &,
epicsGuard < casClientMutex > &,
epicsGuard < evSysMutex > & )
{
this->evSys.dontProcess = true;
this->evSys.pPurgeEvent = NULL;
delete this;
return S_cas_success;
}
caStatus casEventSys::addToEventQueue ( casAsyncIOI & event,
bool & onTheQueue, bool & posted, bool & wakeupNeeded )
{
wakeupNeeded = false;
{
epicsGuard < epicsMutex > guard ( this->mutex );
// dont allow them to post completion more than once
if ( posted || onTheQueue ) {
return S_cas_redundantPost;
}
posted = true;
onTheQueue = true;
wakeupNeeded = ! this->dontProcess && this->eventLogQue.count() == 0;
this->eventLogQue.add ( event );
}
return S_cas_success;
}
bool casEventSys::addToEventQueue ( casChannelI & event, bool & inTheEventQueue )
{
bool wakeupRequired = false;
{
epicsGuard < epicsMutex > guard ( this->mutex );
if ( ! inTheEventQueue ) {
inTheEventQueue = true;
wakeupRequired = ! this->dontProcess && this->eventLogQue.count()==0;
this->eventLogQue.add ( event );
}
}
return wakeupRequired;
}
void casEventSys::removeFromEventQueue ( casAsyncIOI & io, bool & onTheEventQueue )
{
epicsGuard < epicsMutex > guard ( this->mutex );
if ( onTheEventQueue ) {
onTheEventQueue = false;
this->eventLogQue.remove ( io );
}
}
bool casEventSys::addToEventQueue ( channelDestroyEvent & event )
{
epicsGuard < epicsMutex > guard ( this->mutex );
bool wakeupRequired = ! this->dontProcess && this->eventLogQue.count()==0;
this->eventLogQue.add ( event );
return wakeupRequired;
}
void casEventSys::setDestroyPending ()
{
epicsGuard < epicsMutex > guard ( this->mutex );
this->destroyPending = true;
}
inline bool casEventSys::full () const // X aCC 361
{
if ( this->replaceEvents || this->eventLogQue.count() >= this->maxLogEntries ) {
return true;
}
else {
return false;
}
}
bool casEventSys::postEvent ( tsDLList < casMonitor > & monitorList,
const casEventMask & select, const gdd & event )
{
bool signalNeeded = false;
{
epicsGuard < epicsMutex > guard ( this->mutex );
tsDLIter < casMonitor > iter = monitorList.firstIter ();
while ( iter.valid () ) {
if ( iter->selected ( select ) ) {
// get a new block if we havent exceeded quotas
bool full = ( iter->numEventsQueued() >= individualEventEntries )
|| this->full ();
casMonEvent * pLog;
if ( ! full ) {
// should I get rid of this try block by implementing a no
// throw version of the free list alloc? However, crude
// tests on windows with ms visual C++ dont appear to argue
// against the try block.
try {
pLog = new ( this->casMonEventFreeList )
casMonEvent ( *iter, event );
}
catch ( ... ) {
pLog = 0;
}
}
else {
pLog = 0;
}
if ( this->eventLogQue.count() == 0 ) {
signalNeeded = true;
}
iter->installNewEventLog (
this->eventLogQue, pLog, event );
}
++iter;
}
}
return signalNeeded;
}
void casEventSys::casMonEventDestroy (
casMonEvent & ev, epicsGuard < evSysMutex > & guard )
{
guard.assertIdenticalMutex ( this->mutex );
ev.~casMonEvent ();
this->casMonEventFreeList.release ( & ev );
}
void casEventSys::prepareMonitorForDestroy ( casMonitor & mon )
{
bool safeToDestroy = false;
{
epicsGuard < epicsMutex > guard ( this->mutex );
mon.markDestroyPending ();
// if events reference it on the queue then it gets
// deleted when it reaches the top of the queue
if ( mon.numEventsQueued () == 0 ) {
safeToDestroy = true;
}
}
if ( safeToDestroy ) {
this->client.destroyMonitor ( mon );
}
}