![]() |
General Programming »
Threads, Processes & IPC »
General
Intermediate
Create thread from global\static void returning function with any type and number of arguments.By Lone DeveloperThis is a macro which takes the name of any void returning global\static procedure, followed by the list of arguments to be passed to that procedure and invokes the procedure with arguments supplied, in a new thread |
C, ASM, VC6, VC7, VC7.1, VC8.0WinXPVS2005, Dev, Design
|
||||||||
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
This is a very general problem.
Say i have a function like
void func(int a, int b)
{
printf("\n a = %d, b = %d",a,b);
} and i need to call this function in a new thread.
The simplest procedure is:
1>Make a structure like this
struct funcargs { int m_a; int m_b; };
2>Then make a dynamic object of this as
funcargs *argobjptr = new funcargs; 3>Putting arguments in the object like
argobjptr->m_a = a; argobjptr->m_b = b;
4>Creating another wrapper function to be used with CreateProcess or any similar method like this
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
funcargs *argobjptr = (funcargs *)lpParameter;
func(argobjptr->m_a, argobjptr->m_b);
delete argobjptr
return 0;
}
5>And at last calling this ThreadProc with previously created "argobjptr" object in CreateThread somewhat like this
CreateThread( NULL, 0, ThreadProc, argobjptr, 0, NULL):
so on the whole, we have to go through this whole hassle of packing arguments in a temporary wrapper object and then unpack them inside some wrapper function because, all such thread creating api's limit us to create thread with function with only this syntax:
DWORD WINAPI ThreadProc(LPVOID lpParameter)
It would be much more better in case we could have an API that could create thread from any function returning any thing with any number and type of parameters
Unfortunately, this is very tough because the scope of problems is very wide, we have simple functions, returning some object\reference\pointer\void, they could then take some arguments\reference\pointers and the number of arguments could vary with each function.
Then the function can be inline\static\class member\ virtual......
I have tried to solve this problem to some extent and i had created a macro, which just takes the function name and it's parameters (any number and type) as argument and call the function upon the given arguments in a new thread.
Like for the above given function the code would simply be
CALL_IN_THREAD(func, 4, 5);
Off course, this has some limitation too:
First of all, the macro works on only global\static function returning void.
Second, the arguments shouldn't be reference\pointer to any local variable\object (by simple logic, how could a thread can access object allocated some time back in the context\stack of some other thread ? ).
Thirdly, the macro would bang, in case the number of parameters mismatches, as expected by the actual function.
Fourthly, the macro is highly platform\hardware\compiler dependent. I have tested it on x86 machine with Windows NT versions(NT, 2000 and XP) on Visual Studio 6.0 and Visual Studio 2005 Compiler.
In short, keep in mind that, this macro uses the same old process described above inside it to accomplish this task.
The working of this code is simple.
This code works on the principle that whenever a function is called, to pass arguments to that function, they are pushed on stack first, before the call to that function would be made.
So if we need to call the same function in the context of some other thread, we need to push the same arguments on the stack of that thread before calling this function there.
What we can do is, we can copy all the arguments from the stack of this function, store them in a temporary heap location and paste them on the stack of new thread and then can call that function. The function would work as if called from original thread.
This is exactly what we do above in a conventional approach, but the difference here is that the things are just automated (packing\unpacking of arguments and calling that function) here.
But, how would we know the start and end address of stack memory area holding argument values?
We can, if we can just some how calculate the address of stack pointer just before first argument (last in argument list as arguments are pushed from last in __cdecl calling convention) is passed. The end position would be 4 bytes before the address of pointer to function passed itself (which is pushed as last argument).
Have a look at code.
#define CALL_IN_THREAD if(CThreadWrapper tempobj=0) tempobj.FunctionCaller //Main wrapper class for calling a function in a thread. //manages variable and data types used in this process class CThreadWrapper { //Structure to store function call information struct FUNC_CALL_INFO{ FUNC_TYPE m_pFuncAddress; DWORD *m_pArgs; int m_pArgLength; }; //Stores value of stack pointer before the call of desired function DWORD m_InitialStackAddress; //Uses 'FUNC_CALL_INFO' object to call the //desired function with given arguments static DWORD WINAPI ThreadFunc( LPVOID lpParam ); public: CThreadWrapper(const int); // Creates and object of 'FUNC_CALL_INFO' to be used in ThreadFunc void FunctionCaller(LPTHREAD_INFORMATION p_pthreadinfo, FUNC_TYPE func_address, ...); //helper function for MACRO with if condition __forceinline operator const bool(){return true;} }; /* Constructor function. in addition it also calculate the address of stack pointer before the call to this function */ CThreadWrapper::CThreadWrapper(const int) { DWORD ebpvalue; __asm mov ebpvalue, ebp; m_InitialStackAddress = ebpvalue + 12; #ifdef _DEBUG printf("\n m_InitialStackAddress = 0x%08X",m_InitialStackAddress); #endif } /* This functions does following tasks: 1>Copies the arguments passed to a temperory buffer a> calculate address of last argument b> subtract address of last argument from first to calculate number of arguments c>allocate a new buffer of the size of all arguments d>copies all the arguments to buffer 2>Put all information in a wraaper object 'FUNC_CALL_INFO' 3>Call CreateThread on 'ThreadFunc' */ void CThreadWrapper::FunctionCaller(LPTHREAD_INFORMATION p_pthreadinfo, FUNC_TYPE func_address, ...) { DWORD CurrentStackAddress = (DWORD)&func_address; // _asm mov CurrentStackAddress, ebp; #ifdef _DEBUG printf("\n FunctionAddress = 0x%08X",func_address); #endif #ifdef _DEBUG printf("\n CurrentStackAddress = 0x%08X",CurrentStackAddress); #endif //Calculate no of arguments passed in 'DWORD' DWORD arglen = ( ( m_InitialStackAddress - CurrentStackAddress ) - OTHER_STACK_DATA_SIZE ) / STACK_DATA_SIZE; #ifdef _DEBUG printf("\n Number of Arguments = %d",arglen); #endif //Allocate memory on heap to copy arguments from stack DWORD *args = (DWORD *) malloc( arglen * STACK_DATA_SIZE ); //Set pointer to argument on stack DWORD *argadd = (DWORD *)(CurrentStackAddress + OTHER_STACK_DATA_SIZE); //Copy arguments on heap for( unsigned int index=0; index < arglen; argadd++,index++) *(args + index) = *argadd; //Wrap up address of function to call and arguments in a object FUNC_CALL_INFO *finfo = (FUNC_CALL_INFO *)malloc(sizeof(FUNC_CALL_INFO)); finfo->m_pFuncAddress = func_address; finfo->m_pArgLength = arglen; finfo->m_pArgs = args; //Create a thread with 'ThreadFunc' as wrapper function p_pthreadinfo->hThread = CreateThread( NULL, 0, CThreadWrapper::ThreadFunc, finfo, 0, &(p_pthreadinfo->dwThreadId)); } /* This functions does following tasks: 1>fetch information from FUNC_CALL_INFO object a> fetch all arguments b> fectch function to be called 2>Push arguments into stack to be used by function to be called 3>Call the function 4>Adjust stack 5>free up allocated memory */ DWORD WINAPI CThreadWrapper::ThreadFunc( LPVOID lpParam ) { //Retrieve Function Call information FUNC_CALL_INFO *finfo = (FUNC_CALL_INFO *)lpParam; DWORD *args = finfo->m_pArgs; DWORD arglen = finfo->m_pArgLength; FUNC_TYPE funcadd = finfo->m_pFuncAddress; //Push all arguments to stack long index = arglen-1; unsigned int value = 0; unsigned stackdisp = arglen*sizeof(DWORD); while( index>=0 ) { value = *( args + index ); index--; __asm push value; } //Call actual function __asm call funcadd; //Rectify stack __asm add esp,stackdisp; //Free up memory free(finfo->m_pArgs); free(finfo); return 0; }
bool complete = false; void func(char u, char e, int a, int b) { while(complete == false) printf("a = %d, b = %d\n",a,b); int t=7, y= 8; int c = t+y; } void func1(int a, int b) { Sleep(2000); printf("\n\n\n\n\na = %d, b = %d\n\n\n\n\n\n",a,b); complete = true; } int main(int argc, char* argv[]) { printf("Hello World!\n"); THREAD_INFORMATION t; //func & func1, would be called in a thread. for(int i=0;i<3;i++) CALL_IN_THREAD(&t, (FUNC_TYPE)func, 'U', 'A', 7+i, 8*i); CALL_IN_THREAD(&t, (FUNC_TYPE)func1, 4, 5); while(complete == false){}; getchar(); return 0; }Here functions 'func' and 'func1' has been invoked in a different thread.
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+PgUp/PgDown to switch pages.
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 9 Nov 2007 Editor: |
Copyright 2007 by Lone Developer Everything else Copyright © CodeProject, 1999-2010 Web10 | Advertise on the Code Project |