Check-in [1e3df2a217]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Corrected a problem in removing items from the delayed event queue where expired events were not taken into account.
Timelines: family | ancestors | trunk
Files: files | file ages | folders
SHA1:1e3df2a2175bd64870b5520fe07f1543f4652c37
User & Date: andrewm 2017-05-09 23:30:14
Context
2017-05-09
23:30
Corrected a problem in removing items from the delayed event queue where expired events were not taken into account. Leaf check-in: 1e3df2a217 user: andrewm tags: trunk
2016-11-06
20:09
Build of pycca and tack for macosx. check-in: 15694b418d user: andrewm tags: trunk
Changes

Changes to mechs/code/cortex-m3/mechs.c.

     1      1   /*
     2      2    * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT IT.
     3         - * This file corresponds to Version 1.7 of the STSA literate
            3  + * This file corresponds to Version 1.8 of the STSA literate
     4      4    * program.
     5      5    */
     6      6   /*
     7      7    * This software is copyrighted 2007 - 2014 by G. Andrew
     8      8    * Mangogna.  The following terms apply to all files associated
     9      9    * with the software unless explicitly disclaimed in individual
    10     10    * files.
................................................................................
   531    531       eventQueueInsert(ecb, iter) ;
   532    532       /*
   533    533        * Since we have stored a reference to the ECB we do
   534    534        * the bookkeeping.
   535    535        */
   536    536       mechEventIncrRef(ecb) ;
   537    537   }
          538  +#define MECH_DELAY_EXPIRED  UINT32_MAX
   538    539   static void
   539    540   removeFromDelayedQueue(
   540    541       MechEcb ecb)
   541    542   {
   542    543       /*
   543    544        * If we are not at the end of the queue, all the delay
   544    545        * from the removed entry is accumulated on the next
   545    546        * entry in the queue.
   546    547        */
   547         -    if (ecb->next != eventQueueEnd(&delayedEventQueue)) {
          548  +    if (!(ecb->delay == MECH_DELAY_EXPIRED ||
          549  +            ecb->next == eventQueueEnd(&delayedEventQueue))) {
   548    550           ecb->next->delay += ecb->delay ;
   549    551       }
   550    552       /*
   551    553        * Remove the ECB from the delayed queue.
   552    554        */
   553    555       eventQueueRemove(ecb) ;
   554    556       /*
   555    557        * Return the ECB back to the pool.
   556    558        */
   557    559       mechEventDelete(ecb) ;
   558    560   }
   559         -#define MECH_DELAY_EXPIRED  UINT32_MAX
   560    561   static MechEcb
   561    562   expireDelayedEvents(void)
   562    563   {
   563    564       /*
   564    565        * Iterate along the delayed event queue.
   565    566        */
   566    567       for (MechEcb iter = eventQueueBegin(&delayedEventQueue) ;

Changes to mechs/code/cortex-m3/mechs.h.

     1      1   /*
     2      2    * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT IT.
     3         - * This file corresponds to Version 1.7 of the STSA literate
            3  + * This file corresponds to Version 1.8 of the STSA literate
     4      4    * program.
     5      5    */
     6      6   /*
     7      7    * This software is copyrighted 2007 - 2014 by G. Andrew
     8      8    * Mangogna.  The following terms apply to all files associated
     9      9    * with the software unless explicitly disclaimed in individual
    10     10    * files.

Changes to mechs/code/msp430/mechs.c.

     1      1   /*
     2      2    * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT IT.
     3         - * This file corresponds to Version 1.7 of the STSA literate
            3  + * This file corresponds to Version 1.8 of the STSA literate
     4      4    * program.
     5      5    */
     6      6   /*
     7      7    * This software is copyrighted 2007 - 2014 by G. Andrew
     8      8    * Mangogna.  The following terms apply to all files associated
     9      9    * with the software unless explicitly disclaimed in individual
    10     10    * files.
................................................................................
   517    517       eventQueueInsert(ecb, iter) ;
   518    518       /*
   519    519        * Since we have stored a reference to the ECB we do
   520    520        * the bookkeeping.
   521    521        */
   522    522       mechEventIncrRef(ecb) ;
   523    523   }
          524  +#define MECH_DELAY_EXPIRED  UINT32_MAX
   524    525   static void
   525    526   removeFromDelayedQueue(
   526    527       MechEcb ecb)
   527    528   {
   528    529       /*
   529    530        * If we are not at the end of the queue, all the delay
   530    531        * from the removed entry is accumulated on the next
   531    532        * entry in the queue.
   532    533        */
   533         -    if (ecb->next != eventQueueEnd(&delayedEventQueue)) {
          534  +    if (!(ecb->delay == MECH_DELAY_EXPIRED ||
          535  +            ecb->next == eventQueueEnd(&delayedEventQueue))) {
   534    536           ecb->next->delay += ecb->delay ;
   535    537       }
   536    538       /*
   537    539        * Remove the ECB from the delayed queue.
   538    540        */
   539    541       eventQueueRemove(ecb) ;
   540    542       /*
   541    543        * Return the ECB back to the pool.
   542    544        */
   543    545       mechEventDelete(ecb) ;
   544    546   }
   545         -#define MECH_DELAY_EXPIRED  UINT32_MAX
   546    547   static MechEcb
   547    548   expireDelayedEvents(void)
   548    549   {
   549    550       /*
   550    551        * Iterate along the delayed event queue.
   551    552        */
   552    553       for (MechEcb iter = eventQueueBegin(&delayedEventQueue) ;

Changes to mechs/code/msp430/mechs.h.

     1      1   /*
     2      2    * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT IT.
     3         - * This file corresponds to Version 1.7 of the STSA literate
            3  + * This file corresponds to Version 1.8 of the STSA literate
     4      4    * program.
     5      5    */
     6      6   /*
     7      7    * This software is copyrighted 2007 - 2014 by G. Andrew
     8      8    * Mangogna.  The following terms apply to all files associated
     9      9    * with the software unless explicitly disclaimed in individual
    10     10    * files.

Changes to mechs/code/msp430/platform.c.

     1      1   /*
     2      2    * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT IT.
     3         - * This file corresponds to Version 1.7 of the STSA literate
            3  + * This file corresponds to Version 1.8 of the STSA literate
     4      4    * program.
     5      5    */
     6      6   /*
     7      7    * This software is copyrighted 2007 - 2014 by G. Andrew
     8      8    * Mangogna.  The following terms apply to all files associated
     9      9    * with the software unless explicitly disclaimed in individual
    10     10    * files.

Changes to mechs/code/posix/mechs.c.

     1      1   /*
     2      2    * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT IT.
     3         - * This file corresponds to Version 1.7 of the STSA literate
            3  + * This file corresponds to Version 1.8 of the STSA literate
     4      4    * program.
     5      5    */
     6      6   /*
     7      7    * This software is copyrighted 2007 - 2014 by G. Andrew
     8      8    * Mangogna.  The following terms apply to all files associated
     9      9    * with the software unless explicitly disclaimed in individual
    10     10    * files.
................................................................................
   534    534       eventQueueInsert(ecb, iter) ;
   535    535       /*
   536    536        * Since we have stored a reference to the ECB we do
   537    537        * the bookkeeping.
   538    538        */
   539    539       mechEventIncrRef(ecb) ;
   540    540   }
          541  +#define MECH_DELAY_EXPIRED  UINT32_MAX
   541    542   static void
   542    543   removeFromDelayedQueue(
   543    544       MechEcb ecb)
   544    545   {
   545    546       /*
   546    547        * If we are not at the end of the queue, all the delay
   547    548        * from the removed entry is accumulated on the next
   548    549        * entry in the queue.
   549    550        */
   550         -    if (ecb->next != eventQueueEnd(&delayedEventQueue)) {
          551  +    if (!(ecb->delay == MECH_DELAY_EXPIRED ||
          552  +            ecb->next == eventQueueEnd(&delayedEventQueue))) {
   551    553           ecb->next->delay += ecb->delay ;
   552    554       }
   553    555       /*
   554    556        * Remove the ECB from the delayed queue.
   555    557        */
   556    558       eventQueueRemove(ecb) ;
   557    559       /*
   558    560        * Return the ECB back to the pool.
   559    561        */
   560    562       mechEventDelete(ecb) ;
   561    563   }
   562         -#define MECH_DELAY_EXPIRED  UINT32_MAX
   563    564   static MechEcb
   564    565   expireDelayedEvents(void)
   565    566   {
   566    567       /*
   567    568        * Iterate along the delayed event queue.
   568    569        */
   569    570       for (MechEcb iter = eventQueueBegin(&delayedEventQueue) ;

Changes to mechs/code/posix/mechs.h.

     1      1   /*
     2      2    * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT IT.
     3         - * This file corresponds to Version 1.7 of the STSA literate
            3  + * This file corresponds to Version 1.8 of the STSA literate
     4      4    * program.
     5      5    */
     6      6   /*
     7      7    * This software is copyrighted 2007 - 2014 by G. Andrew
     8      8    * Mangogna.  The following terms apply to all files associated
     9      9    * with the software unless explicitly disclaimed in individual
    10     10    * files.

Changes to mechs/doc/mechs.pdf.

cannot compute difference between binary files

Changes to mechs/src/mechs.nw.

     2      2   \usepackage{noweb}
     3      3   \usepackage{float}
     4      4   \usepackage{fixltx2e}
     5      5   \title{A Single Threaded Software Architecture for Embedded Systems
     6      6   }
     7      7   \author{G. Andrew Mangogna}
     8      8   \date{\today\\
     9         -Version 1.7}
            9  +Version 1.8}
    10     10   \pagestyle{headings}
    11     11   \begin{document}
    12     12   \bibliographystyle{plain}
    13     13   \maketitle
    14     14   \begin{copyright}
    15         -Copyright 2009 - 2014, by G. Andrew Mangogna.
           15  +Copyright 2009 - 2017, by G. Andrew Mangogna.
    16     16   Permission to copy and distribute this article by any means is hereby granted
    17     17   by the copyright holder provided the work is distributed in its entirety and
    18     18   this notice appears on all copies.
    19     19   \end{copyright}
    20     20   \begin{abstract}
    21     21   This paper is a literate program for a set of software architecture mechanisms
    22     22   that constitute the run time support for a single threaded software
................................................................................
  2010   2010       eventQueueInsert(ecb, iter) ;
  2011   2011       /*
  2012   2012        * Since we have stored a reference to the ECB we do
  2013   2013        * the bookkeeping.
  2014   2014        */
  2015   2015       mechEventIncrRef(ecb) ;
  2016   2016   }
  2017         -@
  2018         -Removing an entry from the queue is much simpler.
  2019         -The only job here is to account for the time that the entry would
  2020         -have consumed had it been left in the queue.
  2021         -That time must be added to its next neighbor (if there is one).
  2022         -<<delayed event helper>>=
  2023         -static void
  2024         -removeFromDelayedQueue(
  2025         -    MechEcb ecb)
  2026         -{
  2027         -    /*
  2028         -     * If we are not at the end of the queue, all the delay
  2029         -     * from the removed entry is accumulated on the next
  2030         -     * entry in the queue.
  2031         -     */
  2032         -    if (ecb->next != eventQueueEnd(&delayedEventQueue)) {
  2033         -        ecb->next->delay += ecb->delay ;
  2034         -    }
  2035         -    /*
  2036         -     * Remove the ECB from the delayed queue.
  2037         -     */
  2038         -    eventQueueRemove(ecb) ;
  2039         -    /*
  2040         -     * Return the ECB back to the pool.
  2041         -     */
  2042         -    mechEventDelete(ecb) ;
  2043         -}
  2044   2017   @
  2045   2018   When the timer expires there are several ways to deal with the events
  2046   2019   that need to be dispatched.
  2047   2020   One temptation would be to simply sync to the background and let code
  2048   2021   run there to remove expired events from the delayed event queue
  2049   2022   and restart the timer.
  2050   2023   Unfortunately, that would introduce considerable jitter into the timing.
................................................................................
  2053   2026   This design chooses to mark the events in the delayed event queue as
  2054   2027   expired and that provides a means of knowing which events in the delayed
  2055   2028   queue are active.
  2056   2029   Since a delay value of 0 is meaningful, we use a delay value of
  2057   2030   the maximum for a delay time as the expired marker.
  2058   2031   <<delayed event helper>>=
  2059   2032   #define MECH_DELAY_EXPIRED  UINT32_MAX
         2033  +@
         2034  +Removing an entry from the queue is much simpler.
         2035  +The only job here is to account for the time that the entry would
         2036  +have consumed had it been left in the queue.
         2037  +That time must be added to its next neighbor (if there is one).
         2038  +Note that we must also be careful to ignore any expired events.
         2039  +<<delayed event helper>>=
         2040  +static void
         2041  +removeFromDelayedQueue(
         2042  +    MechEcb ecb)
         2043  +{
         2044  +    /*
         2045  +     * If we are not at the end of the queue, all the delay
         2046  +     * from the removed entry is accumulated on the next
         2047  +     * entry in the queue.
         2048  +     */
         2049  +    if (!(ecb->delay == MECH_DELAY_EXPIRED ||
         2050  +            ecb->next == eventQueueEnd(&delayedEventQueue))) {
         2051  +        ecb->next->delay += ecb->delay ;
         2052  +    }
         2053  +    /*
         2054  +     * Remove the ECB from the delayed queue.
         2055  +     */
         2056  +    eventQueueRemove(ecb) ;
         2057  +    /*
         2058  +     * Return the ECB back to the pool.
         2059  +     */
         2060  +    mechEventDelete(ecb) ;
         2061  +}
  2060   2062   @
  2061   2063   The [[expireDelayedEvents]] function
  2062   2064   is run to traverse the delayed event queue and
  2063   2065   mark any events that have a delay value of 0 as expired.
  2064   2066   It is used both in the delayed event timer service as well as
  2065   2067   in the background processing of the delayed event queue.
  2066   2068   The return value is pointer to an ECB.
................................................................................
  5960   5962   the platform and compiler.
  5961   5963   
  5962   5964   It is convenient to keep track of the version of the literate program
  5963   5965   document in the source code.
  5964   5966   <<version info>>=
  5965   5967   /*
  5966   5968    * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT IT.
  5967         - * This file corresponds to Version 1.7 of the STSA literate
         5969  + * This file corresponds to Version 1.8 of the STSA literate
  5968   5970    * program.
  5969   5971    */
  5970   5972   @
  5971   5973   
  5972   5974   \subsection {POSIX Version}
  5973   5975   The posix version of the header file is named \texttt{mechs-posix-gcc.h}.
  5974   5976   We are only supporting \texttt{gcc} as a compiler.

Changes to mechs/tests/mechstest/build/cortex-m3/Makefile.

    47     47   	core_cm3.c\
    48     48   	log.c\
    49     49   	coreclock.c\
    50     50   	$(NULL)
    51     51   OBJS	= $(patsubst %.c,%.o,$(SRCS))
    52     52   INCLS	= $(patsubst %,-I%,$(VPATH))
    53     53   
    54         -CC = arm-stellaris-eabi-gcc
           54  +CC = arm-none-eabi-gcc
    55     55   CPPFLAGS =\
    56     56   	-DMECH_TEST\
    57     57   	-DCM3_USE_SYSTICK\
           58  +	-mcpu=cortex-m3\
           59  +	-march=armv7-m\
    58     60   	-I$(CURDIR)\
    59     61   	$(INCLS)\
    60     62   	$(NULL)
    61     63   CFLAGS	+=\
    62     64   	-std=c99\
    63     65   	-O3\
    64     66   	-g3\
................................................................................
    72     74   	-mthumb\
    73     75   	-T lm3s811-rom-hosted.ld\
    74     76   	$(NULL)
    75     77   
    76     78   all : mechstest 
    77     79   
    78     80   mechstest : $(OBJS)
    79         -	$(CC) $(LDFLAGS) -o mechstest $(OBJS)
           81  +	-$(CC) $(LDFLAGS) -o mechstest $(OBJS)
    80     82   
    81     83   mechstest.o : mechstest.c mechs.h td.h
    82     84   td.o : td.c td.h
    83     85   mechs.o : mechs.c mechs.h core_cm3.h
    84     86   log.o : log.c log.h
    85     87   core_cm3.o : core_cm3.c core_cm3.h
    86     88   mechs.h : core_cm3.h

Changes to mechs/tests/mechstest/build/posix/mechs.c.gcov.

     1      1           -:    0:Source:../../../../code/posix/mechs.c
     2      2           -:    0:Graph:mechs.gcno
     3      3           -:    0:Data:mechs.gcda
     4      4           -:    0:Runs:1
     5      5           -:    0:Programs:1
     6      6           -:    1:/*
     7      7           -:    2: * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT IT.
     8         -        -:    3: * This file corresponds to Version 1.7 of the STSA literate
            8  +        -:    3: * This file corresponds to Version 1.8 of the STSA literate
     9      9           -:    4: * program.
    10     10           -:    5: */
    11     11           -:    6:/*
    12     12           -:    7: * This software is copyrighted 2007 - 2014 by G. Andrew
    13     13           -:    8: * Mangogna.  The following terms apply to all files associated
    14     14           -:    9: * with the software unless explicitly disclaimed in individual
    15     15           -:   10: * files.
................................................................................
   539    539           9:  534:    eventQueueInsert(ecb, iter) ;
   540    540           -:  535:    /*
   541    541           -:  536:     * Since we have stored a reference to the ECB we do
   542    542           -:  537:     * the bookkeeping.
   543    543           -:  538:     */
   544    544           9:  539:    mechEventIncrRef(ecb) ;
   545    545           9:  540:}
   546         -        -:  541:static void
   547         -        3:  542:removeFromDelayedQueue(
   548         -        -:  543:    MechEcb ecb)
   549         -        -:  544:{
   550         -        -:  545:    /*
   551         -        -:  546:     * If we are not at the end of the queue, all the delay
   552         -        -:  547:     * from the removed entry is accumulated on the next
   553         -        -:  548:     * entry in the queue.
   554         -        -:  549:     */
   555         -        3:  550:    if (ecb->next != eventQueueEnd(&delayedEventQueue)) {
   556         -    #####:  551:        ecb->next->delay += ecb->delay ;
   557         -        -:  552:    }
   558         -        -:  553:    /*
   559         -        -:  554:     * Remove the ECB from the delayed queue.
   560         -        -:  555:     */
   561         -        3:  556:    eventQueueRemove(ecb) ;
   562         -        -:  557:    /*
   563         -        -:  558:     * Return the ECB back to the pool.
   564         -        -:  559:     */
   565         -        3:  560:    mechEventDelete(ecb) ;
   566         -        3:  561:}
   567         -        -:  562:#define MECH_DELAY_EXPIRED  UINT32_MAX
   568         -        -:  563:static MechEcb
   569         -        7:  564:expireDelayedEvents(void)
   570         -        -:  565:{
   571         -        -:  566:    /*
   572         -        -:  567:     * Iterate along the delayed event queue.
   573         -        -:  568:     */
   574         -       21:  569:    for (MechEcb iter = eventQueueBegin(&delayedEventQueue) ;
   575         -       14:  570:            iter != eventQueueEnd(&delayedEventQueue) ;
   576         -        7:  571:            iter = iter->next) {
   577         -        9:  572:        if (iter->delay == 0) {
   578         -        -:  573:            /*
   579         -        -:  574:             * Mark all the events that have zero delay time
   580         -        -:  575:             * as expired.
   581         -        -:  576:             */
   582         -        6:  577:            iter->delay = MECH_DELAY_EXPIRED ;
   583         -        3:  578:        } else if (iter->delay != MECH_DELAY_EXPIRED) {
   584         -        -:  579:            /*
   585         -        -:  580:             * Stop at the first non-zero delay time.  This
   586         -        -:  581:             * marks the boundary of events that need
   587         -        -:  582:             * additional delay time.  The first such event
   588         -        -:  583:             * is the next amount of time to delay.
   589         -        -:  584:             */
   590         -        2:  585:            return iter ;
   591         -        -:  586:        }
   592         -        -:  587:        /*
   593         -        -:  588:         * else ... Skip any events that might already be
   594         -        -:  589:         * expired.
   595         -        -:  590:         */
   596         -        -:  591:    }
   597         -        -:  592:    /*
   598         -        -:  593:     * We have run the queue without finding an unexpired
   599         -        -:  594:     * event.
   600         -        -:  595:     */
   601         -        5:  596:    return NULL ;
   602         -        -:  597:}
   603         -        -:  598:static void
   604         -       13:  599:transferExpiredEvents(void)
   605         -        -:  600:{
   606         -        -:  601:    /*
   607         -        -:  602:     * Iterate through the delayed event queue looking for
   608         -        -:  603:     * those entries that have been marked as expired.
   609         -        -:  604:     */
   610         -       32:  605:    for (MechEcb iter = eventQueueBegin(&delayedEventQueue) ;
   611         -       33:  606:            iter != eventQueueEnd(&delayedEventQueue) &&
   612         -       14:  607:            iter->delay == MECH_DELAY_EXPIRED ; ) {
   613         -        -:  608:        /*
   614         -        -:  609:         * Advance the iterator, because we are about to
   615         -        -:  610:         * invalidate it by removing the entry from the
   616         -        -:  611:         * queue.
   617         -        -:  612:         */
   618         -        6:  613:        MechEcb ecb = iter ;
   619         -        6:  614:        iter = iter->next ;
   620         -        -:  615:
   621         -        -:  616:        /*
   622         -        -:  617:         * Remove the ECB from the delayed queue and insert
   623         -        -:  618:         * it into event queue for dispatch.
   624         -        -:  619:         */
   625         -        6:  620:        eventQueueRemove(ecb) ;
   626         -        6:  621:        eventQueueInsert(ecb, &eventQueue) ;
   627         -        6:  622:        assert(ecb->referenceCount != 0) ;
   628         -        -:  623:    }
   629         -       13:  624:}
   630         -        -:  625:static void
   631         -       13:  626:startDelayedQueueTiming(void)
   632         -        -:  627:{
   633         -       13:  628:    if (!eventQueueEmpty(&delayedEventQueue)) {
   634         -       10:  629:        MechEcb ecb = eventQueueBegin(&delayedEventQueue) ;
   635         -       10:  630:        assert(ecb->delay != 0) ;
   636         -       10:  631:        sysTimerStart(ecb->delay) ;
   637         -       10:  632:        ecb->delay = 0 ;
   638         -        -:  633:    }
   639         -       13:  634:}
   640         -        -:  635:static void
   641         -       13:  636:stopDelayedQueueTiming(void)
   642         -        -:  637:{
   643         -        -:  638:    /*
   644         -        -:  639:     * Avoid the whole thing if there is nothing in the
   645         -        -:  640:     * delayed event queue.
   646         -        -:  641:     */
   647         -       13:  642:    if (!eventQueueEmpty(&delayedEventQueue)) {
   648         -        -:  643:        /*
   649         -        -:  644:         * Stop the timer, obtaining the residual time.
   650         -        -:  645:         */
   651         -        7:  646:        MechDelayTime remain = sysTimerStop() ;
   652         -        -:  647:        /*
   653         -        -:  648:         * There are two cases here. It is possible for the
   654         -        -:  649:         * remaining time returned from sysTimerStop() to be
   655         -        -:  650:         * zero. This can happen if the physical timing
   656         -        -:  651:         * resource (which might be running asynchronously
   657         -        -:  652:         * to the processor) happens to expire within a
   658         -        -:  653:         * single tick as we are stopping it.
   659         -        -:  654:         */
   660         -        7:  655:        if (remain == 0) {
   661         -        -:  656:            /*
   662         -        -:  657:             * Since the timer has expired we must mark any
   663         -        -:  658:             * events with a zero delay time value as
   664         -        -:  659:             * expired and, since we are running in the
   665         -        -:  660:             * background here, transfer the expired events
   666         -        -:  661:             * to be dispatched.
   667         -        -:  662:             */
   668         -        1:  663:            expireDelayedEvents() ;
   669         -        1:  664:            transferExpiredEvents() ;
   670         -        -:  665:            /*
   671         -        -:  666:             * At this point, either the delayed event queue
   672         -        -:  667:             * is empty, or the event at the head of the
   673         -        -:  668:             * queue has a non-zero delay time.
   674         -        -:  669:             */
   675         -        -:  670:        } else {
   676         -        -:  671:            /*
   677         -        -:  672:             * It is possible that the timing resource
   678         -        -:  673:             * expired and its interrupt service ran just
   679         -        -:  674:             * before we could get the timer stopped. That
   680         -        -:  675:             * would mean that there are expired events on
   681         -        -:  676:             * the delayed queue at this point and we need
   682         -        -:  677:             * to transfer them off the delayed queue to be
   683         -        -:  678:             * dispatched.
   684         -        -:  679:             */
   685         -        6:  680:            transferExpiredEvents() ;
   686         -        -:  681:            /*
   687         -        -:  682:             * If any events expired, the delayed event
   688         -        -:  683:             * queue might now be empty. However, if the
   689         -        -:  684:             * queue is not empty, we must make sure the
   690         -        -:  685:             * entry at the head preserves the remaining
   691         -        -:  686:             * amount of time that needs to elapse.
   692         -        -:  687:             */
   693         -        6:  688:            if (!eventQueueEmpty(&delayedEventQueue)) {
   694         -        6:  689:                MechEcb ecb = eventQueueBegin(&delayedEventQueue) ;
   695         -        6:  690:                assert(ecb->delay == 0) ;
   696         -        6:  691:                ecb->delay = remain ;
   697         -        -:  692:            }
   698         -        -:  693:        }
   699         -        -:  694:    }
   700         -       13:  695:}
   701         -        -:  696:static inline
   702         -        -:  697:MechDelayTime
   703         -        9:  698:mechMsecToTicks(
   704         -        -:  699:    MechDelayTime msec)
   705         -        -:  700:{
   706         -        9:  701:    return msec ;
   707         -        -:  702:}
   708         -        -:  703:static inline
   709         -        -:  704:MechDelayTime
   710         -        1:  705:mechTicksToMsec(
   711         -        -:  706:    MechDelayTime ticks)
   712         -        -:  707:{
   713         -        1:  708:    return ticks ;
   714         -        -:  709:}
   715         -        -:  710:void
   716         -        9:  711:mechEventPostDelay(
   717         -        -:  712:    MechEcb ecb,
   718         -        -:  713:    MechDelayTime time)
   719         -        -:  714:{
   720         -        9:  715:    assert(ecb != NULL) ;
   721         -        -:  716:    /*
   722         -        -:  717:     * A delay time of 0 is valid, and the event will be
   723         -        -:  718:     * queued immediately.
   724         -        -:  719:     */
   725         -        9:  720:    if (time == 0) {
   726         -    #####:  721:        mechEventPost(ecb) ;
   727         -        9:  722:        return ;
   728         -        -:  723:    }
   729         -        9:  724:    ecb->delay = mechMsecToTicks(time) ;
   730         -        -:  725:    /*
   731         -        -:  726:     * Stop the timing queue so we may examine it.
   732         -        -:  727:     */
   733         -        9:  728:    stopDelayedQueueTiming() ;
   734         -        -:  729:    /*
   735         -        -:  730:     * If the event already exists, remove it.
   736         -        -:  731:     */
   737         -        9:  732:    MechEcb prevEvent = findEvent(&delayedEventQueue,
   738         -        -:  733:            ecb->srcInst, ecb->instOrClass.targetInst,
   739         -        9:  734:            ecb->eventNumber) ;
   740         -        9:  735:    if (prevEvent) {
   741         -        1:  736:        removeFromDelayedQueue(prevEvent) ;
   742         -        -:  737:    }
   743         -        -:  738:    /*
   744         -        -:  739:     * Insert the new event.
   745         -        -:  740:     */
   746         -        9:  741:    insertIntoDelayedQueue(ecb) ;
   747         -        9:  742:    assert(!eventQueueEmpty(&delayedEventQueue)) ;
   748         -        -:  743:    /*
   749         -        -:  744:     * Start the timer to expire for the first event
   750         -        -:  745:     * on the queue.
   751         -        -:  746:     */
   752         -        9:  747:    startDelayedQueueTiming() ;
   753         -        -:  748:}
   754         -        -:  749:void
   755         -        3:  750:mechEventDelayCancel(
   756         -        -:  751:    EventCode event,
   757         -        -:  752:    MechInstance targetInst,
   758         -        -:  753:    MechInstance srcInst)
   759         -        -:  754:{
   760         -        3:  755:    assert(targetInst != NULL) ;
   761         -        -:  756:    /*
   762         -        -:  757:     * Stop delayed queue so that we may examine it.
   763         -        -:  758:     */
   764         -        3:  759:    stopDelayedQueueTiming() ;
   765         -        -:  760:    /*
   766         -        -:  761:     * Search for the event in the delayed event queue.
   767         -        -:  762:     */
   768         -        3:  763:    MechEcb foundEvent = findEvent(&delayedEventQueue,
   769         -        -:  764:            srcInst, targetInst, event) ;
   770         -        3:  765:    if (foundEvent) {
   771         -        -:  766:        /*
   772         -        -:  767:         * Removing from the delayed queue requires
   773         -        -:  768:         * additional processing of the delay times.
   774         -        -:  769:         */
   775         -        2:  770:        removeFromDelayedQueue(foundEvent) ;
   776         -        -:  771:    } else {
   777         -        -:  772:        /*
   778         -        -:  773:         * If the event is not in the delayed queue, then
   779         -        -:  774:         * search the event queue. The timer could have
   780         -        -:  775:         * expired and the event placed in the queue.
   781         -        -:  776:         */
   782         -        1:  777:        foundEvent = findEvent(&eventQueue, srcInst,
   783         -        -:  778:                targetInst, event) ;
   784         -        1:  779:        if (foundEvent) {
   785         -        1:  780:            eventQueueRemove(foundEvent) ;
   786         -        1:  781:            mechEventDelete(foundEvent) ;
   787         -        -:  782:        }
   788         -        -:  783:        /*
   789         -        -:  784:         * We can get here, without finding the event in the
   790         -        -:  785:         * delayed queue or the event queue.
   791         -        -:  786:         * That's okay, it just amounts to an expensive
   792         -        -:  787:         * no-op and implies that the event has expired,
   793         -        -:  788:         * was queued and has already been dispatched or
   794         -        -:  789:         * had never been generated at all.
   795         -        -:  790:         */
   796         -        -:  791:    }
   797         -        3:  792:    startDelayedQueueTiming() ;
   798         -        3:  793:}
   799         -        -:  794:MechDelayTime
   800         -        1:  795:mechEventDelayRemaining(
   801         -        -:  796:    EventCode event,
   802         -        -:  797:    MechInstance targetInst,
   803         -        -:  798:    MechInstance srcInst)
   804         -        -:  799:{
   805         -        1:  800:    assert(targetInst != NULL) ;
   806         -        -:  801:
   807         -        1:  802:    stopDelayedQueueTiming() ;
   808         -        -:  803:    /*
   809         -        -:  804:     * Iterate through the delayed event time and sum all
   810         -        -:  805:     * the delay times to give the total amount of time
   811         -        -:  806:     * remaining for the found event.
   812         -        -:  807:     */
   813         -        1:  808:    MechDelayTime remain = 0 ;
   814         -        -:  809:    MechEcb iter ;
   815         -        2:  810:    for (iter = eventQueueBegin(&delayedEventQueue) ;
   816         -        1:  811:            iter != eventQueueEnd(&delayedEventQueue) ;
   817         -    #####:  812:            iter = iter->next) {
   818         -        1:  813:        remain += iter->delay ;
   819         -        2:  814:        if (iter->srcInst == srcInst &&
   820         -        2:  815:                iter->instOrClass.targetInst == targetInst &&
   821         -        1:  816:                iter->eventNumber == event) {
   822         -        1:  817:            break ;
   823         -        -:  818:        }
   824         -        -:  819:    }
   825         -        1:  820:    startDelayedQueueTiming() ;
   826         -        -:  821:    /*
   827         -        -:  822:     * Return the amount of time remaining for the event.
   828         -        -:  823:     * If we didn't find the event, the just return 0.
   829         -        -:  824:     */
   830         -        2:  825:    return iter == eventQueueEnd(&delayedEventQueue) ?
   831         -        1:  826:            0 : mechTicksToMsec(remain) ;
   832         -        -:  827:}
   833         -        -:  828:static void
   834         -       13:  829:sysTimerMask(void)
   835         -        -:  830:{
   836         -        -:  831:    /*
   837         -        -:  832:     * Make sure SIGALRM does not go off.
   838         -        -:  833:     */
   839         -        -:  834:    sigset_t mask ;
   840         -       13:  835:    sigemptyset(&mask) ;
   841         -       13:  836:    sigaddset(&mask, SIGALRM) ;
   842         -       13:  837:    if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0) {
   843         -    #####:  838:        mechFatalError(mechSignalOpFailed, strerror(errno)) ;
   844         -        -:  839:    }
   845         -       13:  840:}
   846         -        -:  841:
   847         -        -:  842:static void
   848         -       18:  843:sysTimerUnmask(void)
   849         -        -:  844:{
   850         -        -:  845:    /*
   851         -        -:  846:     * Allow SIGALRM to notify us.
   852         -        -:  847:     */
   853         -        -:  848:    sigset_t mask ;
   854         -       18:  849:    sigemptyset(&mask) ;
   855         -       18:  850:    sigaddset(&mask, SIGALRM) ;
   856         -       18:  851:    if (sigprocmask(SIG_UNBLOCK, &mask, NULL) != 0) {
   857         -    #####:  852:        mechFatalError(mechSignalOpFailed, strerror(errno)) ;
   858         -        -:  853:    }
   859         -       18:  854:}
   860         -        -:  855:static void
   861         -       12:  856:sysTimerStart(
   862         -        -:  857:    MechDelayTime time)
   863         -        -:  858:{
   864         -        -:  859:    struct itimerval delayedEventTimer ;
   865         -        -:  860:
   866         -       12:  861:    delayedEventTimer.it_interval.tv_sec = 0 ;
   867         -       12:  862:    delayedEventTimer.it_interval.tv_usec = 0 ;
   868         -       12:  863:    delayedEventTimer.it_value.tv_sec = time / 1000 ;
   869         -       12:  864:    delayedEventTimer.it_value.tv_usec = (time % 1000) * 1000 ;
   870         -        -:  865:
   871         -       12:  866:    if (setitimer(ITIMER_REAL, &delayedEventTimer, NULL) != 0) {
   872         -    #####:  867:        mechFatalError(mechTimerOpFailed, strerror(errno)) ;
   873         -        -:  868:    }
   874         -       12:  869:    sysTimerUnmask() ;
   875         -       12:  870:}
   876         -        -:  871:static MechDelayTime
   877         -        7:  872:sysTimerStop(void)
   878         -        -:  873:{
   879         -        7:  874:    sysTimerMask() ;
   880         -        -:  875:    /*
   881         -        -:  876:     * Fetch the remaining time.
   882         -        -:  877:     */
   883         -        -:  878:    struct itimerval delayedEventTimer ;
   884         -        7:  879:    if (getitimer(ITIMER_REAL, &delayedEventTimer) != 0) {
   885         -    #####:  880:        mechFatalError(mechTimerOpFailed, strerror(errno)) ;
   886         -        -:  881:    }
   887         -        -:  882:    /*
   888         -        -:  883:     * Convert the returned time into milliseconds.
   889         -        -:  884:     */
   890         -        7:  885:    MechDelayTime remain =
   891         -       14:  886:            delayedEventTimer.it_value.tv_sec * 1000 +
   892         -        7:  887:            delayedEventTimer.it_value.tv_usec / 1000 ;
   893         -        -:  888:    /*
   894         -        -:  889:     * Set the current timer value to zero to turn it off.
   895         -        -:  890:     */
   896         -        7:  891:    memset(&delayedEventTimer, 0, sizeof(delayedEventTimer)) ;
   897         -        7:  892:    if (setitimer(ITIMER_REAL, &delayedEventTimer, NULL) != 0) {
   898         -    #####:  893:        mechFatalError(mechTimerOpFailed, strerror(errno)) ;
   899         -        -:  894:    }
   900         -        -:  895:
   901         -        7:  896:    return remain ;
   902         -        -:  897:}
   903         -        -:  898:static void
   904         -        6:  899:sysTimerExpire(
   905         -        -:  900:    int signum)
   906         -        -:  901:{
   907         -        6:  902:    MechDelayTime nextTime = mechTimerExpireService() ;
   908         -        6:  903:    if (nextTime != 0) {
   909         -        2:  904:        sysTimerStart(nextTime) ;
   910         -        -:  905:    }
   911         -        6:  906:}
   912         -        -:  907:static void
   913         -        1:  908:sysTimerInit(void)
   914         -        -:  909:{
   915         -        1:  910:    mechRegisterSignal(SIGALRM, sysTimerExpire) ;
   916         -        1:  911:}
   917         -        -:  912:static void
   918         -        6:  913:mechExpiredEventService(
   919         -        -:  914:    SyncParamRef params) /* Not used */
   920         -        -:  915:{
   921         -        -:  916:    /*
   922         -        -:  917:     * Since the expired event queue is accessed here,
   923         -        -:  918:     * we must make sure that the timer interrupt does not
   924         -        -:  919:     * go off.
   925         -        -:  920:     */
   926         -        6:  921:    sysTimerMask() ;
   927         -        6:  922:    transferExpiredEvents() ;
   928         -        6:  923:    sysTimerUnmask() ;
   929         -        6:  924:}
   930         -        -:  925:MechDelayTime
   931         -        6:  926:mechTimerExpireService(void)
   932         -        -:  927:{
   933         -        -:  928:    MechEcb unexpired ;
   934         -        -:  929:    MechDelayTime nextTime ;
   935         -        -:  930:    /*
   936         -        -:  931:     * Sync to the background to request the expired events
   937         -        -:  932:     * be transferred to the event queue.
   938         -        -:  933:     */
   939         -        6:  934:    mechSyncRequest(mechExpiredEventService) ;
   940         -        -:  935:    /*
   941         -        -:  936:     * Mark the delayed events as expired, returning a
   942         -        -:  937:     * pointer to the first unexpired event.
   943         -        -:  938:     */
   944         -        6:  939:    unexpired = expireDelayedEvents() ;
   945         -        6:  940:    if (unexpired) {
   946         -        -:  941:        /*
   947         -        -:  942:         * If there is an unexpired event, then its delay
   948         -        -:  943:         * time is the next time to expire. We return that
   949         -        -:  944:         * time and zero out the delay time.
   950         -        -:  945:         */
   951         -        2:  946:        assert(unexpired->delay != 0) ;
   952         -        2:  947:        nextTime = unexpired->delay ;
   953         -        2:  948:        unexpired->delay = 0 ;
   954         -        -:  949:    } else {
   955         -        -:  950:        /*
   956         -        -:  951:         * Otherwise, there is nothing else to time.
   957         -        -:  952:         */
   958         -        4:  953:        nextTime = 0 ;
   959         -        -:  954:    }
   960         -        -:  955:
   961         -        6:  956:    return nextTime ;
   962         -        -:  957:}
   963         -        -:  958:#ifdef MECH_SM_TRACE
   964         -        -:  959:static MechTraceCallback traceCallback ;
   965         -        -:  960:
   966         -        -:  961:MechTraceCallback
   967         -        -:  962:mechRegisterTrace(
   968         -        -:  963:    MechTraceCallback cb)
   969         -        -:  964:{
   970         -        -:  965:    MechTraceCallback oldcb = traceCallback ;
   971         -        -:  966:    traceCallback = cb ;
   972         -        -:  967:    return oldcb ;
   973         -        -:  968:}
   974         -        -:  969:static inline 
   975         -        -:  970:void
   976         -        -:  971:traceNormalEvent(
   977         -        -:  972:    EventCode event,
   978         -        -:  973:    MechInstance source,
   979         -        -:  974:    MechInstance target,
   980         -        -:  975:    StateCode currentState,
   981         -        -:  976:    StateCode newState)
   982         -        -:  977:{
   983         -        -:  978:    if (traceCallback) {
   984         -        -:  979:        struct mechtraceinfo trace ;
   985         -        -:  980:
   986         -        -:  981:        trace.eventType = NormalEvent ;
   987         -        -:  982:        trace.eventNumber = event ;
   988         -        -:  983:        trace.srcInst = source ;
   989         -        -:  984:        trace.dstInst = target ;
   990         -        -:  985:        trace.info.normalTrace.currState = currentState ;
   991         -        -:  986:        trace.info.normalTrace.newState = newState ;
   992         -        -:  987:
   993         -        -:  988:        traceCallback(&trace) ;
   994         -        -:  989:    }
   995         -        -:  990:}
   996         -        -:  991:static inline 
   997         -        -:  992:void
   998         -        -:  993:tracePolyEvent(
   999         -        -:  994:    EventCode event,
  1000         -        -:  995:    MechInstance source,
  1001         -        -:  996:    MechInstance target,
  1002         -        -:  997:    SubtypeCode subtype,
  1003         -        -:  998:    DispatchCount hierarchy,
  1004         -        -:  999:    EventCode newEvent,
  1005         -        -: 1000:    MechEventType newEventType)
  1006         -        -: 1001:{
  1007         -        -: 1002:    if (traceCallback) {
  1008         -        -: 1003:        struct mechtraceinfo trace ;
  1009         -        -: 1004:
  1010         -        -: 1005:        trace.eventType = PolymorphicEvent ;
  1011         -        -: 1006:        trace.eventNumber = event ;
  1012         -        -: 1007:        trace.srcInst = source ;
  1013         -        -: 1008:        trace.dstInst = target ;
  1014         -        -: 1009:        trace.info.polyTrace.subcode = subtype ;
  1015         -        -: 1010:        trace.info.polyTrace.hierarchy = hierarchy ;
  1016         -        -: 1011:        trace.info.polyTrace.mappedNumber = newEvent ;
  1017         -        -: 1012:        trace.info.polyTrace.mappedType = newEventType ;
  1018         -        -: 1013:
  1019         -        -: 1014:        traceCallback(&trace) ;
  1020         -        -: 1015:    }
  1021         -        -: 1016:}
  1022         -        -: 1017:static inline 
  1023         -        -: 1018:void
  1024         -        -: 1019:traceCreationEvent(
  1025         -        -: 1020:    EventCode event,
  1026         -        -: 1021:    MechInstance source,
  1027         -        -: 1022:    MechInstance target,
  1028         -        -: 1023:    MechClass class)
  1029         -        -: 1024:{
  1030         -        -: 1025:    if (traceCallback) {
  1031         -        -: 1026:        struct mechtraceinfo trace ;
  1032         -        -: 1027:
  1033         -        -: 1028:        trace.eventType = CreationEvent ;
  1034         -        -: 1029:        trace.eventNumber = event ;
  1035         -        -: 1030:        trace.srcInst = source ;
  1036         -        -: 1031:        trace.dstInst = target ;
  1037         -        -: 1032:        trace.info.creationTrace.dstClass = class ;
  1038         -        -: 1033:
  1039         -        -: 1034:        traceCallback(&trace) ;
  1040         -        -: 1035:    }
  1041         -        -: 1036:}
  1042         -        -: 1037:#endif /* MECH_SM_TRACE */
  1043         -        -: 1038:static void dispatchNormalEvent(MechEcb) ;
  1044         -        -: 1039:static void dispatchPolyEvent(MechEcb) ;
  1045         -        -: 1040:static void dispatchCreationEvent(MechEcb) ;
  1046         -        -: 1041:static void (* const ecbDispatchFuncs[])(MechEcb) = {
  1047         -        -: 1042:    dispatchNormalEvent,
  1048         -        -: 1043:    dispatchPolyEvent,
  1049         -        -: 1044:    dispatchCreationEvent,
  1050         -        -: 1045:} ;
  1051         -        -: 1046:static void
  1052         -       23: 1047:mechDispatch(
  1053         -        -: 1048:    MechEcb ecb)
  1054         -        -: 1049:{
  1055         -       23: 1050:    ecbDispatchFuncs[ecb->eventType](ecb) ;
  1056         -       23: 1051:}
  1057         -        -: 1052:static void
  1058         -       21: 1053:dispatchNormalEvent(
  1059         -        -: 1054:    MechEcb ecb)
  1060         -        -: 1055:{
  1061         -       21: 1056:    MechInstance target = ecb->instOrClass.targetInst ;
  1062         -       21: 1057:    ObjectDispatchBlock db = target->instClass->odb ;
  1063         -        -: 1058:
  1064         -        -: 1059:    /*
  1065         -        -: 1060:     * Test for corruption of the current state
  1066         -        -: 1061:     * or event number.
  1067         -        -: 1062:     */
  1068         -       21: 1063:    assert(db->stateCount > target->currentState) ;
  1069         -       21: 1064:    assert(db->eventCount > ecb->eventNumber) ;
  1070         -        -: 1065:    /*
  1071         -        -: 1066:     * Check for the "event-in-flight" error. This occurs
  1072         -        -: 1067:     * when an instance is deleted while there is an event
  1073         -        -: 1068:     * for that instance in the event queue.  For this
  1074         -        -: 1069:     * architecture, such occurrences are considered as
  1075         -        -: 1070:     * run-time detected analysis errors.
  1076         -        -: 1071:     */
  1077         -       21: 1072:    if (target->alloc != ecb->alloc) {
  1078         -    #####: 1073:        mechFatalError(mechEventInFlight, ecb->srcInst,
  1079         -    #####: 1074:                target, ecb->eventNumber) ;
  1080         -        -: 1075:    }
  1081         -        -: 1076:    /*
  1082         -        -: 1077:     * Fetch the new state from the transition table.
  1083         -        -: 1078:     */
  1084         -       42: 1079:    StateCode newState = *(db->transitionTable +
  1085         -       42: 1080:            target->currentState * db->eventCount +
  1086         -       21: 1081:            ecb->eventNumber) ;
  1087         -        -: 1082:#       ifdef MECH_SM_TRACE
  1088         -        -: 1083:    /*
  1089         -        -: 1084:     * Trace the transition.
  1090         -        -: 1085:     */
  1091         -        -: 1086:    traceNormalEvent(ecb->eventNumber, ecb->srcInst,
  1092         -        -: 1087:            ecb->instOrClass.targetInst, target->currentState, newState) ;
  1093         -        -: 1088:#       endif
  1094         -        -: 1089:    /*
  1095         -        -: 1090:     * Check for a can't happen transition.
  1096         -        -: 1091:     */
  1097         -       21: 1092:    if (newState == MECH_STATECODE_CH) {
  1098         -        2: 1093:        mechFatalError(mechCantHappen, target,
  1099         -        2: 1094:                target->currentState, ecb->eventNumber) ;
  1100         -       20: 1095:    } else if (newState != MECH_STATECODE_IG) {
  1101         -        -: 1096:        /*
  1102         -        -: 1097:         * Check for corrupt transition table.
  1103         -        -: 1098:         */
  1104         -       20: 1099:        assert(newState < db->stateCount) ;
  1105         -        -: 1100:        /*
  1106         -        -: 1101:         * We update the current state to reflect the
  1107         -        -: 1102:         * transition before executing the action for the
  1108         -        -: 1103:         * state.
  1109         -        -: 1104:         */
  1110         -       20: 1105:        target->currentState = newState ;
  1111         -        -: 1106:        /*
  1112         -        -: 1107:         * Invoke the state action if there is one.
  1113         -        -: 1108:         */
  1114         -       20: 1109:        PtrActionFunction action = db->actionTable[newState] ;
  1115         -       20: 1110:        if (action) {
  1116         -       20: 1111:            action(target, &ecb->eventParameters) ;
  1117         -        -: 1112:        }
  1118         -        -: 1113:        /*
  1119         -        -: 1114:         * Check if we have entered a final state. If so,
  1120         -        -: 1115:         * the instance is deleted.
  1121         -        -: 1116:         */
  1122         -       20: 1117:        if (db->finalStates && db->finalStates[newState]) {
  1123         -        1: 1118:            mechInstDestroy(target) ;
  1124         -        -: 1119:        }
  1125         -        -: 1120:    }
  1126         -        -: 1121:    /*
  1127         -        -: 1122:     * Return the ECB to the pool.
  1128         -        -: 1123:     */
  1129         -       21: 1124:    mechEventDelete(ecb) ;
  1130         -       21: 1125:}
  1131         -        -: 1126:static void
  1132         -        2: 1127:dispatchPolyEvent(
  1133         -        -: 1128:    MechEcb ecb)
  1134         -        -: 1129:{
  1135         -        2: 1130:    PolyDispatchBlock pdb = ecb->instOrClass.targetInst->instClass->pdb ;
  1136         -        -: 1131:
  1137         -        2: 1132:    assert(pdb != NULL) ;
  1138         -        2: 1133:    assert(ecb->eventNumber < pdb->eventCount) ;
  1139         -        2: 1134:    assert(pdb->hierCount > 0) ;
  1140         -        -: 1135:    /*
  1141         -        -: 1136:     * Each generalization hierarchy that originates at the
  1142         -        -: 1137:     * supertype has an event generated down that
  1143         -        -: 1138:     * hierarchy to one of the subtypes.
  1144         -        -: 1139:     */
  1145         -        2: 1140:    HierarchyDispatch hd = pdb->hierarchy ;
  1146         -        4: 1141:    for (unsigned hnum = 0 ; hnum < pdb->hierCount ; ++hnum) {
  1147         -        -: 1142:        /*
  1148         -        -: 1143:         * The most common case is to dispatch along a
  1149         -        -: 1144:         * single hierarchy.  In any case, we can modify in
  1150         -        -: 1145:         * place the input ECB on the last dispatched event.
  1151         -        -: 1146:         */
  1152         -        -: 1147:        MechEcb newEcb ;
  1153         -        2: 1148:        if (hnum == pdb->hierCount - 1) {
  1154         -        2: 1149:            newEcb = ecb ;
  1155         -        -: 1150:        } else {
  1156         -    #####: 1151:            newEcb = mechEventAlloc() ;
  1157         -        -: 1152:            /*
  1158         -        -: 1153:             * We set the source as the original sender.
  1159         -        -: 1154:             */
  1160         -    #####: 1155:            newEcb->srcInst = ecb->srcInst ;
  1161         -        -: 1156:            /*
  1162         -        -: 1157:             * Copy event parameters.
  1163         -        -: 1158:             */
  1164         -    #####: 1159:            newEcb->eventParameters = ecb->eventParameters ;
  1165         -        -: 1160:        }
  1166         -        2: 1161:        SubtypeCode type =
  1167         -        4: 1162:                *(SubtypeCode *)((char *)ecb->instOrClass.targetInst +
  1168         -        2: 1163:                hd->subCodeOffset) ;
  1169         -        -: 1164:
  1170         -        2: 1165:        assert(type < hd->subtypeCount) ;
  1171         -        4: 1166:        PolyEventMap pem = hd->eventMap +
  1172         -        2: 1167:                (type * pdb->eventCount + ecb->eventNumber) ;
  1173         -        -: 1168:#           ifdef MECH_SM_TRACE
  1174         -        -: 1169:        /*
  1175         -        -: 1170:         * Trace the transition.
  1176         -        -: 1171:         */
  1177         -        -: 1172:        tracePolyEvent(ecb->eventNumber, ecb->srcInst,
  1178         -        -: 1173:                ecb->instOrClass.targetInst, type, hnum,
  1179         -        -: 1174:                pem->event, pem->eventType) ;
  1180         -        -: 1175:#           endif /* MECH_SM_TRACE */
  1181         -        -: 1176:
  1182         -        2: 1177:        newEcb->eventNumber = pem->event ;
  1183         -        2: 1178:        newEcb->eventType = pem->eventType ;
  1184         -        -: 1179:
  1185         -        2: 1180:        void *subTypeRef =
  1186         -        2: 1181:                (char *)ecb->instOrClass.targetInst + hd->subInstOffset ;
  1187         -        4: 1182:        newEcb->instOrClass.targetInst = hd->refStorage == PolyReference ?
  1188         -        -: 1183:            /*
  1189         -        -: 1184:             * When the generalization is implemented via a
  1190         -        -: 1185:             * pointer, we need an extra level of
  1191         -        -: 1186:             * indirection to fetch the address of the
  1192         -        -: 1187:             * subtype.
  1193         -        -: 1188:             */
  1194         -        2: 1189:            *(MechInstance *)subTypeRef :
  1195         -        -: 1190:            /*
  1196         -        -: 1191:             * When the generalization is implemented by a
  1197         -        -: 1192:             * union, we need only point to the address of
  1198         -        -: 1193:             * the subtype as it is contained in the
  1199         -        -: 1194:             * supertype.
  1200         -        -: 1195:             */
  1201         -        -: 1196:            (MechInstance)subTypeRef ;
  1202         -        -: 1197:
  1203         -        2: 1198:        if (newEcb->eventType == NormalEvent) {
  1204         -        2: 1199:            newEcb->alloc = newEcb->instOrClass.targetInst->alloc ;
  1205         -        2: 1200:            assert(newEcb->alloc != 0) ;
  1206         -        -: 1201:        }
  1207         -        -: 1202:
  1208         -        2: 1203:        mechDispatch(newEcb) ;
  1209         -        2: 1204:        ++hd ;
  1210         -        -: 1205:    }
  1211         -        2: 1206:}
  1212         -        -: 1207:static void
  1213         -        2: 1208:dispatchCreationEvent(
  1214         -        -: 1209:    MechEcb ecb)
  1215         -        -: 1210:{
  1216         -        -: 1211:    /*
  1217         -        -: 1212:     * For creation events we must allocate an instance,
  1218         -        -: 1213:     * set the state to be the creation state (by
  1219         -        -: 1214:     * convention the creation state is 0).
  1220         -        -: 1215:     */
  1221         -        2: 1216:    MechInstance inst = mechInstCreate(ecb->instOrClass.targetClass,
  1222         -        -: 1217:            MECH_DISPATCH_CREATION_STATE) ;
  1223         -        -: 1218:#           ifdef MECH_SM_TRACE
  1224         -        -: 1219:    /*
  1225         -        -: 1220:     * Trace the transition.
  1226         -        -: 1221:     */
  1227         -        -: 1222:    traceCreationEvent(ecb->eventNumber, ecb->srcInst,
  1228         -        -: 1223:            inst, ecb->instOrClass.targetClass) ;
  1229         -        -: 1224:#           endif
  1230         -        -: 1225:    /*
  1231         -        -: 1226:     * Modify the event structure in place and dispatch it.
  1232         -        -: 1227:     */
  1233         -        2: 1228:    ecb->instOrClass.targetInst = inst ;
  1234         -        2: 1229:    ecb->eventType = NormalEvent ;
  1235         -        2: 1230:    ecb->alloc = ecb->instOrClass.targetInst->alloc ;
  1236         -        2: 1231:    assert(ecb->alloc != 0) ;
  1237         -        -: 1232:
  1238         -        2: 1233:    dispatchNormalEvent(ecb) ;
  1239         -        2: 1234:}
  1240         -        -: 1235:typedef struct fgsyncblock {
  1241         -        -: 1236:    SyncFunc function ;
  1242         -        -: 1237:    SyncParamType params ;
  1243         -        -: 1238:} *FgSyncBlock ;
  1244         -        -: 1239:struct syncqueue {
  1245         -        -: 1240:    FgSyncBlock head ;
  1246         -        -: 1241:    FgSyncBlock tail ;
  1247         -        -: 1242:} ;
  1248         -        -: 1243:static struct fgsyncblock
  1249         -        -: 1244:mechSyncQueueStorage[MECH_SYNCQUEUESIZE] ;
  1250         -        -: 1245:static struct syncqueue mechSyncQueue = {
  1251         -        -: 1246:    .head = mechSyncQueueStorage,
  1252         -        -: 1247:    .tail = mechSyncQueueStorage,
  1253         -        -: 1248:} ;
  1254         -        -: 1249:static inline
  1255         -        -: 1250:bool
  1256         -       24: 1251:syncQueueEmpty(void)
  1257         -        -: 1252:{
  1258         -       24: 1253:    return mechSyncQueue.head == mechSyncQueue.tail ;
  1259         -        -: 1254:}
  1260         -        -: 1255:static inline
  1261         -        -: 1256:SyncParamRef
  1262         -        6: 1257:syncQueuePut(
  1263         -        -: 1258:    SyncFunc f,
  1264         -        -: 1259:    bool fatal)
  1265         -        -: 1260:{
  1266         -        6: 1261:    FgSyncBlock tail = mechSyncQueue.tail ;
  1267         -        6: 1262:    if (++mechSyncQueue.tail >=
  1268         -        -: 1263:            mechSyncQueueStorage + MECH_SYNCQUEUESIZE) {
  1269         -    #####: 1264:        mechSyncQueue.tail = mechSyncQueueStorage ;
  1270         -        -: 1265:    }
  1271         -        6: 1266:    if (syncQueueEmpty()) {
  1272         -    #####: 1267:        if (fatal) {
  1273         -    #####: 1268:            mechFatalError(mechSyncOverflow) ;
  1274         -        -: 1269:        }
  1275         -    #####: 1270:        return NULL ;
  1276         -        -: 1271:    }
  1277         -        -: 1272:
  1278         -        6: 1273:    tail->function = f ;
  1279         -        6: 1274:    return &tail->params ;
  1280         -        -: 1275:}
  1281         -        -: 1276:static inline
  1282         -        -: 1277:FgSyncBlock
  1283         -       11: 1278:syncQueueGet(void)
  1284         -        -: 1279:{
  1285         -        -: 1280:    FgSyncBlock head ;
  1286         -        -: 1281:
  1287         -       11: 1282:    beginCriticalSection() ;
  1288         -       11: 1283:    if (syncQueueEmpty()) {
  1289         -        5: 1284:        head = NULL ;
  1290         -        -: 1285:    } else {
  1291         -        6: 1286:        head = mechSyncQueue.head ;
  1292         -        6: 1287:        if (++mechSyncQueue.head >=
  1293         -        -: 1288:                mechSyncQueueStorage + MECH_SYNCQUEUESIZE) {
  1294         -    #####: 1289:            mechSyncQueue.head = mechSyncQueueStorage ;
  1295         -        -: 1290:        }
  1296         -        -: 1291:    }
  1297         -       11: 1292:    endCriticalSection() ;
  1298         -        -: 1293:
  1299         -       11: 1294:    return head ;
  1300         -        -: 1295:}
  1301         -        -: 1296:SyncParamRef
  1302         -        6: 1297:mechSyncRequest(
  1303         -        -: 1298:    SyncFunc f)
  1304         -        -: 1299:{
  1305         -        6: 1300:    return syncQueuePut(f, true) ;
  1306         -        -: 1301:}
  1307         -        -: 1302:SyncParamRef
  1308         -    #####: 1303:mechTrySyncRequest(
  1309         -        -: 1304:    SyncFunc f)
  1310         -        -: 1305:{
  1311         -    #####: 1306:    return syncQueuePut(f, false) ;
  1312         -        -: 1307:}
  1313         -        -: 1308:void
  1314         -        1: 1309:mechRegisterSignal(
  1315         -        -: 1310:    int sigNum,
  1316         -        -: 1311:    SignalFunc func)
  1317         -        -: 1312:{
  1318         -        1: 1313:    assert(sigNum > 0) ;
  1319         -        -: 1314:
  1320         -        -: 1315:    struct sigaction action ;
  1321         -        1: 1316:    if (func) {
  1322         -        1: 1317:        action.sa_handler = func ;
  1323         -        1: 1318:        sigaddset(&mechSigMask, sigNum) ;
  1324         -        -: 1319:    } else {
  1325         -    #####: 1320:        action.sa_handler = SIG_DFL ;
  1326         -    #####: 1321:        sigdelset(&mechSigMask, sigNum) ;
  1327         -        -: 1322:    }
  1328         -        1: 1323:    sigfillset(&action.sa_mask) ;
  1329         -        1: 1324:    action.sa_flags = 0 ;
  1330         -        -: 1325:
  1331         -        1: 1326:    int sigresult = sigaction(sigNum, &action, NULL) ;
  1332         -        1: 1327:    if (sigresult != 0) {
  1333         -    #####: 1328:        mechFatalError(mechSignalOpFailed, strerror(errno)) ;
  1334         -        -: 1329:    }
  1335         -        1: 1330:}
  1336         -        -: 1331:typedef struct fdservicemap {
  1337         -        -: 1332:    bool set ;
  1338         -        -: 1333:    FDServiceFunc read ;
  1339         -        -: 1334:    FDServiceFunc write ;
  1340         -        -: 1335:    FDServiceFunc except ;
  1341         -        -: 1336:} *FDServiceMap ;
  1342         -        -: 1337:static struct fdservicemap mechFDServicePool[FD_SETSIZE] ;
  1343         -        -: 1338:static int mechMaxFD = -1 ;
  1344         -        -: 1339:static fd_set mechReadFDS ;
  1345         -        -: 1340:static fd_set mechWriteFDS ;
  1346         -        -: 1341:static fd_set mechExceptFDS ;
  1347         -        -: 1342:void
  1348         -        1: 1343:mechRegisterFDService(
  1349         -        -: 1344:    int fd,
  1350         -        -: 1345:    FDServiceFunc readService,
  1351         -        -: 1346:    FDServiceFunc writeService,
  1352         -        -: 1347:    FDServiceFunc exceptService)
  1353         -        -: 1348:{
  1354         -        1: 1349:    assert(fd >= 0 && fd < FD_SETSIZE) ;
  1355         -        1: 1350:    FDServiceMap fds = mechFDServicePool + fd ;
  1356         -        -: 1351:
  1357         -        1: 1352:    fds->read = readService ;
  1358         -        1: 1353:    if (readService) {
  1359         -    #####: 1354:        FD_SET(fd, &mechReadFDS) ;
  1360         -    #####: 1355:        fds->set = true ;
  1361         -        -: 1356:    } else {
  1362         -        1: 1357:        FD_CLR(fd, &mechReadFDS) ;
  1363         -        -: 1358:    }
  1364         -        -: 1359:
  1365         -        1: 1360:    fds->write = writeService ;
  1366         -        1: 1361:    if (writeService) {
  1367         -        1: 1362:        FD_SET(fd, &mechWriteFDS) ;
  1368         -        1: 1363:        fds->set = true ;
  1369         -        -: 1364:    } else {
  1370         -    #####: 1365:        FD_CLR(fd, &mechWriteFDS) ;
  1371         -        -: 1366:    }
  1372         -        -: 1367:
  1373         -        1: 1368:    fds->except = exceptService ;
  1374         -        1: 1369:    if (exceptService) {
  1375         -    #####: 1370:        FD_SET(fd, &mechExceptFDS) ;
  1376         -    #####: 1371:        fds->set = true ;
  1377         -        -: 1372:    } else {
  1378         -        1: 1373:        FD_CLR(fd, &mechExceptFDS) ;
  1379         -        -: 1374:    }
  1380         -        -: 1375:
  1381         -        1: 1376:    if (fds->read == NULL && fds->write == NULL && fds-> except == NULL) {
  1382         -    #####: 1377:        if (fds->set && fd >= mechMaxFD) {
  1383         -    #####: 1378:            --mechMaxFD ;
  1384         -        -: 1379:        }
  1385         -    #####: 1380:        fds->set = false ;
  1386         -        1: 1381:    } else if (fds->set && fd > mechMaxFD) {
  1387         -        1: 1382:        mechMaxFD = fd ;
  1388         -        -: 1383:    }
  1389         -        1: 1384:}
  1390         -        -: 1385:void
  1391         -        1: 1386:mechRemoveFDService(
  1392         -        -: 1387:    int fd,
  1393         -        -: 1388:    bool rmRead,
  1394         -        -: 1389:    bool rmWrite,
  1395         -        -: 1390:    bool rmExcept)
  1396         -        -: 1391:{
  1397         -        1: 1392:    assert(fd >= 0 && fd < FD_SETSIZE) ;
  1398         -        1: 1393:    FDServiceMap fds = mechFDServicePool + fd ;
  1399         -        -: 1394:
  1400         -        1: 1395:    if (rmRead) {
  1401         -    #####: 1396:        fds->read = NULL ;
  1402         -    #####: 1397:        FD_CLR(fd, &mechReadFDS) ;
  1403         -        -: 1398:    }
  1404         -        -: 1399:
  1405         -        1: 1400:    if (rmWrite) {
  1406         -        1: 1401:        fds->write = NULL ;
  1407         -        1: 1402:        FD_CLR(fd, &mechWriteFDS) ;
  1408         -        -: 1403:    }
  1409         -        -: 1404:
  1410         -        1: 1405:    if (rmExcept) {
  1411         -    #####: 1406:        fds->except = NULL ;
  1412         -    #####: 1407:        FD_CLR(fd, &mechExceptFDS) ;
  1413         -        -: 1408:    }
  1414         -        -: 1409:
  1415         -        2: 1410:    if (fds->read == NULL && fds->write == NULL && fds-> except == NULL &&
  1416         -        1: 1411:            fd >= mechMaxFD) {
  1417         -        1: 1412:        mechMaxFD = fd - 1 ;
  1418         -        -: 1413:    }
  1419         -        1: 1414:}
  1420         -        -: 1415:static void
  1421         -        1: 1416:fdServiceInit(void)
  1422         -        -: 1417:{
  1423         -        1: 1418:    FD_ZERO(&mechReadFDS) ;
  1424         -        1: 1419:    FD_ZERO(&mechWriteFDS) ;
  1425         -        1: 1420:    FD_ZERO(&mechExceptFDS) ;
  1426         -        1: 1421:}
  1427         -        -: 1422:MECH_TEST_STATIC
  1428         -        -: 1423:void
  1429         -        7: 1424:mechWait(void)
  1430         -        -: 1425:{
  1431         -        7: 1426:    beginCriticalSection() ;
  1432         -        7: 1427:    if (syncQueueEmpty()) {
  1433         -        -: 1428:        /*
  1434         -        -: 1429:         * Copy the file descriptor sets since "pselect"
  1435         -        -: 1430:         * modifies them in place upon return.
  1436         -        -: 1431:         */
  1437         -        -: 1432:        fd_set readfds ;
  1438         -        7: 1433:        memcpy(&readfds, &mechReadFDS, sizeof(readfds)) ;
  1439         -        -: 1434:        fd_set writefds ;
  1440         -        7: 1435:        memcpy(&writefds, &mechWriteFDS, sizeof(writefds)) ;
  1441         -        -: 1436:        fd_set exceptfds ;
  1442         -        7: 1437:        memcpy(&exceptfds, &mechExceptFDS, sizeof(exceptfds)) ;
  1443         -        -: 1438:        /*
  1444         -        -: 1439:         * Allow all the signals during the select. We
  1445         -        -: 1440:         * assume that when we enter this function, the
  1446         -        -: 1441:         * registered signals are masked.
  1447         -        -: 1442:         */
  1448         -        -: 1443:        sigset_t mask ;
  1449         -        7: 1444:        sigemptyset(&mask) ;
  1450         -        -: 1445:        /*
  1451         -        -: 1446:         * "mechMaxFD" holds the maximum value of any
  1452         -        -: 1447:         * registered file descriptor. We must add one to
  1453         -        -: 1448:         * get the number of file descriptors "pselect" is
  1454         -        -: 1449:         * to consider.
  1455         -        -: 1450:         */
  1456         -        7: 1451:        int r = pselect(mechMaxFD + 1, &readfds, &writefds,
  1457         -        -: 1452:                &exceptfds, NULL, &mask) ;
  1458         -        7: 1453:        if (r == -1) {
  1459         -        6: 1454:            if (errno != EINTR) {
  1460         -    #####: 1455:                mechFatalError(mechSelectWaitFailed,
  1461         -    #####: 1456:                        strerror(errno)) ;
  1462         -        -: 1457:            }
  1463         -        -: 1458:            /*
  1464         -        -: 1459:             * Got a signal while waiting. We go back to the
  1465         -        -: 1460:             * main loop on the assumption that something
  1466         -        -: 1461:             * has been placed in the sync queue.
  1467         -        -: 1462:             */
  1468         -        -: 1463:        } else {
  1469         -        -: 1464:            /*
  1470         -        -: 1465:             * Dispatch the service functions for the file
  1471         -        -: 1466:             * descriptors.
  1472         -        -: 1467:             */
  1473         -        1: 1468:            FDServiceMap s = mechFDServicePool ;
  1474         -        4: 1469:            for (int fd = 0 ; r > 0 && fd <= mechMaxFD ;
  1475         -        2: 1470:                    ++fd, ++s) {
  1476         -        -: 1471:                /*
  1477         -        -: 1472:                 * Do exceptions first. This is only
  1478         -        -: 1473:                 * important for sockets, but without going
  1479         -        -: 1474:                 * first the OOB data processing won't
  1480         -        -: 1475:                 * work.
  1481         -        -: 1476:                 */
  1482         -        2: 1477:                if (FD_ISSET(fd, &exceptfds)) {
  1483         -    #####: 1478:                    assert(s->except != NULL) ;
  1484         -    #####: 1479:                    s->except(fd) ;
  1485         -    #####: 1480:                    --r ;
  1486         -        -: 1481:                }
  1487         -        2: 1482:                if (FD_ISSET(fd, &readfds)) {
  1488         -    #####: 1483:                    assert(s->read != NULL) ;
  1489         -    #####: 1484:                    s->read(fd) ;
  1490         -    #####: 1485:                    --r ;
  1491         -        -: 1486:                }
  1492         -        2: 1487:                if (FD_ISSET(fd, &writefds)) {
  1493         -        1: 1488:                    assert(s->write != NULL) ;
  1494         -        1: 1489:                    s->write(fd) ;
  1495         -        1: 1490:                    --r ;
  1496         -        -: 1491:                }
  1497         -        -: 1492:            }
  1498         -        -: 1493:        }
  1499         -        -: 1494:    }
  1500         -        7: 1495:    endCriticalSection() ;
  1501         -        7: 1496:}
  1502         -        -: 1497:static void
  1503         -        1: 1498:sysPlatformInit(void)
  1504         -        -: 1499:{
  1505         -        1: 1500:    fdServiceInit() ;
  1506         -        1: 1501:}
  1507         -        -: 1502:MECH_TEST_STATIC
  1508         -        -: 1503:MECH_TEST_INLINE
  1509         -        -: 1504:void
  1510         -        1: 1505:mechInit(void)
  1511         -        -: 1506:{
  1512         -        1: 1507:    sysPlatformInit() ;
  1513         -        1: 1508:    mechEventInit() ;
  1514         -        1: 1509:    initCriticalSection() ;
  1515         -        1: 1510:    sysTimerInit() ;
  1516         -        1: 1511:    sysDeviceInit() ;
  1517         -        1: 1512:    sysDomainInit() ;
  1518         -        1: 1513:}
  1519         -        -: 1514:MECH_TEST_STATIC
  1520         -        -: 1515:MECH_TEST_INLINE
  1521         -        -: 1516:bool
  1522         -       11: 1517:mechInvokeOneSyncFunc(void)
  1523         -        -: 1518:{
  1524         -        -: 1519:    bool didOne ;
  1525         -       11: 1520:    FgSyncBlock blk = syncQueueGet() ;
  1526         -       11: 1521:    if (blk && blk->function) {
  1527         -        6: 1522:        blk->function(&blk->params) ;
  1528         -        6: 1523:        didOne = true ;
  1529         -        -: 1524:    } else {
  1530         -        5: 1525:        didOne = false ;
  1531         -        -: 1526:    }
  1532         -       11: 1527:    return didOne ;
  1533         -        -: 1528:}
  1534         -        -: 1529:MECH_TEST_STATIC
  1535         -        -: 1530:MECH_TEST_INLINE
  1536         -        -: 1531:bool
  1537         -       29: 1532:mechDispatchOneEvent(void)
  1538         -        -: 1533:{
  1539         -       29: 1534:    bool didOne = !eventQueueEmpty(&eventQueue) ;
  1540         -       29: 1535:    if (didOne) {
  1541         -       21: 1536:        MechEcb ecb = eventQueue.next ;
  1542         -       21: 1537:        eventQueueRemove(ecb) ;
  1543         -       21: 1538:        mechDispatch(ecb) ;
  1544         -        -: 1539:    }
  1545         -       29: 1540:    return didOne ;
  1546         -        -: 1541:}
  1547         -        -: 1542:#ifdef MECH_TEST
  1548         -        -: 1543:void
  1549         -    #####: 1544:stsa_main(void)
  1550         -        -: 1545:#else
  1551         -        -: 1546:int
  1552         -        -: 1547:main(void)
  1553         -        -: 1548:#endif /* MECH_TEST */
  1554         -        -: 1549:{
  1555         -    #####: 1550:    mechInit() ;
  1556         -        -: 1551:
  1557         -        -: 1552:    for (;;) { /* Infinite Big Loop */
  1558         -        -: 1553:        /*
  1559         -        -: 1554:         * Empty the foreground/background sync queue.
  1560         -        -: 1555:         */
  1561         -        -: 1556:        #ifndef __ARM_ARCH_7M__
  1562         -        -: 1557:        /*
  1563         -        -: 1558:         * Empty the foreground / background
  1564         -        -: 1559:         * synchronization queue.
  1565         -        -: 1560:         */
  1566         -    #####: 1561:        while (mechInvokeOneSyncFunc()) {
  1567         -        -: 1562:            ; /* empty */
  1568         -        -: 1563:        }
  1569         -        -: 1564:        #endif /* __ARM_ARCH_7M__ */
  1570         -        -: 1565:        /*
  1571         -        -: 1566:         * Dispatch one event from the event queue.
  1572         -        -: 1567:         */
  1573         -    #####: 1568:        if (!mechDispatchOneEvent()) {
  1574         -        -: 1569:            /*
  1575         -        -: 1570:             * Check if this thread of control is complete
  1576         -        -: 1571:             * and wait if there is no additional work to
  1577         -        -: 1572:             * be done.
  1578         -        -: 1573:             */
  1579         -    #####: 1574:            mechWait() ;
  1580         -        -: 1575:        }
  1581         -    #####: 1576:    }
  1582         -        -: 1577:}
          546  +        -:  541:#define MECH_DELAY_EXPIRED  UINT32_MAX
          547  +        -:  542:static void
          548  +        3:  543:removeFromDelayedQueue(
          549  +        -:  544:    MechEcb ecb)
          550  +        -:  545:{
          551  +        -:  546:    /*
          552  +        -:  547:     * If we are not at the end of the queue, all the delay
          553  +        -:  548:     * from the removed entry is accumulated on the next
          554  +        -:  549:     * entry in the queue.
          555  +        -:  550:     */
          556  +        6:  551:    if (!(ecb->delay == MECH_DELAY_EXPIRED ||
          557  +        3:  552:            ecb->next == eventQueueEnd(&delayedEventQueue))) {
          558  +    #####:  553:        ecb->next->delay += ecb->delay ;
          559  +        -:  554:    }
          560  +        -:  555:    /*
          561  +        -:  556:     * Remove the ECB from the delayed queue.
          562  +        -:  557:     */
          563  +        3:  558:    eventQueueRemove(ecb) ;
          564  +        -:  559:    /*
          565  +        -:  560:     * Return the ECB back to the pool.
          566  +        -:  561:     */
          567  +        3:  562:    mechEventDelete(ecb) ;
          568  +        3:  563:}
          569  +        -:  564:static MechEcb
          570  +        7:  565:expireDelayedEvents(void)
          571  +        -:  566:{
          572  +        -:  567:    /*
          573  +        -:  568:     * Iterate along the delayed event queue.
          574  +        -:  569:     */
          575  +       21:  570:    for (MechEcb iter = eventQueueBegin(&delayedEventQueue) ;
          576  +       14:  571:            iter != eventQueueEnd(&delayedEventQueue) ;
          577  +        7:  572:            iter = iter->next) {
          578  +        9:  573:        if (iter->delay == 0) {
          579  +        -:  574:            /*
          580  +        -:  575:             * Mark all the events that have zero delay time
          581  +        -:  576:             * as expired.
          582  +        -:  577:             */
          583  +        6:  578:            iter->delay = MECH_DELAY_EXPIRED ;
          584  +        3:  579:        } else if (iter->delay != MECH_DELAY_EXPIRED) {
          585  +        -:  580:            /*
          586  +        -:  581:             * Stop at the first non-zero delay time.  This
          587  +        -:  582:             * marks the boundary of events that need
          588  +        -:  583:             * additional delay time.  The first such event
          589  +        -:  584:             * is the next amount of time to delay.
          590  +        -:  585:             */
          591  +        2:  586:            return iter ;
          592  +        -:  587:        }
          593  +        -:  588:        /*
          594  +        -:  589:         * else ... Skip any events that might already be
          595  +        -:  590:         * expired.
          596  +        -:  591:         */
          597  +        -:  592:    }
          598  +        -:  593:    /*
          599  +        -:  594:     * We have run the queue without finding an unexpired
          600  +        -:  595:     * event.
          601  +        -:  596:     */
          602  +        5:  597:    return NULL ;
          603  +        -:  598:}
          604  +        -:  599:static void
          605  +       13:  600:transferExpiredEvents(void)
          606  +        -:  601:{
          607  +        -:  602:    /*
          608  +        -:  603:     * Iterate through the delayed event queue looking for
          609  +        -:  604:     * those entries that have been marked as expired.
          610  +        -:  605:     */
          611  +       32:  606:    for (MechEcb iter = eventQueueBegin(&delayedEventQueue) ;
          612  +       33:  607:            iter != eventQueueEnd(&delayedEventQueue) &&
          613  +       14:  608:            iter->delay == MECH_DELAY_EXPIRED ; ) {
          614  +        -:  609:        /*
          615  +        -:  610:         * Advance the iterator, because we are about to
          616  +        -:  611:         * invalidate it by removing the entry from the
          617  +        -:  612:         * queue.
          618  +        -:  613:         */
          619  +        6:  614:        MechEcb ecb = iter ;
          620  +        6:  615:        iter = iter->next ;
          621  +        -:  616:
          622  +        -:  617:        /*
          623  +        -:  618:         * Remove the ECB from the delayed queue and insert
          624  +        -:  619:         * it into event queue for dispatch.
          625  +        -:  620:         */
          626  +        6:  621:        eventQueueRemove(ecb) ;
          627  +        6:  622:        eventQueueInsert(ecb, &eventQueue) ;
          628  +        6:  623:        assert(ecb->referenceCount != 0) ;
          629  +        -:  624:    }
          630  +       13:  625:}
          631  +        -:  626:static void
          632  +       13:  627:startDelayedQueueTiming(void)
          633  +        -:  628:{
          634  +       13:  629:    if (!eventQueueEmpty(&delayedEventQueue)) {
          635  +       10:  630:        MechEcb ecb = eventQueueBegin(&delayedEventQueue) ;
          636  +       10:  631:        assert(ecb->delay != 0) ;
          637  +       10:  632:        sysTimerStart(ecb->delay) ;
          638  +       10:  633:        ecb->delay = 0 ;
          639  +        -:  634:    }
          640  +       13:  635:}
          641  +        -:  636:static void
          642  +       13:  637:stopDelayedQueueTiming(void)
          643  +        -:  638:{
          644  +        -:  639:    /*
          645  +        -:  640:     * Avoid the whole thing if there is nothing in the
          646  +        -:  641:     * delayed event queue.
          647  +        -:  642:     */
          648  +       13:  643:    if (!eventQueueEmpty(&delayedEventQueue)) {
          649  +        -:  644:        /*
          650  +        -:  645:         * Stop the timer, obtaining the residual time.
          651  +        -:  646:         */
          652  +        7:  647:        MechDelayTime remain = sysTimerStop() ;
          653  +        -:  648:        /*
          654  +        -:  649:         * There are two cases here. It is possible for the
          655  +        -:  650:         * remaining time returned from sysTimerStop() to be
          656  +        -:  651:         * zero. This can happen if the physical timing
          657  +        -:  652:         * resource (which might be running asynchronously
          658  +        -:  653:         * to the processor) happens to expire within a
          659  +        -:  654:         * single tick as we are stopping it.
          660  +        -:  655:         */
          661  +        7:  656:        if (remain == 0) {
          662  +        -:  657:            /*
          663  +        -:  658:             * Since the timer has expired we must mark any
          664  +        -:  659:             * events with a zero delay time value as
          665  +        -:  660:             * expired and, since we are running in the
          666  +        -:  661:             * background here, transfer the expired events
          667  +        -:  662:             * to be dispatched.
          668  +        -:  663:             */
          669  +        1:  664:            expireDelayedEvents() ;
          670  +        1:  665:            transferExpiredEvents() ;
          671  +        -:  666:            /*
          672  +        -:  667:             * At this point, either the delayed event queue
          673  +        -:  668:             * is empty, or the event at the head of the
          674  +        -:  669:             * queue has a non-zero delay time.
          675  +        -:  670:             */
          676  +        -:  671:        } else {
          677  +        -:  672:            /*
          678  +        -:  673:             * It is possible that the timing resource
          679  +        -:  674:             * expired and its interrupt service ran just
          680  +        -:  675:             * before we could get the timer stopped. That
          681  +        -:  676:             * would mean that there are expired events on
          682  +        -:  677:             * the delayed queue at this point and we need
          683  +        -:  678:             * to transfer them off the delayed queue to be
          684  +        -:  679:             * dispatched.
          685  +        -:  680:             */
          686  +        6:  681:            transferExpiredEvents() ;
          687  +        -:  682:            /*
          688  +        -:  683:             * If any events expired, the delayed event
          689  +        -:  684:             * queue might now be empty. However, if the
          690  +        -:  685:             * queue is not empty, we must make sure the
          691  +        -:  686:             * entry at the head preserves the remaining
          692  +        -:  687:             * amount of time that needs to elapse.
          693  +        -:  688:             */
          694  +        6:  689:            if (!eventQueueEmpty(&delayedEventQueue)) {
          695  +        6:  690:                MechEcb ecb = eventQueueBegin(&delayedEventQueue) ;
          696  +        6:  691:                assert(ecb->delay == 0) ;
          697  +        6:  692:                ecb->delay = remain ;
          698  +        -:  693:            }
          699  +        -:  694:        }
          700  +        -:  695:    }
          701  +       13:  696:}
          702  +        -:  697:static inline
          703  +        -:  698:MechDelayTime
          704  +        9:  699:mechMsecToTicks(
          705  +        -:  700:    MechDelayTime msec)
          706  +        -:  701:{
          707  +        9:  702:    return msec ;
          708  +        -:  703:}
          709  +        -:  704:static inline
          710  +        -:  705:MechDelayTime
          711  +        1:  706:mechTicksToMsec(
          712  +        -:  707:    MechDelayTime ticks)
          713  +        -:  708:{
          714  +        1:  709:    return ticks ;
          715  +        -:  710:}
          716  +        -:  711:void
          717  +        9:  712:mechEventPostDelay(
          718  +        -:  713:    MechEcb ecb,
          719  +        -:  714:    MechDelayTime time)
          720  +        -:  715:{
          721  +        9:  716:    assert(ecb != NULL) ;
          722  +        -:  717:    /*
          723  +        -:  718:     * A delay time of 0 is valid, and the event will be
          724  +        -:  719:     * queued immediately.
          725  +        -:  720:     */
          726  +        9:  721:    if (time == 0) {
          727  +    #####:  722:        mechEventPost(ecb) ;
          728  +    #####:  723:        return ;
          729  +        -:  724:    }
          730  +        9:  725:    ecb->delay = mechMsecToTicks(time) ;
          731  +        -:  726:    /*
          732  +        -:  727:     * Stop the timing queue so we may examine it.
          733  +        -:  728:     */
          734  +        9:  729:    stopDelayedQueueTiming() ;
          735  +        -:  730:    /*
          736  +        -:  731:     * If the event already exists, remove it.
          737  +        -:  732:     */
          738  +        9:  733:    MechEcb prevEvent = findEvent(&delayedEventQueue,
          739  +        -:  734:            ecb->srcInst, ecb->instOrClass.targetInst,
          740  +        9:  735:            ecb->eventNumber) ;
          741  +        9:  736:    if (prevEvent) {
          742  +        1:  737:        removeFromDelayedQueue(prevEvent) ;
          743  +        -:  738:    }
          744  +        -:  739:    /*
          745  +        -:  740:     * Insert the new event.
          746  +        -:  741:     */
          747  +        9:  742:    insertIntoDelayedQueue(ecb) ;
          748  +        9:  743:    assert(!eventQueueEmpty(&delayedEventQueue)) ;
          749  +        -:  744:    /*
          750  +        -:  745:     * Start the timer to expire for the first event
          751  +        -:  746:     * on the queue.
          752  +        -:  747:     */
          753  +        9:  748:    startDelayedQueueTiming() ;
          754  +        -:  749:}
          755  +        -:  750:void
          756  +        3:  751:mechEventDelayCancel(
          757  +        -:  752:    EventCode event,
          758  +        -:  753:    MechInstance targetInst,
          759  +        -:  754:    MechInstance srcInst)
          760  +        -:  755:{
          761  +        3:  756:    assert(targetInst != NULL) ;
          762  +        -:  757:    /*
          763  +        -:  758:     * Stop delayed queue so that we may examine it.
          764  +        -:  759:     */
          765  +        3:  760:    stopDelayedQueueTiming() ;
          766  +        -:  761:    /*
          767  +        -:  762:     * Search for the event in the delayed event queue.
          768  +        -:  763:     */
          769  +        3:  764:    MechEcb foundEvent = findEvent(&delayedEventQueue,
          770  +        -:  765:            srcInst, targetInst, event) ;
          771  +        3:  766:    if (foundEvent) {
          772  +        -:  767:        /*
          773  +        -:  768:         * Removing from the delayed queue requires
          774  +        -:  769:         * additional processing of the delay times.
          775  +        -:  770:         */
          776  +        2:  771:        removeFromDelayedQueue(foundEvent) ;
          777  +        -:  772:    } else {
          778  +        -:  773:        /*
          779  +        -:  774:         * If the event is not in the delayed queue, then
          780  +        -:  775:         * search the event queue. The timer could have
          781  +        -:  776:         * expired and the event placed in the queue.
          782  +        -:  777:         */
          783  +        1:  778:        foundEvent = findEvent(&eventQueue, srcInst,
          784  +        -:  779:                targetInst, event) ;
          785  +        1:  780:        if (foundEvent) {
          786  +        1:  781:            eventQueueRemove(foundEvent) ;
          787  +        1:  782:            mechEventDelete(foundEvent) ;
          788  +        -:  783:        }
          789  +        -:  784:        /*
          790  +        -:  785:         * We can get here, without finding the event in the
          791  +        -:  786:         * delayed queue or the event queue.
          792  +        -:  787:         * That's okay, it just amounts to an expensive
          793  +        -:  788:         * no-op and implies that the event has expired,
          794  +        -:  789:         * was queued and has already been dispatched or
          795  +        -:  790:         * had never been generated at all.
          796  +        -:  791:         */
          797  +        -:  792:    }
          798  +        3:  793:    startDelayedQueueTiming() ;
          799  +        3:  794:}
          800  +        -:  795:MechDelayTime
          801  +        1:  796:mechEventDelayRemaining(
          802  +        -:  797:    EventCode event,
          803  +        -:  798:    MechInstance targetInst,
          804  +        -:  799:    MechInstance srcInst)
          805  +        -:  800:{
          806  +        1:  801:    assert(targetInst != NULL) ;
          807  +        -:  802:
          808  +        1:  803:    stopDelayedQueueTiming() ;
          809  +        -:  804:    /*
          810  +        -:  805:     * Iterate through the delayed event time and sum all
          811  +        -:  806:     * the delay times to give the total amount of time
          812  +        -:  807:     * remaining for the found event.
          813  +        -:  808:     */
          814  +        1:  809:    MechDelayTime remain = 0 ;
          815  +        -:  810:    MechEcb iter ;
          816  +        2:  811:    for (iter = eventQueueBegin(&delayedEventQueue) ;
          817  +        1:  812:            iter != eventQueueEnd(&delayedEventQueue) ;
          818  +    #####:  813:            iter = iter->next) {
          819  +        1:  814:        remain += iter->delay ;
          820  +        2:  815:        if (iter->srcInst == srcInst &&
          821  +        2:  816:                iter->instOrClass.targetInst == targetInst &&
          822  +        1:  817:                iter->eventNumber == event) {
          823  +        1:  818:            break ;
          824  +        -:  819:        }
          825  +        -:  820:    }
          826  +        1:  821:    startDelayedQueueTiming() ;
          827  +        -:  822:    /*
          828  +        -:  823:     * Return the amount of time remaining for the event.
          829  +        -:  824:     * If we didn't find the event, the just return 0.
          830  +        -:  825:     */
          831  +        1:  826:    return iter == eventQueueEnd(&delayedEventQueue) ?
          832  +        1:  827:            0 : mechTicksToMsec(remain) ;
          833  +        -:  828:}
          834  +        -:  829:static void
          835  +       13:  830:sysTimerMask(void)
          836  +        -:  831:{
          837  +        -:  832:    /*
          838  +        -:  833:     * Make sure SIGALRM does not go off.
          839  +        -:  834:     */
          840  +        -:  835:    sigset_t mask ;
          841  +       13:  836:    sigemptyset(&mask) ;
          842  +       13:  837:    sigaddset(&mask, SIGALRM) ;
          843  +       13:  838:    if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0) {
          844  +    #####:  839:        mechFatalError(mechSignalOpFailed, strerror(errno)) ;
          845  +        -:  840:    }
          846  +       13:  841:}
          847  +        -:  842:
          848  +        -:  843:static void
          849  +       18:  844:sysTimerUnmask(void)
          850  +        -:  845:{
          851  +        -:  846:    /*
          852  +        -:  847:     * Allow SIGALRM to notify us.
          853  +        -:  848:     */
          854  +        -:  849:    sigset_t mask ;
          855  +       18:  850:    sigemptyset(&mask) ;
          856  +       18:  851:    sigaddset(&mask, SIGALRM) ;
          857  +       18:  852:    if (sigprocmask(SIG_UNBLOCK, &mask, NULL) != 0) {
          858  +    #####:  853:        mechFatalError(mechSignalOpFailed, strerror(errno)) ;
          859  +        -:  854:    }
          860  +       18:  855:}
          861  +        -:  856:static void
          862  +       12:  857:sysTimerStart(
          863  +        -:  858:    MechDelayTime time)
          864  +        -:  859:{
          865  +        -:  860:    struct itimerval delayedEventTimer ;
          866  +        -:  861:
          867  +       12:  862:    delayedEventTimer.it_interval.tv_sec = 0 ;
          868  +       12:  863:    delayedEventTimer.it_interval.tv_usec = 0 ;
          869  +       12:  864:    delayedEventTimer.it_value.tv_sec = time / 1000 ;
          870  +       12:  865:    delayedEventTimer.it_value.tv_usec = (time % 1000) * 1000 ;
          871  +        -:  866:
          872  +       12:  867:    if (setitimer(ITIMER_REAL, &delayedEventTimer, NULL) != 0) {
          873  +    #####:  868:        mechFatalError(mechTimerOpFailed, strerror(errno)) ;
          874  +        -:  869:    }
          875  +       12:  870:    sysTimerUnmask() ;
          876  +       12:  871:}
          877  +        -:  872:static MechDelayTime
          878  +        7:  873:sysTimerStop(void)
          879  +        -:  874:{
          880  +        7:  875:    sysTimerMask() ;
          881  +        -:  876:    /*
          882  +        -:  877:     * Fetch the remaining time.
          883  +        -:  878:     */
          884  +        -:  879:    struct itimerval delayedEventTimer ;
          885  +        7:  880:    if (getitimer(ITIMER_REAL, &delayedEventTimer) != 0) {
          886  +    #####:  881:        mechFatalError(mechTimerOpFailed, strerror(errno)) ;
          887  +        -:  882:    }
          888  +        -:  883:    /*
          889  +        -:  884:     * Convert the returned time into milliseconds.
          890  +        -:  885:     */
          891  +        7:  886:    MechDelayTime remain =
          892  +       14:  887:            delayedEventTimer.it_value.tv_sec * 1000 +
          893  +        7:  888:            delayedEventTimer.it_value.tv_usec / 1000 ;
          894  +        -:  889:    /*
          895  +        -:  890:     * Set the current timer value to zero to turn it off.
          896  +        -:  891:     */
          897  +        7:  892:    memset(&delayedEventTimer, 0, sizeof(delayedEventTimer)) ;
          898  +        7:  893:    if (setitimer(ITIMER_REAL, &delayedEventTimer, NULL) != 0) {
          899  +    #####:  894:        mechFatalError(mechTimerOpFailed, strerror(errno)) ;
          900  +        -:  895:    }
          901  +        -:  896:
          902  +        7:  897:    return remain ;
          903  +        -:  898:}
          904  +        -:  899:static void
          905  +        6:  900:sysTimerExpire(
          906  +        -:  901:    int signum)
          907  +        -:  902:{
          908  +        6:  903:    MechDelayTime nextTime = mechTimerExpireService() ;
          909  +        6:  904:    if (nextTime != 0) {
          910  +        2:  905:        sysTimerStart(nextTime) ;
          911  +        -:  906:    }
          912  +        6:  907:}
          913  +        -:  908:static void
          914  +        1:  909:sysTimerInit(void)
          915  +        -:  910:{
          916  +        1:  911:    mechRegisterSignal(SIGALRM, sysTimerExpire) ;
          917  +        1:  912:}
          918  +        -:  913:static void
          919  +        6:  914:mechExpiredEventService(
          920  +        -:  915:    SyncParamRef params) /* Not used */
          921  +        -:  916:{
          922  +        -:  917:    /*
          923  +        -:  918:     * Since the expired event queue is accessed here,
          924  +        -:  919:     * we must make sure that the timer interrupt does not
          925  +        -:  920:     * go off.
          926  +        -:  921:     */
          927  +        6:  922:    sysTimerMask() ;
          928  +        6:  923:    transferExpiredEvents() ;
          929  +        6:  924:    sysTimerUnmask() ;
          930  +        6:  925:}
          931  +        -:  926:MechDelayTime
          932  +        6:  927:mechTimerExpireService(void)
          933  +        -:  928:{
          934  +        -:  929:    MechEcb unexpired ;
          935  +        -:  930:    MechDelayTime nextTime ;
          936  +        -:  931:    /*
          937  +        -:  932:     * Sync to the background to request the expired events
          938  +        -:  933:     * be transferred to the event queue.
          939  +        -:  934:     */
          940  +        6:  935:    mechSyncRequest(mechExpiredEventService) ;
          941  +        -:  936:    /*
          942  +        -:  937:     * Mark the delayed events as expired, returning a
          943  +        -:  938:     * pointer to the first unexpired event.
          944  +        -:  939:     */
          945  +        6:  940:    unexpired = expireDelayedEvents() ;
          946  +        6:  941:    if (unexpired) {
          947  +        -:  942:        /*
          948  +        -:  943:         * If there is an unexpired event, then its delay
          949  +        -:  944:         * time is the next time to expire. We return that
          950  +        -:  945:         * time and zero out the delay time.
          951  +        -:  946:         */
          952  +        2:  947:        assert(unexpired->delay != 0) ;
          953  +        2:  948:        nextTime = unexpired->delay ;
          954  +        2:  949:        unexpired->delay = 0 ;
          955  +        -:  950:    } else {
          956  +        -:  951:        /*
          957  +        -:  952:         * Otherwise, there is nothing else to time.
          958  +        -:  953:         */
          959  +        4:  954:        nextTime = 0 ;
          960  +        -:  955:    }
          961  +        -:  956:
          962  +        6:  957:    return nextTime ;
          963  +        -:  958:}
          964  +        -:  959:#ifdef MECH_SM_TRACE
          965  +        -:  960:static MechTraceCallback traceCallback ;
          966  +        -:  961:
          967  +        -:  962:MechTraceCallback
          968  +        -:  963:mechRegisterTrace(
          969  +        -:  964:    MechTraceCallback cb)
          970  +        -:  965:{
          971  +        -:  966:    MechTraceCallback oldcb = traceCallback ;
          972  +        -:  967:    traceCallback = cb ;
          973  +        -:  968:    return oldcb ;
          974  +        -:  969:}
          975  +        -:  970:static inline 
          976  +        -:  971:void
          977  +        -:  972:traceNormalEvent(
          978  +        -:  973:    EventCode event,
          979  +        -:  974:    MechInstance source,
          980  +        -:  975:    MechInstance target,
          981  +        -:  976:    StateCode currentState,
          982  +        -:  977:    StateCode newState)
          983  +        -:  978:{
          984  +        -:  979:    if (traceCallback) {
          985  +        -:  980:        struct mechtraceinfo trace ;
          986  +        -:  981:
          987  +        -:  982:        trace.eventType = NormalEvent ;
          988  +        -:  983:        trace.eventNumber = event ;
          989  +        -:  984:        trace.srcInst = source ;
          990  +        -:  985:        trace.dstInst = target ;
          991  +        -:  986:        trace.info.normalTrace.currState = currentState ;
          992  +        -:  987:        trace.info.normalTrace.newState = newState ;
          993  +        -:  988:
          994  +        -:  989:        traceCallback(&trace) ;
          995  +        -:  990:    }
          996  +        -:  991:}
          997  +        -:  992:static inline 
          998  +        -:  993:void
          999  +        -:  994:tracePolyEvent(
         1000  +        -:  995:    EventCode event,
         1001  +        -:  996:    MechInstance source,
         1002  +        -:  997:    MechInstance target,
         1003  +        -:  998:    SubtypeCode subtype,
         1004  +        -:  999:    DispatchCount hierarchy,
         1005  +        -: 1000:    EventCode newEvent,
         1006  +        -: 1001:    MechEventType newEventType)
         1007  +        -: 1002:{
         1008  +        -: 1003:    if (traceCallback) {
         1009  +        -: 1004:        struct mechtraceinfo trace ;
         1010  +        -: 1005:
         1011  +        -: 1006:        trace.eventType = PolymorphicEvent ;
         1012  +        -: 1007:        trace.eventNumber = event ;
         1013  +        -: 1008:        trace.srcInst = source ;
         1014  +        -: 1009:        trace.dstInst = target ;
         1015  +        -: 1010:        trace.info.polyTrace.subcode = subtype ;
         1016  +        -: 1011:        trace.info.polyTrace.hierarchy = hierarchy ;
         1017  +        -: 1012:        trace.info.polyTrace.mappedNumber = newEvent ;
         1018  +        -: 1013:        trace.info.polyTrace.mappedType = newEventType ;
         1019  +        -: 1014:
         1020  +        -: 1015:        traceCallback(&trace) ;
         1021  +        -: 1016:    }
         1022  +        -: 1017:}
         1023  +        -: 1018:static inline 
         1024  +        -: 1019:void
         1025  +        -: 1020:traceCreationEvent(
         1026  +        -: 1021:    EventCode event,
         1027  +        -: 1022:    MechInstance source,
         1028  +        -: 1023:    MechInstance target,
         1029  +        -: 1024:    MechClass class)
         1030  +        -: 1025:{
         1031  +        -: 1026:    if (traceCallback) {
         1032  +        -: 1027:        struct mechtraceinfo trace ;
         1033  +        -: 1028:
         1034  +        -: 1029:        trace.eventType = CreationEvent ;
         1035  +        -: 1030:        trace.eventNumber = event ;
         1036  +        -: 1031:        trace.srcInst = source ;
         1037  +        -: 1032:        trace.dstInst = target ;
         1038  +        -: 1033:        trace.info.creationTrace.dstClass = class ;
         1039  +        -: 1034:
         1040  +        -: 1035:        traceCallback(&trace) ;
         1041  +        -: 1036:    }
         1042  +        -: 1037:}
         1043  +        -: 1038:#endif /* MECH_SM_TRACE */
         1044  +        -: 1039:static void dispatchNormalEvent(MechEcb) ;
         1045  +        -: 1040:static void dispatchPolyEvent(MechEcb) ;
         1046  +        -: 1041:static void dispatchCreationEvent(MechEcb) ;
         1047  +        -: 1042:static void (* const ecbDispatchFuncs[])(MechEcb) = {
         1048  +        -: 1043:    dispatchNormalEvent,
         1049  +        -: 1044:    dispatchPolyEvent,
         1050  +        -: 1045:    dispatchCreationEvent,
         1051  +        -: 1046:} ;
         1052  +        -: 1047:static void
         1053  +       23: 1048:mechDispatch(
         1054  +        -: 1049:    MechEcb ecb)
         1055  +        -: 1050:{
         1056  +       23: 1051:    ecbDispatchFuncs[ecb->eventType](ecb) ;
         1057  +       23: 1052:}
         1058  +        -: 1053:static void
         1059  +       21: 1054:dispatchNormalEvent(
         1060  +        -: 1055:    MechEcb ecb)
         1061  +        -: 1056:{
         1062  +       21: 1057:    MechInstance target = ecb->instOrClass.targetInst ;
         1063  +       21: 1058:    ObjectDispatchBlock db = target->instClass->odb ;
         1064  +        -: 1059:
         1065  +        -: 1060:    /*
         1066  +        -: 1061:     * Test for corruption of the current state
         1067  +        -: 1062:     * or event number.
         1068  +        -: 1063:     */
         1069  +       21: 1064:    assert(db->stateCount > target->currentState) ;
         1070  +       21: 1065:    assert(db->eventCount > ecb->eventNumber) ;
         1071  +        -: 1066:    /*
         1072  +        -: 1067:     * Check for the "event-in-flight" error. This occurs
         1073  +        -: 1068:     * when an instance is deleted while there is an event
         1074  +        -: 1069:     * for that instance in the event queue.  For this
         1075  +        -: 1070:     * architecture, such occurrences are considered as
         1076  +        -: 1071:     * run-time detected analysis errors.
         1077  +        -: 1072:     */
         1078  +       21: 1073:    if (target->alloc != ecb->alloc) {
         1079  +    #####: 1074:        mechFatalError(mechEventInFlight, ecb->srcInst,
         1080  +    #####: 1075:                target, ecb->eventNumber) ;
         1081  +        -: 1076:    }
         1082  +        -: 1077:    /*
         1083  +        -: 1078:     * Fetch the new state from the transition table.
         1084  +        -: 1079:     */
         1085  +       42: 1080:    StateCode newState = *(db->transitionTable +
         1086  +       42: 1081:            target->currentState * db->eventCount +
         1087  +       21: 1082:            ecb->eventNumber) ;
         1088  +        -: 1083:#       ifdef MECH_SM_TRACE
         1089  +        -: 1084:    /*
         1090  +        -: 1085:     * Trace the transition.
         1091  +        -: 1086:     */
         1092  +        -: 1087:    traceNormalEvent(ecb->eventNumber, ecb->srcInst,
         1093  +        -: 1088:            ecb->instOrClass.targetInst, target->currentState, newState) ;
         1094  +        -: 1089:#       endif
         1095  +        -: 1090:    /*
         1096  +        -: 1091:     * Check for a can't happen transition.
         1097  +        -: 1092:     */
         1098  +       21: 1093:    if (newState == MECH_STATECODE_CH) {
         1099  +        2: 1094:        mechFatalError(mechCantHappen, target,
         1100  +        2: 1095:                target->currentState, ecb->eventNumber) ;
         1101  +       20: 1096:    } else if (newState != MECH_STATECODE_IG) {
         1102  +        -: 1097:        /*
         1103  +        -: 1098:         * Check for corrupt transition table.
         1104  +        -: 1099:         */
         1105  +       20: 1100:        assert(newState < db->stateCount) ;
         1106  +        -: 1101:        /*
         1107  +        -: 1102:         * We update the current state to reflect the
         1108  +        -: 1103:         * transition before executing the action for the
         1109  +        -: 1104:         * state.
         1110  +        -: 1105:         */
         1111  +       20: 1106:        target->currentState = newState ;
         1112  +        -: 1107:        /*
         1113  +        -: 1108:         * Invoke the state action if there is one.
         1114  +        -: 1109:         */
         1115  +       20: 1110:        PtrActionFunction action = db->actionTable[newState] ;
         1116  +       20: 1111:        if (action) {
         1117  +       20: 1112:            action(target, &ecb->eventParameters) ;
         1118  +        -: 1113:        }
         1119  +        -: 1114:        /*
         1120  +        -: 1115:         * Check if we have entered a final state. If so,
         1121  +        -: 1116:         * the instance is deleted.
         1122  +        -: 1117:         */
         1123  +       20: 1118:        if (db->finalStates && db->finalStates[newState]) {
         1124  +        1: 1119:            mechInstDestroy(target) ;
         1125  +        -: 1120:        }
         1126  +        -: 1121:    }
         1127  +        -: 1122:    /*
         1128  +        -: 1123:     * Return the ECB to the pool.
         1129  +        -: 1124:     */
         1130  +       21: 1125:    mechEventDelete(ecb) ;
         1131  +       21: 1126:}
         1132  +        -: 1127:static void
         1133  +        2: 1128:dispatchPolyEvent(
         1134  +        -: 1129:    MechEcb ecb)
         1135  +        -: 1130:{
         1136  +        2: 1131:    PolyDispatchBlock pdb = ecb->instOrClass.targetInst->instClass->pdb ;
         1137  +        -: 1132:
         1138  +        2: 1133:    assert(pdb != NULL) ;
         1139  +        2: 1134:    assert(ecb->eventNumber < pdb->eventCount) ;
         1140  +        2: 1135:    assert(pdb->hierCount > 0) ;
         1141  +        -: 1136:    /*
         1142  +        -: 1137:     * Each generalization hierarchy that originates at the
         1143  +        -: 1138:     * supertype has an event generated down that
         1144  +        -: 1139:     * hierarchy to one of the subtypes.
         1145  +        -: 1140:     */
         1146  +        2: 1141:    HierarchyDispatch hd = pdb->hierarchy ;
         1147  +        4: 1142:    for (unsigned hnum = 0 ; hnum < pdb->hierCount ; ++hnum) {
         1148  +        -: 1143:        /*
         1149  +        -: 1144:         * The most common case is to dispatch along a
         1150  +        -: 1145:         * single hierarchy.  In any case, we can modify in
         1151  +        -: 1146:         * place the input ECB on the last dispatched event.
         1152  +        -: 1147:         */
         1153  +        -: 1148:        MechEcb newEcb ;
         1154  +        2: 1149:        if (hnum == pdb->hierCount - 1) {
         1155  +        2: 1150:            newEcb = ecb ;
         1156  +        -: 1151:        } else {
         1157  +    #####: 1152:            newEcb = mechEventAlloc() ;
         1158  +        -: 1153:            /*
         1159  +        -: 1154:             * We set the source as the original sender.
         1160  +        -: 1155:             */
         1161  +    #####: 1156:            newEcb->srcInst = ecb->srcInst ;
         1162  +        -: 1157:            /*
         1163  +        -: 1158:             * Copy event parameters.
         1164  +        -: 1159:             */
         1165  +    #####: 1160:            newEcb->eventParameters = ecb->eventParameters ;
         1166  +        -: 1161:        }
         1167  +        2: 1162:        SubtypeCode type =
         1168  +        4: 1163:                *(SubtypeCode *)((char *)ecb->instOrClass.targetInst +
         1169  +        2: 1164:                hd->subCodeOffset) ;
         1170  +        -: 1165:
         1171  +        2: 1166:        assert(type < hd->subtypeCount) ;
         1172  +        4: 1167:        PolyEventMap pem = hd->eventMap +
         1173  +        2: 1168:                (type * pdb->eventCount + ecb->eventNumber) ;
         1174  +        -: 1169:#           ifdef MECH_SM_TRACE
         1175  +        -: 1170:        /*
         1176  +        -: 1171:         * Trace the transition.
         1177  +        -: 1172:         */
         1178  +        -: 1173:        tracePolyEvent(ecb->eventNumber, ecb->srcInst,
         1179  +        -: 1174:                ecb->instOrClass.targetInst, type, hnum,
         1180  +        -: 1175:                pem->event, pem->eventType) ;
         1181  +        -: 1176:#           endif /* MECH_SM_TRACE */
         1182  +        -: 1177:
         1183  +        2: 1178:        newEcb->eventNumber = pem->event ;
         1184  +        2: 1179:        newEcb->eventType = pem->eventType ;
         1185  +        -: 1180:
         1186  +        2: 1181:        void *subTypeRef =
         1187  +        2: 1182:                (char *)ecb->instOrClass.targetInst + hd->subInstOffset ;
         1188  +        4: 1183:        newEcb->instOrClass.targetInst = hd->refStorage == PolyReference ?
         1189  +        -: 1184:            /*
         1190  +        -: 1185:             * When the generalization is implemented via a
         1191  +        -: 1186:             * pointer, we need an extra level of
         1192  +        -: 1187:             * indirection to fetch the address of the
         1193  +        -: 1188:             * subtype.
         1194  +        -: 1189:             */
         1195  +        2: 1190:            *(MechInstance *)subTypeRef :
         1196  +        -: 1191:            /*
         1197  +        -: 1192:             * When the generalization is implemented by a
         1198  +        -: 1193:             * union, we need only point to the address of
         1199  +        -: 1194:             * the subtype as it is contained in the
         1200  +        -: 1195:             * supertype.
         1201  +        -: 1196:             */
         1202  +        -: 1197:            (MechInstance)subTypeRef ;
         1203  +        -: 1198:
         1204  +        2: 1199:        if (newEcb->eventType == NormalEvent) {
         1205  +        2: 1200:            newEcb->alloc = newEcb->instOrClass.targetInst->alloc ;
         1206  +        2: 1201:            assert(newEcb->alloc != 0) ;
         1207  +        -: 1202:        }
         1208  +        -: 1203:
         1209  +        2: 1204:        mechDispatch(newEcb) ;
         1210  +        2: 1205:        ++hd ;
         1211  +        -: 1206:    }
         1212  +        2: 1207:}
         1213  +        -: 1208:static void
         1214  +        2: 1209:dispatchCreationEvent(
         1215  +        -: 1210:    MechEcb ecb)
         1216  +        -: 1211:{
         1217  +        -: 1212:    /*
         1218  +        -: 1213:     * For creation events we must allocate an instance,
         1219  +        -: 1214:     * set the state to be the creation state (by
         1220  +        -: 1215:     * convention the creation state is 0).
         1221  +        -: 1216:     */
         1222  +        2: 1217:    MechInstance inst = mechInstCreate(ecb->instOrClass.targetClass,
         1223  +        -: 1218:            MECH_DISPATCH_CREATION_STATE) ;
         1224  +        -: 1219:#           ifdef MECH_SM_TRACE
         1225  +        -: 1220:    /*
         1226  +        -: 1221:     * Trace the transition.
         1227  +        -: 1222:     */
         1228  +        -: 1223:    traceCreationEvent(ecb->eventNumber, ecb->srcInst,
         1229  +        -: 1224:            inst, ecb->instOrClass.targetClass) ;
         1230  +        -: 1225:#           endif
         1231  +        -: 1226:    /*
         1232  +        -: 1227:     * Modify the event structure in place and dispatch it.
         1233  +        -: 1228:     */
         1234  +        2: 1229:    ecb->instOrClass.targetInst = inst ;
         1235  +        2: 1230:    ecb->eventType = NormalEvent ;
         1236  +        2: 1231:    ecb->alloc = ecb->instOrClass.targetInst->alloc ;
         1237  +        2: 1232:    assert(ecb->alloc != 0) ;
         1238  +        -: 1233:
         1239  +        2: 1234:    dispatchNormalEvent(ecb) ;
         1240  +        2: 1235:}
         1241  +        -: 1236:typedef struct fgsyncblock {
         1242  +        -: 1237:    SyncFunc function ;
         1243  +        -: 1238:    SyncParamType params ;
         1244  +        -: 1239:} *FgSyncBlock ;
         1245  +        -: 1240:struct syncqueue {
         1246  +        -: 1241:    FgSyncBlock head ;
         1247  +        -: 1242:    FgSyncBlock tail ;
         1248  +        -: 1243:} ;
         1249  +        -: 1244:static struct fgsyncblock
         1250  +        -: 1245:mechSyncQueueStorage[MECH_SYNCQUEUESIZE] ;
         1251  +        -: 1246:static struct syncqueue mechSyncQueue = {
         1252  +        -: 1247:    .head = mechSyncQueueStorage,
         1253  +        -: 1248:    .tail = mechSyncQueueStorage,
         1254  +        -: 1249:} ;
         1255  +        -: 1250:static inline
         1256  +        -: 1251:bool
         1257  +       24: 1252:syncQueueEmpty(void)
         1258  +        -: 1253:{
         1259  +       24: 1254:    return mechSyncQueue.head == mechSyncQueue.tail ;
         1260  +        -: 1255:}
         1261  +        -: 1256:static inline
         1262  +        -: 1257:SyncParamRef
         1263  +        6: 1258:syncQueuePut(
         1264  +        -: 1259:    SyncFunc f,
         1265  +        -: 1260:    bool fatal)
         1266  +        -: 1261:{
         1267  +        6: 1262:    FgSyncBlock tail = mechSyncQueue.tail ;
         1268  +       12: 1263:    if (++mechSyncQueue.tail >=
         1269  +        6: 1264:            mechSyncQueueStorage + MECH_SYNCQUEUESIZE) {
         1270  +    #####: 1265:        mechSyncQueue.tail = mechSyncQueueStorage ;
         1271  +        -: 1266:    }
         1272  +        6: 1267:    if (syncQueueEmpty()) {
         1273  +    #####: 1268:        if (fatal) {
         1274  +    #####: 1269:            mechFatalError(mechSyncOverflow) ;
         1275  +        -: 1270:        }
         1276  +    #####: 1271:        return NULL ;
         1277  +        -: 1272:    }
         1278  +        -: 1273:
         1279  +        6: 1274:    tail->function = f ;
         1280  +        6: 1275:    return &tail->params ;
         1281  +        -: 1276:}
         1282  +        -: 1277:static inline
         1283  +        -: 1278:FgSyncBlock
         1284  +       11: 1279:syncQueueGet(void)
         1285  +        -: 1280:{
         1286  +        -: 1281:    FgSyncBlock head ;
         1287  +        -: 1282:
         1288  +       11: 1283:    beginCriticalSection() ;
         1289  +       11: 1284:    if (syncQueueEmpty()) {
         1290  +        5: 1285:        head = NULL ;
         1291  +        -: 1286:    } else {
         1292  +        6: 1287:        head = mechSyncQueue.head ;
         1293  +       12: 1288:        if (++mechSyncQueue.head >=
         1294  +        6: 1289:                mechSyncQueueStorage + MECH_SYNCQUEUESIZE) {
         1295  +    #####: 1290:            mechSyncQueue.head = mechSyncQueueStorage ;
         1296  +        -: 1291:        }
         1297  +        -: 1292:    }
         1298  +       11: 1293:    endCriticalSection() ;
         1299  +        -: 1294:
         1300  +       11: 1295:    return head ;
         1301  +        -: 1296:}
         1302  +        -: 1297:SyncParamRef
         1303  +        6: 1298:mechSyncRequest(
         1304  +        -: 1299:    SyncFunc f)
         1305  +        -: 1300:{
         1306  +        6: 1301:    return syncQueuePut(f, true) ;
         1307  +        -: 1302:}
         1308  +        -: 1303:SyncParamRef
         1309  +    #####: 1304:mechTrySyncRequest(
         1310  +        -: 1305:    SyncFunc f)
         1311  +        -: 1306:{
         1312  +    #####: 1307:    return syncQueuePut(f, false) ;
         1313  +        -: 1308:}
         1314  +        -: 1309:void
         1315  +        1: 1310:mechRegisterSignal(
         1316  +        -: 1311:    int sigNum,
         1317  +        -: 1312:    SignalFunc func)
         1318  +        -: 1313:{
         1319  +        1: 1314:    assert(sigNum > 0) ;
         1320  +        -: 1315:
         1321  +        -: 1316:    struct sigaction action ;
         1322  +        1: 1317:    if (func) {
         1323  +        1: 1318:        action.sa_handler = func ;
         1324  +        1: 1319:        sigaddset(&mechSigMask, sigNum) ;
         1325  +        -: 1320:    } else {
         1326  +    #####: 1321:        action.sa_handler = SIG_DFL ;
         1327  +    #####: 1322:        sigdelset(&mechSigMask, sigNum) ;
         1328  +        -: 1323:    }
         1329  +        1: 1324:    sigfillset(&action.sa_mask) ;
         1330  +        1: 1325:    action.sa_flags = 0 ;
         1331  +        -: 1326:
         1332  +        1: 1327:    int sigresult = sigaction(sigNum, &action, NULL) ;
         1333  +        1: 1328:    if (sigresult != 0) {
         1334  +    #####: 1329:        mechFatalError(mechSignalOpFailed, strerror(errno)) ;
         1335  +        -: 1330:    }
         1336  +        1: 1331:}
         1337  +        -: 1332:typedef struct fdservicemap {
         1338  +        -: 1333:    bool set ;
         1339  +        -: 1334:    FDServiceFunc read ;
         1340  +        -: 1335:    FDServiceFunc write ;
         1341  +        -: 1336:    FDServiceFunc except ;
         1342  +        -: 1337:} *FDServiceMap ;
         1343  +        -: 1338:static struct fdservicemap mechFDServicePool[FD_SETSIZE] ;
         1344  +        -: 1339:static int mechMaxFD = -1 ;
         1345  +        -: 1340:static fd_set mechReadFDS ;
         1346  +        -: 1341:static fd_set mechWriteFDS ;
         1347  +        -: 1342:static fd_set mechExceptFDS ;
         1348  +        -: 1343:void
         1349  +        1: 1344:mechRegisterFDService(
         1350  +        -: 1345:    int fd,
         1351  +        -: 1346:    FDServiceFunc readService,
         1352  +        -: 1347:    FDServiceFunc writeService,
         1353  +        -: 1348:    FDServiceFunc exceptService)
         1354  +        -: 1349:{
         1355  +        1: 1350:    assert(fd >= 0 && fd < FD_SETSIZE) ;
         1356  +        1: 1351:    FDServiceMap fds = mechFDServicePool + fd ;
         1357  +        -: 1352:
         1358  +        1: 1353:    fds->read = readService ;
         1359  +        1: 1354:    if (readService) {
         1360  +    #####: 1355:        FD_SET(fd, &mechReadFDS) ;
         1361  +    #####: 1356:        fds->set = true ;
         1362  +        -: 1357:    } else {
         1363  +        1: 1358:        FD_CLR(fd, &mechReadFDS) ;
         1364  +        -: 1359:    }
         1365  +        -: 1360:
         1366  +        1: 1361:    fds->write = writeService ;
         1367  +        1: 1362:    if (writeService) {
         1368  +        1: 1363:        FD_SET(fd, &mechWriteFDS) ;
         1369  +        1: 1364:        fds->set = true ;
         1370  +        -: 1365:    } else {
         1371  +    #####: 1366:        FD_CLR(fd, &mechWriteFDS) ;
         1372  +        -: 1367:    }
         1373  +        -: 1368:
         1374  +        1: 1369:    fds->except = exceptService ;
         1375  +        1: 1370:    if (exceptService) {
         1376  +    #####: 1371:        FD_SET(fd, &mechExceptFDS) ;
         1377  +    #####: 1372:        fds->set = true ;
         1378  +        -: 1373:    } else {
         1379  +        1: 1374:        FD_CLR(fd, &mechExceptFDS) ;
         1380  +        -: 1375:    }
         1381  +        -: 1376:
         1382  +        1: 1377:    if (fds->read == NULL && fds->write == NULL && fds-> except == NULL) {
         1383  +    #####: 1378:        if (fds->set && fd >= mechMaxFD) {
         1384  +    #####: 1379:            --mechMaxFD ;
         1385  +        -: 1380:        }
         1386  +    #####: 1381:        fds->set = false ;
         1387  +        1: 1382:    } else if (fds->set && fd > mechMaxFD) {
         1388  +        1: 1383:        mechMaxFD = fd ;
         1389  +        -: 1384:    }
         1390  +        1: 1385:}
         1391  +        -: 1386:void
         1392  +        1: 1387:mechRemoveFDService(
         1393  +        -: 1388:    int fd,
         1394  +        -: 1389:    bool rmRead,
         1395  +        -: 1390:    bool rmWrite,
         1396  +        -: 1391:    bool rmExcept)
         1397  +        -: 1392:{
         1398  +        1: 1393:    assert(fd >= 0 && fd < FD_SETSIZE) ;
         1399  +        1: 1394:    FDServiceMap fds = mechFDServicePool + fd ;
         1400  +        -: 1395:
         1401  +        1: 1396:    if (rmRead) {
         1402  +    #####: 1397:        fds->read = NULL ;
         1403  +    #####: 1398:        FD_CLR(fd, &mechReadFDS) ;
         1404  +        -: 1399:    }
         1405  +        -: 1400:
         1406  +        1: 1401:    if (rmWrite) {
         1407  +        1: 1402:        fds->write = NULL ;
         1408  +        1: 1403:        FD_CLR(fd, &mechWriteFDS) ;
         1409  +        -: 1404:    }
         1410  +        -: 1405:
         1411  +        1: 1406:    if (rmExcept) {
         1412  +    #####: 1407:        fds->except = NULL ;
         1413  +    #####: 1408:        FD_CLR(fd, &mechExceptFDS) ;
         1414  +        -: 1409:    }
         1415  +        -: 1410:
         1416  +        2: 1411:    if (fds->read == NULL && fds->write == NULL && fds-> except == NULL &&
         1417  +        1: 1412:            fd >= mechMaxFD) {
         1418  +        1: 1413:        mechMaxFD = fd - 1 ;
         1419  +        -: 1414:    }
         1420  +        1: 1415:}
         1421  +        -: 1416:static void
         1422  +        1: 1417:fdServiceInit(void)
         1423  +        -: 1418:{
         1424  +        1: 1419:    FD_ZERO(&mechReadFDS) ;
         1425  +        1: 1420:    FD_ZERO(&mechWriteFDS) ;
         1426  +        1: 1421:    FD_ZERO(&mechExceptFDS) ;
         1427  +        1: 1422:}
         1428  +        -: 1423:MECH_TEST_STATIC
         1429  +        -: 1424:void
         1430  +        7: 1425:mechWait(void)
         1431  +        -: 1426:{
         1432  +        7: 1427:    beginCriticalSection() ;
         1433  +        7: 1428:    if (syncQueueEmpty()) {
         1434  +        -: 1429:        /*
         1435  +        -: 1430:         * Copy the file descriptor sets since "pselect"
         1436  +        -: 1431:         * modifies them in place upon return.
         1437  +        -: 1432:         */
         1438  +        -: 1433:        fd_set readfds ;
         1439  +        7: 1434:        memcpy(&readfds, &mechReadFDS, sizeof(readfds)) ;
         1440  +        -: 1435:        fd_set writefds ;
         1441  +        7: 1436:        memcpy(&writefds, &mechWriteFDS, sizeof(writefds)) ;
         1442  +        -: 1437:        fd_set exceptfds ;
         1443  +        7: 1438:        memcpy(&exceptfds, &mechExceptFDS, sizeof(exceptfds)) ;
         1444  +        -: 1439:        /*
         1445  +        -: 1440:         * Allow all the signals during the select. We
         1446  +        -: 1441:         * assume that when we enter this function, the
         1447  +        -: 1442:         * registered signals are masked.
         1448  +        -: 1443:         */
         1449  +        -: 1444:        sigset_t mask ;
         1450  +        7: 1445:        sigemptyset(&mask) ;
         1451  +        -: 1446:        /*
         1452  +        -: 1447:         * "mechMaxFD" holds the maximum value of any
         1453  +        -: 1448:         * registered file descriptor. We must add one to
         1454  +        -: 1449:         * get the number of file descriptors "pselect" is
         1455  +        -: 1450:         * to consider.
         1456  +        -: 1451:         */
         1457  +        7: 1452:        int r = pselect(mechMaxFD + 1, &readfds, &writefds,
         1458  +        -: 1453:                &exceptfds, NULL, &mask) ;
         1459  +        7: 1454:        if (r == -1) {
         1460  +        6: 1455:            if (errno != EINTR) {
         1461  +    #####: 1456:                mechFatalError(mechSelectWaitFailed,
         1462  +    #####: 1457:                        strerror(errno)) ;
         1463  +        -: 1458:            }
         1464  +        -: 1459:            /*
         1465  +        -: 1460:             * Got a signal while waiting. We go back to the
         1466  +        -: 1461:             * main loop on the assumption that something
         1467  +        -: 1462:             * has been placed in the sync queue.
         1468  +        -: 1463:             */
         1469  +        -: 1464:        } else {
         1470  +        -: 1465:            /*
         1471  +        -: 1466:             * Dispatch the service functions for the file
         1472  +        -: 1467:             * descriptors.
         1473  +        -: 1468:             */
         1474  +        1: 1469:            FDServiceMap s = mechFDServicePool ;
         1475  +        4: 1470:            for (int fd = 0 ; r > 0 && fd <= mechMaxFD ;
         1476  +        2: 1471:                    ++fd, ++s) {
         1477  +        -: 1472:                /*
         1478  +        -: 1473:                 * Do exceptions first. This is only
         1479  +        -: 1474:                 * important for sockets, but without going
         1480  +        -: 1475:                 * first the OOB data processing won't
         1481  +        -: 1476:                 * work.
         1482  +        -: 1477:                 */
         1483  +        2: 1478:                if (FD_ISSET(fd, &exceptfds)) {
         1484  +    #####: 1479:                    assert(s->except != NULL) ;
         1485  +    #####: 1480:                    s->except(fd) ;
         1486  +    #####: 1481:                    --r ;
         1487  +        -: 1482:                }
         1488  +        2: 1483:                if (FD_ISSET(fd, &readfds)) {
         1489  +    #####: 1484:                    assert(s->read != NULL) ;
         1490  +    #####: 1485:                    s->read(fd) ;
         1491  +    #####: 1486:                    --r ;
         1492  +        -: 1487:                }
         1493  +        2: 1488:                if (FD_ISSET(fd, &writefds)) {
         1494  +        1: 1489:                    assert(s->write != NULL) ;
         1495  +        1: 1490:                    s->write(fd) ;
         1496  +        1: 1491:                    --r ;
         1497  +        -: 1492:                }
         1498  +        -: 1493:            }
         1499  +        -: 1494:        }
         1500  +        -: 1495:    }
         1501  +        7: 1496:    endCriticalSection() ;
         1502  +        7: 1497:}
         1503  +        -: 1498:static void
         1504  +        1: 1499:sysPlatformInit(void)
         1505  +        -: 1500:{
         1506  +        1: 1501:    fdServiceInit() ;
         1507  +        1: 1502:}
         1508  +        -: 1503:MECH_TEST_STATIC
         1509  +        -: 1504:MECH_TEST_INLINE
         1510  +        -: 1505:void
         1511  +        1: 1506:mechInit(void)
         1512  +        -: 1507:{
         1513  +        1: 1508:    sysPlatformInit() ;
         1514  +        1: 1509:    mechEventInit() ;
         1515  +        1: 1510:    initCriticalSection() ;
         1516  +        1: 1511:    sysTimerInit() ;
         1517  +        1: 1512:    sysDeviceInit() ;
         1518  +        1: 1513:    sysDomainInit() ;
         1519  +        1: 1514:}
         1520  +        -: 1515:MECH_TEST_STATIC
         1521  +        -: 1516:MECH_TEST_INLINE
         1522  +        -: 1517:bool
         1523  +       11: 1518:mechInvokeOneSyncFunc(void)
         1524  +        -: 1519:{
         1525  +        -: 1520:    bool didOne ;
         1526  +       11: 1521:    FgSyncBlock blk = syncQueueGet() ;
         1527  +       11: 1522:    if (blk && blk->function) {
         1528  +        6: 1523:        blk->function(&blk->params) ;
         1529  +        6: 1524:        didOne = true ;
         1530  +        -: 1525:    } else {
         1531  +        5: 1526:        didOne = false ;
         1532  +        -: 1527:    }
         1533  +       11: 1528:    return didOne ;
         1534  +        -: 1529:}
         1535  +        -: 1530:MECH_TEST_STATIC
         1536  +        -: 1531:MECH_TEST_INLINE
         1537  +        -: 1532:bool
         1538  +       29: 1533:mechDispatchOneEvent(void)
         1539  +        -: 1534:{
         1540  +       29: 1535:    bool didOne = !eventQueueEmpty(&eventQueue) ;
         1541  +       29: 1536:    if (didOne) {
         1542  +       21: 1537:        MechEcb ecb = eventQueue.next ;
         1543  +       21: 1538:        eventQueueRemove(ecb) ;
         1544  +       21: 1539:        mechDispatch(ecb) ;
         1545  +        -: 1540:    }
         1546  +       29: 1541:    return didOne ;
         1547  +        -: 1542:}
         1548  +        -: 1543:#ifdef MECH_TEST
         1549  +        -: 1544:void
         1550  +    #####: 1545:stsa_main(void)
         1551  +        -: 1546:#else
         1552  +        -: 1547:int
         1553  +        -: 1548:main(void)
         1554  +        -: 1549:#endif /* MECH_TEST */
         1555  +        -: 1550:{
         1556  +    #####: 1551:    mechInit() ;
         1557  +        -: 1552:
         1558  +        -: 1553:    for (;;) { /* Infinite Big Loop */
         1559  +        -: 1554:        /*
         1560  +        -: 1555:         * Empty the foreground/background sync queue.
         1561  +        -: 1556:         */
         1562  +        -: 1557:        #ifndef __ARM_ARCH_7M__
         1563  +        -: 1558:        /*
         1564  +        -: 1559:         * Empty the foreground / background
         1565  +        -: 1560:         * synchronization queue.
         1566  +        -: 1561:         */
         1567  +    #####: 1562:        while (mechInvokeOneSyncFunc()) {
         1568  +        -: 1563:            ; /* empty */
         1569  +        -: 1564:        }
         1570  +        -: 1565:        #endif /* __ARM_ARCH_7M__ */
         1571  +        -: 1566:        /*
         1572  +        -: 1567:         * Dispatch one event from the event queue.
         1573  +        -: 1568:         */
         1574  +    #####: 1569:        if (!mechDispatchOneEvent()) {
         1575  +        -: 1570:            /*
         1576  +        -: 1571:             * Check if this thread of control is complete
         1577  +        -: 1572:             * and wait if there is no additional work to
         1578  +        -: 1573:             * be done.
         1579  +        -: 1574:             */
         1580  +    #####: 1575:            mechWait() ;
         1581  +        -: 1576:        }
         1582  +        -: 1577:    }
         1583  +        -: 1578:}