Ejemplo n.º 1
0
void
bufRxManager::receive(epicsUInt8* raw,unsigned int usedlen)
{
    /* CONTAINER doesn't work when the member is a pointer
     * because the GNU version's check isn't correct
    buffer *buf=CONTAINER(raw, buffer, data);
     */
    buffer *buf=(buffer*)((char*)(raw) - offsetof(buffer, data));

    if (usedlen>bsize())
        throw std::out_of_range("User admitted overflowing Rx buffer");
    buf->used=usedlen;

    if (usedlen==0) {
        // buffer returned w/o being used
        {
            SCOPED_LOCK(guard);
            ellAdd(&freebufs, &buf->node);
        }
        return;
    }

    {
        SCOPED_LOCK(guard);
        ellAdd(&usedbufs, &buf->node);
    }

    callbackRequest(&received_cb);
}
Ejemplo n.º 2
0
static char *msgbufGetFree(int noConsoleMessage)
{
    msgNode *pnextSend;

    if (epicsMutexLock(pvtData.msgQueueLock) != epicsMutexLockOK)
        return 0;

    if ((ellCount(&pvtData.msgQueue) == 0) && pvtData.missedMessages) {
        int nchar;

        pnextSend = msgbufGetNode();
        nchar = sprintf(pnextSend->message,
            "errlog: %d messages were discarded\n", pvtData.missedMessages);
        pnextSend->length = nchar + 1;
        pvtData.missedMessages = 0;
        ellAdd(&pvtData.msgQueue, &pnextSend->node);
    }

    pvtData.pnextSend = pnextSend = msgbufGetNode();
    if (pnextSend) {
        pnextSend->noConsoleMessage = noConsoleMessage;
        pnextSend->length = 0;
        return pnextSend->message;  /* NB: msgQueueLock is still locked */
    }

    ++pvtData.missedMessages;
    epicsMutexUnlock(pvtData.msgQueueLock);
    return 0;
}
Ejemplo n.º 3
0
static void createChannelCallFunction(const iocshArgBuf *args)
{
	drvM6802_taskConfig *ptaskConfig = NULL;
	drvM6802_channelConfig *pchannelConfig = NULL;
	char task_name[60];
	char channel_name[60];
	int channel_ID=0;

	if(args[0].sval) strcpy(task_name, args[0].sval);
	if(args[1].sval) channel_ID = strtoul(args[1].sval,NULL,0);
	if(args[2].sval) strcpy(channel_name, args[2].sval);

	ptaskConfig = find_taskConfig(task_name);
	if(!ptaskConfig) return;

	pchannelConfig = (drvM6802_channelConfig*) malloc(sizeof(drvM6802_channelConfig));
	if(!pchannelConfig) return;

	pchannelConfig->channelStatus = 0x0000;
	strcpy(pchannelConfig->chanName, channel_name);
	pchannelConfig->chanIndex = channel_ID;

	pchannelConfig->gain = INIT_GAIN;
	pchannelConfig->ptaskConfig = ptaskConfig;
	pchannelConfig->conversionTime_usec = 0.;

	ellAdd(ptaskConfig->pchannelConfig, &pchannelConfig->node);


#ifdef DEBUG
	epicsPrintf("createChannelCallFunction... ok\n");
#endif /* DEBUG */
	
	return;
}
Ejemplo n.º 4
0
static void addAction(caLink *pca, short link_action)
{
    int callAdd;

    epicsMutexMustLock(workListLock);
    callAdd = (pca->link_action == 0);
    if (pca->link_action & CA_CLEAR_CHANNEL) {
        errlogPrintf("dbCa::addAction %d with CA_CLEAR_CHANNEL set\n",
            link_action);
        printLinks(pca);
        link_action = 0;
    }
    if (link_action & CA_CLEAR_CHANNEL) {
        if (++removesOutstanding >= removesOutstandingWarning) {
            errlogPrintf("dbCa::addAction pausing, %d channels to clear\n",
                removesOutstanding);
            printLinks(pca);
        }
        while (removesOutstanding >= removesOutstandingWarning) {
            epicsMutexUnlock(workListLock);
            epicsThreadSleep(1.0);
            epicsMutexMustLock(workListLock);
        }
    }
    pca->link_action |= link_action;
    if (callAdd)
        ellAdd(&workList, &pca->node);
    epicsMutexUnlock(workListLock);
    if (callAdd)
        epicsEventSignal(workListEvent);
}
Ejemplo n.º 5
0
int epicsJobMove(epicsJob *job, epicsThreadPool *newpool)
{
    epicsThreadPool *pool = job->pool;

    /* remove from current pool */
    if (pool) {
        epicsMutexMustLock(pool->guard);

        if (job->queued || job->running) {
            epicsMutexUnlock(pool->guard);
            return S_pool_jobBusy;
        }

        ellDelete(&pool->owned, &job->jobnode);

        epicsMutexUnlock(pool->guard);
    }

    pool = job->pool = newpool;

    /* add to new pool */
    if (pool) {
        epicsMutexMustLock(pool->guard);

        ellAdd(&pool->owned, &job->jobnode);

        epicsMutexUnlock(pool->guard);
    }

    return 0;
}
Ejemplo n.º 6
0
Archivo: taskwd.c Proyecto: ukaea/epics
void taskwdInsert(epicsThreadId tid, TASKWDFUNC callback, void *usr)
{
    struct tNode *pt;
    struct mNode *pm;

    taskwdInit();
    if (tid == 0)
       tid = epicsThreadGetIdSelf();

    pt = &allocNode()->t;
    pt->tid = tid;
    pt->callback = callback;
    pt->usr = usr;
    pt->suspended = FALSE;

    epicsMutexMustLock(mLock);
    pm = (struct mNode *)ellFirst(&mList);
    while (pm) {
        if (pm->funcs->insert) {
            pm->funcs->insert(pm->usr, tid);
        }
        pm = (struct mNode *)ellNext(&pm->node);
    }
    epicsMutexUnlock(mLock);

    epicsMutexMustLock(tLock);
    ellAdd(&tList, (void *)pt);
    epicsMutexUnlock(tLock);
}
Ejemplo n.º 7
0
/*
 * Driver initialisation at IOC startup (ecAsynInit)
 *
 * path - location of Unix Domain Socket, must match the scanner's
 * max_message - maximum size of messages between scanner and ioc
 *               This must be able to accommodate the configuration
 *               of the chain that is transferred from the scanner to
 *               the ioc.
 */
