C vs. C++ in pthreads...

J

John David Ratliff

Maybe someone here can help me. I want to use C++, but pthreads seems to be nonfunctional in it.

The following test code I've written in C and C++. The C version works, while the C++ one
does not. Can anyone tell me why?

--- main.c -> compile with gcc main.c -lpthread ---

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

static int p = 0;

static pthread_mutex_t mutex;
static pthread_cond_t cond;

static void *run(void *arg) {
printf("starting run...\n");

for (;;) {
pthread_mutex_lock(&mutex);

while (p == 0) {
printf("nothing to do... taking a nap...\n");

pthread_cond_wait(&cond, &mutex);

printf("getting up...\n");
}

printf("p is now %d\n", p--);

pthread_mutex_unlock(&mutex);
}
}

static void play() {
for (;;) {
printf("sleeping for 3 seconds...\n");
sleep(3);

printf("playing with the value of p...\n");
pthread_mutex_lock(&mutex);
++p;
pthread_mutex_unlock(&mutex);

pthread_cond_signal(&cond);
}
}

int main(int argc, char **argv) {
pthread_t thread;

pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);

if (pthread_create(&thread, NULL, run, NULL) != 0) {
perror("pthread_create");

exit(EXIT_FAILURE);
}

play();

pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);

return 0;
}

--- end of main.c ---

Here is the C++ version that does not work...

--- main.cc - compile with g++ main.cc -lpthread ---

#include <cstdio>
#include <iostream>
#include <unistd.h>
#include <pthread.h>

using namespace std;

class ThreadTest {
int p;
pthread_mutex_t mutex;
pthread_cond_t cond;
public:
ThreadTest();
~ThreadTest();
void play();
void report_for_work();
};

static void *thread_run(void *arg) {
cout << "starting thread_run..." << endl;

ThreadTest obj = *(ThreadTest*)arg;

obj.report_for_work();

cout << "ending thread_run..." << endl;

return NULL;
}

ThreadTest::ThreadTest() {
pthread_t thread;

p = 0;

pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);

if (pthread_create(&thread, NULL, thread_run, this) != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
}

ThreadTest::~ThreadTest() {
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
}

void ThreadTest::play() {
cout << "entering play..." << endl;

for (;;) {
cout << "sleeping for 3 seconds..." << endl;
sleep(3);

cout << "incrementing p" << endl;
pthread_mutex_lock(&mutex);
++p;
pthread_mutex_unlock(&mutex);

cout << "calling up the worker..." << endl;
pthread_cond_signal(&cond);
}

cout << "leaving play..." << endl;
}

void ThreadTest::report_for_work() {
cout << "reporting for work..." << endl;

for (;;) {
pthread_mutex_lock(&mutex);

while (p == 0) {
cout << "nothing to do. taking a nap..." << endl;

pthread_cond_wait(&cond, &mutex);

cout << "getting up..." << endl;
}

cout << "p is " << p-- << "..." << endl;
}

cout << "I've been called home..." << endl;
}

int main(int argc, char **argv) {
ThreadTest tt;

tt.play();
}

--- end of main.cc ---

They both compile, and both of them start threads. However, after the C++ thread goes to sleep,
it never wakes up, no matter how many times it is signaled.

I am using RedHat Fedora Linux, gcc/g++ 3.3.2

Please let me know if you have any ideas. Thanks,

--Dominic

Tired of bad newsreaders?
Get NaN! It's free and multi-platform!
http://www.technoplaza.net/nan/
 
D

Donovan Rebbechi

Maybe someone here can help me. I want to use C++, but pthreads seems to be nonfunctional in it.

The following test code I've written in C and C++. The C version works, while
the C++ one does not. Can anyone tell me why?

The result of copying mutexes/conditions is undefined.

