📄 thread.c
字号:
/*!***************************************************************************************************** * * File : thread.c * * Purpose : implement a thread package with makecontext()/setcontext() and setitimer() * * OS : Tru64 Unix V4.0F * * Designer : Hao Chen * * Authour : Hao Chen * *----------------------------------------------------------------------------------------------------- * * Appended Notice * * 1. 2003/12/15 * 1. can not use setitimer()/alarm(). * ------------------------------------------------------------------------------------------------------ * * Revistion History * * 0.2 2003/12/17 Hao Chen * * 1. add a field( a_cThreadState) in "struct context" . the field represent the state of current * running thread. * * 2. fix a serios bug. The bug is that : "the two threads in version 0.1 can switch only once" . * If the thread is swith by other thread, it should run normally instead of setcontext(). * * in order to solve the problem, I add a state in "struct context". when the timer_hdl() * arrange other thread to run, it will set the thread's state is "RUNNING". after the context * of the target thread is switched and the target thread get the control, it will first check * it's state. if the state is RUNNIG, it will execute without setcontext(). * * * 0.1 2003/12/15 Hao Chen * * Initial version. can not run correctly * ******************************************************************************************************* */#include <stdio.h>#include <stdlib.h>#include <signal.h>#include <ucontext.h>#define CONTEXT_ARRAY_LEN 4#define TIMER__MICROSECOND 100 /*! internal timer is 100 microsecond */#define TIMER__SECOND 0 /*! internal timer is 3 second */#define TIMER__SIGNAL 14 /*! timer signal */#define STACK_SIZE 8192 /*! new thread's size */#define THREAD__READY_STATE 1 /*! not running, but it will be run */#define THREAD__RUNNING_STATE 2 /*! is running */struct context{ ucontext_t a_stContext[ CONTEXT_ARRAY_LEN ]; int a_iUsedFlg[ CONTEXT_ARRAY_LEN ]; /*! 1 : used; 0 : unused */ char a_cThreadState[ CONTEXT_ARRAY_LEN ]; /*! current thead state */};struct context g_stContext;int g_iCurrentRunIdx;int g_iContextNumber ;int iTP_Init( void);int iTP_FindFreeSlot( void);void iTP_ClearSlot( int idx );int TP_create( int *thread_id, void (*func)( void *), void *arg );void iTP_TimerHdl( int signal_num );void TP_exit( void);/*!********************************************************************** * * Purpose : init global variable * * Return : 0 : successful * <0 : Failed . * * Failed Reason : -1 : failed to sigaction(). * -2 : failed to setitimer(). * ************************************************************************ */#define __FUNC__ "iTP_Init"int iTP_Init( void){ int iRetSts; struct sigaction stSigAction; struct itimerval stTimer; /*!---------------------------------------- *! init variable *!---------------------------------------- */ memset( &g_stContext, 0, sizeof( g_stContext)); memset( &stSigAction, 0, sizeof( stSigAction)); /*!------------------------------------------ *! we can get here once and only once. *! then we should give us a a context_space. *! *! we should register ourself. *!------------------------------------------ */ g_iCurrentRunIdx = 0; g_stContext.a_iUsedFlg[ g_iCurrentRunIdx ] = 1; g_iContextNumber = 1; /*!---------------------------------------- *! build signal action *!---------------------------------------- */ stSigAction.sa_handler = iTP_TimerHdl; sigemptyset( &stSigAction.sa_mask); if( sigaction( TIMER__SIGNAL, &stSigAction, NULL)) return -1; /*!---------------------------------------- *! create new timer *!---------------------------------------- */ stTimer.it_interval.tv_sec = TIMER__SECOND; stTimer.it_interval.tv_usec = TIMER__MICROSECOND; stTimer.it_value.tv_sec = TIMER__SECOND; stTimer.it_value.tv_usec = TIMER__MICROSECOND; if( setitimer( ITIMER_REAL, &stTimer, NULL)) return -2;}/*!********************************************************************** * * Purpose : find an unused slot * * Return : >=0 : find . * -1 : find nothing. * ************************************************************************ */#define __FUNC__ "iTP_FindFreeSlot"int iTP_FindFreeSlot( void){ unsigned int i; for( i = 0; i < CONTEXT_ARRAY_LEN; i++) { if( g_stContext.a_iUsedFlg[ i ] == 0) { g_stContext.a_iUsedFlg[ i ] = 1; break; } } return ( i >= 0) ? i : -1;}/*!********************************************************************** * * Purpose : clear a slot * * Return : None. * ************************************************************************ */#define __FUNC__ "iTP_ClearSlot"void iTP_ClearSlot( int idx /*! i : index in context_array */ ){ if( idx >= 0 && idx < CONTEXT_ARRAY_LEN) g_stContext.a_iUsedFlg[ idx ] = 0;}/*!********************************************************************** * * Purpose : create a new thread with appointed fucntion pointer * * Return : 0 : successful. * <0 : failed . * * Fail Reason : -1 : the argument func is NULL. * -2 : the context_array is full. * -3 : can not malloc() memory. * -4 : internal error : we can not init signal handler. * ************************************************************************ */#define __FUNC__ "TP_create"int TP_create( int *thread_id, /*! o : thread id */ void (*func)( void *), /*! i : user function */ void *arg /*! i : argument */ ){ static int iHaveInitFlg = 0; /*! 1 : have called TP_init(); 0 : not called */ int iSlotIdx; /*!-------------------------------- *! make sure that we have inited. *!-------------------------------- */ if( iHaveInitFlg == 0) { if( iTP_Init()) return -4; iHaveInitFlg = 1; } /*!-------------------------------- *! validate the user argument *!-------------------------------- */ if( func == NULL) return -1; /*!-------------------------------- *! find a free conext slot *!-------------------------------- */ iSlotIdx = iTP_FindFreeSlot(); if( iSlotIdx == -1) return -2; /*!-------------------------------- *! make a next context *!-------------------------------- */ g_stContext.a_stContext[ iSlotIdx].uc_link = NULL; g_stContext.a_stContext[ iSlotIdx].uc_stack.ss_sp = ( void *) malloc( STACK_SIZE); g_stContext.a_stContext[ iSlotIdx].uc_stack.ss_size = STACK_SIZE; g_stContext.a_stContext[ iSlotIdx].uc_stack.ss_flags = 0; if( !g_stContext.a_stContext[ iSlotIdx].uc_stack.ss_sp ) { iTP_ClearSlot( iSlotIdx); return -3; } /*!-------------------------------- *! make the next context. *!-------------------------------- */ makecontext( &g_stContext.a_stContext[ iSlotIdx], func, 1, arg); ++g_iContextNumber; return 0;}/*!************************************************************** * * Purpose : This is our dispatcher since it is a signal_handler. * * *!************************************************************** */#define __FUNC__ "iTP_TimerHdl"void iTP_TimerHdl( int signal_num /*! i: signal number */ ){ int i, iOldRunIdx; int iRetSts; struct sigaction stSigAction; /*!---------------------------------------- *! rebuild signal action *!---------------------------------------- */ memset( &stSigAction, 0, sizeof( stSigAction)); stSigAction.sa_handler = iTP_TimerHdl; sigemptyset( &stSigAction.sa_mask); iRetSts = sigaction( TIMER__SIGNAL, &stSigAction, NULL); if( iRetSts == -1) { perror( "sigaction() failed in iTP_TimerHdl because :"); abort(); } /*!------------------------------------------ *! if there is zero or one context, we can not switch . *!------------------------------------------ */ if( g_iContextNumber <= 1) return ; /*!------------------------------------------ *! find next runnable conext *!------------------------------------------ */ i = ( g_iCurrentRunIdx + 1) % CONTEXT_ARRAY_LEN; while( i != g_iCurrentRunIdx) { if( g_stContext.a_iUsedFlg[ i ] == 1) break; i = ( i + 1) % CONTEXT_ARRAY_LEN;; } if( i == g_iCurrentRunIdx) return; /*!------------------------------------------ *! we should switch now. *! 1. save current context *! 2. set the older context now. *!------------------------------------------ */ g_stContext.a_cThreadState[ g_iCurrentRunIdx ] = THREAD__READY_STATE; getcontext( &g_stContext.a_stContext[ g_iCurrentRunIdx ]); /*!-------------------------------------------------------------------- *! NOTE: *! we can get here from two ways : *! 1. from above codes line by line. *! 2. "swithed" by other "thread" *!-------------------------------------------------------------------- */ if( g_stContext.a_cThreadState[ g_iCurrentRunIdx ] == THREAD__READY_STATE) { g_iCurrentRunIdx = i; g_stContext.a_cThreadState[ g_iCurrentRunIdx ] = THREAD__RUNNING_STATE; setcontext( &g_stContext.a_stContext[ g_iCurrentRunIdx]); }}/*!************************************************************** * * Purpose : exit the thread now * *!************************************************************** */#define __FUNC__ "iTP_TimerHdl"void TP_exit( void){ sigset_t iNewSigSet, iOldSigSet; /*!--------------------------------------------------------------- *! tell the OS not to send TIMER__SIGNAL when we exit a thread for atomic reason. *!--------------------------------------------------------------- */ sigemptyset( &iNewSigSet); sigaddset( TIMER__SIGNAL, &iNewSigSet); sigpromask( SIG_BLOCK, &iNewSigSet, &iOldSigSet); iTP_ClearSlot( g_iCurrentRunIdx); --g_iContextNumber; /*!--------------------------------------------------------------- *! resume the signal set mask. *!--------------------------------------------------------------- */ sigpromask( SIG_SETMASK, &iOldSigSet);}void gg( void *arg){ int i = 0; while(1) { printf( "gg \t\t\t\t%d\n", ++i); if( i == 700) TP_exit(); }}void ff( void *arg){ int i = 0; while(1) { printf( "ff %d\n", ++i); }}void main( void){ int iThreadId; int j = 0; TP_create( &iThreadId, ff, NULL); TP_create( &iThreadId, gg, NULL); while(1) { printf( "\t\tmain %d\n", ++j); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -