asynStatus mar345::erase() { int numErase; int i; asynStatus status=asynSuccess; //const char *functionName = "erase"; getIntegerParam(mar345NumErase, &numErase); if (numErase < 1) numErase=1; setIntegerParam(ADStatus, mar345StatusErase); setIntegerParam(mar345NumErased, 0); callParamCallbacks(); for (i=0; i<numErase; i++) { if (epicsEventTryWait(this->abortEventId) == epicsEventWaitOK) { status = asynError; break; } epicsSnprintf(this->toServer, sizeof(this->toServer), "COMMAND ERASE"); writeServer(this->toServer); status = waitForCompletion("SCAN_DATA Ended o.k.", MAR345_COMMAND_TIMEOUT); if (status) break; setIntegerParam(mar345NumErased, i+1); callParamCallbacks(); } setIntegerParam(ADStatus, mar345StatusIdle); setIntegerParam(mar345Erase, 0); callParamCallbacks(); return(status); }
static void cancelTest(asynUser *pasynUser) { cmdInfo *pcmdInfo = (cmdInfo *)pasynUser->userPvt; threadInfo *pthreadInfo = pcmdInfo->pthreadInfo; asynStatus status; int wasQueued; fprintf(pcmdInfo->file,"%s %s cancelRequest should remove\n", pthreadInfo->threadName,pcmdInfo->message); startBusy(pthreadInfo); epicsEventTryWait(pcmdInfo->callbackDone); status = pasynManager->queueRequest(pasynUser,asynQueuePriorityLow,0.0); if(checkStatus(status,pthreadInfo,"testCancelRequest")) return; epicsThreadSleep(.01); status = pasynManager->cancelRequest(pasynUser,&wasQueued); if(checkStatus(status,pthreadInfo,"testCancelRequest")) return; fprintf(pcmdInfo->file,"%s %s cancelRequest wasQueued %d\n", pthreadInfo->threadName,pcmdInfo->message,wasQueued); if(!wasQueued) epicsEventMustWait(pcmdInfo->callbackDone); epicsThreadSleep(.04); /*wait for busy to complete*/ fprintf(pcmdInfo->file,"%s %s should find callback active\n", pthreadInfo->threadName,pcmdInfo->message); epicsEventTryWait(pcmdInfo->callbackDone); status = pasynManager->queueRequest(pasynUser,asynQueuePriorityLow,0.05); if(checkStatus(status,pthreadInfo,"testCancelRequest")) return; epicsThreadSleep(.01); status = pasynManager->cancelRequest(pasynUser,&wasQueued); if(checkStatus(status,pthreadInfo,"testCancelRequest")) return; fprintf(pcmdInfo->file,"%s %s cancelRequest wasQueued %d\n", pthreadInfo->threadName,pcmdInfo->message,wasQueued); if(!wasQueued) epicsEventMustWait(pcmdInfo->callbackDone); fprintf(pcmdInfo->file,"%s %s should find timeout active\n", pthreadInfo->threadName,pcmdInfo->message); startBusy(pthreadInfo); epicsEventTryWait(pcmdInfo->callbackDone); status = pasynManager->queueRequest(pasynUser,asynQueuePriorityLow,0.02); if(checkStatus(status,pthreadInfo,"testCancelRequest")) return; epicsThreadSleep(.03); status = pasynManager->cancelRequest(pasynUser,&wasQueued); if(checkStatus(status,pthreadInfo,"testCancelRequest")) return; fprintf(pcmdInfo->file,"%s %s cancelRequest wasQueued %d\n", pthreadInfo->threadName,pcmdInfo->message,wasQueued); if(!wasQueued) epicsEventMustWait(pcmdInfo->callbackDone); }
static void connectTest(asynUser *pasynUser) { cmdInfo *pcmdInfo = (cmdInfo *)pasynUser->userPvt; threadInfo *pthreadInfo = pcmdInfo->pthreadInfo; asynStatus status; fprintf(pcmdInfo->file,"%s connect queueRequest\n", pthreadInfo->threadName); epicsEventTryWait(pcmdInfo->callbackDone); status = pasynManager->queueRequest(pasynUser,asynQueuePriorityConnect,0.0); if(checkStatus(status,pthreadInfo,"connect")) return; epicsEventMustWait(pcmdInfo->callbackDone); }
/* * Return whether the last get completed. In safe mode, as a * side effect, copy value from shared buffer to state set local buffer. */ epicsShareFunc boolean epicsShareAPI seq_pvGetComplete(SS_ID ss, VAR_ID varId) { epicsEventId getSem = ss->getSemId[varId]; SPROG *sp = ss->sprog; CHAN *ch = sp->chan + varId; pvStat status; if (!ch->dbch) { /* Anonymous PVs always complete immediately */ if (!(sp->options & OPT_SAFE)) errlogSevPrintf(errlogMajor, "pvGetComplete(%s): user error (variable not assigned)\n", ch->varName); return TRUE; } if (!ss->getReq[varId]) { errlogSevPrintf(errlogMinor, "pvGetComplete(%s): no pending get request for this variable\n", ch->varName); return TRUE; } switch (epicsEventTryWait(getSem)) { case epicsEventWaitOK: ss->getReq[varId] = NULL; epicsEventSignal(getSem); status = check_connected(ch->dbch, metaPtr(ch,ss)); /* TODO: returning either TRUE or FALSE here seems wrong. We return TRUE, so that state sets don't hang. Still means that user code has to check status by calling pvStatus and/or pvMessage. */ if (status) return TRUE; /* In safe mode, copy value and meta data from shared buffer to ss local buffer. */ if (sp->options & OPT_SAFE) /* Copy regardless of whether dirty flag is set or not */ ss_read_buffer(ss, ch, FALSE); return TRUE; case epicsEventWaitTimeout: return FALSE; case epicsEventWaitError: ss->getReq[varId] = NULL; epicsEventSignal(getSem); errlogSevPrintf(errlogFatal, "pvGetComplete: " "epicsEventTryWait(getSemId[%d]) failure\n", varId); default: /* pacify gcc which does not understand the we checked all possibilities */ return FALSE; } }
epicsEventStatus epicsEventWaitWithTimeout(epicsEventId id, double timeOut) { rtems_id sid = (rtems_id)id; rtems_status_code sc; rtems_interval delay; extern double rtemsTicksPerSecond_double; if (timeOut <= 0.0) return epicsEventTryWait(id); SEMSTAT(1) delay = timeOut * rtemsTicksPerSecond_double; if (delay == 0) delay++; sc = rtems_semaphore_obtain (sid, RTEMS_WAIT, delay); if (sc == RTEMS_SUCCESSFUL) return epicsEventOK; else if (sc == RTEMS_TIMEOUT) return epicsEventWaitTimeout; else return epicsEventError; }
static void blockTest(asynUser *pasynUser) { cmdInfo *pcmdInfo = (cmdInfo *)pasynUser->userPvt; threadInfo *pthreadInfo = pcmdInfo->pthreadInfo; asynStatus status; status = pasynManager->blockProcessCallback(pasynUser,0); if(checkStatus(status,pthreadInfo,"testBlock")) return; fprintf(pcmdInfo->file,"%s %s first queueRequest\n", pthreadInfo->threadName,pcmdInfo->message); epicsEventTryWait(pcmdInfo->callbackDone); status = pasynManager->queueRequest(pasynUser,asynQueuePriorityLow,0.0); if(checkStatus(status,pthreadInfo,"testBlock")) return; epicsEventMustWait(pcmdInfo->callbackDone); epicsThreadSleep(.1); fprintf(pcmdInfo->file,"%s %s second queueRequest\n", pthreadInfo->threadName,pcmdInfo->message); status = pasynManager->queueRequest(pasynUser,asynQueuePriorityLow,0.0); if(checkStatus(status,pthreadInfo,"testBlock")) return; epicsEventMustWait(pcmdInfo->callbackDone); status = pasynManager->unblockProcessCallback(pasynUser,0); if(checkStatus(status,pthreadInfo,"testBlock")) return; }
static void errlogThread(void) { listenerNode *plistenerNode; int noConsoleMessage; char *pmessage; epicsAtExit(errlogExitHandler,0); while (TRUE) { epicsEventMustWait(pvtData.waitForWork); while ((pmessage = msgbufGetSend(&noConsoleMessage))) { epicsMutexMustLock(pvtData.listenerLock); if (pvtData.toConsole && !noConsoleMessage) { fprintf(pvtData.console,"%s",pmessage); fflush(pvtData.console); } plistenerNode = (listenerNode *)ellFirst(&pvtData.listenerList); while (plistenerNode) { (*plistenerNode->listener)(plistenerNode->pPrivate, pmessage); plistenerNode = (listenerNode *)ellNext(&plistenerNode->node); } epicsMutexUnlock(pvtData.listenerLock); msgbufFreeSend(); } if (pvtData.atExit) break; if (epicsEventTryWait(pvtData.flush) != epicsEventWaitOK) continue; epicsThreadSleep(.2); /*just wait an extra .2 seconds*/ epicsEventSignal(pvtData.waitForFlush); } epicsEventSignal(pvtData.waitForExit); }
/** This thread controls handling of slow events - erase, acquire, change mode */ void mar345::mar345Task() { int status = asynSuccess; int numImages, numImagesCounter; int imageMode; int acquire; double acquirePeriod; double elapsedTime, delayTime; const char *functionName = "mar345Task"; this->lock(); /* Loop forever */ while (1) { setStringParam(ADStatusMessage, "Waiting for event"); callParamCallbacks(); /* Release the lock while we wait for an event that says acquire has started, then lock again */ this->unlock(); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: waiting for start event\n", driverName, functionName); status = epicsEventWait(this->startEventId); this->lock(); switch(this->mode) { case mar345ModeErase: this->erase(); this->mode = mar345ModeIdle; break; case mar345ModeAcquire: getIntegerParam(ADImageMode, &imageMode); getIntegerParam(ADNumImages, &numImages); if (numImages < 1) numImages = 1; if (imageMode == ADImageSingle) numImages=1; for (numImagesCounter=0; numImagesCounter<numImages || (imageMode == ADImageContinuous); numImagesCounter++) { if (epicsEventTryWait(this->abortEventId) == epicsEventWaitOK) break; setIntegerParam(ADNumImagesCounter, numImagesCounter); callParamCallbacks(); status = acquireFrame(); if (status) break; /* We get out of the loop in single shot mode or if acquire was set to 0 by client */ if (imageMode == ADImageSingle) setIntegerParam(ADAcquire, 0); getIntegerParam(ADAcquire, &acquire); if (!acquire) break; /* We are in continuous or multiple mode. * Sleep until the acquire period expires or acquire is set to stop */ epicsTimeGetCurrent(&this->acqEndTime); elapsedTime = epicsTimeDiffInSeconds(&this->acqEndTime, &this->acqStartTime); getDoubleParam(ADAcquirePeriod, &acquirePeriod); delayTime = acquirePeriod - elapsedTime; if (delayTime > 0.) { setIntegerParam(ADStatus, mar345StatusWaiting); callParamCallbacks(); this->unlock(); status = epicsEventWaitWithTimeout(this->abortEventId, delayTime); this->lock(); if (status == epicsEventWaitOK) break; } } this->mode = mar345ModeIdle; setIntegerParam(ADAcquire, 0); setIntegerParam(ADStatus, mar345StatusIdle); break; case mar345ModeChange: this->changeMode(); this->mode = mar345ModeIdle; break; default: break; } /* Call the callbacks to update any changes */ callParamCallbacks(); } }
asynStatus mar345::acquireFrame() { asynStatus status=asynSuccess; epicsTimeStamp startTime, currentTime; int eraseMode; epicsEventWaitStatus waitStatus; int imageCounter; int arrayCallbacks; double acquireTime; double timeRemaining; int size, res; int shutterMode, useShutter; char tempFileName[MAX_FILENAME_LEN]; char fullFileName[MAX_FILENAME_LEN]; //const char *functionName = "acquireframe"; /* Get current values of some parameters */ getDoubleParam(ADAcquireTime, &acquireTime); getIntegerParam(ADShutterMode, &shutterMode); getIntegerParam(mar345Size, &size); getIntegerParam(mar345Res, &res); getIntegerParam(NDArrayCallbacks, &arrayCallbacks); getIntegerParam(mar345EraseMode, &eraseMode); if (shutterMode == ADShutterModeNone) useShutter=0; else useShutter=1; epicsTimeGetCurrent(&this->acqStartTime); createFileName(MAX_FILENAME_LEN, tempFileName); /* We need to append the extension */ epicsSnprintf(fullFileName, sizeof(fullFileName), "%s.mar%d", tempFileName, imageSizes[res][size]); /* Erase before exposure if set */ if (eraseMode == mar345EraseBefore) { status = this->erase(); if (status) return(status); } /* Set the the start time for the TimeRemaining counter */ epicsTimeGetCurrent(&startTime); timeRemaining = acquireTime; if (useShutter) setShutter(1); /* Wait for the exposure time using epicsEventWaitWithTimeout, * so we can abort */ epicsTimerStartDelay(this->timerId, acquireTime); setIntegerParam(ADStatus, mar345StatusExpose); callParamCallbacks(); while(1) { if (epicsEventTryWait(this->abortEventId) == epicsEventWaitOK) { status = asynError; break; } this->unlock(); waitStatus = epicsEventWaitWithTimeout(this->stopEventId, MAR345_POLL_DELAY); this->lock(); if (waitStatus == epicsEventWaitOK) { /* The acquisition was stopped before the time was complete */ epicsTimerCancel(this->timerId); break; } epicsTimeGetCurrent(¤tTime); timeRemaining = acquireTime - epicsTimeDiffInSeconds(¤tTime, &startTime); if (timeRemaining < 0.) timeRemaining = 0.; setDoubleParam(ADTimeRemaining, timeRemaining); callParamCallbacks(); } setDoubleParam(ADTimeRemaining, 0.0); if (useShutter) setShutter(0); setIntegerParam(ADStatus, mar345StatusIdle); callParamCallbacks(); // If the exposure was aborted return error if (status) return asynError; setIntegerParam(ADStatus, mar345StatusScan); callParamCallbacks(); epicsSnprintf(this->toServer, sizeof(this->toServer), "COMMAND SCAN %s", fullFileName); setStringParam(NDFullFileName, fullFileName); callParamCallbacks(); writeServer(this->toServer); status = waitForCompletion("SCAN_DATA Ended o.k.", MAR345_COMMAND_TIMEOUT); if (status) { return asynError; } getIntegerParam(NDArrayCounter, &imageCounter); imageCounter++; setIntegerParam(NDArrayCounter, imageCounter); /* Call the callbacks to update any changes */ callParamCallbacks(); /* If arrayCallbacks is set then read the file back in */ if (arrayCallbacks) { getImageData(); } /* Erase after scanning if set */ if (eraseMode == mar345EraseAfter) status = this->erase(); return status; }
/* * Get value from a channel. * TODO: add optional timeout argument. */ epicsShareFunc pvStat epicsShareAPI seq_pvGet(SS_ID ss, VAR_ID varId, enum compType compType) { SPROG *sp = ss->sprog; CHAN *ch = sp->chan + varId; pvStat status; PVREQ *req; epicsEventId getSem = ss->getSemId[varId]; DBCHAN *dbch = ch->dbch; PVMETA *meta = metaPtr(ch,ss); double tmo = seq_sync_timeout; /* Anonymous PV and safe mode, just copy from shared buffer. Note that completion is always immediate, so no distinction between SYNC and ASYNC needed. See also pvGetComplete. */ if ((sp->options & OPT_SAFE) && !dbch) { /* Copy regardless of whether dirty flag is set or not */ ss_read_buffer(ss, ch, FALSE); return pvStatOK; } /* No named PV and traditional mode => user error */ if (!dbch) { errlogSevPrintf(errlogMajor, "pvGet(%s): user error (variable not assigned)\n", ch->varName ); return pvStatERROR; } if (compType == DEFAULT) { compType = (sp->options & OPT_ASYNC) ? ASYNC : SYNC; } if (compType == SYNC) { double before, after; pvTimeGetCurrentDouble(&before); switch (epicsEventWaitWithTimeout(getSem, tmo)) { case epicsEventWaitOK: status = check_connected(dbch, meta); if (status) return epicsEventSignal(getSem), status; pvTimeGetCurrentDouble(&after); tmo -= (after - before); break; case epicsEventWaitTimeout: errlogSevPrintf(errlogMajor, "pvGet(ss %s, var %s, pv %s): failed (timeout " "waiting for other get requests to finish)\n", ss->ssName, ch->varName, dbch->dbName ); return pvStatERROR; case epicsEventWaitError: /* try to recover */ ss->getReq[varId] = NULL; epicsEventSignal(getSem); errlogSevPrintf(errlogFatal, "pvGet: epicsEventWaitWithTimeout() failure\n"); return pvStatERROR; } } else if (compType == ASYNC) { switch (epicsEventTryWait(getSem)) { case epicsEventWaitOK: if (ss->getReq[varId] != NULL) { /* previous request timed out but user did not call pvGetComplete */ ss->getReq[varId] = NULL; } status = check_connected(dbch, meta); if (status) return epicsEventSignal(getSem), status; break; case epicsEventWaitTimeout: errlogSevPrintf(errlogMajor, "pvGet(ss %s, var %s, pv %s): user error " "(there is already a get pending for this variable/" "state set combination)\n", ss->ssName, ch->varName, dbch->dbName ); return pvStatERROR; case epicsEventWaitError: /* try to recover */ ss->getReq[varId] = NULL; epicsEventSignal(getSem); errlogSevPrintf(errlogFatal, "pvGet: epicsEventTryWait() failure\n"); return pvStatERROR; } } /* Allocate and initialize a pv request */ req = (PVREQ *)freeListMalloc(sp->pvReqPool); req->ss = ss; req->ch = ch; assert(ss->getReq[varId] == NULL); ss->getReq[varId] = req; /* Perform the PV get operation with a callback routine specified. Requesting more than db channel has available is ok. */ status = pvVarGetCallback( dbch->pvid, /* PV id */ ch->type->getType, /* request type */ ch->count, /* element count */ seq_get_handler, /* callback handler */ req); /* user arg */ if (status != pvStatOK) { meta->status = pvStatERROR; meta->severity = pvSevrMAJOR; meta->message = "get failure"; errlogSevPrintf(errlogFatal, "pvGet(var %s, pv %s): pvVarGetCallback() failure: %s\n", ch->varName, dbch->dbName, pvVarGetMess(dbch->pvid)); ss->getReq[varId] = NULL; freeListFree(sp->pvReqPool, req); epicsEventSignal(getSem); check_connected(dbch, meta); return status; } /* Synchronous: wait for completion */ if (compType == SYNC) { epicsEventWaitStatus event_status; pvSysFlush(sp->pvSys); event_status = epicsEventWaitWithTimeout(getSem, tmo); ss->getReq[varId] = NULL; epicsEventSignal(getSem); switch (event_status) { case epicsEventWaitOK: status = check_connected(dbch, meta); if (status) return status; if (sp->options & OPT_SAFE) /* Copy regardless of whether dirty flag is set or not */ ss_read_buffer(ss, ch, FALSE); break; case epicsEventWaitTimeout: meta->status = pvStatTIMEOUT; meta->severity = pvSevrMAJOR; meta->message = "get completion timeout"; return meta->status; case epicsEventWaitError: meta->status = pvStatERROR; meta->severity = pvSevrMAJOR; meta->message = "get completion failure"; return meta->status; } } return pvStatOK; }
/* * Return whether the last put completed. */ epicsShareFunc boolean epicsShareAPI seq_pvPutComplete( SS_ID ss, VAR_ID varId, unsigned length, boolean any, boolean *complete) { SPROG *sp = ss->sprog; boolean anyDone = FALSE, allDone = TRUE; unsigned n; for (n = 0; n < length; n++) { epicsEventId putSem = ss->putSemId[varId+n]; boolean done = FALSE; CHAN *ch = sp->chan + varId + n; if (!ch->dbch) { /* Anonymous PVs always complete immediately */ if (!(sp->options & OPT_SAFE)) errlogSevPrintf(errlogMajor, "pvPutComplete(%s): user error (variable not assigned)\n", ch->varName); done = TRUE; } else if (!ss->putReq[varId]) { errlogSevPrintf(errlogMinor, "pvPutComplete(%s): no pending put request for this variable\n", ch->varName); done = TRUE; } else { switch (epicsEventTryWait(putSem)) { case epicsEventWaitOK: ss->putReq[varId] = NULL; epicsEventSignal(putSem); check_connected(ch->dbch, metaPtr(ch,ss)); /* TODO: returning either TRUE or FALSE here seems wrong. We return TRUE, so that state sets don't hang. Still means that user code has to check status by calling pvStatus and/or pvMessage. */ done = TRUE; break; case epicsEventWaitTimeout: break; case epicsEventWaitError: ss->putReq[varId] = NULL; epicsEventSignal(putSem); errlogSevPrintf(errlogFatal, "pvPutComplete(%s): " "epicsEventTryWait(putSem[%d]) failure\n", ch->varName, varId); break; } } anyDone = anyDone || done; allDone = allDone && done; if (complete) { complete[n] = done; } else if (any && done) { break; } } DEBUG("pvPutComplete: varId=%u, length=%u, anyDone=%u, allDone=%u\n", varId, length, anyDone, allDone); return any?anyDone:allDone; }
/* * Put a variable's value to a PV. */ epicsShareFunc pvStat epicsShareAPI seq_pvPut(SS_ID ss, VAR_ID varId, enum compType compType) { SPROG *sp = ss->sprog; CHAN *ch = sp->chan + varId; pvStat status; unsigned count; char *var = valPtr(ch,ss); /* ptr to value */ PVREQ *req; DBCHAN *dbch = ch->dbch; PVMETA *meta = metaPtr(ch,ss); epicsEventId putSem = ss->putSemId[varId]; double tmo = seq_sync_timeout; DEBUG("pvPut: pv name=%s, var=%p\n", dbch ? dbch->dbName : "<anonymous>", var); /* First handle anonymous PV (safe mode only) */ if ((sp->options & OPT_SAFE) && !dbch) { anonymous_put(ss, ch); return pvStatOK; } if (!dbch) { errlogSevPrintf(errlogMajor, "pvPut(%s): user error (variable not assigned)\n", ch->varName ); return pvStatERROR; } /* Check for channel connected */ status = check_connected(dbch, meta); if (status) return status; /* Determine whether to perform synchronous, asynchronous, or plain put ((+a) option was never honored for put, so DEFAULT means non-blocking and therefore implicitly asynchronous) */ if (compType == SYNC) { double before, after; pvTimeGetCurrentDouble(&before); switch (epicsEventWaitWithTimeout(putSem, tmo)) { case epicsEventWaitOK: pvTimeGetCurrentDouble(&after); tmo -= (after - before); break; case epicsEventWaitTimeout: errlogSevPrintf(errlogMajor, "pvPut(ss %s, var %s, pv %s): failed (timeout " "waiting for other put requests to finish)\n", ss->ssName, ch->varName, dbch->dbName ); return pvStatERROR; case epicsEventWaitError: /* try to recover */ ss->putReq[varId] = NULL; epicsEventSignal(putSem); errlogSevPrintf(errlogFatal, "pvPut: epicsEventWaitWithTimeout() failure\n"); return pvStatERROR; } } else if (compType == ASYNC) { switch (epicsEventTryWait(putSem)) { case epicsEventWaitOK: if (ss->putReq[varId] != NULL) { /* previous request timed out but user did not call pvPutComplete */ ss->putReq[varId] = NULL; } break; case epicsEventWaitTimeout: meta->status = pvStatERROR; meta->severity = pvSevrMAJOR; meta->message = "already one put pending"; status = meta->status; errlogSevPrintf(errlogMajor, "pvPut(ss %s, var %s, pv %s): user error " "(there is already a put pending for this variable/" "state set combination)\n", ss->ssName, ch->varName, dbch->dbName ); return pvStatERROR; case epicsEventWaitError: /* try to recover */ ss->putReq[varId] = NULL; epicsEventSignal(putSem); errlogSevPrintf(errlogFatal, "pvPut: epicsEventTryWait() failure\n"); return pvStatERROR; } } /* Determine number of elements to put (don't try to put more than db count) */ count = dbch->dbCount; /* Perform the PV put operation (either non-blocking or with a callback routine specified) */ if (compType == DEFAULT) { status = pvVarPutNoBlock( dbch->pvid, /* PV id */ ch->type->putType, /* data type */ count, /* element count */ (pvValue *)var); /* data value */ if (status != pvStatOK) { errlogSevPrintf(errlogFatal, "pvPut(var %s, pv %s): pvVarPutNoBlock() failure: %s\n", ch->varName, dbch->dbName, pvVarGetMess(dbch->pvid)); return status; } } else { /* Allocate and initialize a pv request */ req = (PVREQ *)freeListMalloc(sp->pvReqPool); req->ss = ss; req->ch = ch; assert(ss->putReq[varId] == NULL); ss->putReq[varId] = req; status = pvVarPutCallback( dbch->pvid, /* PV id */ ch->type->putType, /* data type */ count, /* element count */ (pvValue *)var, /* data value */ seq_put_handler, /* callback handler */ req); /* user arg */ if (status != pvStatOK) { ss->putReq[varId] = NULL; errlogSevPrintf(errlogFatal, "pvPut(var %s, pv %s): pvVarPutCallback() failure: %s\n", ch->varName, dbch->dbName, pvVarGetMess(dbch->pvid)); freeListFree(sp->pvReqPool, req); epicsEventSignal(putSem); check_connected(dbch, meta); return status; } } /* Synchronous: wait for completion (10s timeout) */ if (compType == SYNC) { epicsEventWaitStatus event_status; pvSysFlush(sp->pvSys); event_status = epicsEventWaitWithTimeout(putSem, tmo); ss->putReq[varId] = NULL; epicsEventSignal(putSem); switch (event_status) { case epicsEventWaitOK: status = check_connected(dbch, meta); if (status) return status; break; case epicsEventWaitTimeout: meta->status = pvStatTIMEOUT; meta->severity = pvSevrMAJOR; meta->message = "put completion timeout"; return meta->status; break; case epicsEventWaitError: meta->status = pvStatERROR; meta->severity = pvSevrMAJOR; meta->message = "put completion failure"; return meta->status; break; } } return pvStatOK; }