Variable Pointers across Threads

Started by
25 comments, last by guywithknife 13 years ago
This is going to sound stupid, but I just need someone to clarify something, because I'm having a weird problem and can't work out why.

Question: Variable Addresses are valid between 2 different threads of the same process, correct?

So I create a global variable in Thread 1, and pass it's Address to Thread 2 - Thread 2 will be able to access it. Right?

Cheers.
Advertisement
Yes. The most common problems are often because of lacking synchronization, which can cause the perceived value at the address to differ from the actual value in some threads.
Well this is the weird thing, I've implemented Critical Sections to protect and places where data may be accessed by the 2 threads.

I pass my Window classes pointer to my rendering thread, from the logic Thread. And this seems to work. However, the Window class includes a pointer to a Material Class. And this is what gets invalidated. The Window's data members are intact, but drilling down into the Material data it returns mush - and the pointer value appears to have changed.
Is the pointer correct before you create the second thread or pass it the window pointer?
If you update the pointer later the other thread might already have cached the value in a register. Make sure you use critical sections everywhere you access a value, and make shared variables volatile.

Also, if you cast your pointers when passing them between threads, make sure you don't break a pointer to a class with virtuals or inheritance, as for example casting a class pointer to a void pointer might yield a different value than the original pointer, which must be properly cast back to restore the correct value.
Yes the pointer is correct before I pass it to the second thread. Once added to the rendered, pointers are never altered, or modified. The class becomes purely Read Only.

Also, no casting of pointers occurs anyway in the engine.

This is how it works at the moment:

- Thread 1 (Messaging Thread/Window Creation) Starts
- Material Class declared Global
- Window Class declared Global
- Initialises Graphics Renderer (Starting Thread 2)
- Material loads TGA file
- Material pointer is passed to Window Class
- Various Window properties set (size, scale, position, etc)
- Pointer to Window is passed to Renderer Thread, where it is placed into a std::vector<Window*>, by means of a push back.

- The renderer thread then iterates through the Window Vector, and renders accordingly. And Window's added by the Renderer thread itself (ie. Debug Window) render fine. Windows passed by Thread 1 have corrupted Material Pointers - but the rest of the Window Data is intact.

Cheers

Well this is the weird thing, I've implemented Critical Sections to protect and places where data may be accessed by the 2 threads.

I pass my Window classes pointer to my rendering thread, from the logic Thread. And this seems to work. However, the Window class includes a pointer to a Material Class. And this is what gets invalidated. The Window's data members are intact, but drilling down into the Material data it returns mush - and the pointer value appears to have changed.

Anything that both threads read has to be synchronized. If you "pass a pointer" then you need locks around that pointer. You can use some atomic operations like CAS to do some specific stuff between threads, but in general you need locks.
The issue is, that if you have:
thread 1 does:
Window *foo = new window;
thread 2 does:
foo->material->color = black;

None of those are atomic operations, so it could really be:
Thread 1: Window *foo = allocate( sizeof( window ) );
Thread 2: *(foo->material)
Thread 1: foo->constructor

This counts for ANY line of C code. It isn't atomic. Multiple lines of C code aren't atomic either, so the compiler is free to rearrange them from what your code says as long as it does the same thing in a single threaded world. Thus, no re-arrangement of code will fix the threading issue. The only way to fix it is to lock around everything you share.
So you're saying that each of my classes should have a Lock/Unlock function, and before any thread does *ANYTHING* with that class, it must call the Lock function, and then the Unlock function after it's done with it.

Would that solve the problem?

So you're saying that each of my classes should have a Lock/Unlock function, and before any thread does *ANYTHING* with that class, it must call the Lock function, and then the Unlock function after it's done with it.

Would that solve the problem?

No, you don't need locks in the class, unless the class is used on both threads at the same time. If you are just passing the material from one thread (loader) to another (render), then you only need to lock around the point where you pass off the data. What you;d then need is a lock around the window_vector.push_back( window * ) and functions where window_vector[ index ] is used. The OS locking functions will place barriers, so that by the time the window_vector contains your pointer, all operations previous to this will be completed and pushed out to memory where the other thread can read it.
That's what I've got implemented - I have a CS around the window_vector.push_back() code, and then another CS around the portion of code that actually tries to render that window.

Example:

In the AddWindow routine:

Lock(csWindows);
window_vector.push_back(someWindowAddress);
Release(csWindows);


In the Render routine:

Lock(csWindows)
for(int i = 0; i < window_vector.size(); ++i)
{
window_vector.Render();
}
Release(csWindows);
;
Multithreading bugs are typically hard to locate. Without a full code dump, it's hard for anyone to speculate as to any more issues you are having with it. One thing you can do is set a hardware breakpoint on the variable in question. on the line where you first set it. The, when something modifies it, you'll be able to see what. If anything is modifying things outside your locks, you'll be able to see it.

This topic is closed to new replies.

Advertisement