Table of Contents

  1. License
  2. Introduction
  3. 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
  4. Macros
  5. Test Code

Table of Figures

Figure 1 - MT_CALL Sequence of Events

1 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 The Code Project Open License (CPOL) at http://www.codeproject.com/info/cpol10.aspx for more details.

2 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 simulate multi-threading systems. Using these macros has two 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 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 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 the MT_SUSPEND and returns to its caller using an MT_RETURN (path 6); the 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 the MT_RESUME which returns back to the interrupt system and finally back to the interrupted task.

3 Usage

3.1 Basics

3.2 Notes

3.3 Setup

The macro’s 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 of 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 macro’s. It is generally not used by the application but since some macro’s 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 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 );

3.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 J ).

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 past 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 variables 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();
}

3.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:

pTcbis a pointer to the Task Control Block to use.
funToCallis the name of an MT_FUNCTION to be called.
pPrevis 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.

3.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

funToCallis the MT_FUNCTION to call.
callStateis one of the states defined in MT_STATES and used in an MT_USE.

3.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 );

3.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

returnCodeis an unsigned int to be returned to the caller. This is obtained via the MT_GET_RETURN_CODE macro.

3.9 Obtaining the return code

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

The calling sequence is:

rc = MT_GET_RETURN_CODE()

Where

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

3.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().

3.11 Suspending within a thread

A thread may suspend execution at anytime by use of 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 enum’s supplied with the MT_USE macro.

3.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.

3.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

pEnvis a pointer to an environment structure of type MT_ENV.
callStateis the state name for the trap and is named in an MT_USE macro.
returnCodeis 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
    }
    …
}

3.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

structTypeis the typedef for the IOB structure
tcbis the name of the TCB structure within the IOB

3.15 User context

Many times 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

pTcbis a pointer to the Task Control Block which is to carry the context pointer
ptris a user context pointer. This can point to anything and is not understood by the macros.

3.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.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 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.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.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.

Sequence_of_events.jpg

Figure 1 - MT_CALL Sequence of Events

4 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;

5 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 );
}
Hosted by uCoz