static void makePorts(char * path, int max_message)
{
    ENGINE_USER * usr = (ENGINE_USER *)callocMustSucceed
        (1, sizeof(ENGINE_USER), "can't allocate socket engine private data");
    ellInit(&usr->ports);
    usr->master = new ecMaster((char *)"MASTER0");
    ellAdd(&usr->ports, &usr->master->node);
    usr->config_ready = rtMessageQueueCreate(1, sizeof(int));
    // TODO - no assert for runtime errors, so what should we use to throw?
    assert(usr->config_ready != NULL);
    usr->config = (EC_CONFIG *)callocMustSucceed
        (1, sizeof(EC_CONFIG), "can't allocate chain config lists");
    usr->writeq = rtMessageQueueCreate(1, max_message);
    assert(usr->writeq != NULL);
    ENGINE * engine = new_engine(max_message);
    engine->path = strdup(path);
    engine->connect = client_connect;
    engine->on_connect = receive_config_on_connect;
    engine->send_message = ioc_send;
    engine->receive_message = ioc_receive;
    engine->usr = usr;
    engine_start(engine);

    new_timer(1000000000, usr->writeq, 0, MSG_HEARTBEAT);

    int ack;
    rtMessageQueueReceive(usr->config_ready, &ack, sizeof(int));

}
Ejemplo n.º 8
0
int epicsJobUnqueue(epicsJob *job)
{
    int ret = S_pool_jobIdle;
    epicsThreadPool *pool = job->pool;

    if (!pool)
        return S_pool_noPool;

    epicsMutexMustLock(pool->guard);

    assert(!job->dead);

    if (job->queued) {
        if (!job->running) {
            ellDelete(&pool->jobs, &job->jobnode);
            ellAdd(&pool->owned, &job->jobnode);
        }
        job->queued = 0;
        ret = 0;
    }

    epicsMutexUnlock(pool->guard);

    return ret;
}
Ejemplo n.º 9
0
motorSimController::motorSimController(const char *portName, int numAxes, int priority, int stackSize)
  :  asynMotorController(portName, numAxes, NUM_SIM_CONTROLLER_PARAMS, 
                         asynInt32Mask | asynFloat64Mask, 
                         asynInt32Mask | asynFloat64Mask,
                         ASYN_CANBLOCK | ASYN_MULTIDEVICE, 
                         1, // autoconnect
                         priority, stackSize)
{
  int axis;
  motorSimControllerNode *pNode;
  
  if (!motorSimControllerListInitialized) {
    motorSimControllerListInitialized = 1;
    ellInit(&motorSimControllerList);
  }
  
  // We should make sure this portName is not already in the list */
  pNode = (motorSimControllerNode*) calloc(1, sizeof(motorSimControllerNode));
  pNode->portName = epicsStrDup(portName);
  pNode->pController = this;
  ellAdd(&motorSimControllerList, (ELLNODE *)pNode);

  if (numAxes < 1 ) numAxes = 1;
  numAxes_ = numAxes;
  this->movesDeferred_ = 0;
  for (axis=0; axis<numAxes; axis++) {
    new motorSimAxis(this, axis, DEFAULT_LOW_LIMIT, DEFAULT_HI_LIMIT, DEFAULT_HOME, DEFAULT_START);
    setDoubleParam(axis, this->motorPosition_, DEFAULT_START);
  }

  this->motorThread_ = epicsThreadCreate("motorSimThread", 
                                         epicsThreadPriorityLow,
                                         epicsThreadGetStackSize(epicsThreadStackMedium),
                                         (EPICSTHREADFUNC) motorSimTaskC, (void *) this);
}
Ejemplo n.º 10
0
static void * start_routine(void *arg)
{
    epicsThreadOSD *pthreadInfo = (epicsThreadOSD *)arg;
    int status;
    int oldtype;
    sigset_t blockAllSig;
 
    sigfillset(&blockAllSig);
    pthread_sigmask(SIG_SETMASK,&blockAllSig,NULL);
    status = pthread_setspecific(getpthreadInfo,arg);
    checkStatusQuit(status,"pthread_setspecific","start_routine");
    status = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&oldtype);
    checkStatusQuit(status,"pthread_setcanceltype","start_routine");
    status = mutexLock(&listLock);
    checkStatusQuit(status,"pthread_mutex_lock","start_routine");
    ellAdd(&pthreadList,&pthreadInfo->node);
    pthreadInfo->isOnThreadList = 1;
    status = pthread_mutex_unlock(&listLock);
    checkStatusQuit(status,"pthread_mutex_unlock","start_routine");

    (*pthreadInfo->createFunc)(pthreadInfo->createArg);

    epicsExitCallAtThreadExits ();

    free_threadInfo(pthreadInfo);
    return(0);
}
Ejemplo n.º 11
0
/*
 *      devInsertAddress()
 */
