By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
429,054 Members | 1,635 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 429,054 IT Pros & Developers. It's quick & easy.

strange threading context

P: n/a
This is my problem:

I have two classes A and B:

class A
{
void methodA();
}

class B
{

void methodB();

struct S
{
int Size;
};

S & getMyStruct();

S MyStruct;

}
methodA and methodB get executed in two different threads concurrently.
They both access the variable Size in the structure MyStruct of type S.
methodA gets a reference to this struct by calling getMyStruct which returns
a reference
to it.
Here comes the strange part:
if methodB alters the value of Size from 0 to 1, this change doesn't get
reflected in methodA - methodA
still reads a 0. The access to the struct MyStruct is sychronized by a
mutex.

Whats going on here? Why are the changes of Size by thread executing methodB
not visible to the thread executing methodA.
I thought that struct myStruct lies in the context of both threads/methods.

Regards

A.Gallus

Aug 16 '08 #1
Share this Question
Share on Google+
5 Replies


P: n/a
A.Gallus wrote:
This is my problem:

I have two classes A and B:

class A
{
void methodA();
}

class B
{

void methodB();

struct S
{
int Size;
};

S & getMyStruct();

S MyStruct;

}
methodA and methodB get executed in two different threads concurrently.
They both access the variable Size in the structure MyStruct of type S.
methodA gets a reference to this struct by calling getMyStruct which
returns a reference
to it.
Here comes the strange part:
if methodB alters the value of Size from 0 to 1, this change doesn't get
reflected in methodA - methodA
still reads a 0. The access to the struct MyStruct is sychronized by a
mutex.
Show the code. If you don't have an appropriate memory barrier each
thread/core may have the local copy in a register.

--
Ian Collins.
Aug 16 '08 #2

P: n/a
"A.Gallus" <uh**@rz.uni-karlsruhe.dewrote in message
news:g8**********@news2.rz.uni-karlsruhe.de...
This is my problem:

I have two classes A and B:

class A
{
void methodA();
}

class B
{

void methodB();

struct S
{
int Size;
};

S & getMyStruct();

S MyStruct;

}
methodA and methodB get executed in two different threads concurrently.
They both access the variable Size in the structure MyStruct of type S.
methodA gets a reference to this struct by calling getMyStruct which
returns a reference
to it.
Here comes the strange part:
if methodB alters the value of Size from 0 to 1, this change doesn't get
reflected in methodA - methodA
still reads a 0. The access to the struct MyStruct is sychronized by a
mutex.
Well, let me try to read your mind and see if I can make a better model in
the form of fully compliable code:

code listing 1 with a race-condition!
__________________________________________________ ____________________
#include <cstdio>
#include <pthread.h>
class mutex_guard {
pthread_mutex_t* const m_mutex;

public:
mutex_guard(pthread_mutex_t* const mutex)
: m_mutex(mutex) {
pthread_mutex_lock(m_mutex);
}

~mutex_guard() {
pthread_mutex_unlock(m_mutex);
}
};
class A {
public:
void methodA();
};

class B {
friend class A;

struct S {
int Size;
S() : Size() {}
};

S MyStruct;

S& getMyStruct() {
return MyStruct;
}

static pthread_mutex_t g_mutex;

public:
void methodB();
};
pthread_mutex_t B::g_mutex = PTHREAD_MUTEX_INITIALIZER;
static B g_global_instance_of_B;
static A g_global_instance_of_A;
void A::methodA() {
mutex_guard lock(&B::g_mutex);
std::printf("A::methodA() - B::MyStruct::Size == %d\n",
g_global_instance_of_B.getMyStruct().Size);
}
void B::methodB() {
mutex_guard lock(&B::g_mutex);
MyStruct.Size = 1;
std::printf("B::methodB() - B::MyStruct::Size == %d\n",
g_global_instance_of_B.getMyStruct().Size);
}
extern "C" void* thread_1(void*) {
g_global_instance_of_A.methodA();
return NULL;
}
extern "C" void* thread_2(void*) {
g_global_instance_of_B.methodB();
return NULL;
}
int main() {
pthread_t tid[2];
pthread_create(&tid[0], NULL, thread_1, NULL);
pthread_create(&tid[1], NULL, thread_2, NULL);
for (int i = 0; i < 2; ++i) {
pthread_join(tid[i], NULL);
}
return 0;
}

