3

I'm a bit at the end of my knowledge.

I have three processes communicating via a shared memory segment. Concurrent access is handled via correct locking (to avoid getting misunderstood here), so I'm pretty sure I'm using the volatile keyword in the way it is intended for.

My shared memory segment is cast to a volatile pointer to a struct, and I can operate on this. The struct has to be volatile, because I sometimes need to spin until some value on the shared memory changes - so not using volatile is not an option.

Now I am using an external C++-library (SystemC, but this should not matter here) from which my struct contains members of sc_time. Although I have access to the source of the library, I don't want to depend on modifications made by me, propably breaking stuff or running into maintenance hell.

Now this class "sc_time" has operators for comparison and assignment. These operators don't work with volatile sc_time - so far not surprising.

Now my question is: Is there a way converting away this volatile-ness without breaking the semantics? I can use the often mentioned const_cast<> or a simple C-cast, but what will the compiler do then? I could propably even just memcpy() the data - but then again, what will the result be?

Any suggestions will be welcome - I would have no problems at all using a C-only wrapper or any other method - as long as it works(tm), but my last resort would be some small memcpy-like assembler code for really reading the data - and that's something I'd like to avoid.

Thanks for your time reading this :-)

Edit: added small code snippet:

struct shared_memory{
     sc_time time1;
     sc_time time2;
     sc_time time3;
     ...
}

...

class foo 
{
    foo();   // attach shared memory and assign to *mem
    ...
    pthread_mutex_t mutex;
    volatile struct shared_memory *mem;
    ...
    void do_stuff();  // periodically called
};

void foo::do_stuff()
{
   ...
   lock_mutex(mutex);
   sc_time t1 = mem->time1;
   sc_time t2 = mem->time2;
   sc_time t3 = mem->time3;
   unlock_mutex(mutex);
   ... 
   while(t1 < t2 || t1 < t3){
       lock_mutex(mutex);
       t1 = mem->time1;
       t2 = mem->time2;
       t3 = mem->time3;
       unlock_mutex(mutex);
   }

}
2
  • So basically you have volatile sc_time timeVar; and you want to pass that into a function that function that takes (sc_time timeTmp) and your compiler is complaining about the type mis-match and you want to avoid this? Also the function you pass the non-volatile'd variable may optimise the variable so that if it changes during execution it may not use these changes, do you care about that? Commented Aug 29, 2013 at 9:19
  • Access is proetected via a mutex anyway - so before it can change, the mutex must get unlocked - so this can not happen. Also, I only have to ensure I always get this value to get read correctly - all synchronization, as complex as it is, is not my issue here. Commented Aug 29, 2013 at 9:29

2 Answers 2

2

If you have to spin waiting for the changes of a variable, you are not locking correctly. Spin waiting always must assume atomic access to the data. This is not only about getting the latest version of data, this is also about coherence, data may be split over several cache lines for example, and you might just get the low bytes of an old version and the high bytes of an updated one.

For this reason the new C and C++ standards have concepts and tools for atomic access, use them if you may. If you don't have them, use compiler (or library) extension: all modern processors have atomic instructions and any decent compiler has an extension that provides such features.

Playing games with volatile is not a path you should go. If you cast away a volatile and pass such an object to a function that doesn't expect things to change under it, all things can happen. In particular, when the library that doesn't have the volatile is compiled optimizations that ruin your data can be performed. Don't do it.

Edit: Since you also tagged your question with gcc, for a good reason they have __sync_lock_test_and_set builtins since a loooong time as an extension. Use it. Maybe on your specific combination of architecture this will resolve in exactly the same code, but trust the gcc guys, they know when they have to add some assembler magic to give you guarantees about your data.

Sign up to request clarification or add additional context in comments.

4 Comments

The locking is not done using volatile data. This is done via correct locking, in this case I am pretty sure I know what I am doing. My issue is, that I need to halt one process until some time in another (not real time, but SystemC simulation time) has passed in other processes. I get these information via shared memory, and I need to busy wait. I don't want any atomic access, but guarantee the compiler reads every iteration, not just once. And this is what volatile is for..
@Michael, first, why don't you want to do that atomic? The cost is basically the same, your system is delayed for the necessary number of bus cycles, until your data arrives. Atomic just gives you a coherence guarantee in addition. In general volatile doesn't suffice, as I said, since parts of data may arrive slightly delayed, which may have your waiter draw some false conclusion. In any case you don't have a guarantee from neither C++ nor C, here, this is why they specified the atomic stuff.
I cannot use atomic, as this is, if I'm not totally wrong, C++11. I'm tied to an older version of GCC which not yet supports C++11. So I cannot use this. Access is serialized via a lock, so basically this whole stuff has atomic semantics, and compared to all other computations, this bit does not really count according to performance. I don't mind if anything arrives delayed, I just need the guarantee my access is performed every time.
@Michael, look at my edit, these gcc builtins are most certainly supported by your version of gcc.
0

You can use const_cast to do away with the 'volatile' as well as the 'const' of variables.

Note: The variable within the function will be treated as non-volatile by the compiler :)

15 Comments

I know about this - but what will happen with the volatile semantics? Will the read still happen like it is a volatile? If not, I would not win anything.
@Michael hmm... I am not sure what you really mean here. You're question suggests that you have read a volatile variable value (which is handled as volatile by the compiler) and then later you pass it on to a non-volatile function where you want to cast away the volatile-ness... This is seperate to the read (which is unaffected by the later cast)... Maybe add a code snippet?
staying with the above volatile sc_time timeVar, I want to do something like: ... = new sc_time(timeVar), which I can't. When I learned one thing is not just cast something to make the compiler happy, but get the clean solution - everything else ends up creating a big, ugly mess.
ah, so the constructor for sc_time takes a type of sc_time (non volatile) and you are trying to take a seperate non-volatile copy of timeVar.. so: timeNonVol = new sc_time(const_cast<sc_time>(timeVar)); This is no different really to what I mentioned in the "Note". Your timeVar stays "volatile" elsewhere, but the passed in value is treated (cast) as non volatile... nothing particularly "messy" about that :)
This still does not really make me happy, although I will try it out. I already ran into a couple of issues with this and looked into it like for hours. But I will try this out.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.