static void devInsertAddress(
ELLLIST     *pRangeList,
rangeItem   *pNewRange)
{
    rangeItem   *pBefore;
    rangeItem   *pAfter;

    epicsMutexMustLock(addrListLock);
    pAfter = (rangeItem *) ellFirst (pRangeList);
    while (pAfter) {
        if (pNewRange->end < pAfter->begin) {
            break;
        }
        pAfter = (rangeItem *) ellNext (&pAfter->node);
    }

    if (pAfter) {
        pBefore = (rangeItem *) ellPrevious (&pAfter->node);
        ellInsert (pRangeList, &pBefore->node, &pNewRange->node);
    }
    else {
        ellAdd (pRangeList, &pNewRange->node);
    }
    epicsMutexUnlock(addrListLock);
}
Ejemplo n.º 12
0
void
bufRxManager::received(CALLBACK* cb)
{
    void *vptr;
    callbackGetUser(vptr,cb);
    bufRxManager &self=*static_cast<bufRxManager*>(vptr);

    SCOPED_LOCK2(self.guard, G);

    while(true) {
        ELLNODE *node=ellGet(&self.usedbufs);

        if (!node)
            break;
        buffer *buf=CONTAINER(node, buffer, node);

        G.unlock();

        for(ELLNODE *cur=ellFirst(&self.dispatch); cur; cur=ellNext(cur)) {
            listener *action=CONTAINER(cur, listener, node);
            (action->fn)(action->fnarg, 0, buf->used, buf->data);
        }

        G.lock();

        ellAdd(&self.freebufs, &buf->node);
    };
}
Ejemplo n.º 13
0
void dbRegisterFilter(const char *name, const chFilterIf *fif, void *puser)
{
    GPHENTRY *pgph;
    chFilterPlugin *pfilt;

    if (!pdbbase) {
        printf("dbRegisterFilter: pdbbase not set!\n");
        return;
    }

    pgph = gphFind(pdbbase->pgpHash, name, &pdbbase->filterList);
    if (pgph)
        return;

    pfilt = dbCalloc(1, sizeof(chFilterPlugin));
    pfilt->name = epicsStrDup(name);
    pfilt->fif = fif;
    pfilt->puser = puser;

    ellAdd(&pdbbase->filterList, &pfilt->node);
    pgph = gphAdd(pdbbase->pgpHash, pfilt->name, &pdbbase->filterList);
    if (!pgph) {
        free((void *) pfilt->name);
        free(pfilt);
        printf("dbRegisterFilter: gphAdd failed\n");
        return;
    }
    pgph->userPvt = pfilt;
}
Ejemplo n.º 14
0
Archivo: taskwd.c Proyecto: ukaea/epics
static void freeNode(union twdNode *pn)
{
    VALGRIND_MEMPOOL_FREE(&fList, pn);
    VALGRIND_MEMPOOL_ALLOC(&fList, pn, sizeof(ELLNODE));
    epicsMutexMustLock(fLock);
    ellAdd(&fList, (void *)pn);
    epicsMutexUnlock(fLock);
}
Ejemplo n.º 15
0
static void notifyCleanup(processNotify *ppn)
{
    notifyPvt *pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;

    pnotifyPvt->state = notifyNotActive;
    ellAdd(&pnotifyGlobal->freeList, &pnotifyPvt->node);
    ppn->pnotifyPvt = 0;
}
Ejemplo n.º 16
0
static void allocTemp(void *pvoid)
{
    tempListNode	*ptempListNode;

    ptempListNode = freeListCalloc(freeListPvt);
    ptempListNode->item = pvoid;
    ellAdd(&tempList,&ptempListNode->node);
}
Ejemplo n.º 17
0
static void putNotifyCleanup(putNotify *ppn)
{
    putNotifyPvt *pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt;

    pputNotifyPvt->state = putNotifyNotActive;
    ellAdd(&pnotifyGlobal->freeList,&pputNotifyPvt->node);
    ppn->pputNotifyPvt = 0;
}
Ejemplo n.º 18
0
static void msgbufSetSize(int size)
{
    msgNode *pnextSend = pvtData.pnextSend;

    pnextSend->length = size+1;
    ellAdd(&pvtData.msgQueue, &pnextSend->node);
    epicsMutexUnlock(pvtData.msgQueueLock);
    epicsEventSignal(pvtData.waitForWork);
}
Ejemplo n.º 19
0
epicsShareFunc epicsThreadPool* epicsThreadPoolGetShared(epicsThreadPoolConfig *opts)
{
    ELLNODE *node;
    epicsThreadPool *cur;
    epicsThreadPoolConfig defopts;
    size_t N = epicsThreadGetCPUs();

    if (!opts) {
        epicsThreadPoolConfigDefaults(&defopts);
        opts = &defopts;
    }
    /* shared pools must have a minimum allowed number of workers.
     * Use the number of CPU cores
     */
    if (opts->maxThreads < N)
        opts->maxThreads = N;

    epicsThreadOnce(&sharedPoolsOnce, &sharedPoolsInit, NULL);

    epicsMutexMustLock(sharedPoolsGuard);

    for (node = ellFirst(&sharedPools); node; node = ellNext(node)) {
        cur = CONTAINER(node, epicsThreadPool, sharedNode);

        /* Must have exactly the requested priority
         * At least the requested max workers
         * and at least the requested stack size
         */
        if (cur->conf.workerPriority != opts->workerPriority)
            continue;
        if (cur->conf.maxThreads < opts->maxThreads)
            continue;
        if (cur->conf.workerStack < opts->workerStack)
            continue;

        cur->sharedCount++;
        assert(cur->sharedCount > 0);
        epicsMutexUnlock(sharedPoolsGuard);

        epicsMutexMustLock(cur->guard);
        *opts = cur->conf;
        epicsMutexUnlock(cur->guard);
        return cur;
    }

    cur = epicsThreadPoolCreate(opts);
    if (!cur) {
        epicsMutexUnlock(sharedPoolsGuard);
        return NULL;
    }
    cur->sharedCount = 1;

    ellAdd(&sharedPools, &cur->sharedNode);
    epicsMutexUnlock(sharedPoolsGuard);
    return cur;
}
Ejemplo n.º 20
0
/** Adds an attribute to the list.
  * If an attribute of the same name already exists then
  * the existing attribute is deleted and replaced with the new one.
  * \param[in] pAttribute A pointer to the attribute to add.
  */
