Click here to Skip to main content
Click here to Skip to main content

Macros to simulate multi-tasking/blocking code at interrupt level

By , 27 Jun 2009
Rate this:
Please Sign up or sign in to vote.

Table of Contents

  1. Introduction
  2. Usage
    1. Basics
    2. Notes
    3. Setup
    4. Defining a function
    5. Start a new thread
    6. Calling a function
    7. Calling a function indirectly
    8. Returning from a function
    9. Obtaining the return code
    10. Finishing a thread
    11. Suspending within a thread
    12. Resuming a thread
    13. Trapping
    14. The parent IOB
    15. User context
    16. MT_CALL sequence of events
  3. Macros
  4. Test code
  5. License

1 Introduction

A user level system uses multi-threading (a.k.a. blocking threads); however, for most interrupt driven code, system multi-threading is not used. This document describes a set of macros called MT_CALL which simulates multi-threading systems. Using these macros has these major advantages:

  1. it uses only one system thread to support thousands of I/O threads,
  2. an Operating System is not required, and
  3. it supports threads on the interrupt level.

Typically, while in interrupt level, work can be either dispatched to task level or kept in interrupt level depending on the implementation of the IO handler. For example, a cache subsystem and RAID subsystem may be best implemented at task level. When the cache handler is activated, it may run a system task; but the storage handler may be more effective if it runs at interrupt level. MT_CALLs can run at interrupt level and provide task style programming.

These macros are used to maintain structured programming techniques within the interrupt context. Just as with normal task level software, it is much easier to follow the flow of control using multi-tasking than it would be to use a traditional state machine. For example, at task level, one would rarely use a state machine to perform disk I/O because the Operating System provides synchronous calls. But, at interrupt level, synchronous I/O is not possible, so asynchronous methods must be employed. Normally, this requires complicated state machines, and complicated state machines require an in depth knowledge of how all states fit together. As new requirements are placed on the software, there is a temptation to use flags to avoid new states. These flags, or even the introduction of new states, often cause "code balance" to be upset.

Figure 1 (MT_CALL sequence) shows a sample flow. Function A starts a thread with MT_START (path 1); the thread (function B) starts into action and performs an MT_CALL to function C (path 2); function C performs the initial setup and performs some I/O which will eventually lead to an interrupt; function C uses MT_SUSPEND to suspend operations; as can be seen by path 3, a hidden return occurs back to the initial creator of the thread, and the thread creator eventually returns to the Operating System which proceeds to other tasks; an interrupt occurs (path 4) and the flow continues to a user provided handler; the user handler identifies the source of the interrupt and uses MT_RESUME to resume execution of the suspended thread (path 5); the suspended thread picks up control after MT_SUSPEND and returns to its caller using an MT_RETURN (path 6); MT_RESUME continues its activities by calling the previous function (path 7) which determines that it is finished; function B uses MT_FINISHED (path 8) to terminate the thread; MT_FINISHED causes control to pass back to MT_RESUME which returns back to the interrupt system and finally back to the interrupted task.

2 Usage

2.1 Basics

  • To start a thread, use MT_START.
  • Each function called with MT_START or MT_CALL must be an MT_FUNCTION.
  • Each MT_FUNCTION must begin with MT_ENTRY and must return using MT_RETURN.
  • If an MT_FUNCTION determines that the thread is finished, it uses MT_FINISHED.
  • If a thread must suspend for an external event (e.g., an I/O operation), it uses MT_SUSPEND.
  • When the external event occurs (e.g., an interrupt), the handler uses MT_RESUME.

2.2 Notes

  • Variables on the stack will not be preserved around an MT_CALL; those variables should be placed within your personal IOB.

2.3 Setup

The macros are used as follows:

  • Define if you are debugging or not
  • The MT_DEBUG macro will cause stack checking, and will print the use of each macro, allowing you to trace your execution. For example:

    #define MT_DEBUG
  • Define the depth of the nesting
  • A stack is setup in the Task Control Block which holds the address and state of each MT_CALL. The maximum depth is defined as in this example:

    #define MT_MAX_DEPTH    3
  • Define the states you will be using
  • These states are defined by the MT_STATES construct. These will be appended to an internal enum typedef called eMT_STATE. Separate each state with a comma, and if separate lines are used, use the macro continuation operator. Be sure to leave the operator off of the last line. For example:

    #define MT_STATES    \
        DO_SCAN,         \
        START_IO,        \
        TEST_UNIT_READY, \
        CHECKING_DRIVE,  \
        GETTING_TYPE,    \
        STOP_SCAN,       \
        EXECUTE_SCSI

    Define the pointer to be used for the Task Control Block.

    This pointer is encapsulated in many of the macros. It is generally not used by the application, but since some macros are general, it may be used for those cases. For example:

    #define MT_TCB_PTR pTcb
  • Include the macro file
  • For example:

    #include <MT_CALL.H>
  • Define the Task Control Block
  • The Task Control Block is the struct MT_TCB, type sMT_TCB, and pointer pMT_TCB. For example:

    sMT_TCB CheckDevicesTcb;
  • Define the prototypes for the MT_Functions
  • An MT function is defined with the MT_FUNCTION macro. For example:

    MT_FUNCTION ( CheckDevicesThread );

2.4 Defining a function

When a function starts a thread or is called by an MT_CALL, it must be defined as an MT_FUNCTION. Furthermore, it must contain an MT_ENTRY to define the states being used, and must use MT_RETURN to return. It may use the macros MT_START, MT_CALL, MT_SUSPEND, MT_SET_TRAP, MT_RESUME, or MT_GOTO_TRAP (and maybe some that I forgot).

The MT_NAME macro is used to define the name of the function, and the MT_FUNCTION macro is used to form the function. The MT_NAME macro will be used within some of the other macros. This name is used to identify the function name in the Task Control Block.

#undef MT_NAME
#define MT_NAME FunctionName
MT_FUNCTION ( FunctionName )

Where:

FunctionName is the name of the function. It must appear within the MT_FUNCTION macro. The macro could be written as MT_FUNCTION( MT_NAME ) if you wish (I prefer using the name so I can just cut and paste to form the prototype).

The function may not take any arguments. This seems unfortunate, but it is the nature of a state driven strategy in any case (remember, this is simply an easy to use state machine); sorry.

The MT_ENTRY macro generally appears after the automatic variable definitions. However, it may appear after code, as explained in an example below.

MT_ENTRY
{
    MT_USE( StateName );
    MT_USE( AnotherStateName );
    MT_END_OF_LIST(); // to overcome warnings with some compilers
}

Where:

StateName and AnotherStateName are the names of the states used in this function. For each state used, there must be a corresponding MT_CALL or MT_SUSPEND, and the states must have been defined in the MT_STATES macro.

The MT_RETURN macro is used to return from an MT_FUNCTION. It does not take an argument.

MT_RETURN();

Caution

Since these functions are actually re-entered when an MT_RESUME is used, any automatic variable will be lost around an MT_CALL or MT_SUSPEND. So, some variables may need to be within a user defined structure.

Here is an example:

#undef MT_NAME
#define MT_NAME CheckDevicesThread
MT_FUNCTION (CheckDevicesThread)
{
    // here is a case where an automatic variable is restored
    FUNNY_IOB *pIob = GetTheIob();
    MT_ENTRY()
    {
        MT_USE( CHECKING_DRIVE );
        MT_USE( GETTING_TYPE );
        MT_END_OF_LIST();
    }
    for(pIob->dn = 0; pIob->dn < MAX_DRIVES; pIob->dn++)
    {
        MT_CALL(CheckDrive, CHECKING_DRIVE);
    }
    MT_CALL(DoInquiry, GETTING_TYPE);
    MT_RETURN();
}

