宏模拟中断级别下的多任务/阻塞代码





4.00/5 (3投票s)
这是一组宏,可以在中断上下文中编写结构化代码,就像它是阻塞代码一样。
目录
- 引言
- 用法
- 基础
- 注释
- 安装
- 定义一个函数
- 启动一个新线程
- 调用一个函数
- 间接调用一个函数
- 从函数返回
- 获取返回码
- 结束一个线程
- 在线程内挂起
- 恢复一个线程
- 陷阱
- 父 IOB
- 用户上下文
- MT_CALL 事件序列
- 宏
- 测试代码
- 许可证
1 引言
用户级别的系统使用多线程(也称为阻塞线程);然而,对于大多数中断驱动的代码,系统多线程并不常用。本文档描述了一组名为 MT_CALL
的宏,它模拟了多线程系统。使用这些宏有以下主要优点:
- 它只使用一个系统线程来支持数千个 I/O 线程;
- 不需要操作系统;
- 它支持中断级别的线程。
通常,在中断级别,工作可以根据 IO 处理程序的实现分派到任务级别或保留在中断级别。例如,缓存子系统和 RAID 子系统最好在任务级别实现。当缓存处理程序激活时,它可能会运行一个系统任务;但是,存储处理程序如果在中断级别运行,可能会更有效。MT_CALL
可以在中断级别运行并提供任务风格的编程。
这些宏用于在中断上下文中维护结构化编程技术。就像普通的任务级软件一样,使用多任务比使用传统的状态机更容易理解控制流。例如,在任务级别,由于操作系统提供了同步调用,我们很少使用状态机来执行磁盘 I/O。但是,在中断级别,同步 I/O 是不可能的,因此必须采用异步方法。通常,这需要复杂的状态机,而复杂的状态机需要深入了解所有状态如何协同工作。随着对软件提出新的要求,人们倾向于使用标志来避免新的状态。这些标志,甚至新状态的引入,通常会破坏“代码平衡”。
图 1(MT_CALL 序列)显示了一个示例流程。函数 A 使用 MT_START
(路径 1)启动一个线程;线程(函数 B)开始执行并对函数 C 进行 MT_CALL
(路径 2);函数 C 执行初始设置并执行一些 I/O,最终会导致中断;函数 C 使用 MT_SUSPEND
挂起操作;如路径 3 所示,会有一个隐藏的返回回到线程的初始创建者,线程创建者最终返回到操作系统,操作系统继续执行其他任务;发生中断(路径 4)并且流程继续到一个用户提供的处理程序;用户处理程序识别中断源并使用 MT_RESUME
恢复被挂起线程的执行(路径 5);被挂起的线程在 MT_SUSPEND
之后恢复控制,并使用 MT_RETURN
返回给其调用者(路径 6);MT_RESUME
通过调用前一个函数(路径 7)继续其活动,该函数确定已完成;函数 B 使用 MT_FINISHED
(路径 8)终止线程;MT_FINISHED
会将控制权传递回 MT_RESUME
,然后返回中断系统,最后返回到被中断的任务。
2 用法
2.1 基本用法
- 要启动线程,请使用
MT_START
。 - 使用
MT_START
或MT_CALL
调用的每个函数都必须是MT_FUNCTION
。 - 每个
MT_FUNCTION
都必须以MT_ENTRY
开始,并使用MT_RETURN
返回。 - 如果
MT_FUNCTION
确定线程已完成,它将使用MT_FINISHED
。 - 如果线程必须因外部事件(例如,I/O 操作)而挂起,它将使用
MT_SUSPEND
。 - 当外部事件发生时(例如,中断),处理程序将使用
MT_RESUME
。
2.2 注意事项
- 堆栈上的变量在
MT_CALL
之后不会被保留;这些变量应放在您的个人 IOB 中。
2.3 设置
宏的使用方式如下:
- 定义是否调试
MT_DEBUG
宏将执行堆栈检查,并打印每个宏的使用情况,从而允许您跟踪执行。例如:
#define MT_DEBUG
在任务控制块 (Task Control Block) 中设置一个堆栈,该堆栈保存每个 MT_CALL
的地址和状态。最大深度定义如下例所示:
#define MT_MAX_DEPTH 3
这些状态由 MT_STATES
构造定义。它们将被附加到名为 eMT_STATE
的内部枚举类型定义中。用逗号分隔每个状态,如果使用单独的行,请使用宏延续运算符。确保在最后一行省略运算符。例如:
#define MT_STATES \
DO_SCAN, \
START_IO, \
TEST_UNIT_READY, \
CHECKING_DRIVE, \
GETTING_TYPE, \
STOP_SCAN, \
EXECUTE_SCSI
定义要用于任务控制块的指针。
此指针封装在许多宏中。应用程序通常不使用它,但由于某些宏是通用的,因此可以将其用于这些情况。例如:
#define MT_TCB_PTR pTcb
例如
#include <MT_CALL.H>
任务控制块是结构 MT_TCB
,类型为 sMT_TCB
,指针为 pMT_TCB
。例如:
sMT_TCB CheckDevicesTcb;
MT_Function
的原型MT 函数使用 MT_FUNCTION
宏定义。例如:
MT_FUNCTION ( CheckDevicesThread );
2.4 定义一个函数
当一个函数启动一个线程或由 MT_CALL
调用时,它必须被定义为 MT_FUNCTION
。此外,它必须包含一个 MT_ENTRY
来定义正在使用的状态,并必须使用 MT_RETURN
来返回。它可以使用的宏包括 MT_START
、MT_CALL
、MT_SUSPEND
、MT_SET_TRAP
、MT_RESUME
或 MT_GOTO_TRAP
(可能还有一些我忘记了)。
MT_NAME
宏用于定义函数的名称,MT_FUNCTION
宏用于构成函数。MT_NAME
宏将在其他一些宏中使用。此名称用于在任务控制块中标识函数名。
#undef MT_NAME
#define MT_NAME FunctionName
MT_FUNCTION ( FunctionName )
其中
FunctionName
是函数的名称。它必须出现在 MT_FUNCTION
宏中。如果需要,该宏可以写成 MT_FUNCTION( MT_NAME )
(我更喜欢使用名称,这样我就可以轻松地复制粘贴来形成原型)。
函数不能有任何参数。这似乎令人遗憾,但无论如何,这就是面向状态的策略的本质(记住,这只是一个易于使用的状态机);抱歉。
MT_ENTRY
宏通常出现在自动变量定义之后。然而,它也可以出现在代码之后,如下面的示例所示。
MT_ENTRY
{
MT_USE( StateName );
MT_USE( AnotherStateName );
MT_END_OF_LIST(); // to overcome warnings with some compilers
}
其中
StateName
和 AnotherStateName
是此函数中使用的状态的名称。对于使用的每个状态,必须有一个相应的 MT_CALL
或 MT_SUSPEND
,并且这些状态必须已在 MT_STATES
宏中定义。
MT_RETURN
宏用于从 MT_FUNCTION
返回。它不接受参数。
MT_RETURN();
注意
由于这些函数在调用 MT_RESUME
时实际上会被重新进入,因此任何自动变量在 MT_CALL
或 MT_SUSPEND
之后都会丢失。因此,一些变量可能需要包含在用户定义的结构中。
这是一个例子。
#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 启动一个新线程
使用 MT_START
宏启动线程。启动后,线程就独立运行,有自己的执行路径。任务控制块将维护线程的状态(MT_TCB_PTR
用于此目的)。
调用顺序如下:
MT_START( pTcb, funToCall, pPrev );
其中
pTcb |
是任务控制块的指针。 |
funToCall |
是要调用的 MT_FUNCTION 的名称。 |
pPrev |
是指向任务控制块的指针,该控制块代表当新线程完成时(使用 MT_FINISHED 宏)需要恢复的线程;如果没有前一个线程,则可能为 NULL 。 |
例如
MT_START( &CheckDevicesTcb, CheckDevicesThread, NULL );
在这种情况下,将调用函数 CheckDevicesThread()
。任务控制块是 CheckDevicesTcb
,并且没有涉及前一个任务控制块。
2.6 调用一个函数
每当调用一个必须挂起或导致挂起的函数时,它必须被编码为 MT_CALL
,并且该函数必须被编码为 MT_FUNCTION
。
警告:进行 MT_CALL
时,自动(堆栈)变量在 MT_CALL
之后不会被保留。
调用顺序如下:
MT_CALL( funToCall, callState );
其中
funToCall |
是要调用的 MT_FUNCTION 。 |
callState |
是 MT_STATES 中定义的,并在 MT_USE 中使用的一个枚举。 |
2.7 间接调用一个函数
可以通过以下方法间接调用函数。
使用双括号定义函数指针,例如:
MT_FUNCTION((*pFunction));
调用函数时,像对待普通 MT_CALL
一样引用该指针,如下所示:
MT_CALL( pFunction, callState );
以下是使用指针数组的示例:
MT_FUNCTION((*pFunction[10]));
...
MT_CALL( pFunction[j], callState );
2.8 从函数返回
当您必须从函数返回时,请使用 MT_RETURN
宏。
调用顺序如下:
MT_RETURN( returnCode )
其中
returnCode |
是要返回给调用者的无符号整数;这可以通过 MT_GET_RETURN_CODE 宏获得。 |
2.9 获取返回码
从 MT_CALL
返回后,可以使用 MT_GET_RETURN_CODE
宏从被调用的函数获取返回码。
调用顺序如下:
rc = MT_GET_RETURN_CODE()
其中
rc
是一个无符号整数。它由 MT_RETURN
、MT_FINISHED
或 MT_GOTO_TRAP
的参数设置。
2.10 结束一个线程
当线程完成时,可以使用 MT_FINISHED
宏终止它。如果在 MT_START
宏中指定了前一个 TCB(参见上面的 pPrev
),则 MT_FINISHED
宏将恢复使用 MT_START
的线程。
注意:使用 MT_START
并指定 pPrev
参数的线程必须使用 MT_SUSPEND
宏挂起,或者必须继续执行到 MT_SUPEND
。
调用顺序如下:
MT_FINISHED( returnCode )
其中
returnCode
是一个无符号整数。可以使用 MT_GET_RETURN_CODE()
获取它。
2.11 在线程内挂起
线程可以随时使用 MT_SUSPEND
宏挂起执行。挂起后,执行必须由用户提供的某些外部逻辑继续。此逻辑通常是中断过程的一部分;然而,该逻辑可能仅仅是满足适当的条件。
调用顺序如下:
MT_SUSPEND( callState )
其中
callState
是使用 MT_USE
宏提供的枚举之一。
2.12 恢复一个线程
可以在线程挂起之前或之后通过 MT_RESUME
宏恢复线程。这种“提前恢复”克服了可能发生的时序问题,即中断可能发生在 I/O 启动之后,但线程实际上执行 MT_SUSPEND
之前。
MT_RESUME
宏通常从中断服务例程中使用;但是,这不是必需的。
调用顺序如下:
MT_RESUME( pTcb )
其中
pTcb
是要恢复的线程的任务控制块的指针。
2.13 陷阱
线程可以将其执行“陷阱”到一个预定的点。当低级别发生错误时,这很有用。它类似于 C 中的 setjmp
/longjmp
函数。
调用顺序如下:
MT_SET_TRAP( pEnv, callState )
MT_GOTO_TRAP(pEnv, returnCode )
其中
pEnv |
是指向类型为 MT_ENV 的环境结构的指针。 |
callState |
是陷阱的状态名称,并在 MT_USE 宏中命名。 |
returnCode |
是一个可以通过 MT_GET_RETURN_CODE 宏获取的返回码。 |
用法
陷阱函数的使用方法如下:
- 使用
MT_SET_TRAP
宏设置陷阱返回点。 - 使用
MT_IS_TRAPPING
宏测试陷阱是否已发生。 - 使用
MT_GOTO_TRAP
宏执行陷阱。
这是一个例子。
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 父 IOB
对于每个 TCB,有时可能有一个 I/O 结构 (IOB) 与之关联。当使用该方法时,通常需要获取包含 TCB 的 IOB 的地址。这可以通过几种方式完成;为了方便起见,可以使用 MT_GET_PARENT_STRUCT
宏。
调用顺序如下:
MT_GET_PARENT_STRUCT( structType, tcb )
其中
structType |
是 IOB 结构的 typedef。 |
tcb |
是 IOB 中 TCB 结构的名称。 |
2.15 用户上下文
通常,需要设置一些用户上下文,这些上下文将随线程一起传递。这可以使用 MT_SET_CONTEXT_PTR
和 MT_GET_CONTEXT_PTR
宏来完成。
调用顺序如下:
MT_SET_CONTEXT_PTR( pTcb, ptr )
ptr = MT_GET_CONTEXT_PTR()
其中
pTcb |
是指向要携带上下文指针的任务控制块的指针。 |
ptr |
是用户上下文指针;它可以指向任何内容,宏不理解它。 |
2.16 MT_CALL 事件序列
参考图 1 - MT_CALL 事件序列,使用这些函数发生的事件序列如下:
路径 1 显示线程使用 MT_START
宏启动。这会初始化调用序列中命名的线程控制块 (TCB)。具体来说,当前状态 (currentState
) 被设置为 -1。
- 由于
MT_START
已将currentState
初始化为 -1,因此新线程会继续执行MT_ENTRY
语句。 - 执行正常继续,路径 2 显示了一个
MT_CALL
。这会增加stackIndex
,将当前函数(由#define MT_NAME
命名)的地址添加到回调堆栈 (callStack
) 中,并设置调用状态 (callState
)。MT_CALL
还将currentState
设置为 -1,以强制新函数通过MT_ENTRY
语句。 - 执行继续正常通过
MT_ENTRY
语句,然后到达MT_SUSPEND
语句。
路径 3 显示返回到操作系统。MT_SUSPEND
也增加了 stackIndex
,并将当前函数的地址添加进来,就像上面一样。现在,事情被暂停了。
- 路径 3 返回到操作系统,因为
MT_SUSPEND
包含一个嵌入式return NULL
语句。启动路径 2 的MT_CALL
检查返回值,如果它是NULL
,它会重复return NULL
。序列中的任何其他MT_CALL
也会做同样的事情,因此控制权返回到MT_START
。MT_START
假设线程正在自由运行,并继续执行(在这种情况下,函数 A 实际上返回到操作系统)。注意:在MT_SUSPEND
执行任何操作之前,它会检查是否已使用MT_RESUME
,如果已使用,则不执行任何操作(本图未显示示例)。
路径 4 显示中断发生。中断系统将控制权传递给用户处理程序,该处理程序在确定中断源后,使用 MT_RESUME
宏恢复线程的执行。
MT_RESUME
是一个循环,它使用stackIndex
逐个调用函数,并为每个函数提供其保存的callState
。如果MT_RESUME
发现MT_SUSPEND
尚未执行,它只记录MT_RESUME
已被使用的事实,然后什么也不做。这允许发生中断但MT_SUSPEND
还没有机会执行的情况。
路径 5 显示恢复函数 C。现在,函数 C 中的 MT_ENTRY
执行一个 switch(callState)
,这会导致 goto
跳过 MT_SUSPEND
。
- 执行继续沿着路径 5 到
MT_RETURN
的位置。
路径 6 显示 MT_RETURN
返回到用户处理程序。MT_RETURN
返回一个非 NULL
值,这使得 MT_RESUME
继续其循环。
路径 7 显示使用其 callState
调用前一个函数。这可能已经进行了更多的 MT_CALL
或 MT_SUSPEND
,在这种情况下,上述场景会继续发生。
- 在我们的例子中,路径 7 进入了一个
MT_FINISHED
语句。如果在MT_START
宏中没有提供前一个 TCB,MT_FINISHED
只会返回NULL
。这会导致一切返回到操作系统,因为当某些东西返回NULL
时,MT_RESUME
会停止。 - 如果函数 B 使用了
MT_SUSPEND
,它也会返回NULL
,这也将导致MT_RESUME
循环终止。
3 宏
/*- * 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 测试代码
#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 许可证
MT_CALL 是免费软件:您可以根据 The Code Project Open License (CPOL) 的条款重新分发和/或修改它,该许可证由 www.codeproject.com 发布(https://codeproject.org.cn/info/cpol10.aspx)。
MT_CALL 的分发希望它会有用,但不附带任何保证;甚至不附带适销性或特定用途适用性的默示保证。有关更多详细信息,请参阅 The Code Project Open License (CPOL),网址为 https://codeproject.org.cn/info/cpol10.aspx。