/* * sequencer() - Sequencer main thread entry point. */ void sequencer (void *arg) /* ptr to original (global) state program table */ { PROG *sp = (PROG *)arg; unsigned nss; size_t threadLen; char threadName[THREAD_NAME_SIZE+10]; /* Get this thread's id */ sp->ss->threadId = epicsThreadGetIdSelf(); /* Add the program to the program list */ seqAddProg(sp); createOrAttachPvSystem(sp); if (!pvSysIsDefined(sp->pvSys)) { sp->die = TRUE; goto exit; } /* Call sequencer init function to initialize variables. */ sp->initFunc(sp); /* Initialize state set variables. In safe mode, copy variable block to state set buffers. Must do all this before connecting. */ if (optTest(sp, OPT_SAFE)) { for (nss = 0; nss < sp->numSS; nss++) { SSCB *ss = sp->ss + nss; memcpy(ss->var, sp->var, sp->varSize); } } /* Attach to PV system */ pvSysAttach(sp->pvSys); /* Initiate connect & monitor requests to database channels, waiting for all connections to be established if the option is set. */ if (seq_connect(sp, optTest(sp, OPT_CONN) != pvStatOK)) goto exit; /* Emulate the 'first monitor event' for anonymous PVs */ if (optTest(sp, OPT_SAFE)) { unsigned nch; for (nch=0; nch<sp->numChans; nch++) if (sp->chan[nch].syncedTo && !sp->chan[nch].dbch) seq_efSet(sp->ss, sp->chan[nch].syncedTo); } /* Call program entry function if defined. Treat as if called from 1st state set. */ if (sp->entryFunc) sp->entryFunc(sp->ss); /* Create each additional state set task (additional state set thread names are derived from the first ss) */ epicsThreadGetName(sp->ss->threadId, threadName, sizeof(threadName)); threadLen = strlen(threadName); for (nss = 1; nss < sp->numSS; nss++) { SSCB *ss = sp->ss + nss; epicsThreadId tid; /* Form thread name from program name + state set number */ sprintf(threadName+threadLen, "_%d", nss); /* Spawn the task */ tid = epicsThreadCreate( threadName, /* thread name */ sp->threadPriority, /* priority */ sp->stackSize, /* stack size */ ss_entry, /* entry point */ ss); /* parameter */ DEBUG("Spawning additional state set thread %p: \"%s\"\n", tid, threadName); } /* First state set jumps directly to entry point */ ss_entry(sp->ss); DEBUG(" Wait for other state sets to exit\n"); for (nss = 1; nss < sp->numSS; nss++) { SSCB *ss = sp->ss + nss; epicsEventMustWait(ss->dead); } /* Call program exit function if defined. Treat as if called from 1st state set. */ if (sp->exitFunc) sp->exitFunc(sp->ss); exit: DEBUG(" Disconnect all channels\n"); seq_disconnect(sp); DEBUG(" Remove program instance from list\n"); seqDelProg(sp); errlogSevPrintf(errlogInfo, "Instance %d of sequencer program \"%s\" terminated\n", sp->instance, sp->progName); /* Free all allocated memory */ seq_free(sp); }
/* * ss_entry() - Thread entry point for all state sets. * Provides the main loop for state set processing. */ static void ss_entry(void *arg) { SSCB *ss = (SSCB *)arg; SPROG *sp = ss->sprog; USER_VAR *var; if (sp->options & OPT_SAFE) var = ss->var; else var = sp->var; /* Attach to PV system; was already done for the first state set */ if (ss != sp->ss) { ss->threadId = epicsThreadGetIdSelf(); pvSysAttach(sp->pvSys); } /* Register this thread with the EPICS watchdog (no callback func) */ taskwdInsert(ss->threadId, 0, 0); /* In safe mode, update local var buffer with global one before entering the event loop. Must do this using ss_read_all_buffer since CA and other state sets could already post events resp. pvPut. */ if (sp->options & OPT_SAFE) ss_read_all_buffer(sp, ss); /* Initial state is the first one */ ss->currentState = 0; ss->nextState = -1; ss->prevState = -1; DEBUG("ss %s: entering main loop\n", ss->ssName); /* * ============= Main loop ============== */ while (TRUE) { boolean ev_trig; int transNum = 0; /* highest prio trans. # triggered */ STATE *st = ss->states + ss->currentState; /* Set state to current state */ assert(ss->currentState >= 0); /* Set state set event mask to this state's event mask */ ss->mask = st->eventMask; /* If we've changed state, do any entry actions. Also do these * even if it's the same state if option to do so is enabled. */ if (st->entryFunc && (ss->prevState != ss->currentState || (st->options & OPT_DOENTRYFROMSELF))) { st->entryFunc(ss, var); } /* Flush any outstanding DB requests */ pvSysFlush(sp->pvSys); clearDelays(ss, st); /* Clear delay list */ st->delayFunc(ss, var); /* Set up new delay list */ /* Setting this semaphore here guarantees that a when() is * always executed at least once when a state is first * entered. */ epicsEventSignal(ss->syncSemId); /* Loop until an event is triggered, i.e. when() returns TRUE */ do { double delay = 0.0; /* Wake up on PV event, event flag, or expired delay */ if (calcTimeout(ss, &delay)) { DEBUG("before epicsEventWaitWithTimeout(ss=%d,delay=%f)\n", ss - sp->ss, delay); epicsEventWaitWithTimeout(ss->syncSemId, delay); DEBUG("after epicsEventWaitWithTimeout()\n"); } else { DEBUG("before epicsEventWait\n"); epicsEventWait(ss->syncSemId); DEBUG("after epicsEventWait\n"); } /* Check whether we have been asked to exit */ if (sp->die) goto exit; /* Copy dirty variable values from CA buffer * to user (safe mode only). */ if (sp->options & OPT_SAFE) ss_read_all_buffer(sp, ss); /* Check state change conditions */ ev_trig = st->eventFunc(ss, var, &transNum, &ss->nextState); /* Clear all event flags (old ef mode only) */ if (ev_trig && !(sp->options & OPT_NEWEF)) { unsigned i; for (i = 0; i < NWORDS(sp->numEvFlags); i++) { sp->evFlags[i] &= ~ss->mask[i]; } } } while (!ev_trig); /* Execute the state change action */ st->actionFunc(ss, var, transNum, &ss->nextState); /* Check whether we have been asked to exit */ if (sp->die) goto exit; /* If changing state, do exit actions */ if (st->exitFunc && (ss->currentState != ss->nextState || (st->options & OPT_DOEXITTOSELF))) { st->exitFunc(ss, var); } /* Change to next state */ ss->prevState = ss->currentState; ss->currentState = ss->nextState; } /* Thread exit has been requested */ exit: taskwdRemove(ss->threadId); /* Declare ourselves dead */ if (ss != sp->ss) epicsEventSignal(ss->dead); }