2.5 Start a new thread

Threads are started with the MT_START macro. Once started, a thread is on its own in its own execution path. The Task Control Block will maintain the state of the thread (the MT_TCB_PTR is used for this purpose).

The calling sequence is:

MT_START( pTcb, funToCall, pPrev );

Where:

pTcb is a pointer to the Task Control Block to use.
funToCall is the name of an MT_FUNCTION to be called.
pPrev is a pointer to a Task Control Block which represents a thread to be resumed when the new thread finishes (using the MT_FINISHED macro); this may be NULL if there is no previous thread.

For example:

MT_START( &CheckDevicesTcb, CheckDevicesThread, NULL );

In this case, the function called CheckDevicesThread() will be called. The Task Control Block is CheckDevicesTcb, and there is no previous Task Control Block involved.

2.6 Calling a function

Whenever a function is called that must suspend or lead to a suspend, it must be coded as an MT_CALL and the function must be coded as an MT_FUNCTION.

Caution: When making an MT_CALL, automatic (stack) variables will not be preserved across the MT_CALL.

The calling sequence is:

MT_CALL( funToCall, callState );

Where

funToCall is the MT_FUNCTION to call.
callState is one of the states defined in MT_STATES and used in an MT_USE.

2.7 Calling a function indirectly

A function can be called indirectly by using the following method.

Define the function pointer using double parenthesis, such as:

MT_FUNCTION((*pFunction));

Call the function referencing the pointer just as if it were a normal MT_CALL, as:

MT_CALL( pFunction, callState );

Following is an example of using an array of pointers:

MT_FUNCTION((*pFunction[10]));
...
MT_CALL( pFunction[j], callState );

2.8 Returning from a function

When you must return from the function, use the MT_RETURN macro.

The calling sequence is:

MT_RETURN( returnCode )

Where

returnCode is an unsigned int to be returned to the caller; this is obtained via the MT_GET_RETURN_CODE macro.

2.9 Obtaining the return code

After a return from an MT_CALL, you can get the return code from the called function with the MT_GET_RETURN_CODE macro.

The calling sequence is:

rc = MT_GET_RETURN_CODE()

Where:

rc is an unsigned int. It is set by an argument of an MT_RETURN, MT_FINISHED, or MT_GOTO_TRAP.

2.10 Finishing a thread

When you are finished with the thread, it can be terminated with an MT_FINISHED macro. If a previous TCB was specified in the MT_START macro (see pPrev above), the MT_FINISHED macro will resume the thread which used MT_START.

Note: The thread that used MT_START and specified a pPrev argument must have suspended with an MT_SUSPEND macro or must be proceeding to an MT_SUPEND.

The calling sequence is:

MT_FINISHED( returnCode )

Where:

returnCode is an unsigned int. It can be retrieved by using MT_GET_RETURN_CODE().

2.11 Suspending within a thread

A thread may suspend execution at anytime by using the MT_SUSPEND macro. When suspended, execution must continue by some external logic supplied by the user. This logic is generally part of the interrupt process; however, the logic could be simply a matter of satisfying the proper conditions.

The calling sequence is:

MT_SUSPEND( callState )

Where:

callState is one of the enums supplied with the MT_USE macro.

2.12 Resuming a thread

A thread may be resumed via the MT_RESUME macro before or after it has suspended. This "early resume" overcomes a possible timing problem where an interrupt may occur after an I/O was started but before the thread actually executed the MT_SUSPEND.

The MT_RESUME macro is generally used from the interrupt service routine; however, this is not a requirement.

The calling sequence is:

MT_RESUME( pTcb )

Where:

pTcb is a pointer to the Task Control Block that has been assigned to the thread to be resumed.

2.13 Trapping

A thread may trap its execution to a predetermined point. This is useful when an error occurs at a low level. It is similar to the setjmp/longjmp functions in C.

The calling sequence is:

MT_SET_TRAP( pEnv, callState )
MT_GOTO_TRAP(pEnv, returnCode )

Where:

pEnv is a pointer to an environment structure of type MT_ENV.
callState is the state name for the trap and is named in an MT_USE macro.
returnCode is a return code that can be obtained with the MT_GET_RETURN_CODE macro.

Usage:

The usage of the trap function is as follows:

  1. Setup the trap return point with the MT_SET_TRAP macro.
  2. Test whether or not the trap has occurred with the MT_IS_TRAPPING macro.
  3. Perform the trap with the MT_GOTO_TRAP macro.

Here is an example:

sMT_ENV Environment;
...
MT_FUNCTION (CheckDrive)
{
    MT_ENTRY()
    {
        MT_USE( STOP_SCAN );
        MT_END_OF_LIST();
    }

    MT_SET_TRAP( &Environment, STOP_SCAN );
    if (MT_IS_TRAPPING())
    {
        printf("\nCheckDrive cought a trap, rc=%d", 
               MT_GET_RETURN_CODE());
        MT_RETURN(0);
    }
    ...
}
...
MT_FUNCTION (ExecuteScsi)
{
    MT_ENTRY()
    {
        ...
    }
    ...
   if( an error occurred )
    {
        printf("\nTrapping in ExecuteScsi");
        MT_GOTO_TRAP( &Environment, 9876);
        // MT_GOTO_TRAP will not return to here
    }
    ...
}

2.14 The parent IOB

With each TCB, sometimes there may be an I/O structure (IOB) associated with it. When that method is used, it is usually necessary to get the address of the IOB that contains the TCB. This can be done in several ways; for convenience, the MT_GET_PARENT_STRUCT macro can be used.

The calling sequence is:

MT_GET_PARENT_STRUCT( structType, tcb )

Where:

structType is the typedef for the IOB structure
tcb is the name of the TCB structure within the IOB

2.15 User context

Very often, it is necessary to set some user context that will be carried along with the thread. This can be done using the MT_SET_CONTEXT_PTR and MT_GET_CONTEXT_PTR macros.

The calling sequence is:

MT_SET_CONTEXT_PTR( pTcb, ptr )
ptr = MT_GET_CONTEXT_PTR()

Where:

pTcb is a pointer to the Task Control Block which is to carry the context pointer.
ptr is a user context pointer; this can point to anything and is not understood by the macros.

2.16 MT_CALL sequence of events

Referencing Figure 1 - MT_CALL sequence of events, the sequence of events that occur with these functions is as follows:

