Skip to main content
GameDev.net gamedev.net
🔒 Locked

volatile Class* to Class*

Started by janta Jan 18, 2008 at 3:08 PM 11 replies 3.9k views
Original Post
janta
janta
Hey this is a compile error (VS2005) that I had never encountered before:

class Test
{
public:
    void f() {};

};

volatile Test *p = new Test;

const_cast<Test*>(p)->f(); // OK
p->f();	// cannot convert 'this' pointer from 'volatile Test' to 'Test &'
That would make sense in the case I had called a non const member on a const member, in which case I'd just fix my design, but here... Even if there was a way to force the implicit "this" to be considered a "volatile Test*", I could want to call the member function on non volatile pointers as well. What do you say? Is there any better way to fix this, or is that a legitimate use of const_cast?
SiCrane
SiCrane
Like const pointers, in order to call a function on a volatile pointer, the member function needs to be declared volatile. Ex:
class Test {  public:    void f() volatile {};};
janta
janta
Damn, I've tried "volatile void f()" but not void f() volatile". I should have...
Yet another C++ feature I did not know of [smile]
Thank you!
smart_idiot
smart_idiot
LOL @ 'volatile void'.
Chess is played by three people. Two people play the game; the third provides moral support for the pawns. The object of the game is to kill your opponent by flinging captured pieces at his head. Since the only piece that can be killed is a pawn, the two armies agree to meet in a pawn-infested area (or even a pawn shop) and kill as many pawns as possible in the crossfire. If the game goes on for an hour, one player may legally attempt to gouge out the other
Nitage
Nitage
Quote:
Even if there was a way to force the implicit "this" to be considered a "volatile Test*", I could want to call the member function on non volatile pointers as well.


It's exactly like marking a member function 'const' - it allows the function to be called with a volatile object pointer but it doesn't require the pointer to be volatile.
janta
janta
In fact I don't think this solved my problem. If I understand correctly, marking the function like
void Class::f() volatile;
will ensure that all accessed members are considered volatile, so that if one of them is changed by another thread, the function will know about it, but this doesn't apply to the value if "this" itself which won't (and can't) change during the function call.

I'm doing something like:

volatile Task* task = NULL;void EntryPoint(void*){    for(;;)    {        if (task)        {            task->Run(); // virtual void Run() volatile = 0            task = NULL;        }         WaitForSingleObject(...);    }}


So what I think I really need is to make sure the Run() method is called on the right object.
Where am I wrong?
SiCrane
SiCrane
You probably want Task * volatile task and not volatile Task* task.
janta
janta
-_-' I should really have figured this out myself...
Thx.
the_edd
the_edd
Quote:
Original post by janta
In fact I don't think this solved my problem. If I understand correctly, marking the function like
void Class::f() volatile;
will ensure that all accessed members are considered volatile, so that if one of them is changed by another thread, the function will know about it


Stop!

volatile does little for thread safety! You need to use appropriate synchronisation primitives (some kind of mutex, usually).

A simplistic explanation of why this is the case is that volatile stops the compiler from reordering operations, but it doesn't do anything to stop the processor from doing so (memory fences need not be generated).

Further reading.

EDIT: If you're coming from Java (and it looks like you might be by the way you're new-ing up a Test object), this kind of volatile usage might be correct. Things are different in C++ however, where a portable multithreaded memory model doesn't even exist, yet.

[Edited by - the_edd on January 20, 2008 11:19:51 AM]
janta
janta
Quote:
Original post by the_edd
Quote:
Original post by janta
all accessed members are considered volatile, so that if one of them is changed by another thread, the function will know about it

volatile does little for thread safety! You need to use appropriate synchronisation primitives (some kind of mutex, usually).


I didn't say volatile provides thread safety, just that volatile prevents cached reads and delayed writes and, as you said, reordering of operation on volatile variables but relative to other volatile variables. (however, Visual Studio 2005: "from volatile variables are defined to have read-acquire semantics, and writes to volatile variables are defined to have write-release semantics" but this is certainly not to be taken for granted, especially if writing code for multiple platforms.)

I should have been more explicit: "the function will know about it, probably in a very wrong way unless appropriate synchronization primitives and/or appropriate atomic operations and barriers are used"

Another usefull read

(BTW having 2 links in one sentence like "further reading" almost made me miss the first link)

EDIT: I'm not coming from Java, I'm just lazy (and that was pseudo-code so I didn't bother)
iMalc
iMalc
From the source you linked to:
Quote:
However, the guarantees of the standard are not sufficient for using volatile for multi-threading. The C++ Standard does not stop the compiler from reordering non-volatile reads and writes relative to volatile reads and writes, and it says nothing about preventing CPU reordering.

Visual C++ 2005 goes beyond standard C++ to define multi-threading-friendly semantics for volatile variable access. Starting with Visual C++ 2005, reads from volatile variables are defined to have read-acquire semantics, and writes to volatile variables are defined to have write-release semantics. This means that the compiler will not rearrange any reads and writes past them, and on Windows it will ensure that the CPU does not do so either.

It is important to understand that these new guarantees only apply to Visual C++ 2005 and future versions of Visual C++, and that on Xbox 360, the compiler does not insert any instructions to prevent the CPU from reordering reads and writes.
volatile is a compiler specific method of doing multi-threading synchronisation at best. On Windows you're better off using the OSes synchronisation funtions. To be cross-platform I've been told that you can use volatile with sig_atomic_t (IIRC) and that this is the only portable use of the volatile keyword. Anything else if compiler dependent.
janta
janta
Quote:
Original post by iMalcvolatile is a compiler specific method of doing multi-threading synchronisation at best. On Windows you're better off using the OSes synchronisation funtions.

I agree that volatile alone is not sufficient to write thread safe and efficient code. Doing so requires using a subtle combination of: volatile in the right place, locks in the right place (OS's critical section, semaphore, and so on), interlocked operations in the right place and the right type of memorry barrier in the right place.

So, I'm not arguing that using volatile alone results in thread safe code, i just made up the simplest example I could think of in order to illustrate the question: "how to call a non volatile member function through a pointer to a volatile object?" to whcih the answer was either:
- your function must be volatile
- you want a volatile pointer to your object rather than a pointer to a volatile object.

the_edd
the_edd
Quote:
Original post by janta
Quote:
Original post by iMalcvolatile is a compiler specific method of doing multi-threading synchronisation at best. On Windows you're better off using the OSes synchronisation funtions.

I agree that volatile alone is not sufficient to write thread safe and efficient code. Doing so requires using a subtle combination of: volatile in the right place, locks in the right place (OS's critical section, semaphore, and so on), interlocked operations in the right place and the right type of memorry barrier in the right place.


Or: doing so requires a much less subtle and much more obvious use of OS/POSIX supplied synchronisation primitives (mutexes, conditions etc) and nothing else. Adding volatile in to the mix will only make things slower.

You might get away with using volatile in conjunction with interlocked operations and forgoing more usual synchronisation methods with some specific compilers, targeting some specific architectures, but checking that you have a seriously nasty contention scenario by profiling before changing to some highly-specific alternative is an absolute must. You've got to assume that the stuff provided by your system is implemented as efficiently as possible.

You may already know this but if someone didn't mention it, people that read this thread who don't know any better might come away with the idea that the use of the volatile keyword is widely applicable in multi-threaded programming, which is of course a depressingly common misconception.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.