__________________________________________________ ____________________
Okay, this program can run 3 ways... The first is that thread_1 runs first
and therefore misses the update. Second, thread_2 runs first and the update
is observed when thread_1 runs. The third is that they run together and race
through in which anything can happen. For instance, if thread_1 hits the
mutex first, then the update will not be observed.

If you want to ensure that thread_1 will ALWAYS see the mutation generated
by thread_2, well, you need someway to make sure that thread_2 runs first. A
simple and niave soultion is to use a simple semaphore. Think of this:
code listing 2 without a race-condition!
__________________________________________________ ____________________
#include <cstdio>
#include <pthread.h>
#include <semaphore.h>
class mutex_guard {
pthread_mutex_t* const m_mutex;

public:
mutex_guard(pthread_mutex_t* const mutex)
: m_mutex(mutex) {
pthread_mutex_lock(m_mutex);
}

~mutex_guard() {
pthread_mutex_unlock(m_mutex);
}
};
class A {
public:
void methodA();
};

class B {
friend class A;

struct S {
int Size;
S() : Size() {}
};

S MyStruct;

S& getMyStruct() {
return MyStruct;
}

static pthread_mutex_t g_mutex;

public:
void methodB();
};
pthread_mutex_t B::g_mutex = PTHREAD_MUTEX_INITIALIZER;
static B g_global_instance_of_B;
static A g_global_instance_of_A;
void A::methodA() {
mutex_guard lock(&B::g_mutex);
std::printf("A::methodA() - B::MyStruct::Size == %d\n",
g_global_instance_of_B.getMyStruct().Size);
}
void B::methodB() {
mutex_guard lock(&B::g_mutex);
MyStruct.Size = 1;
std::printf("B::methodB() - B::MyStruct::Size == %d\n",
g_global_instance_of_B.getMyStruct().Size);
}
extern "C" void* thread_1(void* state) {
sem_t* const sem = (sem_t*)state;
sem_wait(sem);
g_global_instance_of_A.methodA();
return NULL;
}
extern "C" void* thread_2(void* state) {
sem_t* const sem = (sem_t*)state;
g_global_instance_of_B.methodB();
sem_post(sem);
return NULL;
}
int main() {
pthread_t tid[2];
sem_t sem;
sem_init(&sem, 0, 0);
pthread_create(&tid[0], NULL, thread_1, &sem);
pthread_create(&tid[1], NULL, thread_2, &sem);
for (int i = 0; i < 2; ++i) {
pthread_join(tid[i], NULL);
}
sem_destroy(&sem);
return 0;
}

__________________________________________________ ____________________
Now, thread_1 will always see the mutation made by thread_2. Period, end of
story. thread_1 will ALWAYS output:
A::methodA() - B::MyStruct::Size == 1

Whats going on here? Why are the changes of Size by thread executing
methodB not visible to the thread executing methodA.
I thought that struct myStruct lies in the context of both
threads/methods.
Perhaps the thread in which methodA gets invoked happens to execute first,
thus it has no chance to observe the mutation made by the thread which
invokes methodB.

Aug 17 '08 #3

P: n/a
When I create MyStruct on the heap (with new) instead writing it as a
reference class member everything works fine:

S * getMyStruct();

S * MyStruct;

It seems to me that both classes are working on another copy of MyStruct
when I don't use
new(). I doubt that I have a race condition, unfortunately the code is way
to long to post it here.

Thx guys!

Regards

A.Gallus
Aug 17 '08 #4

P: n/a
A.Gallus wrote:
When I create MyStruct on the heap (with new) instead writing it as a
reference class member everything works fine:

S * getMyStruct();

S * MyStruct;

It seems to me that both classes are working on another copy of MyStruct
when I don't use
new(). I doubt that I have a race condition, unfortunately the code is
way to long to post it here.
Then post a simplified example. You'll probably solve the problem
writing it.

--
Ian Collins.
Aug 17 '08 #5

P: n/a
A.Gallus schrieb:
When I create MyStruct on the heap (with new) instead writing it as a
reference class member everything works fine:
You had it as reference class member instead of as value member?
S * getMyStruct();

S * MyStruct;

It seems to me that both classes are working on another copy of MyStruct
when I don't use
new(). I doubt that I have a race condition, unfortunately the code is
way to long to post it here.
You could check that by printing the address of the member and the
address of the containing class in both threads. If the addresses are
not equal, you are working with two distinct copies.

--
Thomas
Aug 17 '08 #6

This discussion thread is closed

Replies have been disabled for this discussion.