I am trying to implement simple user level thread library in c.when one thread start and this thread call second thread. this second thread run correctly but when it exit program crash.here is my coding.
//**********************************************
#include <setjmp.h>
typedef void *any_ptr;
/* Define Boolean type, and associated constants. */
typedef int Boolean;
typedef void (*ThreadFunc)(any_ptr);
#define TRUE ((Boolean)1);
#define FALSE ((Boolean)0);
typedef struct TheadSystem
{
queue<any_ptr> readyQ;
// currently executing thread
jmp_buf lastContext; // location on which the system jumps after all threads have exited
char name[30]; // optional name string of a thread, may be used for debugging
jmp_buf context; // saved context of this thread
signal_t *sig; // signal that wakes up a waiting thread
ThreadFunc func; // function that this thread started executing
any_ptr arg;
}TheadSystem;
void t_start(ThreadFunc f, any_ptr v, char *name);
void t_yield();
void block();
void unblock();
void t_sig(Condition cond, any_ptr val, Boolean queue_signal);
void t_fork(ThreadFunc f, any_ptr v, char *name);
void t_exit(int val);
My implementation of threads.h
#include "threads.h"
#include<iostream>
#include<queue>
using namespace std;
TheadSystem th;
queue<any_ptr> blocked_queue;
jmp_buf set_env,ready_env,yeild_buf;
void t_start(ThreadFunc f, any_ptr v, char *name){
if(!th.ready_queue.empty()){
cout<<"sorry thread already started now you have to create by t_fork:"<<endl;
}
else{
th.ready_queue.push(th.context);
if(!setjmp(th.context)){
memcpy(th.lastContext,th.context,sizeof(jmp_buf));
th.arg=v;
th.func=f;
//memcpy(th.currentThread->context,set_env,sizeof(jmp_buf));
//cout<<"when jmp buf set then:"<<endl;
th.ready_queue.push(th.context);
th.func(th.arg);
}
//cout<<"after come back from long jump:"<<endl;
}
}
void t_yield(){
jmp_buf *j=(jmp_buf *)th.ready_queue.front();
th.ready_queue.front()=th.context;
longjmp(*j,2);
}
void t_fork(ThreadFunc f, any_ptr v, char *name){
memcpy(th.lastContext,th.context,sizeof(jmp_buf));
if(!setjmp(th.context)){
f(v);
th.ready_queue.push(th.context);
}else
{
}
}//end of t_fork
void t_exit(int val){
cout<<"before long jump in t_exit"<<endl;
jmp_buf *j=(jmp_buf *)th.ready_queue.front();
th.ready_queue.pop();
longjmp(*j,2);
}
void block(){
blocked_queue.push(th.context);
jmp_buf *j=(jmp_buf *)th.ready_queue.front();
th.ready_queue.pop();
longjmp(*j,2);
}
void unblock(){
th.ready_queue.push(th.context);
jmp_buf *j=(jmp_buf *)blocked_queue.front();
blocked_queue.pop();
longjmp(*j,2);
}
my test case is
#include<iostream>
#include<setjmp.h>
#include<stdio.h>
#include "threads.h"
#include<queue>
using namespace std;
void fun2(any_ptr v){
cout<<"in 2nd function:"<<endl;
t_exit(0);
}
void helloworld(any_ptr v){
cout<<"in hello world from start"<<endl;
t_fork(fun2,NULL,"no value");
cout<<"after second thread:"<<endl;
cout<<"before exit"<<endl;
t_exit(0);
}
void main(){
cout<<"1 start"<<endl;
t_start(helloworld, NULL, "my function");
cout<<"main function"<<endl;
}//end of void main
Here is one problem:
In the t_start function you do this:
th.ready_queue.push(th.context);
The ready_queue is a queue of pointers, but th.context is not a pointer.
Then in the t_yield function you do
jmp_buf *j=(jmp_buf *)th.ready_queue.front();
So you push non-pointer object, and pop them as pointers. If you try to access a non-pointer object as a pointer you have undefined behavior.
You code, if it compiles without errors, should at least give you a lot of warnings, and if you only get a few warnings then I suggest you enable more warnings. When the compiler gives you a warning, it's often a sign about you doing something you should not be doing, like doing something that leads just to undefined behavior. Just silencing the warnings by e.g. type-casting is a very bad solution as it doesn't actually solve the cause of the warning.
Also, using void* is a very good sign of bad code coming. Don't use it if you can avoid it, and in this case it's really not needed in most of the places you use it (like the ready_queue).
There are SEVERAL problems with this code, some of which Joachim Pileborg points out.
Another problem is that you only have one context, which you are using multiple times to store different data, yet expect the data to be there when you come back.
The solution is to split your ThreadSystem and your Thread (the actual context of a thread) into separate objects:
struct Thread
{
jmp_buf context; // saved context of this thread
void* arg;
ThreadFunc func; // function that this thread started executing
};
After removing stuff that isn't currently used, the ThreadSystem looks like this:
struct ThreadSystem
{
queue<Thread*> ready_queue;
};
The thread creation/exit functions now look like this:
void t_start(ThreadFunc f, void* v)
{
if(!sys.ready_queue.empty()){
cout<<"sorry thread already started now you have to create by t_fork:"<<endl;
}
else{
Thread* th = new Thread;
sys.ready_queue.push(th);
if(!setjmp(th->context)){
th->arg=v;
th->func=f;
cout << "&th->context=" << &th->context << endl;
th->func(th->arg);
}
}
}
void t_fork(ThreadFunc f, void* v){
Thread* th = new Thread;
th->func = f;
th->arg = v;
if(!setjmp(th->context))
{
cout << "&th->context=" << &th->context << endl;
f(v);
sys.ready_queue.push(th);
}
}//end of t_fork
void t_exit(int val){
cout<<"before long jump in t_exit"<<endl;
Thread* th=sys.ready_queue.front();
sys.ready_queue.pop();
// Memory leak here. We can't delete `th`, and still have a context.
longjmp(th->context,2);
}
But as you can see, there is a problem in destroying the thread - so some other solution would have to be found for this. I'm not sure this is a great solution, but this works (to the limited degree of executing the test-code posted), where the original code didn't.
OK. My first pass at this was inadequate as I didn't spend sufficient time understanding the original code.
The code is buggy and messy, but probably fixable. When you push th.context onto the ready_queue you need to save the whole buffer, not just the buffer address. Probably many other problems.
Update 1
Solved first problem by wrapping the jmp_buf in a struct declaration and then making ready_queue and blocked_queue queues of structs. Then a simple assign will transfer the buffer contents.
struct SJBuff
{
jmp_buf jb;
};
Second problem: in t_start(), don't push th.context before it is first initialised.
else
{
// remove this line
// th.readyQ.push(th.context);
if(!setjmp(th.context.jb))
{
End Update 1
Notwithstanding that, I really cannot recommend using setjmp(). Modern architectures have moved on and just saving a few registers does not really capture enough state. I shudder to think what an optimizing compiler might do to your code. Piplining, conditional execution, lazy evaluation, extra registers, unscheduled system interrupts, ...
If you focus on your real objectives, there is probably a better way to do it.
Related
The following code strangely crashes when entering the run function. None of the printfs trigger, and single-stepping into the function will cause a crash. Is it undefined behaviour, or a compiler bug? I'm on MacOS compiling with Apple clang 12.0.0. It crashes with EXC_BAD_ACCESS (code = 2).
#include <iostream>
#include <thread>
#include <vector>
struct Chunk {
// the array must be this big
// other array sizes don't trigger the bug consistently
uint8_t array[524288];
};
std::thread generator_thread;
std::mutex generator_lock;
std::vector<Chunk*> to_generate;
std::condition_variable data_ready;
std::atomic<bool> running;
void begin();
void run();
void begin() {
running = true;
auto func = [] {
run();
};
generator_thread = std::thread(func);
}
void run() {
printf("Running in generator\n");
while (running) {
printf("Running in loop\n");
Chunk *task;
// take a chunk from the queue
{
std::unique_lock<std::mutex> lock(generator_lock);
data_ready.wait(lock, [] { return to_generate.size() > 0 || !running; });
if (!running) break;
task = to_generate.back();
to_generate.pop_back();
}
printf("deref chunk\n");
// Despite never being executed in this example, this line merely existing
// will cause a crash when entering the run_generator function.
Chunk chunk = *task;
// *task.chunk;
// ^^^ Only having the line above will not trigger the bug; it must be assigned
}
}
int main(int argc, const char *argv[]) {
begin();
while (true) {
printf("run\n");
}
return 0;
}
So, when you change your function to pre-reserve a stack frame with space for a half-megabyte object ... it crashes right at the start of the function when setting up that stack frame?
That's probably because you made sizeof Chunk equal to the entire default OSX thread stack size of 512Kb. If you single-step into the function, you should be able to see the instruction that triggers the fault, and it will likely be part of the stack frame setup/function preamble.
All of this is implementation-specific (the stack frames, the per-thread stack size), but putting really big things on the stack is generally a bad idea.
I have a requirement for creating a Event based Multi-thread application for which i am trying to use boost::thread and boost/interprocess/ipc/message_queue for sending messages between threads.
What i am doing currently is making the thread wait in its workerfunction to wait for a message.
Actually this is just for basic start where the sender and receiver both is a same thread, on later stage i have thought to store a list of message_queue corresponding for each thread and then fetch it accordingly or something like that.
But now, as per the code below i am using
//in a common class
typedef struct s_Request{
int id;
}st_Request;
//in thread(XYZ) class
st_Request dataone;
message_queue *mq;
void XYZ::threadfunc(void *ptr)
{
XYZ*obj = (XYZ*) ptr;
obj->RecieveMsg();
}
void XYZ::RecieveMsg()
{
message_queue mq1(open_only,"message_queue");
if(!(mq1.try_receive(&dataone, sizeof(st_Request), recvd_size, priority)))
printf("msg not received");
printf("id = %d",dataone.id);
}
void XYZ::Create()
{
mq= new message_queue(open_or_create,"message_queue",100,sizeof(st_Request));
boost:thread workerthread(threadfunc,this);
workerthread.join();
}
void XYZ::Send(st_Request *data)
{
if (!(mq->try_send(data, sizeof(st_Request), 0)))
printf("message sending failed");
}
//I am calling it like
class ABC: public XYZ
{
..some functions to do stuff... };
void ABC::createMSGQ()
{
create();
st_Request *data;
data->id =10;
send(data);
}
My thread is waiting in RecieveMsg but i am not getting any msg and the prints are coming till Send function entry and than the code crash.
Please Guide me for what i am doing wrong, if the approach is entirely wrong, i am open to move to new approach.
P.s. this is my first question on stack overflow i tried follow the guidelines still if i strayed away anywhere please do correct.
st_Request *data;
data->id =10;
data is uninitialized, you cannot dereference it. Pointers should point to something before you dereference them.
I don't understand the point of this function:
void XYZ::Create()
{
mq= new message_queue(open_or_create,"message_queue",100,sizeof(st_Request));
boost:thread workerthread(threadfunc,this);
workerthread.join();
}
You create a new thread, then block and wait for it to finish so you can join it. Why not just do the work here, instead of creating a new thread and waiting for it to finish?
What is threadfunc? Do you mean ThreadFunc?
This function is written strangely:
void XYZ::ThreadFunc(void *ptr)
{
XYZ*obj = (XYZ*) ptr;
obj->RecieveMsg();
}
Why not pass the argument as XYZ* instead of void*? Boost.Thread doesn't require everything to be passed as void*. Is that function static? It doesn't need to be:
struct XYZ {
void threadFunc();
void create();
void recv();
};
void XYZ::threadFunc()
{
recv();
}
void XYZ::create()
{
boost::thread thr(&XYZ::threadFunc, this);
thr.join();
}
As per pthread_key_create man page we can associate a destructor to be called at thread shut down. My problem is that the destructor function I have registered is not being called. Gist of my code is as follows.
static pthread_key_t key;
static pthread_once_t tls_init_flag = PTHREAD_ONCE_INIT;
void destructor(void *t) {
// thread local data structure clean up code here, which is not getting called
}
void create_key() {
pthread_key_create(&key, destructor);
}
// This will be called from every thread
void set_thread_specific() {
ts = new ts_stack; // Thread local data structure
pthread_once(&tls_init_flag, create_key);
pthread_setspecific(key, ts);
}
Any idea what might prevent this destructor being called? I am also using atexit() at moment to do some cleanup in the main thread. Is there any chance that is interfering with destructor function being called? I tried removing that as well. Still didn't work though. Also I am not clear if I should handle the main thread as a separate case with atexit. (It's a must to use atexit by the way, since I need to do some application specific cleanup at application exit)
This is by design.
The main thread exits (by returning or calling exit()), and that doesn't use pthread_exit(). POSIX documents pthread_exit calling the thread-specific destructors.
You could add pthread_exit() at the end of main. Alternatively, you can use atexit to do your destruction. In that case, it would be clean to set the thread-specific value to NULL so in case the pthread_exit was invoked, the destruction wouldn't happen twice for that key.
UPDATE Actually, I've solved my immediate worries by simply adding this to my global unit test setup function:
::atexit([] { ::pthread_exit(0); });
So, in context of my global fixture class MyConfig:
struct MyConfig {
MyConfig() {
GOOGLE_PROTOBUF_VERIFY_VERSION;
::atexit([] { ::pthread_exit(0); });
}
~MyConfig() { google::protobuf::ShutdownProtobufLibrary(); }
};
Some of the references used:
http://www.resolvinghere.com/sof/6357154.shtml
https://sourceware.org/ml/pthreads-win32/2008/msg00007.html
http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.html
http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_exit.html
PS. Of course c++11 introduced <thread> so you have better and more portable primitves to work with.
It's already in sehe's answer, just to present the key points in a compact way:
pthread_key_create() destructor calls are triggered by a call to pthread_exit().
If the start routine of a thread returns, the behaviour is as if pthread_exit() was called (i. e., destructor calls are triggered).
However, if main() returns, the behaviour is as if exit() was called — no destructor calls are triggered.
This is explained in http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_create.html. See also C++17 6.6.1p5 or C11 5.1.2.2.3p1.
I wrote a quick test and the only thing I changed was moving the create_key call of yours outside of the set_thread_specific.
That is, I called it within the main thread.
I then saw my destroy get called when the thread routine exited.
I call destructor() manually at the end of main():
void * ThreadData = NULL;
if ((ThreadData = pthread_getspecific(key)) != NULL)
destructor(ThreadData);
Of course key should be properly initialized earlier in main() code.
PS. Calling Pthread_Exit() at the end to main() seems to hang entire application...
Your initial thought of handling the main thread as a separate case with atexit worked best for me.
Be ware that pthread_exit(0) overwrites the exit value of the process. For example, the following program will exit with status of zero even though main() returns with number three:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
class ts_stack {
public:
ts_stack () {
printf ("init\n");
}
~ts_stack () {
printf ("done\n");
}
};
static void cleanup (void);
static pthread_key_t key;
static pthread_once_t tls_init_flag = PTHREAD_ONCE_INIT;
void destructor(void *t) {
// thread local data structure clean up code here, which is not getting called
delete (ts_stack*) t;
}
void create_key() {
pthread_key_create(&key, destructor);
atexit(cleanup);
}
// This will be called from every thread
void set_thread_specific() {
ts_stack *ts = new ts_stack (); // Thread local data structure
pthread_once(&tls_init_flag, create_key);
pthread_setspecific(key, ts);
}
static void cleanup (void) {
pthread_exit(0); // <-- Calls destructor but sets exit status to zero as a side effect!
}
int main (int argc, char *argv[]) {
set_thread_specific();
return 3; // Attempt to exit with status of 3
}
I had similar issue as yours: pthread_setspecific sets a key, but the destructor never gets called. To fix it we simply switched to thread_local in C++. You could also do something like if that change is too complicated:
For example, assume you have some class ThreadData that you want some action to be done on when the thread finishes execution. You define the destructor something on these lines:
void destroy_my_data(ThreadlData* t) {
delete t;
}
When your thread starts, you allocate memory for ThreadData* instance and assign a destructor to it like this:
ThreadData* my_data = new ThreadData;
thread_local ThreadLocalDestructor<ThreadData> tld;
tld.SetDestructorData(my_data, destroy_my_data);
pthread_setspecific(key, my_data)
Notice that ThreadLocalDestructor is defined as thread_local. We rely on C++11 mechanism that when the thread exits, the destructor of ThreadLocalDestructor will be automatically called, and ~ThreadLocalDestructor is implemented to call function destroy_my_data.
Here is the implementation of ThreadLocalDestructor:
template <typename T>
class ThreadLocalDestructor
{
public:
ThreadLocalDestructor() : m_destr_func(nullptr), m_destr_data(nullptr)
{
}
~ThreadLocalDestructor()
{
if (m_destr_func) {
m_destr_func(m_destr_data);
}
}
void SetDestructorData(void (*destr_func)(T*), T* destr_data)
{
m_destr_data = destr_data;
m_destr_func = destr_func;
}
private:
void (*m_destr_func)(T*);
T* m_destr_data;
};
My critical section code does not work!!!
Backgrounder.run IS able to modify MESSAGE_QUEUE g_msgQueue and LockSections destructor hadn't been called yet !!!
Extra code :
typedef std::vector<int> MESSAGE_LIST; // SHARED OBJECT .. MUST LOCK!
class MESSAGE_QUEUE : MESSAGE_LIST{
public:
MESSAGE_LIST * m_pList;
MESSAGE_QUEUE(MESSAGE_LIST* pList){ m_pList = pList; }
~MESSAGE_QUEUE(){ }
/* This class will be shared between threads that means any
* attempt to access it MUST be inside a critical section.
*/
void Add( int messageCode ){ if(m_pList) m_pList->push_back(messageCode); }
int getLast()
{
if(m_pList){
if(m_pList->size() == 1){
Add(0x0);
}
m_pList->pop_back();
return m_pList->back();
}
}
void removeLast()
{
if(m_pList){
m_pList->erase(m_pList->end()-1,m_pList->end());
}
}
};
class Backgrounder{
public:
MESSAGE_QUEUE* m_pMsgQueue;
static void __cdecl Run( void* args){
MESSAGE_QUEUE* s_pMsgQueue = (MESSAGE_QUEUE*)args;
if(s_pMsgQueue->getLast() == 0x45)printf("It's a success!");
else printf("It's a trap!");
}
Backgrounder(MESSAGE_QUEUE* pMsgQueue)
{
m_pMsgQueue = pMsgQueue;
_beginthread(Run,0,(void*)m_pMsgQueue);
}
~Backgrounder(){ }
};
int main(){
MESSAGE_LIST g_List;
CriticalSection crt;
ErrorHandler err;
LockSection lc(&crt,&err); // Does not work , see question #2
MESSAGE_QUEUE g_msgQueue(&g_List);
g_msgQueue.Add(0x45);
printf("%d",g_msgQueue.getLast());
Backgrounder back_thread(&g_msgQueue);
while(!kbhit());
return 0;
}
#ifndef CRITICALSECTION_H
#define CRITICALSECTION_H
#include <windows.h>
#include "ErrorHandler.h"
class CriticalSection{
long m_nLockCount;
long m_nThreadId;
typedef CRITICAL_SECTION cs;
cs m_tCS;
public:
CriticalSection(){
::InitializeCriticalSection(&m_tCS);
m_nLockCount = 0;
m_nThreadId = 0;
}
~CriticalSection(){ ::DeleteCriticalSection(&m_tCS); }
void Enter(){ ::EnterCriticalSection(&m_tCS); }
void Leave(){ ::LeaveCriticalSection(&m_tCS); }
void Try();
};
class LockSection{
CriticalSection* m_pCS;
ErrorHandler * m_pErrorHandler;
bool m_bIsClosed;
public:
LockSection(CriticalSection* pCS,ErrorHandler* pErrorHandler){
m_bIsClosed = false;
m_pCS = pCS;
m_pErrorHandler = pErrorHandler;
// 0x1AE is code prefix for critical section header
if(!m_pCS)m_pErrorHandler->Add(0x1AE1);
if(m_pCS)m_pCS->Enter();
}
~LockSection(){
if(!m_pCS)m_pErrorHandler->Add(0x1AE2);
if(m_pCS && m_bIsClosed == false)m_pCS->Leave();
}
void ForceCSectionClose(){
if(!m_pCS)m_pErrorHandler->Add(0x1AE3);
if(m_pCS){m_pCS->Leave();m_bIsClosed = true;}
}
};
/*
Safe class basic structure;
class SafeObj
{
CriticalSection m_cs;
public:
void SafeMethod()
{
LockSection myLock(&m_cs);
//add code to implement the method ...
}
};
*/
#endif
Two questions in one. I don't know about the first, but the critical section part is easy to explain. The background thread isn't trying to claim the lock and so, of course, is not blocked. You need to make the critical section object crt visible to the thread so that it can lock it.
The way to use this lock class is that each section of code that you want serialised must create a LockSection object and hold on to it until the end of the serialised block:
Thread 1:
{
LockSection lc(&crt,&err);
//operate on shared object from thread 1
}
Thread 2:
{
LockSection lc(&crt,&err);
//operate on shared object from thread 2
}
Note that it has to be the same critical section instance crt that is used in each block of code that is to be serialised.
This code has a number of problems.
First of all, deriving from the standard containers is almost always a poor idea. In this case you're using private inheritance, which reduces the problems, but doesn't eliminate them entirely. In any case, you don't seem to put the inheritance to much (any?) use anyway. Even though you've derived your MESSAGE_QUEUE from MESSAGE_LIST (which is actually std::vector<int>), you embed a pointer to an instance of a MESSAGE_LIST into MESSAGE_QUEUE anyway.
Second, if you're going to use a queue to communicate between threads (which I think is generally a good idea) you should make the locking inherent in the queue operations rather than requiring each thread to manage the locking correctly on its own.
Third, a vector isn't a particularly suitable data structure for representing a queue anyway, unless you're going to make it fixed size, and use it roughly like a ring buffer. That's not a bad idea either, but it's quite a bit different from what you've done. If you're going to make the size dynamic, you'd probably be better off starting with a deque instead.
Fourth, the magic numbers in your error handling (0x1AE1, 0x1AE2, etc.) is quite opaque. At the very least, you need to give these meaningful names. The one comment you have does not make the use anywhere close to clear.
Finally, if you're going to go to all the trouble of writing code for a thread-safe queue, you might as well make it generic so it can hold essentially any kind of data you want, instead of dedicating it to one specific type.
Ultimately, your code doesn't seem to save the client much work or trouble over using the Windows functions directly. For the most part, you've just provided the same capabilities under slightly different names.
IMO, a thread-safe queue should handle almost all the work internally, so that client code can use it about like it would any other queue.
// Warning: untested code.
// Assumes: `T::T(T const &) throw()`
//
template <class T>
class queue {
std::deque<T> data;
CRITICAL_SECTION cs;
HANDLE semaphore;
public:
queue() {
InitializeCriticalSection(&cs);
semaphore = CreateSemaphore(NULL, 0, 2048, NULL);
}
~queue() {
DeleteCriticalSection(&cs);
CloseHandle(semaphore);
}
void push(T const &item) {
EnterCriticalSection(&cs);
data.push_back(item);
LeaveCriticalSection(&cs);
ReleaseSemaphore(semaphore, 1, NULL);
}
T pop() {
WaitForSingleObject(semaphore, INFINITE);
EnterCriticalSection(&cs);
T item = data.front();
data.pop_front();
LeaveCriticalSection(&cs);
return item;
}
};
HANDLE done;
typedef queue<int> msgQ;
enum commands { quit, print };
void backgrounder(void *qq) {
// I haven't quite puzzled out what your background thread
// was supposed to do, so I've kept it really simple, executing only
// the two commands listed above.
msgQ *q = (msgQ *)qq;
int command;
while (quit != (command = q->pop()))
printf("Print\n");
SetEvent(done);
}
int main() {
msgQ q;
done = CreateEvent(NULL, false, false, NULL);
_beginthread(backgrounder, 0, (void*)&q);
for (int i=0; i<20; i++)
q.push(print);
q.push(quit);
WaitForSingleObject(done, INFINITE);
return 0;
}
Your background thread needs access to the same CriticalSection object and it needs to create LockSection objects to lock it -- the locking is collaborative.
You are trying to return the last element after popping it.
I have a question concerning this code which I want to run on QNX:
class ConcreteThread : public Thread
{
public:
ConcreteThread(int test)
{
testNumber = test;
}
void *start_routine()
{
for(int i = 0; i < 10; i++)
{
sleep(1);
cout << testNumber << endl;
}
}
private:
int testNumber;
};
class Thread
{
public:
Thread(){};
int Create()
{
pthread_t m_id;
return pthread_create(&m_id, NULL, &(this->start_routine_trampoline), this);
}
protected:
virtual void *start_routine() = 0;
private:
static void *start_routine_trampoline(void *p)
{
Thread *pThis = (Thread *)p;
return pThis->start_routine();
}
};
Now, when I run this code without the sleep in *start_routine, it will simply print the number 10 times, before continuing on to the next line of code (sequential instead of parallel). However, when I use a sleep like in the code, it doesn't print any numbers at all and simply goes on to the next line of code. Why doesn't sleep work and how can I make a thread like this work, instead of running sequential?
Note 1: If you only have 1 processor the code can only be done sequentially no matter how many threads you create. Each thread is given a slice of processor time before it is swapped out for the next threads.
Note 2: If the main thread exits pthreads will kill all child threads before they have a chance to execute.
Now to answer you questions:
Without the sleep. The thread once started has enough time in the single slice it was given to execute the loop 10 times completely.
With the sleep: Your worker thread is going to sleep for a full second. So your main thread has time to do a lot of work. If the main thread exits in this time the worker will be killed.
I would make the following changes:
// Remove the Create() method
// Put thread creation in the constructor.
// Make the thread variable part of the object
pthread_t m_id;
Thread()
{
if (pthread_create(&m_id, NULL, &(this->start_routine_trampoline), this) != 0)
{
throw std::runtime_error("Thread was not created");
}
}
// Make sure the destructor waits for the thread to exit.
~Thread()
{
pthread_join(m_id);
}
If you go and look at boost threading library. you will find that all the little mistakes like this have already been taken care of; Thus making threading easier to use.
Also note. That using a static may work but it is non portable. This is because pthread's is a C library and is thus expecting a function pointer with a C ABI. You are just getting lucky for your platform here. You need to define this as a function and declare the ABI by using extern "C"
// This needs to be a standard function with C Interface.
extern "C" void *start_routine_trampoline(void *p)
{
}
Try to make the pthread_t id a class member instead of a function local variable. That way the caller can pthread_join it.
Not doing this is technically a resource leak (unless the thread is specifically not joinable). And joining will avoid the issue that Martin York described.
From man pthread_join:
The joined thread th must be in the joinable state: it must not have
been detached using pthread_detach(3) or the PTHREAD_CREATE_DETACHED
attribute to pthread_create(3).
When a joinable thread terminates, its memory resources (thread
descriptor and stack) are not deallocated until another thread performs
pthread_join on it. Therefore, pthread_join must be called once for
each joinable thread created to avoid memory leaks.
Going off on a tangent here... With respect to Martin York's post:
Also note. That using a static may work but it is non portable. This is because pthread's is a C library and is thus expecting a function pointer with a C ABI. You are just getting lucky for your platform here. You need to define this as a function and declare the ABI by using extern "C" // This needs to be a standard function with C Interface. extern "C" void * start_routine_trampoline(void * p) {...}
I'm not so sure about that...
(1) C++ was designed to be as compatible with C as possible. There are a few differences... But I was under the impression that extern "C" was used mostly to circumvent the name-mangling required to implement C++ function overloading.
(2) It seems like, once you have the function pointer, the calling conventions (what gets pushed on the stack to make the function call) just has to be the same between C & C++. Otherwise, how would function pointers work?
E.g.:
C code:
void bar( int i ) { printf( "bar %d\n", i ); }
C++ code:
class Foo
{
public:
static void foo( int i ) { cout << "foo " << i << endl; }
};
extern "C" { void bar(int); }
int main()
{
void (*p)(int);
p = & Foo::foo;
(*p)(1);
p = & bar;
(*p)(2);
}