text: /*
   * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT IT.
   * This file corresponds to Version 1.7 of the STSA literate
   * program.
   */
  /*
   * This software is copyrighted 2007 - 2014 by G. Andrew
   * Mangogna.  The following terms apply to all files associated
   * with the software unless explicitly disclaimed in individual
   * files.
   *
   * The authors hereby grant permission to use, copy, modify,
   * distribute, and license this software and its documentation
   * for any purpose, provided that existing copyright notices
   * are retained in all copies and that this notice is included
   * verbatim in any distributions. No written agreement, license,
   * or royalty fee is required for any of the authorized uses.
   * Modifications to this software may be copyrighted by their
   * authors and need not follow the licensing terms described here,
   * provided that the new terms are clearly indicated on the first
   * page of each file where they apply.
   *
   * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE
   * TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
   * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE,
   * ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF THE
   * AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
   * AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN "AS IS"
   * BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION
   * TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
   * OR MODIFICATIONS.
   *
   * GOVERNMENT USE: If you are acquiring this software on behalf of
   * the U.S. government, the Government shall have only "Restricted
   * Rights" in the software and related documentation as defined in
   * the Federal Acquisition Regulations (FARs) in Clause 52.227.19
   * (c) (2).  If you are acquiring the software on behalf of the
   * Department of Defense, the software shall be classified as
   * "Commercial Computer Software" and the Government shall have
   * only "Restricted Rights" as defined in Clause 252.227-7013 (c)
   * (1) of DFARs.  Notwithstanding the foregoing, the authors
   * grant the U.S. Government and others acting in its behalf
   * permission to use and distribute the software in accordance
   * with the terms specified in this license.
  */
  #include <stdlib.h>
  #include <stdarg.h>
  #include <string.h>
  #include <stdint.h>
  #include <assert.h>
  #ifndef MECH_NINCL_STDIO
  #   include <stdio.h>
  #endif /* MECH_NINCL_STDIO */
  #include <signal.h>
  #include <errno.h>
  #include <sys/select.h>
  #include <sys/time.h>
  #include "mechs.h"
  #if defined(__GNUC__)
  #   define __WEAK  __attribute__((weak))
  #else
  #   define __WEAK
  #endif /* __GNUC__ */
  #ifdef MECH_TEST
  #   define MECH_TEST_INLINE
  #   define MECH_TEST_STATIC
  #else
  #   define MECH_TEST_INLINE  inline
  #   define MECH_TEST_STATIC  static
  #endif /* MECH_TEST */
  static void mechFatalError(MechErrorCode errNum, ...) ;
  static sigset_t mechSigMask ;
  static inline
  void
  initCriticalSection(void)
  {
      sigemptyset(&mechSigMask) ;
  }
  static inline
  void
  beginCriticalSection(void)
  {
      if (sigprocmask(SIG_BLOCK, &mechSigMask, NULL) != 0) {
          mechFatalError(mechSignalOpFailed, strerror(errno)) ;
      }
  }
  static inline
  void
  endCriticalSection(void)
  {
      if (sigprocmask(SIG_UNBLOCK, &mechSigMask, NULL) != 0) {
          mechFatalError(mechSignalOpFailed, strerror(errno)) ;
      }
  }
  static char const * const errMsgs[] = {
      "no error",     /* place holder */
      "can't happen transition: %p: %u - %u -> CH\n",
      "event in flight error: %p -> %p %u\n",
      "no available Event Control Blocks\n",
      "no available instance slots: %p\n",
      "synchronization queue overflow\n",
      #ifdef __unix__
      "interval timer operation failed: %s\n",
      "signal operation failed: %s\n",
      "blocking on pselect() failed: %s\n",
      #endif /* __unix__ */
  } ;
  static void
  MechDefaultFatalErrorHandler(
      MechErrorCode errNum,
      char const *fmt,
      va_list ap)
  {
  #   ifndef MECH_NINCL_STDIO
      vfprintf(stderr, errMsgs[errNum], ap) ;
  #   endif /* MECH_NINCL_STDIO */
  }
  static MechFatalErrHandler errHandler = MechDefaultFatalErrorHandler ;
  MechFatalErrHandler
  mechSetFatalErrHandler(
      MechFatalErrHandler newHandler)
  {
      MechFatalErrHandler prevHandler = errHandler ;
      if (newHandler) {
          errHandler = newHandler ;
      }
      return prevHandler ;
  }
  static void
  mechFatalError(
      MechErrorCode errNum,
      ...)
  {
      va_list ap ;
      /*
       * All hope is lost here. Make sure we don't
       * execute any asynchronous code.
       */
      beginCriticalSection() ;
  
      assert(errHandler != NULL) ;
      assert(errNum < (sizeof(errMsgs) / sizeof(errMsgs[0]))) ;
  
      va_start(ap, errNum) ;
      errHandler(errNum, errMsgs[errNum], ap) ;
      /*
       *  If the handler does return, we insist that all errors
       *  are fatal. So we abort() unless we are testing.
       */
  #   ifndef MECH_TEST
      abort() ;
  #   endif /* MECH_TEST */
  }
  static inline 
  void *
  mechInstNext(
      InstAllocBlock iab,
      void *ptr)
  {
      ptr = (char *)ptr + iab->instanceSize ;
      if (ptr >= iab->storageFinish) {
          ptr = iab->storageStart ;
      }
      return ptr ;
  }
  static inline 
  AllocCount
  mechInstIncrCounter(
      InstAllocBlock iab)
  {
      /*
       * Catch any wrap around to zero.
       */
      if (++iab->allocCounter == 0) {
          ++iab->allocCounter ;
      }
      return iab->allocCounter ;
  }
  static MechInstance
  mechInstFindSlot(
      MechClass instClass)
  {
      assert(instClass != NULL) ;
      InstAllocBlock iab = instClass->iab ;
      if (iab == NULL) {
          return NULL ;
      }
      assert(iab->storageLast < iab->storageFinish) ;
      /*
       * Search for an empty slot in the pool.
       */
      MechInstance inst ;
      for (inst = mechInstNext(iab, iab->storageLast) ;
              inst->alloc != 0 && inst != iab->storageLast ;
              inst = mechInstNext(iab, inst)) {
          /*
           * Empty
           */
      }
      /*
       * Check if we ended up on a slot that is not allocated.
       */
      return inst->alloc == 0 ? inst : NULL ;
  }
  MechInstance
  mechInstCreate(
      MechClass instClass,
      StateCode initialState)
  {
      assert(instClass != NULL) ;
      InstAllocBlock iab = instClass->iab ;
      #ifndef NDEBUG
      if (instClass->odb) {
          assert(initialState < instClass->odb->stateCount) ;
      }
      #endif /* NDEBUG */
  
      /*
       * Search for an empty slot in the pool.
       */
      MechInstance inst = mechInstFindSlot(instClass) ;
      if (inst == NULL) {
          mechFatalError(mechNoInstSlot, instClass) ;
      }
      /*
       * Record where we left off for the next time around.
       */
      iab->storageLast = inst ;
      /*
       * Mark the slot as in use.
       */
      inst->alloc = mechInstIncrCounter(iab) ;
      inst->currentState = initialState ;
      inst->instClass = instClass ;
      /*
       * Run the constructor if there is one.
       */
      if (iab->construct) {
          iab->construct(inst) ;
      }
      return inst ;
  }
  void
  mechInstDestroy(
      MechInstance inst)
  {
      InstAllocBlock iab = inst->instClass->iab ;
      assert(iab != NULL) ;
      /*
       * Run the destructor, if there is one.
       */
      if (iab->destruct) {
          iab->destruct(inst) ;
      }
      /*
       * Mark the slot as free.
       */
      inst->alloc = 0 ;
  }
  bool mechInstAvail(
      MechClass instClass)
  {
      return mechInstFindSlot(instClass) != NULL ;
  }
  static inline
  struct mechecb *
  eventQueueBegin(
      MechEcb iter)
  {
      return iter->next ;
  }
  static inline
  struct mechecb *
  eventQueueEnd(
      MechEcb iter)
  {
      return iter ;
  }
  static inline
  bool
  eventQueueEmpty(
      MechEcb iter)
  {
      return iter->next == iter ;
  }
  static inline
  void
  eventQueueInsert(
      MechEcb item,
      MechEcb at)
  {
      item->prev = at->prev ;
      item->next = at ;
      at->prev->next = item ;
      at->prev = item ;
  }
  static inline
  void
  eventQueueRemove(
      MechEcb item)
  {
      item->prev->next = item->next ;
      item->next->prev = item->prev ;
  }
  static struct mechecb mechECBPool[MECH_EVENTPOOLSIZE] ;
  static struct mechecb eventQueue ;
  static struct mechecb delayedEventQueue ;
  static struct mechecb freeEventQueue ;
  static void
  mechEventInit(void)
  {
      assert(MECH_EVENTPOOLSIZE >= 1) ;
      /*
       * Initialize the ECB used as the queue terminus.
       */
      eventQueue.next = eventQueue.prev = &eventQueue ;
      delayedEventQueue.next = delayedEventQueue.prev =
              &delayedEventQueue ;
      freeEventQueue.next = freeEventQueue.prev = &freeEventQueue ;
      /*
       * Place all the event control blocks on the free event
       * queue.  Allocation occurs from there.
       */
      for (MechEcb ecb = mechECBPool ;
              ecb < mechECBPool + MECH_EVENTPOOLSIZE ; ++ecb) {
          eventQueueInsert(ecb, &freeEventQueue) ;
      }
  }
  static inline 
  MechEcb
  mechEventAlloc(void)
  {
      if (eventQueueEmpty(&freeEventQueue)) {
          mechFatalError(mechNoECB) ;
      }
  
      MechEcb ecb = freeEventQueue.next ;
      eventQueueRemove(ecb) ;
      ecb->referenceCount = 0 ;
      return ecb ;
  }
  static void
  mechEventDelete(
      MechEcb ecb)
  {
      assert(ecb != NULL) ;
  
      if (ecb->referenceCount <= 1) {
          eventQueueInsert(ecb, &freeEventQueue) ;
      } else {
          --ecb->referenceCount ;
      }
  }
  static inline 
  MechEcb
  mechEventCtor(
      EventCode event,
      MechEventType type,
      MechInstance targetInst,
      MechInstance srcInst)
  {
      MechEcb ecb = mechEventAlloc() ;
  
      ecb->eventNumber = event ;
      ecb->eventType = type ;
      ecb->instOrClass.targetInst = targetInst ;
      ecb->srcInst = srcInst ;
  
      return ecb ;
  }
  MechEcb
  mechEventNew(
      EventCode event,
      MechInstance targetInst,
      MechInstance srcInst)
  {
      assert(targetInst != NULL) ;
      assert(targetInst->alloc != 0) ;
      assert(targetInst->instClass != NULL) ;
      assert(targetInst->instClass->odb != NULL) ;
  
      MechEcb ecb = mechEventCtor(event, NormalEvent, targetInst,
              srcInst) ;
      /*
       * Take a copy of the alloc member for event-in-flight
       * detection.
       */
      ecb->alloc = targetInst->alloc ;
      return ecb ;
  }
  MechEcb
  mechPolyEventNew(
      EventCode event,
      MechInstance targetInst,
      MechInstance srcInst)
  {
      assert(targetInst != NULL) ;
      assert(targetInst->instClass != NULL) ;
      assert(targetInst->instClass->pdb != NULL) ;
  
      MechEcb ecb = mechEventCtor(event, PolymorphicEvent,
              targetInst, srcInst) ;
      return ecb ;
  }
  MechEcb
  mechCreationEventNew(
      EventCode event,
      MechClass targetClass,
      MechInstance srcInst)
  {
      assert(targetClass != NULL) ;
      assert(targetClass->iab != NULL) ;
      assert(targetClass->odb != NULL) ;
  
      MechEcb ecb = mechEventAlloc() ;
  
      ecb->eventNumber = event ;
      ecb->eventType = CreationEvent ;
      ecb->instOrClass.targetClass = targetClass ;
      ecb->srcInst = srcInst ;
      ecb->alloc = 0 ;
  
      return ecb ;
  }
  static inline 
  RefCount
  mechEventIncrRef(
      MechEcb ecb)
  {
      return ++ecb->referenceCount ;
  }
  void
  mechEventPost(
      MechEcb ecb)
  {
      mechEventIncrRef(ecb) ;
      eventQueueInsert(ecb, &eventQueue) ;
  }
  void
  mechEventPostSelf(
      MechEcb ecb)
  {
      assert(ecb->instOrClass.targetInst == ecb->srcInst) ;
      /*
       * Find the first event that is not self-directed.
       */
      MechEcb iter ;
      for (iter = eventQueueBegin(&eventQueue) ;
              iter != eventQueueEnd(&eventQueue) ;
              iter = iter->next) {
          if (iter->srcInst != iter->instOrClass.targetInst) {
              break ;
          }
      }
  
      mechEventIncrRef(ecb) ;
      eventQueueInsert(ecb, iter) ;
  }
  bool
  mechEventAvail(void)
  {
      return !eventQueueEmpty(&freeEventQueue) ;
  }
  static void sysTimerStart(MechDelayTime) ;
  static MechDelayTime sysTimerStop(void) ;
  static MechEcb
  findEvent(
      MechEcb queue,
      MechInstance srcInst,
      MechInstance targetInst,
      EventCode event)
  {
      /*
       * Simple iteration through the list of events
       * in the queue.
       */
      for (MechEcb iter = eventQueueBegin(queue) ;
              iter != eventQueueEnd(queue) ;
              iter = iter->next) {
          if (iter->srcInst == srcInst &&
                  iter->instOrClass.targetInst == targetInst &&
                  iter->eventNumber == event) {
              return iter ;
          }
      }
      return NULL ;
  }
  static void
  insertIntoDelayedQueue(
      MechEcb ecb)
  {
      /*
       * We walk down the queue to find the correct slot.
       * That slot is the first place in the queue where our
       * delay value is less than the delay value at that
       * place in the queue.  As we walk the queue, we
       * subtract the delay value of each entry we pass since
       * that entry will have expired before the one being
       * inserted.
       */
      MechEcb iter ;
      for (iter = eventQueueBegin(&delayedEventQueue) ;
              iter != eventQueueEnd(&delayedEventQueue) ;
              iter = iter->next) {
          /*
           * By keeping this comparison to be strictly less
           * than, we preserve the order of event dispatch to
           * match that of event generation.
           */
          if (ecb->delay < iter->delay) {
              /*
               * We are going to insert before the entry
               * pointed to by "iter". Therefore, we need to
               * decrease its delay value by the amount of
               * time we will cause to elapse by expiring the
               * entry we are about to insert.
               */
              iter->delay -= ecb->delay ;
              break ;
          } else {
              ecb->delay -= iter->delay ;
          }
      }
      /*
       * At this point we have found our place in the queue.
       * Either we are between entries or this delay was
       * longer than the cumulative delays of all the ECB's
       * in the queue. Insert the ECB.
       */
      eventQueueInsert(ecb, iter) ;
      /*
       * Since we have stored a reference to the ECB we do
       * the bookkeeping.
       */
      mechEventIncrRef(ecb) ;
  }
  static void
  removeFromDelayedQueue(
      MechEcb ecb)
  {
      /*
       * If we are not at the end of the queue, all the delay
       * from the removed entry is accumulated on the next
       * entry in the queue.
       */
      if (ecb->next != eventQueueEnd(&delayedEventQueue)) {
          ecb->next->delay += ecb->delay ;
      }
      /*
       * Remove the ECB from the delayed queue.
       */
      eventQueueRemove(ecb) ;
      /*
       * Return the ECB back to the pool.
       */
      mechEventDelete(ecb) ;
  }
  #define MECH_DELAY_EXPIRED  UINT32_MAX
  static MechEcb
  expireDelayedEvents(void)
  {
      /*
       * Iterate along the delayed event queue.
       */
      for (MechEcb iter = eventQueueBegin(&delayedEventQueue) ;
              iter != eventQueueEnd(&delayedEventQueue) ;
              iter = iter->next) {
          if (iter->delay == 0) {
              /*
               * Mark all the events that have zero delay time
               * as expired.
               */
              iter->delay = MECH_DELAY_EXPIRED ;
          } else if (iter->delay != MECH_DELAY_EXPIRED) {
              /*
               * Stop at the first non-zero delay time.  This
               * marks the boundary of events that need
               * additional delay time.  The first such event
               * is the next amount of time to delay.
               */
              return iter ;
          }
          /*
           * else ... Skip any events that might already be
           * expired.
           */
      }
      /*
       * We have run the queue without finding an unexpired
       * event.
       */
      return NULL ;
  }
  static void
  transferExpiredEvents(void)
  {
      /*
       * Iterate through the delayed event queue looking for
       * those entries that have been marked as expired.
       */
      for (MechEcb iter = eventQueueBegin(&delayedEventQueue) ;
              iter != eventQueueEnd(&delayedEventQueue) &&
              iter->delay == MECH_DELAY_EXPIRED ; ) {
          /*
           * Advance the iterator, because we are about to
           * invalidate it by removing the entry from the
           * queue.
           */
          MechEcb ecb = iter ;
          iter = iter->next ;
  
          /*
           * Remove the ECB from the delayed queue and insert
           * it into event queue for dispatch.
           */
          eventQueueRemove(ecb) ;
          eventQueueInsert(ecb, &eventQueue) ;
          assert(ecb->referenceCount != 0) ;
      }
  }
  static void
  startDelayedQueueTiming(void)
  {
      if (!eventQueueEmpty(&delayedEventQueue)) {
          MechEcb ecb = eventQueueBegin(&delayedEventQueue) ;
          assert(ecb->delay != 0) ;
          sysTimerStart(ecb->delay) ;
          ecb->delay = 0 ;
      }
  }
  static void
  stopDelayedQueueTiming(void)
  {
      /*
       * Avoid the whole thing if there is nothing in the
       * delayed event queue.
       */
      if (!eventQueueEmpty(&delayedEventQueue)) {
          /*
           * Stop the timer, obtaining the residual time.
           */
          MechDelayTime remain = sysTimerStop() ;
          /*
           * There are two cases here. It is possible for the
           * remaining time returned from sysTimerStop() to be
           * zero. This can happen if the physical timing
           * resource (which might be running asynchronously
           * to the processor) happens to expire within a
           * single tick as we are stopping it.
           */
          if (remain == 0) {
              /*
               * Since the timer has expired we must mark any
               * events with a zero delay time value as
               * expired and, since we are running in the
               * background here, transfer the expired events
               * to be dispatched.
               */
              expireDelayedEvents() ;
              transferExpiredEvents() ;
              /*
               * At this point, either the delayed event queue
               * is empty, or the event at the head of the
               * queue has a non-zero delay time.
               */
          } else {
              /*
               * It is possible that the timing resource
               * expired and its interrupt service ran just
               * before we could get the timer stopped. That
               * would mean that there are expired events on
               * the delayed queue at this point and we need
               * to transfer them off the delayed queue to be
               * dispatched.
               */
              transferExpiredEvents() ;
              /*
               * If any events expired, the delayed event
               * queue might now be empty. However, if the
               * queue is not empty, we must make sure the
               * entry at the head preserves the remaining
               * amount of time that needs to elapse.
               */
              if (!eventQueueEmpty(&delayedEventQueue)) {
                  MechEcb ecb = eventQueueBegin(&delayedEventQueue) ;
                  assert(ecb->delay == 0) ;
                  ecb->delay = remain ;
              }
          }
      }
  }
  static inline
  MechDelayTime
  mechMsecToTicks(
      MechDelayTime msec)
  {
      return msec ;
  }
  static inline
  MechDelayTime
  mechTicksToMsec(
      MechDelayTime ticks)
  {
      return ticks ;
  }
  void
  mechEventPostDelay(
      MechEcb ecb,
      MechDelayTime time)
  {
      assert(ecb != NULL) ;
      /*
       * A delay time of 0 is valid, and the event will be
       * queued immediately.
       */
      if (time == 0) {
          mechEventPost(ecb) ;
          return ;
      }
      ecb->delay = mechMsecToTicks(time) ;
      /*
       * Stop the timing queue so we may examine it.
       */
      stopDelayedQueueTiming() ;
      /*
       * If the event already exists, remove it.
       */
      MechEcb prevEvent = findEvent(&delayedEventQueue,
              ecb->srcInst, ecb->instOrClass.targetInst,
              ecb->eventNumber) ;
      if (prevEvent) {
          removeFromDelayedQueue(prevEvent) ;
      }
      /*
       * Insert the new event.
       */
      insertIntoDelayedQueue(ecb) ;
      assert(!eventQueueEmpty(&delayedEventQueue)) ;
      /*
       * Start the timer to expire for the first event
       * on the queue.
       */
      startDelayedQueueTiming() ;
  }
  void
  mechEventDelayCancel(
      EventCode event,
      MechInstance targetInst,
      MechInstance srcInst)
  {
      assert(targetInst != NULL) ;
      /*
       * Stop delayed queue so that we may examine it.
       */
      stopDelayedQueueTiming() ;
      /*
       * Search for the event in the delayed event queue.
       */
      MechEcb foundEvent = findEvent(&delayedEventQueue,
              srcInst, targetInst, event) ;
      if (foundEvent) {
          /*
           * Removing from the delayed queue requires
           * additional processing of the delay times.
           */
          removeFromDelayedQueue(foundEvent) ;
      } else {
          /*
           * If the event is not in the delayed queue, then
           * search the event queue. The timer could have
           * expired and the event placed in the queue.
           */
          foundEvent = findEvent(&eventQueue, srcInst,
                  targetInst, event) ;
          if (foundEvent) {
              eventQueueRemove(foundEvent) ;
              mechEventDelete(foundEvent) ;
          }
          /*
           * We can get here, without finding the event in the
           * delayed queue or the event queue.
           * That's okay, it just amounts to an expensive
           * no-op and implies that the event has expired,
           * was queued and has already been dispatched or
           * had never been generated at all.
           */
      }
      startDelayedQueueTiming() ;
  }
  MechDelayTime
  mechEventDelayRemaining(
      EventCode event,
      MechInstance targetInst,
      MechInstance srcInst)
  {
      assert(targetInst != NULL) ;
  
      stopDelayedQueueTiming() ;
      /*
       * Iterate through the delayed event time and sum all
       * the delay times to give the total amount of time
       * remaining for the found event.
       */
      MechDelayTime remain = 0 ;
      MechEcb iter ;
      for (iter = eventQueueBegin(&delayedEventQueue) ;
              iter != eventQueueEnd(&delayedEventQueue) ;
              iter = iter->next) {
          remain += iter->delay ;
          if (iter->srcInst == srcInst &&
                  iter->instOrClass.targetInst == targetInst &&
                  iter->eventNumber == event) {
              break ;
          }
      }
      startDelayedQueueTiming() ;
      /*
       * Return the amount of time remaining for the event.
       * If we didn't find the event, the just return 0.
       */
      return iter == eventQueueEnd(&delayedEventQueue) ?
              0 : mechTicksToMsec(remain) ;
  }
  static void
  sysTimerMask(void)
  {
      /*
       * Make sure SIGALRM does not go off.
       */
      sigset_t mask ;
      sigemptyset(&mask) ;
      sigaddset(&mask, SIGALRM) ;
      if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0) {
          mechFatalError(mechSignalOpFailed, strerror(errno)) ;
      }
  }
  
  static void
  sysTimerUnmask(void)
  {
      /*
       * Allow SIGALRM to notify us.
       */
      sigset_t mask ;
      sigemptyset(&mask) ;
      sigaddset(&mask, SIGALRM) ;
      if (sigprocmask(SIG_UNBLOCK, &mask, NULL) != 0) {
          mechFatalError(mechSignalOpFailed, strerror(errno)) ;
      }
  }
  static void
  sysTimerStart(
      MechDelayTime time)
  {
      struct itimerval delayedEventTimer ;
  
      delayedEventTimer.it_interval.tv_sec = 0 ;
      delayedEventTimer.it_interval.tv_usec = 0 ;
      delayedEventTimer.it_value.tv_sec = time / 1000 ;
      delayedEventTimer.it_value.tv_usec = (time % 1000) * 1000 ;
  
      if (setitimer(ITIMER_REAL, &delayedEventTimer, NULL) != 0) {
          mechFatalError(mechTimerOpFailed, strerror(errno)) ;
      }
      sysTimerUnmask() ;
  }
  static MechDelayTime
  sysTimerStop(void)
  {
      sysTimerMask() ;
      /*
       * Fetch the remaining time.
       */
      struct itimerval delayedEventTimer ;
      if (getitimer(ITIMER_REAL, &delayedEventTimer) != 0) {
          mechFatalError(mechTimerOpFailed, strerror(errno)) ;
      }
      /*
       * Convert the returned time into milliseconds.
       */
      MechDelayTime remain =
              delayedEventTimer.it_value.tv_sec * 1000 +
              delayedEventTimer.it_value.tv_usec / 1000 ;
      /*
       * Set the current timer value to zero to turn it off.
       */
      memset(&delayedEventTimer, 0, sizeof(delayedEventTimer)) ;
      if (setitimer(ITIMER_REAL, &delayedEventTimer, NULL) != 0) {
          mechFatalError(mechTimerOpFailed, strerror(errno)) ;
      }
  
      return remain ;
  }
  static void
  sysTimerExpire(
      int signum)
  {
      MechDelayTime nextTime = mechTimerExpireService() ;
      if (nextTime != 0) {
          sysTimerStart(nextTime) ;
      }
  }
  static void
  sysTimerInit(void)
  {
      mechRegisterSignal(SIGALRM, sysTimerExpire) ;
  }
  static void
  mechExpiredEventService(
      SyncParamRef params) /* Not used */
  {
      /*
       * Since the expired event queue is accessed here,
       * we must make sure that the timer interrupt does not
       * go off.
       */
      sysTimerMask() ;
      transferExpiredEvents() ;
      sysTimerUnmask() ;
  }
  MechDelayTime
  mechTimerExpireService(void)
  {
      MechEcb unexpired ;
      MechDelayTime nextTime ;
      /*
       * Sync to the background to request the expired events
       * be transferred to the event queue.
       */
      mechSyncRequest(mechExpiredEventService) ;
      /*
       * Mark the delayed events as expired, returning a
       * pointer to the first unexpired event.
       */
      unexpired = expireDelayedEvents() ;
      if (unexpired) {
          /*
           * If there is an unexpired event, then its delay
           * time is the next time to expire. We return that
           * time and zero out the delay time.
           */
          assert(unexpired->delay != 0) ;
          nextTime = unexpired->delay ;
          unexpired->delay = 0 ;
      } else {
          /*
           * Otherwise, there is nothing else to time.
           */
          nextTime = 0 ;
      }
  
      return nextTime ;
  }
  #ifdef MECH_SM_TRACE
  static MechTraceCallback traceCallback ;
  
  MechTraceCallback
  mechRegisterTrace(
      MechTraceCallback cb)
  {
      MechTraceCallback oldcb = traceCallback ;
      traceCallback = cb ;
      return oldcb ;
  }
  static inline 
  void
  traceNormalEvent(
      EventCode event,
      MechInstance source,
      MechInstance target,
      StateCode currentState,
      StateCode newState)
  {
      if (traceCallback) {
          struct mechtraceinfo trace ;
  
          trace.eventType = NormalEvent ;
          trace.eventNumber = event ;
          trace.srcInst = source ;
          trace.dstInst = target ;
          trace.info.normalTrace.currState = currentState ;
          trace.info.normalTrace.newState = newState ;
  
          traceCallback(&trace) ;
      }
  }
  static inline 
  void
  tracePolyEvent(
      EventCode event,
      MechInstance source,
      MechInstance target,
      SubtypeCode subtype,
      DispatchCount hierarchy,
      EventCode newEvent,
      MechEventType newEventType)
  {
      if (traceCallback) {
          struct mechtraceinfo trace ;
  
          trace.eventType = PolymorphicEvent ;
          trace.eventNumber = event ;
          trace.srcInst = source ;
          trace.dstInst = target ;
          trace.info.polyTrace.subcode = subtype ;
          trace.info.polyTrace.hierarchy = hierarchy ;
          trace.info.polyTrace.mappedNumber = newEvent ;
          trace.info.polyTrace.mappedType = newEventType ;
  
          traceCallback(&trace) ;
      }
  }
  static inline 
  void
  traceCreationEvent(
      EventCode event,
      MechInstance source,
      MechInstance target,
      MechClass class)
  {
      if (traceCallback) {
          struct mechtraceinfo trace ;
  
          trace.eventType = CreationEvent ;
          trace.eventNumber = event ;
          trace.srcInst = source ;
          trace.dstInst = target ;
          trace.info.creationTrace.dstClass = class ;
  
          traceCallback(&trace) ;
      }
  }
  #endif /* MECH_SM_TRACE */
  static void dispatchNormalEvent(MechEcb) ;
  static void dispatchPolyEvent(MechEcb) ;
  static void dispatchCreationEvent(MechEcb) ;
  static void (* const ecbDispatchFuncs[])(MechEcb) = {
      dispatchNormalEvent,
      dispatchPolyEvent,
      dispatchCreationEvent,
  } ;
  static void
  mechDispatch(
      MechEcb ecb)
  {
      ecbDispatchFuncs[ecb->eventType](ecb) ;
  }
  static void
  dispatchNormalEvent(
      MechEcb ecb)
  {
      MechInstance target = ecb->instOrClass.targetInst ;
      ObjectDispatchBlock db = target->instClass->odb ;
  
      /*
       * Test for corruption of the current state
       * or event number.
       */
      assert(db->stateCount > target->currentState) ;
      assert(db->eventCount > ecb->eventNumber) ;
      /*
       * Check for the "event-in-flight" error. This occurs
       * when an instance is deleted while there is an event
       * for that instance in the event queue.  For this
       * architecture, such occurrences are considered as
       * run-time detected analysis errors.
       */
      if (target->alloc != ecb->alloc) {
          mechFatalError(mechEventInFlight, ecb->srcInst,
                  target, ecb->eventNumber) ;
      }
      /*
       * Fetch the new state from the transition table.
       */
      StateCode newState = *(db->transitionTable +
              target->currentState * db->eventCount +
              ecb->eventNumber) ;
  #       ifdef MECH_SM_TRACE
      /*
       * Trace the transition.
       */
      traceNormalEvent(ecb->eventNumber, ecb->srcInst,
              ecb->instOrClass.targetInst, target->currentState, newState) ;
  #       endif
      /*
       * Check for a can't happen transition.
       */
      if (newState == MECH_STATECODE_CH) {
          mechFatalError(mechCantHappen, target,
                  target->currentState, ecb->eventNumber) ;
      } else if (newState != MECH_STATECODE_IG) {
          /*
           * Check for corrupt transition table.
           */
          assert(newState < db->stateCount) ;
          /*
           * We update the current state to reflect the
           * transition before executing the action for the
           * state.
           */
          target->currentState = newState ;
          /*
           * Invoke the state action if there is one.
           */
          PtrActionFunction action = db->actionTable[newState] ;
          if (action) {
              action(target, &ecb->eventParameters) ;
          }
          /*
           * Check if we have entered a final state. If so,
           * the instance is deleted.
           */
          if (db->finalStates && db->finalStates[newState]) {
              mechInstDestroy(target) ;
          }
      }
      /*
       * Return the ECB to the pool.
       */
      mechEventDelete(ecb) ;
  }
  static void
  dispatchPolyEvent(
      MechEcb ecb)
  {
      PolyDispatchBlock pdb = ecb->instOrClass.targetInst->instClass->pdb ;
  
      assert(pdb != NULL) ;
      assert(ecb->eventNumber < pdb->eventCount) ;
      assert(pdb->hierCount > 0) ;
      /*
       * Each generalization hierarchy that originates at the
       * supertype has an event generated down that
       * hierarchy to one of the subtypes.
       */
      HierarchyDispatch hd = pdb->hierarchy ;
      for (unsigned hnum = 0 ; hnum < pdb->hierCount ; ++hnum) {
          /*
           * The most common case is to dispatch along a
           * single hierarchy.  In any case, we can modify in
           * place the input ECB on the last dispatched event.
           */
          MechEcb newEcb ;
          if (hnum == pdb->hierCount - 1) {
              newEcb = ecb ;
          } else {
              newEcb = mechEventAlloc() ;
              /*
               * We set the source as the original sender.
               */
              newEcb->srcInst = ecb->srcInst ;
              /*
               * Copy event parameters.
               */
              newEcb->eventParameters = ecb->eventParameters ;
          }
          SubtypeCode type =
                  *(SubtypeCode *)((char *)ecb->instOrClass.targetInst +
                  hd->subCodeOffset) ;
  
          assert(type < hd->subtypeCount) ;
          PolyEventMap pem = hd->eventMap +
                  (type * pdb->eventCount + ecb->eventNumber) ;
  #           ifdef MECH_SM_TRACE
          /*
           * Trace the transition.
           */
          tracePolyEvent(ecb->eventNumber, ecb->srcInst,
                  ecb->instOrClass.targetInst, type, hnum,
                  pem->event, pem->eventType) ;
  #           endif /* MECH_SM_TRACE */
  
          newEcb->eventNumber = pem->event ;
          newEcb->eventType = pem->eventType ;
  
          void *subTypeRef =
                  (char *)ecb->instOrClass.targetInst + hd->subInstOffset ;
          newEcb->instOrClass.targetInst = hd->refStorage == PolyReference ?
              /*
               * When the generalization is implemented via a
               * pointer, we need an extra level of
               * indirection to fetch the address of the
               * subtype.
               */
              *(MechInstance *)subTypeRef :
              /*
               * When the generalization is implemented by a
               * union, we need only point to the address of
               * the subtype as it is contained in the
               * supertype.
               */
              (MechInstance)subTypeRef ;
  
          if (newEcb->eventType == NormalEvent) {
              newEcb->alloc = newEcb->instOrClass.targetInst->alloc ;
              assert(newEcb->alloc != 0) ;
          }
  
          mechDispatch(newEcb) ;
          ++hd ;
      }
  }
  static void
  dispatchCreationEvent(
      MechEcb ecb)
  {
      /*
       * For creation events we must allocate an instance,
       * set the state to be the creation state (by
       * convention the creation state is 0).
       */
      MechInstance inst = mechInstCreate(ecb->instOrClass.targetClass,
              MECH_DISPATCH_CREATION_STATE) ;
  #           ifdef MECH_SM_TRACE
      /*
       * Trace the transition.
       */
      traceCreationEvent(ecb->eventNumber, ecb->srcInst,
              inst, ecb->instOrClass.targetClass) ;
  #           endif
      /*
       * Modify the event structure in place and dispatch it.
       */
      ecb->instOrClass.targetInst = inst ;
      ecb->eventType = NormalEvent ;
      ecb->alloc = ecb->instOrClass.targetInst->alloc ;
      assert(ecb->alloc != 0) ;
  
      dispatchNormalEvent(ecb) ;
  }
  typedef struct fgsyncblock {
      SyncFunc function ;
      SyncParamType params ;
  } *FgSyncBlock ;
  struct syncqueue {
      FgSyncBlock head ;
      FgSyncBlock tail ;
  } ;
  static struct fgsyncblock
  mechSyncQueueStorage[MECH_SYNCQUEUESIZE] ;
  static struct syncqueue mechSyncQueue = {
      .head = mechSyncQueueStorage,
      .tail = mechSyncQueueStorage,
  } ;
  static inline
  bool
  syncQueueEmpty(void)
  {
      return mechSyncQueue.head == mechSyncQueue.tail ;
  }
  static inline
  SyncParamRef
  syncQueuePut(
      SyncFunc f,
      bool fatal)
  {
      FgSyncBlock tail = mechSyncQueue.tail ;
      if (++mechSyncQueue.tail >=
              mechSyncQueueStorage + MECH_SYNCQUEUESIZE) {
          mechSyncQueue.tail = mechSyncQueueStorage ;
      }
      if (syncQueueEmpty()) {
          if (fatal) {
              mechFatalError(mechSyncOverflow) ;
          }
          return NULL ;
      }
  
      tail->function = f ;
      return &tail->params ;
  }
  static inline
  FgSyncBlock
  syncQueueGet(void)
  {
      FgSyncBlock head ;
  
      beginCriticalSection() ;
      if (syncQueueEmpty()) {
          head = NULL ;
      } else {
          head = mechSyncQueue.head ;
          if (++mechSyncQueue.head >=
                  mechSyncQueueStorage + MECH_SYNCQUEUESIZE) {
              mechSyncQueue.head = mechSyncQueueStorage ;
          }
      }
      endCriticalSection() ;
  
      return head ;
  }
  SyncParamRef
  mechSyncRequest(
      SyncFunc f)
  {
      return syncQueuePut(f, true) ;
  }
  SyncParamRef
  mechTrySyncRequest(
      SyncFunc f)
  {
      return syncQueuePut(f, false) ;
  }
  void
  mechRegisterSignal(
      int sigNum,
      SignalFunc func)
  {
      assert(sigNum > 0) ;
  
      struct sigaction action ;
      if (func) {
          action.sa_handler = func ;
          sigaddset(&mechSigMask, sigNum) ;
      } else {
          action.sa_handler = SIG_DFL ;
          sigdelset(&mechSigMask, sigNum) ;
      }
      sigfillset(&action.sa_mask) ;
      action.sa_flags = 0 ;
  
      int sigresult = sigaction(sigNum, &action, NULL) ;
      if (sigresult != 0) {
          mechFatalError(mechSignalOpFailed, strerror(errno)) ;
      }
  }
  typedef struct fdservicemap {
      bool set ;
      FDServiceFunc read ;
      FDServiceFunc write ;
      FDServiceFunc except ;
  } *FDServiceMap ;
  static struct fdservicemap mechFDServicePool[FD_SETSIZE] ;
  static int mechMaxFD = -1 ;
  static fd_set mechReadFDS ;
  static fd_set mechWriteFDS ;
  static fd_set mechExceptFDS ;
  void
  mechRegisterFDService(
      int fd,
      FDServiceFunc readService,
      FDServiceFunc writeService,
      FDServiceFunc exceptService)
  {
      assert(fd >= 0 && fd < FD_SETSIZE) ;
      FDServiceMap fds = mechFDServicePool + fd ;
  
      fds->read = readService ;
      if (readService) {
          FD_SET(fd, &mechReadFDS) ;
          fds->set = true ;
      } else {
          FD_CLR(fd, &mechReadFDS) ;
      }
  
      fds->write = writeService ;
      if (writeService) {
          FD_SET(fd, &mechWriteFDS) ;
          fds->set = true ;
      } else {
          FD_CLR(fd, &mechWriteFDS) ;
      }
  
      fds->except = exceptService ;
      if (exceptService) {
          FD_SET(fd, &mechExceptFDS) ;
          fds->set = true ;
      } else {
          FD_CLR(fd, &mechExceptFDS) ;
      }
  
      if (fds->read == NULL && fds->write == NULL && fds-> except == NULL) {
          if (fds->set && fd >= mechMaxFD) {
              --mechMaxFD ;
          }
          fds->set = false ;
      } else if (fds->set && fd > mechMaxFD) {
          mechMaxFD = fd ;
      }
  }
  void
  mechRemoveFDService(
      int fd,
      bool rmRead,
      bool rmWrite,
      bool rmExcept)
  {
      assert(fd >= 0 && fd < FD_SETSIZE) ;
      FDServiceMap fds = mechFDServicePool + fd ;
  
      if (rmRead) {
          fds->read = NULL ;
          FD_CLR(fd, &mechReadFDS) ;
      }
  
      if (rmWrite) {
          fds->write = NULL ;
          FD_CLR(fd, &mechWriteFDS) ;
      }
  
      if (rmExcept) {
          fds->except = NULL ;
          FD_CLR(fd, &mechExceptFDS) ;
      }
  
      if (fds->read == NULL && fds->write == NULL && fds-> except == NULL &&
              fd >= mechMaxFD) {
          mechMaxFD = fd - 1 ;
      }
  }
  static void
  fdServiceInit(void)
  {
      FD_ZERO(&mechReadFDS) ;
      FD_ZERO(&mechWriteFDS) ;
      FD_ZERO(&mechExceptFDS) ;
  }
  MECH_TEST_STATIC
  void
  mechWait(void)
  {
      beginCriticalSection() ;
      if (syncQueueEmpty()) {
          /*
           * Copy the file descriptor sets since "pselect"
           * modifies them in place upon return.
           */
          fd_set readfds ;
          memcpy(&readfds, &mechReadFDS, sizeof(readfds)) ;
          fd_set writefds ;
          memcpy(&writefds, &mechWriteFDS, sizeof(writefds)) ;
          fd_set exceptfds ;
          memcpy(&exceptfds, &mechExceptFDS, sizeof(exceptfds)) ;
          /*
           * Allow all the signals during the select. We
           * assume that when we enter this function, the
           * registered signals are masked.
           */
          sigset_t mask ;
          sigemptyset(&mask) ;
          /*
           * "mechMaxFD" holds the maximum value of any
           * registered file descriptor. We must add one to
           * get the number of file descriptors "pselect" is
           * to consider.
           */
          int r = pselect(mechMaxFD + 1, &readfds, &writefds,
                  &exceptfds, NULL, &mask) ;
          if (r == -1) {
              if (errno != EINTR) {
                  mechFatalError(mechSelectWaitFailed,
                          strerror(errno)) ;
              }
              /*
               * Got a signal while waiting. We go back to the
               * main loop on the assumption that something
               * has been placed in the sync queue.
               */
          } else {
              /*
               * Dispatch the service functions for the file
               * descriptors.
               */
              FDServiceMap s = mechFDServicePool ;
              for (int fd = 0 ; r > 0 && fd <= mechMaxFD ;
                      ++fd, ++s) {
                  /*
                   * Do exceptions first. This is only
                   * important for sockets, but without going
                   * first the OOB data processing won't
                   * work.
                   */
                  if (FD_ISSET(fd, &exceptfds)) {
                      assert(s->except != NULL) ;
                      s->except(fd) ;
                      --r ;
                  }
                  if (FD_ISSET(fd, &readfds)) {
                      assert(s->read != NULL) ;
                      s->read(fd) ;
                      --r ;
                  }
                  if (FD_ISSET(fd, &writefds)) {
                      assert(s->write != NULL) ;
                      s->write(fd) ;
                      --r ;
                  }
              }
          }
      }
      endCriticalSection() ;
  }
  static void
  sysPlatformInit(void)
  {
      fdServiceInit() ;
  }
  MECH_TEST_STATIC
  MECH_TEST_INLINE
  void
  mechInit(void)
  {
      sysPlatformInit() ;
      mechEventInit() ;
      initCriticalSection() ;
      sysTimerInit() ;
      sysDeviceInit() ;
      sysDomainInit() ;
  }
  MECH_TEST_STATIC
  MECH_TEST_INLINE
  bool
  mechInvokeOneSyncFunc(void)
  {
      bool didOne ;
      FgSyncBlock blk = syncQueueGet() ;
      if (blk && blk->function) {
          blk->function(&blk->params) ;
          didOne = true ;
      } else {
          didOne = false ;
      }
      return didOne ;
  }
  MECH_TEST_STATIC
  MECH_TEST_INLINE
  bool
  mechDispatchOneEvent(void)
  {
      bool didOne = !eventQueueEmpty(&eventQueue) ;
      if (didOne) {
          MechEcb ecb = eventQueue.next ;
          eventQueueRemove(ecb) ;
          mechDispatch(ecb) ;
      }
      return didOne ;
  }
  #ifdef MECH_TEST
  void
  stsa_main(void)
  #else
  int
  main(void)
  #endif /* MECH_TEST */
  {
      mechInit() ;
  
      for (;;) { /* Infinite Big Loop */
          /*
           * Empty the foreground/background sync queue.
           */
          #ifndef __ARM_ARCH_7M__
          /*
           * Empty the foreground / background
           * synchronization queue.
           */
          while (mechInvokeOneSyncFunc()) {
              ; /* empty */
          }
          #endif /* __ARM_ARCH_7M__ */
          /*
           * Dispatch one event from the event queue.
           */
          if (!mechDispatchOneEvent()) {
              /*
               * Check if this thread of control is complete
               * and wait if there is no additional work to
               * be done.
               */
              mechWait() ;
          }
      }
  }
  