Trail 1 shows the thread starting with an MT_START macro. This initializes the Thread Control Block (TCB) named in the calling sequence. Specifically, the current state (currentState) is set to -1.

  • The new thread proceeds through the MT_ENTRY statement because the currentState has been initialized to -1 by MT_START.
  • Execution continues normally, and trail 2 shows an MT_CALL. This increments the stackIndex, adds the address of the current function (named by #define MT_NAME) to the callback stack (callStack), and sets the call state (callState). MT_CALL also sets currentState to -1 to force the new function to proceed through the MT_ENTRY statement.
  • Execution continues normally through the MT_ENTRY statement and on to the MT_SUSPEND statement.

Trail 3 shows the departure back to the Operating System. MT_SUSPEND has also incremented the stackIndex and added the address of the current function just as above. Now, things are suspended.

  • Trail 3 returns to the Operating System because MT_SUSPEND has an embedded return NULL statement. The MT_CALL that started trail 2 checks the return value, and if it is a NULL, it repeats the return NULL. Any other MT_CALLs in the sequence also do the same, hence control returns to MT_START. The MT_START assumes the thread is free running and just continues execution (in this case, Function A actually does the return to the Operating System). Note: before MT_SUSPEND does anything, it checks to see if an MT_RESUME has already been used, and if so, it does nothing (example not shown in this figure).

Trail 4 shows an interrupt occurring. The interrupt system passes control to a user handler, and that handler, after determining the source of the interrupt, uses the MT_RESUME macro to resume execution of the thread.

  • The MT_RESUME is a loop that calls the functions back using stackIndex and supplies each function with its saved callState. If MT_RESUME sees that MT_SUSPEND has not yet been used, it only records the fact that MT_RESUME was used and then does nothing. This allows for the case where an interrupt may actually occur before MT_SUSPEND had a chance to execute.

Trail 5 shows the resumption of Function C. Now, the MT_ENTRY in Function C performs a switch(callState) which causes a goto around the MT_SUSPEND.

  • Execution continues along trail 5 to the point of an MT_RETURN.

Trail 6 shows that an MT_RETURN returns back to the User Handler. The MT_RETURN returns a non-NULL value which keeps MT_RESUME in its loop.

Trail 7 shows that the previous function is called with its callState. This could have done more MT_CALLs or an MT_SUSPEND, in which case, the above scenario continues to occur.

  • In our case, trail 7 runs into an MT_FINISHED statement. If no previous TCB was supplied in the MT_START macro, MT_FINISHED just does a return NULL. That causes everything to return to the Operating System because MT_RESUME stops when something returns a NULL.
  • If Function B had used an MT_SUSPEND, that would return NULL, which would also cause the MT_RESUME loop to terminate.

Sequence_of_events.jpg

Figure 1 - MT_CALL sequence of events

3 Macros

/*-
 * Copyright (c) 1995, 2000, 2007 Edward S. Quicksall
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Edward S. Quicksall.
 * 3. Neither the name Edward S. Quicksall nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY EDWARD S. QUICKSALL AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EDWARD S. QUICKSALL OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


/*
#ifdef __KERNEL__
    #define PRINTF printk
#else
    #ifndef _ARC
        #define PRINTF printf
    #endif
#endif
*/
////////////////////////////////////////////////////////////////////////
//
//  Name:           MtCall.h
//  Description:    Simulates multi-tasking for interrupt driven code.
//  Returns:        none
//  Arguments:      none
//  Notes:          none
//
////////////////////////////////////////////////////////////////////////


//#define MT_DEBUG
//#define MT_DEBUG_PRINT

//  These default defines allow the .h file to be included when the
//  structures are needed but the actual MT_CALL macros are not
#ifndef MT_MAX_DEPTH
    #define MT_MAX_DEPTH    6 // the debugger picks this up by mistake so for
                              // debugging, make this the same as the real one
#endif
#ifndef MT_STATES
    #define MT_STATES   MT_NOTHING
#endif


// ----------------- pre-define issues -----------------------------------
#ifndef __MT_CALL_PREDEF__
#define __MT_CALL_PREDEF__

//  The things within __MT_CALL_PREDEF__ are needed by .h files which are used
//  before the formal use of MtCall.h

typedef struct MT_SEMAPHORE
{
    int             signaled;    // + means it has been signaled that many times
    struct MT_TCB   *pTcb;       // pointer to
} sMT_SEMAPHORE, *pMT_SEMAPHORE;

#endif  //  __MT_CALL_PREDEF__
// -----------------------------------------------------------------------



//  The definition of MT_MAX_DEPTH means this is the formal use of MtCall.h
//#ifdef MT_MAX_DEPTH

#ifndef __MT_CALL_H__
#define __MT_CALL_H__

#ifdef __KERNEL__
    #include <linux>
#else
    #include <stddef.h> // needed for offsetof macro
#endif

#ifdef WIN32
    #define MT_ENTER_DEBUGGER() _asm { int 3 }
#endif

#ifdef IDISX_MIPS_COMPILER
    #define MT_ENTER_DEBUGGER() printk("%d\n", *(unsigned int *)0)
#endif

#define MT_FUNCTION(funName) sMT_TCB * funName(struct MT_TCB *MT_TCB_PTR)

typedef enum MT_STATE
{
    MT_INITIAL_CALL = -1,
    MT_UNINITIALIZED    = 0,
    MT_STATES              
} eMT_STATE;

typedef struct MT_TCB * (*MT_FUNCTION_PTR) (struct MT_TCB *);

typedef struct MT_CALLBACK
{
    MT_FUNCTION_PTR pFunction;      // calling function
    eMT_STATE       functionState;  // state of the function (pFunction)
} sMT_CALLBACK, *pMT_CALLBACK;

typedef struct MT_TCB
{
    int             stackIndex;     // index into call stack
    sMT_CALLBACK    callStack[ MT_MAX_DEPTH ];  // call stack
    enum MT_STATE   currentState;   // (callStack.functionState)
    int             suspends;       // 1 if suspends, 0 if not, negative means
                                    // resumed that many times
    struct MT_TCB   *pPrevTcb;      // pointer to the previous TCB
    void            *pContext;      // pointer to a user IOB
    MT_FUNCTION_PTR pTrappingFunction; // ptr if GOTO_TRAP, 1 if
                                    // RESUME_AND_TRAP, else 0
    union
    {
        unsigned int    returnCode;     // a return code
        void            *returnContext; // not referenced, just to be sure there
                                        // is enough space for a pointer here
    } u;
} sMT_TCB, *pMT_TCB;

typedef struct MT_ENVIRONMENT
{
    eMT_STATE       currentState;   // (callStack.functionState)
    int             stackIndex;     // index into call stack
    sMT_CALLBACK    callBack;       // function and state to call back to
} sMT_ENVIRONMENT, *pMT_ENVIRONMENT;


////////////////////////////////////////////////////////////////////////
//
//  Name:           N/A
//  Description:    These are debug macros. They do nothing if MT_DEBUG is not
//                  defined.
//  Returns:        N/A
//  Arguments:      N/A
//  Notes:          MT_STACK_CHECK checks for a stack overflow
//                  MT_CLEAR_CALL clears the current call entry
//                  MT_CLEAR_ALL clears all entries in the stack
//                  MT_PRINTF is a simulation of printf
//
////////////////////////////////////////////////////////////////////////
#ifdef MT_DEBUG

    #ifdef WIN32
        #include <assert.h>
        #include>stdio.h>
    #endif

    //#define MT_ASSERT(a) assert(a)
    // I ran into a bug and the debugger would just exit upon a bad assertion.
    // Then, the int 3 just exited but I found that if I did the scanf first,
    // it worked.
    // Hence, the following macro for ASSERT has been created.
    //#define MT_ASSERT(exp) if (!(exp)) { char ch; PRINTF("\nAssert failed: "\
    // #exp", "__FILE__", press enter""\n"); scanf( "%c", &ch );\
    // MT_ENTER_DEBUGGER(); }
    #define MT_ASSERT(exp) if (!(exp)) { PRINTF("\nAssert failed: "\
      #exp", "__FILE__", %d\n", __LINE__); MT_ENTER_DEBUGGER(); }

    #define MT_STACK_CHECK(a)                                                  \
        MT_ASSERT ( ((a)->stackIndex+1) < (MT_MAX_DEPTH) );

      
#if 0   // This does not always work because if an MT_RESUME is the last resume
        // of a thread, the thread could free the memory where the TCB is
        // sitting and then (a) would point to garbage. A way to handle it could
        // be to clear it before the callback.
    #define MT_CLEAR_CALL(a)                                                   \
        if ((a) != NULL)                                                       \
        {                                                                      \
            (a)->currentState = MT_UNINITIALIZED;                              \
            (a)->callStack[ (a)->stackIndex+1 ].pFunction                      \
                              = NULL;                                          \
            (a)->callStack[ (a)->stackIndex+1 ].functionState                  \
                              = MT_UNINITIALIZED;                              \
        }
#else
    #define MT_CLEAR_CALL(a)
#endif

    #define MT_CLEAR_ALL(a)                                                    \
        {                                                                      \
            /* since "a" may have an "i" subscript on it, use a silly i */     \
            int iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii;                             \
            void *savedContext = (a)->pContext;                                \
            for (iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii = 0;                        \
                 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii < sizeof(sMT_TCB);          \
               ++iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)                            \
            {                                                                  \
                ((char *)(a))[iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii] = 0;          \
            }                                                                  \
            (a)->pContext = savedContext;                                      \
            (a)->stackIndex = -1; /* this is for MT_PRINTF in MT_START */      \
        }

    #ifdef MT_DEBUG_PRINT
        #define MT_PRINTF(a,b)                                                 \
        {                                                                      \
            /* since "a" may have an "i" subscript on it, use a silly i */     \
            int iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii;                             \
            for (iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii = 0;                        \
                 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii < (a)->stackIndex;          \
               ++iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii) PRINTF("    ");            \
        }                                                                      \
        PRINTF b;                                                              \
        PRINTF("\n");
    #else
        #define MT_PRINTF(a,b)
    #endif

#else
    #define MT_ASSERT(a)
    #define MT_STACK_CHECK(a)
    #define MT_CLEAR_CALL(a)
    #define MT_CLEAR_ALL(a)
    #define MT_PRINTF(a,b)
#endif

////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_START
//  Description:    Start a new thread
//  Returns:        none
//  Arguments:      pTcb = a pointer to a Task Control Block
//                  FunToCall = the function to call (don't supply arguments)
//                  pPrev = The current TCB if you will suspend waiting for 
//                  the new thread to use MT_FINISHED, otherwise NULL
//  Notes:          Don't forget to use MT_SUSPEND if you use pPrev.
//                  This will set the context pointer to NULL. That way, you
//                  can tell if the context has already been filled in when
//                  FunToCall is entered (!firstTime). See processLogin for
//                  an exmaple.
// 
//                  Don't clear pContext because it may have already been
//                  set before the MT_START was used.
//
////////////////////////////////////////////////////////////////////////
#define MT_START(pTcb,FunToCall,pPrev)                                         \
{                                                                              \
    MT_CLEAR_ALL( (pTcb) )                                                     \
    MT_PRINTF((pTcb),("MT_START "#FunToCall", TCB=%08X, "#pPrev"=%08X", pTcb,  \
                                                                     pPrev))   \
    (pTcb)->currentState = MT_INITIAL_CALL;                                    \
    (pTcb)->stackIndex  = -1;                                                  \
    (pTcb)->suspends = 0;                                                      \
    (pTcb)->pTrappingFunction = NULL;                                          \
    (pTcb)->u.returnCode = 0;                                                  \
    (pTcb)->pPrevTcb = pPrev;                                                  \
    FunToCall(pTcb);                                                           \
}                                                                              \


////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_ENTRY
//  Description:    Define the entry of an MT_FUNCTION. This will contain MT_USE
//                  macros.
//  Returns:        none
//  Arguments:      none
//  Notes:          
//
////////////////////////////////////////////////////////////////////////
#define MT_ENTRY()                                                             \
    switch( (MT_TCB_PTR)->currentState )                                       \


////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_USE
//  Description:    Define states to be used in the MT_FUNCTION
//  Returns:        N/A
//  Arguments:      A state that will be used in the function (it is OK
//                  to use the same state in two different functions)
//  Notes:
//
////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
    #define MT_USE(callState)                                                  \
        case callState:                                                        \
            goto MT_LABEL_##callState;                                         \
    MT_USE_##callState:                                                        \
        return NULL                                                            \


#else
    #define MT_USE(callState)                                                  \
        case callState:                                                        \
            goto MT_LABEL_##callState;

#endif


////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_RETURN
//  Description:    Return from an MT_FUNCTION
//  Returns:        N/A
//  Arguments:      code = a return code (use MT_GET_RETURN_CODE)
//  Notes:          If at the root level, do an MT_FINISHED
//
////////////////////////////////////////////////////////////////////////
#define MT_RETURN(code)                                                        \
{                                                                              \
    if ( (MT_TCB_PTR)->stackIndex == -1 )                                      \
    {                                                                          \
        MT_FINISHED(code)                                                      \
    }                                                                          \
    else                                                                       \
    {                                                                          \
        (MT_TCB_PTR)->u.returnCode = code;                                     \
        return MT_TCB_PTR;                                                     \
    }                                                                          \
}                                                                              \


////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_CALL
//  Description:    Call an MT_FUNCTION
//  Returns:        The value from MT_RETURN (use MT_GET_RETURN_CODE)
//  Arguments:      FunToCall is the function to call (no args in this)
//                  callState is the state of the calling function
//  Notes:          none
//
////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
    #define MT_CALL(FunToCall,callState)                                       \
    {                                                                          \
        MT_PRINTF(MT_TCB_PTR,("MT_CALL "#FunToCall", "#callState", TCB=%08X",  \
                                                               MT_TCB_PTR))    \
        MT_STACK_CHECK(MT_TCB_PTR)                                             \
        (MT_TCB_PTR)->currentState = MT_INITIAL_CALL;                          \
        ++(MT_TCB_PTR)->stackIndex;                                            \
        (MT_TCB_PTR)->callStack[ (MT_TCB_PTR)->stackIndex ].pFunction =        \
                                                           MT_NAME;            \
        (MT_TCB_PTR)->callStack[ (MT_TCB_PTR)->stackIndex ].functionState =    \
                                                               callState;      \
        if ( FunToCall(MT_TCB_PTR) == NULL )                                   \
        {                                                                      \
            goto MT_USE_##callState;                                           \
        }                                                                      \
        MT_LABEL_##callState:                                                  \
        --(MT_TCB_PTR)->stackIndex;                                            \
        MT_PRINTF(MT_TCB_PTR,("MT_RETURN "#FunToCall", "#callState", TCB=%08X",\
                                                                 MT_TCB_PTR))  \
    }                                                                          \



#else
    #define MT_CALL(FunToCall,callState)                                       \
    {                                                                          \
        MT_PRINTF(MT_TCB_PTR,("MT_CALL "#FunToCall", "#callState", TCB=%08X",  \
                                                                 MT_TCB_PTR))  \
        MT_STACK_CHECK(MT_TCB_PTR)                                             \
        (MT_TCB_PTR)->currentState = MT_INITIAL_CALL;                          \
        ++(MT_TCB_PTR)->stackIndex;                                            \
        (MT_TCB_PTR)->callStack[ (MT_TCB_PTR)->stackIndex ].pFunction =        \
                                                                  MT_NAME;     \
        (MT_TCB_PTR)->callStack[ (MT_TCB_PTR)->stackIndex ].functionState =    \
                                                               callState;      \
        if ( FunToCall(MT_TCB_PTR) == NULL )                                   \
        {                                                                      \
            return NULL;/*goto MT_USE_##callState;*/                           \
        }                                                                      \
        MT_LABEL_##callState:                                                  \
        --(MT_TCB_PTR)->stackIndex;                                            \
        MT_PRINTF(MT_TCB_PTR,("MT_RETURN "#FunToCall", "#callState", TCB=%08X",\
                                                                 MT_TCB_PTR))  \
    }                                                                          \



#endif

////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_SUSPEND/MT_RESUME
//  Description:    Suspend or resume a thread.
//  Returns:        none
//  Arguments:      callState = the state of the suspending thread
//  Notes:          none
//
////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
    #define MT_SUSPEND(callState)                                              \
    {                                                                          \
        ASSERT( (MT_TCB_PTR)->suspends <= 1 );                                 \
        ASSERT( (MT_TCB_PTR)->suspends >= -1 );                                \
        ++(MT_TCB_PTR)->suspends;                                              \
        if ((MT_TCB_PTR)->suspends == 1)                                       \
        {                                                                      \
            MT_PRINTF(MT_TCB_PTR,("MT_SUSPEND, "#callState", TCB=%08X",        \
                                                               MT_TCB_PTR))    \
            MT_STACK_CHECK(MT_TCB_PTR)                                         \
            ++(MT_TCB_PTR)->stackIndex;                                        \
            (MT_TCB_PTR)->callStack[ (MT_TCB_PTR)->stackIndex ].pFunction =    \
                                                               MT_NAME;        \
            (MT_TCB_PTR)->callStack[ (MT_TCB_PTR)->stackIndex ].functionState =\
                                                                   callState;  \
            goto MT_USE_##callState;                                           \
            MT_LABEL_##callState:                                              \
            --(MT_TCB_PTR)->stackIndex;                                        \
            MT_PRINTF(MT_TCB_PTR,("MT_RESUME,  "#callState", TCB=%08X",        \
                                                           MT_TCB_PTR))        \
        }                                                                      \
        else                                                                   \
        {                                                                      \
            MT_PRINTF(MT_TCB_PTR,("MT_SUSPEND, "#callState", TCB=%08X - "      \
                                                "already resumed", MT_TCB_PTR))\
        }                                                                      \
    }                                                                          \


#else
    #define MT_SUSPEND(callState)                                              \
    {                                                                          \
        ++(MT_TCB_PTR)->suspends;                                              \
        if ((MT_TCB_PTR)->suspends == 1)                                       \
        {                                                                      \
            MT_PRINTF(MT_TCB_PTR,("MT_SUSPEND, "#callState", TCB=%08X",        \
                                                           MT_TCB_PTR))        \
            MT_STACK_CHECK(MT_TCB_PTR)                                         \
            ++(MT_TCB_PTR)->stackIndex;                                        \
            (MT_TCB_PTR)->callStack[ (MT_TCB_PTR)->stackIndex ].pFunction =    \
                                                               MT_NAME;        \
            (MT_TCB_PTR)->callStack[ (MT_TCB_PTR)->stackIndex ].functionState =\
                                                                   callState;  \
            return NULL;/*goto MT_USE_##callState;*/                           \
            MT_LABEL_##callState:                                              \
            --(MT_TCB_PTR)->stackIndex;                                        \
            MT_PRINTF(MT_TCB_PTR,("MT_RESUME,  "#callState", TCB=%08X",        \
                                                           MT_TCB_PTR))        \
        }                                                                      \
        else                                                                   \
        {                                                                      \
            MT_PRINTF(MT_TCB_PTR,("MT_SUSPEND, "#callState", TCB=%08X - "      \
                                                "already resumed", MT_TCB_PTR))\
        }                                                                      \
    }                                                                          \




#endif


#define MT_RESUME(pTcb)                                                        \
{                                                                              \
    sMT_TCB *pNext;                                                            \
    pNext = pTcb;                                                              \
    ASSERT( pNext->suspends <= 1 );                                            \
    ASSERT( pNext->suspends >= -1 );                                           \
    --pNext->suspends;                                                         \
    if (pNext->suspends == 0)                                                  \
    {                                                                          \
        while (pNext != NULL && pNext->stackIndex >= 0)                        \
        {                                                                      \
            register sMT_TCB *pNext1;                                          \
            register int stackIndex;                                           \
            stackIndex = pNext->stackIndex;                                    \
            pNext->currentState = pNext->callStack[ stackIndex ].functionState;\
            pNext1 = (*pNext->callStack[ stackIndex ].pFunction)(pNext);       \
            MT_CLEAR_CALL( pNext );                                            \
            pNext = pNext1;                                                    \
        }                                                                      \
    }                                                                          \
}                                                                              \


////////////////////////////////////////////////////////////////////////
//
//  Name:       MT_FINISHED     
//  Description:Indicate that you are finished with the thread. If a
//              TCB was supplied with the MT_START, this will resume
//              the starting thread. Otherwise, the thread just dies.
//  Returns:    none    
//  Arguments:  A value to be saved for retrieval by MT_GET_RETURN_CODE    
//  Notes:      If the MT_START supplied a NULL for the previous TCB, then
//              the MT_FINISHED macro will cause the thread to just die out.
//
//  Important:  If the starting thread supplied a TCB, it is assumed that
//              it has also suspended (or will suspend) and is waiting for
//              this thread to finish. 
//
////////////////////////////////////////////////////////////////////////
#define MT_FINISHED(code)                                                      \
{                                                                              \
    MT_PRINTF(MT_TCB_PTR,("MT_FINISHED, TCB=%08X, pPrevTcb=%08X", MT_TCB_PTR,  \
                                                (MT_TCB_PTR)->pPrevTcb))       \
    if ((MT_TCB_PTR)->pPrevTcb == NULL)                                        \
    {                                                                          \
        MT_CLEAR_ALL(MT_TCB_PTR);                                              \
        return NULL;                                                           \
    }                                                                          \
    else                                                                       \
    {                                                                          \
        sMT_TCB *pPrev = (MT_TCB_PTR)->pPrevTcb;                               \
        pPrev->u.returnCode = code;                                            \
        ASSERT( (MT_TCB_PTR)->suspends <= 1 );                                 \
        ASSERT( (MT_TCB_PTR)->suspends >= -1 );                                \
        --pPrev->suspends;                                                     \
        if (pPrev->suspends == 0)                                              \
        {                                                                      \
            return pPrev;                                                      \
        }                                                                      \
        MT_PRINTF(MT_TCB_PTR,("MT_FINISHED, TCB=%08X, pPrevTcb=%08X - early "  \
                                                 "resume", MT_TCB_PTR, pPrev)) \
        return NULL;                                                           \
    }                                                                          \
}                                                                              \


////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_SET_TRAP/MT_GOTO_TRAP/MT_IS_TRAP
//  Description:    Setup a trap function
//  Returns:        none
//  Arguments:      pEnv = a pointer to an MT_ENV structure
//                  callState = the caller's state
//  Notes:          MT_SET_TRAP will setup so control will return if an
//                  MT_GOTO_TRAP.
//                  MT_IS_TRAP can be used to check if the return is from setup
//                  or a trap.
//                  
//                  MT_GOTO_TRAP is used to trap out of yourself and does not
//                  give control back to you so only use it within an
//                  MT_FUNCTION.
//
//                  MT_RESUME_AT_TRAP is similar to RESUME except that instead
//                  of resuming the thread at its suspended place, it
//                  resumes to the code that used MT_SET_TRAP. MT_RESUME_AT_TRAP
//                  will give control back to you so don't use it within
//                  an MT_FUNCTION.
//
////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
    #define MT_SET_TRAP(pEnv,callState)                                        \
        MT_PRINTF(MT_TCB_PTR, ("MT_SET_TRAP, TCB=%08X, ENV=%08X, "#callState,  \
                                                          MT_TCB_PTR, pEnv));  \
        (pEnv)->stackIndex   = (MT_TCB_PTR)->stackIndex;                       \
        /* it looks to me like (pEnv)->callBack.pFunction is not needed   */   \
        /* because the stackIndex will cause it to point to that function */   \
        /* anyway (Eddy)                                                  */   \
        (pEnv)->callBack.pFunction = MT_NAME;                                  \
        (pEnv)->callBack.functionState = callState;                            \
        (MT_TCB_PTR)->pTrappingFunction = NULL;                                \
        /* Use a goto to be sure MT_USE is in place */                         \
        if ((MT_TCB_PTR)->pTrappingFunction != NULL) goto MT_USE_##callState;  \
        MT_LABEL_##callState:                                                  \


#else
    #define MT_SET_TRAP(pEnv,callState)                                        \
        MT_PRINTF(MT_TCB_PTR, ("MT_SET_TRAP, TCB=%08X, ENV=%08X, "#callState,  \
                                                          MT_TCB_PTR, pEnv));  \
        (pEnv)->stackIndex   = (MT_TCB_PTR)->stackIndex;                       \
        /* it looks to me like (pEnv)->callBack.pFunction is not needed   */   \
        /* because the stackIndex will cause it to point to that function */   \
        /* anyway (Eddy)                                                  */   \
        (pEnv)->callBack.pFunction = MT_NAME;                                  \
        (pEnv)->callBack.functionState = callState;                            \
        (MT_TCB_PTR)->pTrappingFunction = NULL;                                \
        if ((MT_TCB_PTR)->pTrappingFunction != NULL) return NULL;              \
        MT_LABEL_##callState:                                                  \



#endif


#define MT_GOTO_TRAP(pEnv,code)                                                \
{                                                                              \
    (MT_TCB_PTR)->stackIndex = (pEnv)->stackIndex;                             \
    MT_PRINTF(MT_TCB_PTR,("MT_GOTO_TRAP, TCB=%08X, ENV=%08X", MT_TCB_PTR,      \
                                                                 pEnv));       \
    (MT_TCB_PTR)->currentState = (pEnv)->callBack.functionState;               \
    (MT_TCB_PTR)->pTrappingFunction = MT_NAME;                                 \
    (MT_TCB_PTR)->u.returnCode = code;                                         \
    if ( (*(pEnv)->callBack.pFunction)(MT_TCB_PTR) == NULL )                   \
    {                                                                          \
        return NULL;                                                           \
    }                                                                          \
    else                                                                       \
    {                                                                          \
        sMT_TCB *pNext;                                                        \
        pNext = MT_TCB_PTR;                                                    \
        while (pNext != NULL && pNext->stackIndex >= 0)                        \
        {                                                                      \
            register sMT_TCB *pNext1;                                          \
            register int stackIndex;                                           \
            stackIndex = pNext->stackIndex;                                    \
            pNext->currentState = pNext->callStack[ stackIndex ].functionState;\
            pNext1 = (*pNext->callStack[ stackIndex ].pFunction)(pNext);       \
            MT_CLEAR_CALL( pNext );                                            \
            pNext = pNext1;                                                    \
        }                                                                      \
        return NULL;                                                           \
    }                                                                          \
}                                                                              


#define MT_RESUME_AT_TRAP(pTcb,pEnv,code)                                      \
{                                                                              \
    (pTcb)->stackIndex = (pEnv)->stackIndex;                                   \
    MT_PRINTF(pTcb,("MT_GOTO_TRAP, TCB=%08X, ENV=%08X", pTcb, pEnv));          \
    (pTcb)->currentState = (pEnv)->callBack.functionState;                     \
    (pTcb)->pTrappingFunction = (MT_FUNCTION_PTR)1;                            \
    (pTcb)->u.returnCode = code;                                               \
    if ( (*(pEnv)->callBack.pFunction)(pTcb) == NULL )                         \
    {                                                                          \
        /*return NULL;*/                                                       \
    }                                                                          \
    else                                                                       \
    {                                                                          \
        sMT_TCB *pNext;                                                        \
        pNext = pTcb;                                                          \
        while (pNext != NULL && pNext->stackIndex >= 0)                        \
        {                                                                      \
            register sMT_TCB *pNext1;                                          \
            register int stackIndex;                                           \
            stackIndex = pNext->stackIndex;                                    \
            pNext->currentState = pNext->callStack[ stackIndex ].functionState;\
            pNext1 = (*pNext->callStack[ stackIndex ].pFunction)(pNext);       \
            MT_CLEAR_CALL( pNext );                                            \
            pNext = pNext1;                                                    \
        }                                                                      \
        /*return NULL;*/                                                       \
    }                                                                          \
}



#define MT_IS_TRAP()                                                           \
    (MT_TCB_PTR)->pTrappingFunction                                            \
    

////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_GET_PARENT_STRUCT
//  Description:    Create a pointer to the IOB given a pointer to an embedded
//                  TCB
//  Returns:        none
//  Arguments:      iobType = the typedef for the IOB
//                  tcb     = the element name of the TCB which is embedded
//                            within the IOB
//  Notes:          This can only be used when the TCB is contained within the
//                  IOB.
//  
//                  typedef struct ITARG_IOB
//                  {
//                      int         something;
//                      sMT_TCB     tcb;
//                  } sITARG_IOB;
//
//                  pITARG_IOB pIob;
//                  
//                  pIob = MT_GET_PARENT_STRUCT(sITARG_IOB, tcb);
//
////////////////////////////////////////////////////////////////////////
#define MT_GET_PARENT_STRUCT(iobType, tcb)                                     \
    (iobType *)((char *)MT_TCB_PTR - offsetof(iobType, tcb))                   \


////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_SET_CONTEXT_PTR / MT_GET_CONTEXT_PTR
//  Description:    Save a context pointer in the TCB
//                  or Get the context pointer saved
//  Returns:        For SET, nothing
//                  For GET, the pointer which was saved by SET
//  Arguments:      For SET, the pointer to save
//                  For GET, the type of pointer that was saved
//  Notes:          
//
////////////////////////////////////////////////////////////////////////
#define MT_SET_CONTEXT_PTR(pTcb,ptr)                                           \
    (pTcb)->pContext = (void *)ptr


#define MT_GET_CONTEXT_PTR()                                                   \
    (MT_TCB_PTR)->pContext


#define MT_GET_CONTEXT_PTR_FROM_TCB(pTcb)                                      \
    (pTcb)->pContext


////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_SET_RETURN_CODE/MT_GET_RETURN_CODE
//  Description:    Set/Get the return code
//  Returns:        none
//  Arguments:      code = a code to be retrieved by MT_GET_RETURN_CODE
//  Notes:          The return code can also be set by MT_FINISHED.
//                  I make you give the TCB in the set function so you
//                  can supply a context (using the return code) for an 
//                  MT_RESUME operation.
//
////////////////////////////////////////////////////////////////////////
#define MT_SET_RETURN_CODE(tcb,code)                                           \
    (tcb)->u.returnCode = code                                                 \


#define MT_GET_RETURN_CODE()                                                   \
    (MT_TCB_PTR)->u.returnCode                                                 \


////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_SEM_INITIALIZE
//  Description:    Initialize a semaphore
//  Returns:        none
//  Arguments:      pSemaphore = a pointer to an MT_SEMAPHORE structure
//  Notes:          none
//
////////////////////////////////////////////////////////////////////////
#define MT_SEM_INITIALIZE(pSemaphore)                                          \
    {                                                                          \
        (pSemaphore)->signaled = 0;                                            \
    }                                                                          \


////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_SEM_WAIT
//  Description:    Wait on a semaphore
//  Returns:        none
//  Arguments:      pSemaphore = a pointer to an MT_SEMAPHORE structure
//                  callState = a state named by an MT_USE
//  Notes:          none
//
////////////////////////////////////////////////////////////////////////
#define MT_SEM_WAIT(pSemaphore,callState)                                      \
    {                                                                          \
        --(pSemaphore)->signaled;                                              \
        if ((pSemaphore)->signaled < 0)                                        \
        {                                                                      \
            (pSemaphore)->pTcb = MT_TCB_PTR;                                   \
            MT_SUSPEND(callState);                                             \
        }                                                                      \
    }                                                                          \


////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_SEM_SIGNAL
//  Description:    Signal a semaphore
//  Returns:        none
//  Arguments:      pSemaphore = a pointer to an MT_SEMAPHORE structure
//  Notes:          none
//
////////////////////////////////////////////////////////////////////////
#define MT_SEM_SIGNAL(pSemaphore)                                              \
    {                                                                          \
        ++(pSemaphore)->signaled;                                              \
        if ((pSemaphore)->signaled <= 0)                                       \
        {                                                                      \
            MT_RESUME((pSemaphore)->pTcb);                                     \
        }                                                                      \
    }                                                                          \


#endif  // ifdef __MT_CALL_H__
//#endif  // ifdef MT_MAX_DEPTH

////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_ABORT_THREAD
//  Description:    Aborts a thread that is suspended
//  Returns:        none
//  Arguments:      none
//  Notes:          If the thread is in execution, this cannot abort it.
//
//                  This will cause the MT_GOTO_TRAP routine to be called.
//                  The MT_GOTO_TRAP code MUST terminate. I.e., it can't do
//                  or lead up to an MT_SUSPEND and it can't do an MT_RETURN.
//
////////////////////////////////////////////////////////////////////////
#define MT_ABORT_THREAD(pTcb,pEnv,code)                                        \
    (pTcb)->stackIndex = (pEnv)->stackIndex;                                   \
    MT_PRINTF(pTcb,("MT_ABORT_THREAD, TCB=%08X, ENV=%08X, code=%08X", pTcb,    \
                                                               pEnv, code));   \
    (pTcb)->currentState = (pEnv)->callBack.functionState;                     \
    (pTcb)->pTrappingFunction = (MT_FUNCTION_PTR)1;                            \
    (pTcb)->u.returnCode = code;                                               \
    if ( (*(pEnv)->callBack.pFunction)(pTcb) == NULL )                         \
    {                                                                          \
        MT_ASSERT(!"MT_ABORT_THREAD didn't abort");                            \
    }


////////////////////////////////////////////////////////////////////////
//
//  Name:           MT_EXIT
//  Description:    Exit the thread. Something else will take over.
//  Returns:        none
//  Arguments:      none
//  Notes:          It is expected that something else will begin using
//                  the thread and take over control.
//
////////////////////////////////////////////////////////////////////////
#define MT_EXIT()                                                              \
    return NULL;

4 Test code

#include <stddef.h>
#include <stdio.h>
#include <process.h>
#include <assert.h>

#define ASSERT assert
#define PRINTF printf
#define MT_ENTER_DEBUGGER() _asm { int 3 }

#define MT_DEBUG
#define MT_DEBUG_PRINT

#define MT_MAX_DEPTH        3
#define MT_STATES           \
    DO_SCAN,                \
    START_IO,               \
    TEST_UNIT_READY,        \
    CHECKING_DRIVE,         \
    GETTING_TYPE,           \
    TRAP_SCAN,             \
    EXECUTE_SCSI,           \
    WAIT_FOR_ITEM,          \
    FINISHED_NO_SUSPEND,    \
    FINISHED_WITH_SUSPEND,  \
    SUSPEND_THREAD
#define MT_TCB_PTR pTcb

#include "MtCall.h"

void main(void);
void Isr(void);

sMT_TCB CheckDevicesTcb;
sMT_ENVIRONMENT Environment;

typedef struct FUNNY_IOB
{
    int     something;
    sMT_TCB DetermineTypeTcb;
} sFUNNY_IOB, *pFUNNY_IOB;

sFUNNY_IOB DetermineTypeIob;

pMT_TCB pPendingTcb;
int     InterruptPending;

sMT_SEMAPHORE   Semaphore;

//#define TEST_NO_INTERRUPT_ON_IO
//#define TEST_INTERRUPT_IS_EARLY
//#define TEST_ASYNC_RESUME
//#define TEST_FINISH_FROM_CALL
#define TEST_TRAP_FROM_CALL
//#define TEST_TRAP_FROM_RESUME
#define MAX_DRIVES   1 

MT_FUNCTION (CheckDevicesThread);
MT_FUNCTION (DetermineTypeThread);
MT_FUNCTION (CheckDrive);
MT_FUNCTION (ExecuteScsi);
MT_FUNCTION (NoSuspendThread);
MT_FUNCTION (SuspendThread);

int DriveNum;

unsigned int *pItemToDo;
unsigned int *FirstItem;
unsigned int *SecondItem;


////////////////////////////////////////////////////////////////////////
//
//  Name:           StartIo
//  Description:    Simulate a start I/O
//  Returns:        1 if an interrupt is expected, 0 if not
//  Arguments:      none
//  Notes:          This will lead to an interrupt.
//
////////////////////////////////////////////////////////////////////////
int StartIo(sMT_TCB *pTcb)
{
    printf("Starting I/O\n");

    #ifdef TEST_NO_INTERRUPT_ON_IO
        return 0;
    #endif
        pPendingTcb = pTcb;
        InterruptPending = 1;
    #ifdef TEST_INTERRUPT_IS_EARLY
        Isr();
    #endif

    return 1;
}

////////////////////////////////////////////////////////////////////////
//
//  Name:           Isr
//  Description:    Simulate an ISR
//  Returns:        none
//  Arguments:      none
//  Notes:          
//
////////////////////////////////////////////////////////////////////////
void Isr(void)
{
    printf("Enter interrupt level\n");
    InterruptPending = 0;
    MT_RESUME(pPendingTcb);
    printf("Leave interrupt level\n");
}


////////////////////////////////////////////////////////////////////////
//
//  Name:           main
//  Description:    none
//  Returns:        none
//  Arguments:      none
//  Notes:          none
//
////////////////////////////////////////////////////////////////////////
void main(void)
{
    char ch;

    MT_SEM_INITIALIZE( &Semaphore );

    MT_START( &CheckDevicesTcb, CheckDevicesThread, NULL );

    SecondItem = NULL;
    FirstItem = (unsigned int *)&SecondItem;
    pItemToDo = (unsigned int *)&FirstItem;

    //  Test to be sure several signals in a row will work
    MT_SEM_SIGNAL( &Semaphore );
    MT_SEM_SIGNAL( &Semaphore );
    MT_SEM_SIGNAL( &Semaphore );

    #ifndef MT_USE_FUNCTIONS_DIRECTLY
        while (InterruptPending)
        {
            printf("Running some other task\n");
            Isr();
        }
    #endif

    if (CheckDevicesTcb.suspends != 0)
    {
        printf("**************Error, CheckDevicesTcb is still suspended\n");
    }
    else if (DetermineTypeIob.DetermineTypeTcb.suspends != 0)
    {
        printf("**************Error, DetermineTypeIob.DetermineTypeTcb is still"
               " suspended\n");
    }
    else
    {
        printf("All done.\n");
    }

    printf("Press Enter to exit ...");
    scanf_s("%c", &ch, 1);
    printf("\n");
}

////////////////////////////////////////////////////////////////////////
//
//  Name:           CheckDevicesThread
//  Description:    
//  Returns:        MT_FUNCTION
//  Arguments:      pTcb    = a pointer to the MT_TCB
//  Notes:          
//
////////////////////////////////////////////////////////////////////////
#undef MT_NAME
#define MT_NAME CheckDevicesThread
MT_FUNCTION (CheckDevicesThread)
{

    unsigned int *pItem;

    MT_ENTRY()
    {
        MT_USE( CHECKING_DRIVE );
        MT_USE( GETTING_TYPE );
        MT_USE( WAIT_FOR_ITEM );
        MT_USE( FINISHED_NO_SUSPEND );
//        MT_USE( FINISHED_WITH_SUSPEND );
    }

    //  Do this until the main code has no more items to do
    for (;;)
    {
        //  Wait until the main code has given us something to do via
        //  MT_SEM_SIGNAL
        MT_SEM_WAIT( &Semaphore, WAIT_FOR_ITEM );
        pItem = pItemToDo;
        if (pItem == NULL)
        {
            printf("pItem == NULL, nothing to do\n");
            break;
        }

        printf("Working on item %08X\n", pItem );
        pItemToDo = (unsigned int *)*pItemToDo;

        for (DriveNum = 0; DriveNum < MAX_DRIVES; DriveNum++)
        {
            //  Call a function that itself may make MT_CALLs
            MT_CALL(CheckDrive, CHECKING_DRIVE);
        }


        MT_START( &DetermineTypeIob.DetermineTypeTcb, NoSuspendThread,
           MT_TCB_PTR );
        MT_SUSPEND( FINISHED_NO_SUSPEND );
        
        // Start another thread
        MT_START( &DetermineTypeIob.DetermineTypeTcb, DetermineTypeThread,
           MT_TCB_PTR );
        
        // Suspend until the thread is done
        MT_SUSPEND( GETTING_TYPE );
        
        printf("Finished with DetermineTypeThread, rc=%d\n", 
           MT_GET_RETURN_CODE());
    }


    MT_RETURN( 0 );
    
}

#undef MT_NAME
#define MT_NAME NoSuspendThread
MT_FUNCTION (NoSuspendThread)
{
    
    MT_FINISHED(4321);
}

#undef MT_NAME
#define MT_NAME SuspendThread
MT_FUNCTION (SuspendThread)
{
    MT_ENTRY()
    {
        MT_USE( SUSPEND_THREAD );
    }

    MT_SUSPEND( SUSPEND_THREAD );
    MT_FINISHED(6789);
}

////////////////////////////////////////////////////////////////////////
//
//  Name:           DetermineTypeThread
//  Description:    
//  Returns:        MT_FUNCTION
//  Arguments:      implied by MT_TCB_PTR
//  Notes:          This is a new thread. It will resume the previous
//                  thread when it is done.
//
////////////////////////////////////////////////////////////////////////
#undef MT_NAME
#define MT_NAME DetermineTypeThread
MT_FUNCTION (DetermineTypeThread)
{
    pFUNNY_IOB pIob;

    pIob = MT_GET_PARENT_STRUCT( sFUNNY_IOB, DetermineTypeTcb);

    MT_ENTRY()
    {
        MT_USE( EXECUTE_SCSI );
    }

    #ifdef TEST_FINISH_FROM_CALL
        MT_FINISHED(4321);
    #endif

    //  Test to be sure nested MT_CALLs work
    MT_CALL( ExecuteScsi, EXECUTE_SCSI );

    //  This thread is finished
    MT_FINISHED(1234);

}

////////////////////////////////////////////////////////////////////////
//
//  Name:           CheckDrive
//  Description:    
//  Returns:        MT_FUNCTION
//  Arguments:      Implied by MT_TCB_PTR
//  Notes:
//
////////////////////////////////////////////////////////////////////////
#undef MT_NAME
#define MT_NAME CheckDrive
MT_FUNCTION (CheckDrive)
{
    MT_ENTRY()
    {
        MT_USE( TEST_UNIT_READY );
        #if defined(TEST_TRAP_FROM_CALL) || defined(TEST_TRAP_FROM_RESUME)
            MT_USE( TRAP_SCAN );
        #endif
    }

    #if defined(TEST_TRAP_FROM_CALL) || defined(TEST_TRAP_FROM_RESUME)
        MT_SET_CONTEXT_PTR( MT_TCB_PTR, &Environment );
        /* this is it */
        MT_SET_TRAP( &Environment, TRAP_SCAN );  
        if (MT_IS_TRAP())
        {
            printf("CheckDrive cought trap, rc=%d\n", MT_GET_RETURN_CODE());
            MT_RETURN( 0 );
        }
    #endif

    //  Test to be sure nested MT_CALLs work
    MT_CALL( ExecuteScsi, TEST_UNIT_READY );

    MT_RETURN( 0 );
}


////////////////////////////////////////////////////////////////////////
//
//  Name:           ExecuteScsi
//  Description:    
//  Returns:        MT_FUNCTION
//  Arguments:      Implied by MT_TCB_PTR
//  Notes:          
//
////////////////////////////////////////////////////////////////////////
#undef MT_NAME
#define MT_NAME ExecuteScsi
MT_FUNCTION (ExecuteScsi)
{
    MT_ENTRY()
    {
        MT_USE( START_IO );
    }

    #ifdef TEST_TRAP_FROM_CALL
        if (MT_GET_CONTEXT_PTR() != NULL)
        {
            printf("Traping in ExecuteScsi\n");
            MT_GOTO_TRAP((sMT_ENVIRONMENT *)MT_GET_CONTEXT_PTR(), 9876);
            printf("\n*************error, trap returned\n");
            exit(1);
        }
    #endif

    #ifdef TEST_ASYNC_RESUME
        {
            static int done;
            if (!done)
            {
                done = 1;
                MT_RESUME(  );
            }
        }
    #endif

    if (StartIo(pTcb))
    {
        MT_SUSPEND( START_IO );

        #ifdef TEST_TRAP_FROM_RESUME
            if (MT_GET_CONTEXT_PTR(pMT_ENV) != NULL)
            {
                printf("Traping in ExecuteScsi\n");
                MT_GOTO_TRAP(MT_GET_CONTEXT_PTR(sMT_ENVIRONMENT), 6789);
                printf("\n*************error, trap returned\n");
                exit(1);
            }
        #endif
    }

    MT_RETURN( 0 );
}

5 License

MT_CALL is free software: you can redistribute it and/or modify it under the terms of the The Code Project Open License (CPOL) as published by www.codeproject.com (http://www.codeproject.com/info/cpol10.aspx).

MT_CALL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See The Code Project Open License (CPOL) at http://www.codeproject.com/info/cpol10.aspx for more details.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Eddy Quicksall

United States United States
No Biography provided

Comments and Discussions

 
GeneralHTML formatting PinmemberEddy Quicksall7-Jun-09 3:40 
GeneralRe: HTML formatting PinmemberPaolo Messina7-Jun-09 22:46 
GeneralRe: HTML formatting PinmemberDanila Korablin25-Jun-09 8:54 
GeneralRe: HTML formatting PinmemberEddy Quicksall25-Jun-09 15:10 
GeneralRe: HTML formatting PinmemberDanila Korablin25-Jun-09 18:45 
GeneralRe: HTML formatting PinmemberEddy Quicksall26-Jun-09 3:25 
GeneralRe: HTML formatting PinmemberDanila Korablin26-Jun-09 7:24 
GeneralRe: HTML formatting PinmemberEddy Quicksall26-Jun-09 7:56 
GeneralRe: HTML formatting PinmemberDanila Korablin27-Jun-09 1:29 
GeneralRe: HTML formatting PinmemberEddy Quicksall27-Jun-09 7:44 
GeneralRe: HTML formatting PinmemberDanila Korablin27-Jun-09 10:06 
GeneralRe: HTML formatting PinmemberEddy Quicksall27-Jun-09 15:42 
GeneralRe: HTML formatting PinmemberDanila Korablin27-Jun-09 21:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140421.2 | Last Updated 27 Jun 2009
Article Copyright 2009 by Eddy Quicksall
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid