/*************************************************************************\ * 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. * SPDX-License-Identifier: EPICS * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeffrey O. Hill * hill@luke.lanl.gov * (505) 665 1831 */ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "iocinf.h" #include "oldAccess.h" #include "syncGroup.h" /* * ca_sg_create() */ extern "C" int epicsStdCall ca_sg_create ( CA_SYNC_GID * pgid ) { ca_client_context * pcac; int caStatus; CASG * pcasg; caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } try { epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); pcasg = new ( pcac->casgFreeList ) CASG ( guard, *pcac ); *pgid = pcasg->getId (); return ECA_NORMAL; } catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } catch ( ... ) { return ECA_INTERNAL; } } int ca_sync_group_destroy ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard, ca_client_context & cac, const CA_SYNC_GID gid ) { int caStatus; CASG * pcasg = cac.lookupCASG ( guard, gid ); if ( pcasg ) { pcasg->destructor ( cbGuard, guard ); cac.casgFreeList.release ( pcasg ); caStatus = ECA_NORMAL; } else { caStatus = ECA_BADSYNCGRP; } return caStatus; } /* * ca_sg_delete() */ extern "C" int epicsStdCall ca_sg_delete ( const CA_SYNC_GID gid ) { ca_client_context * pcac; int caStatus = fetchClientContext ( & pcac ); if ( caStatus == ECA_NORMAL ) { if ( pcac->pCallbackGuard.get() && pcac->createdByThread == epicsThreadGetIdSelf () ) { epicsGuard < epicsMutex > guard ( pcac->mutex ); caStatus = ca_sync_group_destroy ( *pcac->pCallbackGuard.get(), guard, *pcac, gid ); } else { // // we will definitely stall out here if all of the // following are true // // o user creates non-preemptive mode client library context // o user doesn't periodically call a ca function // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( pcac->cbMutex ); epicsGuard < epicsMutex > guard ( pcac->mutex ); caStatus = ca_sync_group_destroy ( cbGuard, guard, *pcac, gid ); } } return caStatus; } void sync_group_reset ( ca_client_context & client, CASG & sg ) { if ( client.pCallbackGuard.get() && client.createdByThread == epicsThreadGetIdSelf () ) { epicsGuard < epicsMutex > guard ( client.mutex ); sg.reset ( *client.pCallbackGuard.get(), guard ); } else { // // we will definitely stall out here if all of the // following are true // // o user creates non-preemptive mode client library context // o user doesn't periodically call a ca function // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( client.cbMutex ); epicsGuard < epicsMutex > guard ( client.mutex ); sg.reset ( cbGuard, guard ); } } // // ca_sg_block () // // !!!! This routine is only visible in the old interface - or in a new ST interface. // !!!! In the old interface we restrict thread attach so that calls from threads // !!!! other than the initializing thread are not allowed if preemptive callback // !!!! is disabled. This prevents the preemptive callback lock from being released // !!!! by other threads than the one that locked it. // extern "C" int epicsStdCall ca_sg_block ( const CA_SYNC_GID gid, ca_real timeout ) { ca_client_context *pcac; int status = fetchClientContext ( &pcac ); if ( status == ECA_NORMAL ) { CASG * pcasg; { epicsGuard < epicsMutex > guard ( pcac->mutex ); pcasg = pcac->lookupCASG ( guard, gid ); if ( pcasg ) { status = pcasg->block ( pcac->pCallbackGuard.get (), guard, timeout ); } else { status = ECA_BADSYNCGRP; } } if ( pcasg ) { sync_group_reset ( *pcac, *pcasg ); } } return status; } /* * ca_sg_reset */ extern "C" int epicsStdCall ca_sg_reset ( const CA_SYNC_GID gid ) { ca_client_context *pcac; int caStatus = fetchClientContext (&pcac); if ( caStatus == ECA_NORMAL ) { CASG * pcasg; { epicsGuard < epicsMutex > guard ( pcac->mutex ); pcasg = pcac->lookupCASG ( guard, gid ); } if ( pcasg ) { sync_group_reset ( *pcac, *pcasg ); caStatus = ECA_NORMAL; } else { caStatus = ECA_BADSYNCGRP; } } return caStatus; } /* * ca_sg_stat */ extern "C" int epicsStdCall ca_sg_stat ( const CA_SYNC_GID gid ) { ca_client_context * pcac; int caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); CASG * pcasg = pcac->lookupCASG ( guard, gid ); if ( ! pcasg ) { ::printf ( "Bad Sync Group Id\n"); return ECA_BADSYNCGRP; } pcasg->show ( guard, 1000u ); return ECA_NORMAL; } /* * ca_sg_test */ extern "C" int epicsStdCall ca_sg_test ( const CA_SYNC_GID gid ) { ca_client_context * pcac; int caStatus = fetchClientContext ( &pcac ); if ( caStatus == ECA_NORMAL ) { epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); CASG * pcasg = pcac->lookupCASG ( guard, gid ); if ( pcasg ) { bool isComplete; if ( pcac->pCallbackGuard.get() && pcac->createdByThread == epicsThreadGetIdSelf () ) { epicsGuard < epicsMutex > guard ( pcac->mutex ); isComplete = pcasg->ioComplete ( *pcac->pCallbackGuard.get(), guard ); } else { // // we will definitely stall out here if all of the // following are true // // o user creates non-preemptive mode client library context // o user doesn't periodically call a ca function // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( pcac->cbMutex ); epicsGuard < epicsMutex > guard ( pcac->mutex ); isComplete = pcasg->ioComplete ( cbGuard, guard ); } if ( isComplete ) { caStatus = ECA_IODONE; } else{ caStatus = ECA_IOINPROGRESS; } } else { caStatus = ECA_BADSYNCGRP; } } return caStatus; } /* * ca_sg_array_put() */ extern "C" int epicsStdCall ca_sg_array_put ( const CA_SYNC_GID gid, chtype type, arrayElementCount count, chid pChan, const void *pValue ) { ca_client_context *pcac; int caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); CASG * const pcasg = pcac->lookupCASG ( guard, gid ); if ( ! pcasg ) { return ECA_BADSYNCGRP; } try { pcasg->put ( guard, pChan, type, static_cast < unsigned > ( count ), pValue ); return ECA_NORMAL; } catch ( cacChannel::badString & ) { return ECA_BADSTR; } catch ( cacChannel::badType & ) { return ECA_BADTYPE; } catch ( cacChannel::outOfBounds & ) { return ECA_BADCOUNT; } catch ( cacChannel::noWriteAccess & ) { return ECA_NOWTACCESS; } catch ( cacChannel::notConnected & ) { return ECA_DISCONN; } catch ( cacChannel::unsupportedByService & ) { return ECA_UNAVAILINSERV; } catch ( cacChannel::requestTimedOut & ) { return ECA_TIMEOUT; } catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } catch ( ... ) { return ECA_INTERNAL; } } /* * ca_sg_array_get() */ extern "C" int epicsStdCall ca_sg_array_get ( const CA_SYNC_GID gid, chtype type, arrayElementCount count, chid pChan, void *pValue ) { ca_client_context *pcac; int caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); CASG * const pcasg = pcac->lookupCASG ( guard, gid ); if ( ! pcasg ) { return ECA_BADSYNCGRP; } try { pcasg->get ( guard, pChan, type, static_cast < unsigned > ( count ), pValue ); return ECA_NORMAL; } catch ( cacChannel::badString & ) { return ECA_BADSTR; } catch ( cacChannel::badType & ) { return ECA_BADTYPE; } catch ( cacChannel::outOfBounds & ) { return ECA_BADCOUNT; } catch ( cacChannel::noReadAccess & ) { return ECA_NORDACCESS; } catch ( cacChannel::notConnected & ) { return ECA_DISCONN; } catch ( cacChannel::unsupportedByService & ) { return ECA_UNAVAILINSERV; } catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } catch ( ... ) { return ECA_INTERNAL; } }