ZivS
ZivS

Reputation: 2124

Should a pointer to stack variable be volatile?

I know that I should use the volatile keyword to tell the compiler not to optimize memory read\write to variables. I also know that in most cases it should only be used to talk to non-C++ memory.

However, I would like to know if I have to use volatile when holding a pointer to some local (stack) variable.

For example:

//global or member variable
/* volatile? */bool* p_stop;

void worker()
{
    /* volatile? */ bool stop = false;
    p_stop = &stop;
    while(!stop)
    {
        //Do some work
        //No usage of "stop" or p_stop" here
    }
}

void stop_worker()
{
    *p_stop = true;
}

It looks to me like a compiler with some optimization level might see that stop is a local variable, that is never changed and could replace while(!stop) with a while(true) and thus changing *p_stop while do nothing.

So, is it required to mark the pointer as volatile in such a case?

P.S: Please do not lecture me on why not to use this, the real code that uses this hack does so for a (complex-to-explain) reason.

EDIT:

  1. I failed to mention that these two functions run on different threads. The worker() is a function of the first thread, and it should be stopped from another thread using the p_stop pointer.

  2. I am not interested in knowing what better ways there are to solve the real reason that is behind this sort of hack. I simply want to know if this is defined\undefined behavior in C++ (11 for that matter), and also if this is compiler\platform\etc dependent. So far I see @Puppy saying that everyone is wrong and that this is wrong, but without referencing a specific standard that denoted this.

I understand that some of you are offended by the "don't lecture me" part, but please stick to the real question - Should I use volatile or not? or is this UB? and if you can please help me (and others) learn something new by providing a complete answer.

Upvotes: 0

Views: 488

Answers (4)

Daniel Jour
Daniel Jour

Reputation: 16156

I simply want to know if this is defined\undefined behavior in C++ (11 for that matter)

Ta-da (from N3337, "quasi C++11")

Two expression evaluations conflict if one of them modifies a memory location [..] and the other one accesses or modifies the same memory location.

§1.10/4

and:

The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior. [..]

§1.10/21

You're accessing the (memory location of) object stop from different threads, both accesses are not atomic, thus also in no "happens before" relation. Simply put, you have a data race and thus undefined behavior.

I am not interested in knowing what better ways there are to solve the real reason that is behind this sort of hack.

Atomic operations (as defined by the C++ standard) are the only way to (reliably) solve this.

Upvotes: 7

Puppy
Puppy

Reputation: 147036

So, is it required to mark the pointer as volatile in such a case?

No. It's not required, principally because volatile doesn't even remotely cover what you need it to do in this case. You must use an actual synchronization primitive, like an atomic operation or mutex. Using volatile here is undefined behaviour and your program will explode.

volatile is NOT useful for concurrency. It may be useful for implementing concurrent primitives but it is far from sufficient.

Frankly, whether or not you want to use actual synchronization primitives is irrelevant. If you want to write correct code, you have no choice.

Upvotes: 4

Persixty
Persixty

Reputation: 8589

This question simply cannot be answered from the details provided.

As is stated in the question this is an entirely unsupported way of communicating between threads.

So the only answer is:

Specify the compiler versions you're using and hope someone knows its darkest secrets or refer to your documentation. All the C++ standard will tell you is this won't work and all anyone can tell you is "might work but don't".

There isn't a "oh, come on guys everyone knows it pretty much works what do I do as the workaround? wink wink" answer.

Unless your compiler doesn't support atomics or suitably concurrent mechanisms there is no justifiable reason for doing this. "It's not supported" isn't "complex-to-explain" so I'd be fascinated based on that code fragment to understand what possible reason there is for not doing this properly (other than ancient compiler).

Upvotes: 1

Jason S
Jason S

Reputation: 189876

P.S: Please do not lecture me on why not to use this,

I am not sure what we are supposed to say. The compiler manages the stack, so anything you are doing with it is technically undefined behavior and may not work when you upgrade to the next version of the compiler.

You are also making assumptions that may be different than the compiler's assumptions when it optimizes. This is the real reason to use (or not use) volatile; you give guidance to the compiler that helps it decide whether optimizations are safe. The use of volatile tells the compiler that it should assume that these variables may change due to external influences (other threads or special hardware behavior).

So yes, in this case, it looks like you would need to mark both p_stop and stop with a volatile qualifier.

(Note: this is necessary but not sufficient, as it does not cause the appropriate behaviors to happen in a language implementation with a relaxed memory model that requires barriers to ensure correctness. See https://en.wikipedia.org/wiki/Memory_ordering#Runtime_memory_ordering )

Upvotes: 2

Related Questions