Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Volatile Placement New

How does one do a placement new operation on a volatile pointer.

For example, I want to do something like this:

volatile SomeStruct Object;
volatile SomeStruct* thing = &Object;
new (thing) SomeStruct(/*arguments to SomeStruct's constructor*/);

I know this would work if there was no volatile keyword......but how can I do this with a volatile variable?

Note:

Placement new is defined like this:

void* operator new(size_t memoryRequested, void* pointer)
{
  return pointer;
}

(By the way here is how GCC implements it):

// Default placement versions of operator new.
inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }

The problem is that I am trying to convert thing of type volatile SomeStruct* to void*, which is not allowed.

For example if I change the new operator to this:

void* operator new(size_t memoryRequested, volatile void* pointer)
{
  return (void*)pointer;
} 

It would compile, but would invoke undefined behavior.

like image 637
DarthRubik Avatar asked Sep 19 '16 23:09

DarthRubik


People also ask

What does volatile in C mean?

C's volatile keyword is a qualifier that is applied to a variable when it is declared. It tells the compiler that the value of the variable may change at any time--without any action being taken by the code the compiler finds nearby.

What is volatile variable in C where it is used?

The volatile qualifier is applied to a variable when we declare it. It is used to tell the compiler, that the value may change at any time. These are some properties of volatile. The volatile keyword cannot remove the memory assignment. It cannot cache the variables in register.

Where are volatile variables stored in C?

There's no reason for a volatile variable to be stored in any "special" section of memory. It is normally stored together with any other variables, including non-volatile ones. If some compiler decides to store volatile variables in some special section of memory - there's nothing to prevent it from doing so.

Can we have a volatile pointer?

Yes, a pointer can be volatile if the variable that it points to can change unexpectedly even though how this might happen is not evident from the code. An example is an object that can be modified by something that is external to the controlling thread and that the compiler should not optimize.


2 Answers

I want to say you can do it like this:

new (const_cast<SomeStruct*>(thing)) volatile SomeStruct(...);

But I'm not actually sure whether this is valid or not. The problem is that since the allocation function returns a void* into which to construct the volatile SomeStruct object, accesses to the memory may not have volatile semantics, leading to undefined behavior.

So I'm not sure whether it's legal to use placement new to construct an object into a volatile-qualified block of memory. However, assuming the memory was originally, say, a non-volatile array of char, this seems like the correct solution.

like image 82
Brian Bi Avatar answered Oct 27 '22 00:10

Brian Bi


I know this would work if there was no volatile keyword......but how can I do this with a volatile variable?

Placement new has to do with constructing an object at a given location. cv-qualifiers are only applied after the object is constructed. The const-ness or volatile-ity are only applicable once the object is constructed. In that sense, it makes sense that the placement new does not provide an overload that accepts a volatile (or const) pointer. From the C++ standard (draft) [class.ctor/3] here;

A constructor can be invoked for a const, volatile or const volatile object. const and volatile semantics ([dcl.type.cv]) are not applied on an object under construction. They come into effect when the constructor for the most derived object ([intro.object]) ends.

Any attempt to cast away the volatile leads to undefined behavior, see the cppreference here;

Modifying a const object through a non-const access path and referring to a volatile object through a non-volatile glvalue results in undefined behavior.

See also [expr.const.cast/6].

Given the use of the volatile and the placement new, the assertion in the question (and some of the comments) is that the object is required for use with a signal handler and maps to a specific location in memory.

There are some alternatives though...

If the specific location is not needed, the best is to not use the placement new and just add a volatile qualifer to the object wherever it is declared;

struct SomeStruct {
    /*...*/
};
// ...
volatile SomeStruct Object;

If both the placement new and the volatile is needed, then reorder their use. Construct the object as required and then add the qualifier;

SomeStruct Object;
// ...
void* p = &Object; // or at the required location
volatile SomeStruct* p2 = new (p) SomeStruct;

Does the struct have to be volatile? The volatile parts of the struct could be internalised/abstracted and the cv-qualifiers of the data would not need to be exposed to the client to begin with, it is dealt with internally to the struct;

struct SomeStruct {
    volatile int data;
    void DoSomething()
    {
        data = 42;
    }
};

SomeStruct Object;
/* ... */
void* p = &Object;
auto p2 = new (p) SomeStruct{};
p2->DoSomething();

Internalise the initialise of the volatile object, an alternative is to allow the SomeStruct to lazy initialise (or re-initialise/reset) itself as needed. Given some of the apparent constraints, this may not be that feasible.

struct SomeStruct {
    void Initialise() volatile
    {
        /*...*/
    }
}
like image 36
Niall Avatar answered Oct 27 '22 01:10

Niall