The topical C++ lesson in this is that you should probably declare a private
assignment operator and copy constructor in your ThreadTest class since this is
not a copyable data type.
static void *thread_run(void *arg) {
cout << "starting thread_run..." << endl;

ThreadTest obj = *(ThreadTest*)arg;

Should be:

ThreadTest& obj = *(ThreadTest*)arg;

BTW, you're off topic.

Cheers,
 
G

Gianni Mariani

John said:
Maybe someone here can help me. I want to use C++, but pthreads seems to be nonfunctional in it.

The following test code I've written in C and C++. The C version works, while the C++ one
does not. Can anyone tell me why?

Works for me.

At a guess, this is not a C++ problem but a bug in your pthreads code.

Maybe (maybe not) it's this ...

C version :

for (;;) {
pthread_mutex_lock(&mutex);

while (p == 0) {
printf("nothing to do... taking a nap...\n");

pthread_cond_wait(&cond, &mutex);

printf("getting up...\n");
}

printf("p is now %d\n", p--);

pthread_mutex_unlock(&mutex); <<<<<<<<< good
}

C++ version

for (;;) {
pthread_mutex_lock(&mutex);

while (p == 0) {
cout << "nothing to do. taking a nap..." << endl;

pthread_cond_wait(&cond, &mutex);

cout << "getting up..." << endl;
}

cout << "p is " << p-- << "..." << endl;}


depending on the lock type, you can double trip on the lock at the top
of the loop - because it's already locked after pthread_cond_wait returns.

BTW - I don't know what your plans are but there are some really neat
interfaces you could whip up that makes MT code much easier to develop with.
 
J

John Ratliff

Are you sure it works?

I've tested this code on Linux and Solaris. Two different machines,
compilers, operating systems, and processors. Seems odd they would both
yield the same bad results while you claim it works fine.

Does it print the "getting up" line in the C++ version? I never see it
do that.

I'm not saying they don't compile. They just don't run properly.

The lock thing shouldn't matter. Yes, it's a problem. But I noticed this
after I posted it, and it shouldn't affect sleep and wakeup since it
clearly reaches the pthread_cond_wait call. If you take that out, the
program just runs continually...

--Dominic
 
G

Gianni Mariani

John said:
Are you sure it works?

I've tested this code on Linux and Solaris. Two different machines,
compilers, operating systems, and processors. Seems odd they would both
yield the same bad results while you claim it works fine.

Does it print the "getting up" line in the C++ version? I never see it
do that.

I'm not saying they don't compile. They just don't run properly.

The lock thing shouldn't matter. Yes, it's a problem. But I noticed this
after I posted it, and it shouldn't affect sleep and wakeup since it
clearly reaches the pthread_cond_wait call. If you take that out, the
program just runs continually...


Nope - I'm wrong - it does not work. (I was looking at the strace and it
looked ok - my bad).

The problem is that the ThreadTest class is being copy constructed and
the thread that is initialized in the real constructor is pointed to the
original object.

static void *thread_run(void *arg) {
cout << "starting thread_run..." << endl;

ThreadTest obj = *(ThreadTest*)arg;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - this is your problem

obj.report_for_work();

cout << "ending thread_run..." << endl;

return NULL;
}


Change the function to this ....

static void *thread_run(void *arg) {
cout << "starting thread_run..." << endl;

ThreadTest & obj = * static_cast<ThreadTest*>(arg);

obj.report_for_work();

cout << "ending thread_run..." << endl;

return NULL;
}

Also, add a non default copy constructor ...

ThreadTest( const ThreadTest & );

AND DON'T DEFINE IT. That way if anyone else does this - the program
won't link. You probably don't want an assignment operator either.
Also - the destructor MUST wait for the thread to exit before returning
otherwise all other kinds of hell happen.
 
J

John Ratliff

Donovan said:
The result of copying mutexes/conditions is undefined.

The topical C++ lesson in this is that you should probably declare a private
assignment operator and copy constructor in your ThreadTest class since this is
not a copyable data type.




Should be:

ThreadTest& obj = *(ThreadTest*)arg;

BTW, you're off topic.

Cheers,

a) thank you.
b) what topics does one discuss on a C++ newsgroup, if not C++
questions?

--Dominic
 
K

Karl Heinz Buchegger