int NDAttributeList::add(NDAttribute *pAttribute)
{
  //const char *functionName = "NDAttributeList::add";

  epicsMutexLock(this->lock);
  /* Remove any existing attribute with this name */
  this->remove(pAttribute->pName);
  ellAdd(&this->list, &pAttribute->listNode.node);
  epicsMutexUnlock(this->lock);
  return(ND_SUCCESS);
}
Ejemplo n.º 21
0
static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg)
{
    int status = -1;
    exitNode * pExitNode
        = calloc ( 1, sizeof( *pExitNode ) );
    if ( pExitNode ) {
        pExitNode->func = func;
        pExitNode->arg = arg;
        ellAdd ( & pep->list, & pExitNode->node );
        status = 0;
    }
    return status;
}
Ejemplo n.º 22
0
/*
 * removeDuplicateAddresses ()
 */
extern "C" void epicsShareAPI removeDuplicateAddresses 
    ( ELLLIST *pDestList, ELLLIST *pSrcList, int silent )
{
    ELLNODE *pRawNode;

    while ( (pRawNode  = ellGet ( pSrcList ) ) ) {
		STATIC_ASSERT ( offsetof (osiSockAddrNode, node) == 0 );
		osiSockAddrNode *pNode = reinterpret_cast <osiSockAddrNode *> ( pRawNode );
        osiSockAddrNode *pTmpNode;

        if ( pNode->addr.sa.sa_family == AF_INET ) {

            pTmpNode = (osiSockAddrNode *) ellFirst (pDestList);    // X aCC 749
            while ( pTmpNode ) {
                if (pTmpNode->addr.sa.sa_family == AF_INET) {
                    if ( pNode->addr.ia.sin_addr.s_addr == pTmpNode->addr.ia.sin_addr.s_addr && 
                            pNode->addr.ia.sin_port == pTmpNode->addr.ia.sin_port ) {
						if ( ! silent ) {
                            char buf[64];
                            ipAddrToDottedIP ( &pNode->addr.ia, buf, sizeof (buf) );
							fprintf ( stderr, 
								"Warning: Duplicate EPICS CA Address list entry \"%s\" discarded\n", buf );
						}
                        free (pNode);
                        pNode = NULL;
                        break;
                    }
                }
                pTmpNode = (osiSockAddrNode *) ellNext (&pTmpNode->node); // X aCC 749
            }
            if (pNode) {
                ellAdd (pDestList, &pNode->node);
            }
        }
        else {
            ellAdd (pDestList, &pNode->node);
        }
    }
}
Ejemplo n.º 23
0
dbStateId dbStateCreate(const char *name)
{
    dbStateId id;

    if ((id = dbStateFind(name)))
        return id;

    id = callocMustSucceed(1, sizeof(dbState), "createDbState");
    id->name = epicsStrDup(name);
    id->lock = epicsMutexMustCreate();
    ellAdd(&states, &id->node);

    return id;
}
Ejemplo n.º 24
0
static void readConfig(ENGINE_USER * usr)
{
    EC_CONFIG * cfg = usr->config;
    ELLNODE * node;
    int ndev = 0;
    for(node = ellFirst(&cfg->devices); node; node = ellNext(node))
    {
        EC_DEVICE * device = (EC_DEVICE *)node;
        printf("Creating ecAsyn port No %d: %s\n", ndev, device->name);
        ecAsyn * port = new ecAsyn(device,
                                   device->pdo_entry_mappings.count,
                                   device->sdo_requests.count,
                                   usr, ndev);
        ellAdd(&usr->ports, &port->node);
        if (port->sdos > 0)
        {
            char *sdoportname= format("%s_SDO", device->name);
            ecSdoAsyn * sdoport = new ecSdoAsyn(sdoportname, port);
            ellAdd(&usr->sdo_observers, &sdoport->node);
            free(sdoportname);
        }
        ndev++;
    }
}
Ejemplo n.º 25
0
Archivo: main.cpp Proyecto: klauer/pmcv
bool addToList(const char *portName, PMCVController *drv) {
    if (!PMCVListInitialized) {
        PMCVListInitialized = 1;
        ellInit(&PMCVList);
    } else if (findByPortName(portName) != NULL) {
        fprintf(stderr, "ERROR: Re-using portName=%s\n", portName);
        return false;
    }

    PMCVNode *pNode = (PMCVNode*)calloc(1, sizeof(PMCVNode));
    pNode->portName = epicsStrDup(portName);
    pNode->pController = drv;
    ellAdd(&PMCVList, (ELLNODE*)pNode);
    return true;
}
Ejemplo n.º 26
0
static void dbRecordtypeCdef(char *text) {
    dbText		*pdbCdef;
    tempListNode	*ptempListNode;
    dbRecordType	*pdbRecordType;
    
    if (!pdbbase->loadCdefs || duplicate) return;
    ptempListNode = (tempListNode *)ellFirst(&tempList);
    pdbRecordType = ptempListNode->item;
    
    pdbCdef = dbCalloc(1,sizeof(dbText));
    if (text[0] == ' ') text++;	/* strip leading space if present */
    pdbCdef->text = epicsStrDup(text);
    ellAdd(&pdbRecordType->cdefList, &pdbCdef->node);
    return;
}
Ejemplo n.º 27
0
static void chf_value(parseContext *parser, parse_result *presult)
{
    chFilter *filter = parser->filter;

    if (*presult == parse_stop || parser->depth > 0)
        return;

    parser->filter = NULL;
    if (filter->plug->fif->parse_end(filter) == parse_continue) {
        ellAdd(&parser->chan->filters, &filter->list_node);
    } else {
        freeListFree(chFilterFreeList, filter);
        *presult = parse_stop;
    }
}
Ejemplo n.º 28
0
static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg, const char *name)
{
    int status = -1;
    exitNode * pExitNode = calloc ( 1, sizeof( *pExitNode ) + (name?strlen(name):0) );

    if ( pExitNode ) {
        pExitNode->func = func;
        pExitNode->arg = arg;
        if(name)
            strcpy(pExitNode->name, name);
        ellAdd ( & pep->list, & pExitNode->node );
        status = 0;
    }
    return status;
}
Ejemplo n.º 29
0
Archivo: taskwd.c Proyecto: ukaea/epics
void taskwdMonitorAdd(const taskwdMonitor *funcs, void *usr)
{
    struct mNode *pm;

    if (funcs == NULL) return;

    taskwdInit();

    pm = &allocNode()->m;
    pm->funcs = funcs;
    pm->usr = usr;

    epicsMutexMustLock(mLock);
    ellAdd(&mList, (void *)pm);
    epicsMutexUnlock(mLock);
}
Ejemplo n.º 30
0
/** Adds an attribute to the list.
  * This is a convenience function for adding attributes to a list.  
  * It first searches the list to see if there is an existing attribute
  * with the same name.  If there is it just changes the properties of the
  * existing attribute.  If not, it creates a new attribute with the
  * specified properties. 
  * IMPORTANT: This method is only capable of creating attributes
  * of the NDAttribute base class type, not derived class attributes.
  * To add attributes of a derived class to a list the NDAttributeList::add(NDAttribute*)
  * method must be used.
  * \param[in] pName The name of the attribute to be added. 
  * \param[in] pDescription The description of the attribute.
  * \param[in] dataType The data type of the attribute.
  * \param[in] pValue A pointer to the value for this attribute.
  *
  */
NDAttribute* NDAttributeList::add(const char *pName, const char *pDescription, NDAttrDataType_t dataType, void *pValue)
{
  //const char *functionName = "NDAttributeList::add";
  NDAttribute *pAttribute;

  epicsMutexLock(this->lock);
  pAttribute = this->find(pName);
  if (pAttribute) {
    pAttribute->setDescription(pDescription);
    pAttribute->setValue(dataType, pValue);
  } else {
    pAttribute = new NDAttribute(pName, pDescription, dataType, pValue);
    ellAdd(&this->list, &pAttribute->listNode.node);
  }
  epicsMutexUnlock(this->lock);
  return(pAttribute);
}