Threads, Processes, IPC, and Synchronization |
Since Open C standard libraries can be used by hybrid applications with standard C, standard C++, and Symbian C++ codes, we can make use of some design patterns for using some of these APIs in such hybrid applications. One such example can be the use of pthread_create within C++ codes with many classes.
A prototype of pthread_create is shown below:
int pthread_create(pthread_t* threadhdl, pthread_attr_t* attrib, thread_begin_routine begin_routine, void* param);
This function creates a thread, with attributes specified by attrib, within a process. If attrib is NULL, the default attributes will be used. On successful completion, pthread_create() will store the ID of the created thread in the location referenced by threadhdl and will create the thread for which the user-entry point will be begin_routine.
In case of hybrid applications, we can also refer to C++ methods and class members as thread_begin_routine. There are different ways in which we can have a class member function as thread_begin_routine. Let's take a scenario where a user wants to send a member function of a class as the ThreadEntry point:
Assume that the class is as shown below:
class Sample { public: Sample(); ~Sample(); void ThreadEntryFunction(void*); ... private: ... };
If the user tries to create a thread with the following code:
Sample sam; pthread_t thrd; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); int pId = pthread_create(&thrd, &attr, sam.ThreadEntryFunction, NULL); // Error Here...
the code will cause a compilation error as we are referring to a class member as the thread entry function. There are different solutions to handle the resulting compilation error.
Change the function to static,
class Sample { public: Sample(); ~Sample(); static void ThreadEntryFunction(void* ); ... private: ... };
and use it as follows:
int pId = pthread_create(&thrd, &attr, Sample::ThreadEntryFunction, NULL);
This solution does not allow us to access non-static members from a static function. So all the members that are referred to by function ThreadEntryFunction should be made static.
As we need to make all the class data members accessed within the static member function as static, the alternative will be to add one local function, which will act as the thread entry function and an send object to that function. This function will invoke the actual function as the thread entry function. The code below illustrates this:
void* EntryFunction(void* aPtr) { Sample* sam = static_castSample*>(aPtr); sam->ThreadEntryFunction(); }
And create the pthread using the following code:
Sample sam; .... int pId = pthread_create(&thrd, &attr, EntryFunction, &sam);
This solution is a variation of solution 2. We can make use of a template function which takes care of calling a predefined member function of any class. The requirement for this solution is that all the classes (object) that we are sending to the template function should have a thread entry function with the same name.
templatetypename <ClassName> void* EntryFunction(void* aPtr) { ClassName* sam = static_cast<ClassName*>(aPtr); sam->ThreadEntryFunction(); }
The advantage is that the user can use it for any type of class, but we should make sure that there is a function named void ThreadEntryFunction() (with definition) in that class. We can impose this by having an abstract class with pure virtual method for ThreadEntryFunction(), so that all the derived classes will implement this function. The example below assumes that we have two classes:
class Sample { public: Sample(int aVal = 0) : iVal(aVal) { } ~Sample() { } //Will be used as thread entry function void ThreadEntryFunction() {} private: int iVal; }; class OtherClass { public: OtherClass(int aVal = 0) : iVal(aVal) { } ~OtherClass() { } //Will be used as thread entry function void ThreadEntryFunction() {} private: int iVal; };
Notice that both Sample and OtherClass have a function ThreadEntryFunction(). This solution can be used as shown below:
int main() { Sample sam; OtherClass obj; pthread_t thrd; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&thrd, &attr, EntryFunction<Sample>, &sam); pthread_create(&thrd, &attr, EntryFunction<OtherClass>, &obj); pthread_attr_destroy(&attr); return 0; }
This is a generic solution that may look a little complex at first, but is efficient to use.
The solution is as shown below:
template<class T, void (T::*EntryFunction)()> void* MyThreadEntryFun(void* aPtr) { T* p = static_cast<T*>(aPtr); (p->*EntryFunction)(); return 0; }
This template thread entry function will take an object (pointer) and a function pointer that needs to be called. The signature or prototype of the class member function that will be passed should match the one mentioned in the template.
Let's assume that we have two classes as shown below:
class Sample { public: Sample(int aVal = 0) : iVal(aVal) { } ~Sample() { } //Will be used as thread entry function void ThreadEntryFunctionOne() {} private: int iVal; }; class OtherClass { public: OtherClass(int aVal = 0) : iVal(aVal) { } ~OtherClass() { } //Will be used as thread entry function void ThreadEntryFunctionTwo() {} private: int iVal; };
Observe the prototype of those two functions within the class. Notice that they have different names. Now, we can have some codes like the following:
int main() { Sample sam; OtherClass obj; pthread_t thrd; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&thrd, &attr, MyEntryFun<Sample, Sample::ThreadEntryFunctionOne>, &sam); pthread_create(&thrd, &attr, MyEntryFun<OtherClass, OtherClass::ThreadEntryFunctionTwo>, &obj); pthread_attr_destroy(&attr); return 0; }
The advantage of this solution is that the user can call any function of a class as the thread entry point that has a matching prototype.
©Nokia 2007 |