John said:
a) thank you.
b) what topics does one discuss on a C++ newsgroup, if not C++
questions?

Not every C++ question is topical in comp.lang.c++.
Here we discuss only thing which are defined in Standard C++
(The language as described by the ISO document). But standard C++
has no idea about threads, mutex, etc... It also has non knowledge
about directories, graphics, controlling hardware etc ...
In short: everything platform specific is not topical in comp.lang.c++
 
J

John Ratliff

Karl said:
Not every C++ question is topical in comp.lang.c++.
Here we discuss only thing which are defined in Standard C++
(The language as described by the ISO document). But standard C++
has no idea about threads, mutex, etc... It also has non knowledge
about directories, graphics, controlling hardware etc ...
In short: everything platform specific is not topical in comp.lang.c++

Sorry. For future reference, what newsgroup should I post on?

--Dominic
 
R

Rolf Magnus

John said:
Sorry. For future reference, what newsgroup should I post on?

If it's about programming with threads, comp.programming.threads is
(surprise!) a good choice.
 
A

Andre Kostur

Sorry. For future reference, what newsgroup should I post on?

comp.programming.threads (for pthreads, and probably other threading
implementations)?

Depending on your platform, comp.os.linux.development.apps (for
application development on linux in general)?
 
A

Attila Feher

Rolf said:
If it's about programming with threads, comp.programming.threads is
(surprise!) a good choice.

Most of the newsreaders I have seen have the ability to sreach, some even to
iteratively search inside the newsgroup names. So if you go to newsgroup
list and type into the search box thread, I assume that it will show up.
This is how I have found the C++ newsgroups long time ago. (To the regret
of many ;-)
 
R

Rolf Magnus

Andre said:
comp.programming.threads (for pthreads, and probably other threading
implementations)?

Depending on your platform, comp.os.linux.development.apps (for
application development on linux in general)?

comp.unix.programmer comes to mind, too.
 
G

Gianni Mariani

Karl said:
Not every C++ question is topical in comp.lang.c++.
Here we discuss only thing which are defined in Standard C++
(The language as described by the ISO document). But standard C++
has no idea about threads, mutex, etc... It also has non knowledge
about directories, graphics, controlling hardware etc ...
In short: everything platform specific is not topical in comp.lang.c++

I think this falls *a little" into the grey area. While I think you're
mostly right, the OP problem was probably somthing that only a frequent
poster on comp.lang.c++ would find easily. So yeah, in strict terms,
the OP broke the speed limit but he probably wouldn't get a ticket for
3kmph over the limit. Also, the OP went to ALOT of trouble to give 2
easily compiled chunks-o-code so all platform issues were really not
questions per-se.

In support of this position, I think that there are some things in the
answer to the OP that are things we regularly discuss in c.l.c++'s.

i.e.

a) How to make a class non-copyable - (this was the problem - the class
should have been designed so that it could not be copied).

b) The rule of 3. In this case it applies big time.

So yeah, to really understand the problem required an understanding of
the MT problem space but the underlying issue was in the true hard core
c.l.c++ problem space. It may be that the OP had no idea about how to
use these C++ tools and this is a great way to demonstrate how versatile
C++ is and hence how standard C++ idioms map to a problem space.
 
S

SenderX

ThreadTest::ThreadTest() {
pthread_t thread;

p = 0;

pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);

if (pthread_create(&thread, NULL, thread_run, this) != 0) { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

perror("pthread_create");
exit(EXIT_FAILURE);
}
}

You have a race condition here. The thread could be created, receive the
'this' pointer and use it, BEFORE, the constructor finishes. It may not
crash in all situations, but it is a racer nonetheless.

:O
 
G

Gianni Mariani

SenderX said:
You have a race condition here. The thread could be created, receive the
'this' pointer and use it, BEFORE, the constructor finishes. It may not
crash in all situations, but it is a racer nonetheless.

In this case, I don't think there is a problem because the mutex and
cond variables are initialized and that's all that's needed. However, I
agree that it's better to complete the construction and then start the
thread.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top