3

question is rather simple Q: If I have

settings[N_STNGS];//used by many threads  
std::atomic<size_t> current_settings(0);
void updateSettings()//called by single thread  , always the same thread if that is important
{

    auto new_settings = (current_settings+1)%N_STNGS;
    settings[new_settings].loadFromFileSystem(); //line A
    current_settings=new_settings; //line B
}

does standard guarantee that line A wont be reordered after line B? Also will users of STNGS always see consistent(commited-as in memory visibility visible) data?

Edit: for multiple reader threads and nontrivial settings is this worth the trouble compared to simple mutexing?

9
  • 1
    Even if it's not, this code is absolutely broken. (current_settings can be changed by other thread between the first line and the last line.) Commented Jan 9, 2012 at 17:21
  • @DavidSchwartz : consider char padding[padding_size]; at the beginning and at the end of struct. Also STNGS can be pointer arraythat never changes, just the pointed to stuff does... Commented Jan 9, 2012 at 17:23
  • @NoSenseEtAl Is this a C++ question or a question about some particular platform? If it's a C++ question, there's no guarantee padding is all you need. If it's a platform-specific question, the answer will depend on the platform. (You are correct about settings never changing.) Commented Jan 9, 2012 at 17:24
  • @DavidSchwartz: What you say is false for C++11, different array members can be accessed concurrently without worries. Commented Jan 9, 2012 at 17:28
  • 3
    @DavidSchwartz: Really. Quoting 1.3/7: "A memory location is either an object of scalar type or a maximal sequence of adjacent bit-fields all having non-zero width. Two threads of execution (1.10) can update and access separate memory locations without interfering with each other." Commented Jan 9, 2012 at 17:43

2 Answers 2

2

Given the definition

int settings[N_STNGS];
std::atomic<size_t> current_settings(0);

and Thread 1 executing:

settings[new_settings] = somevalue;  // line A
current_settings=new_settings;       // line B

and Thread 2 executing:

int cur_settings = current_settings;        // line X
int setting_value = settings[cur_settings]; // line Y

then yes, if Thread 2 at line X reads new_settings written by Thread 1 in line B, and there are no other modifications to settings[new_settings] (by some code we don't see), Thread 2 is bound to read somevalue and no undefined behavior occurs. This is because all the operations are (by default) memory_order_seq_cst and a release-write (line B) synchronizes with an acquire-read (line X). Note that you need two statements in Thread 2 to get a sequenced-before relationship between the atomic read of the index and the read of the value (a memory_order_consume operation would do instead).

I'd certainly implement it with rw-mutexes for start.

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

6 Comments

I agree. Line X can either read the initial value 0 or one of the values written by B, in which case there is a happens-before relation between B and X. A happens before B by program order, and X happens before Y. Since the happens-before relationship is transitive, A happens before Y. It doesn't matter that code in A and Y is not atomic. The only worry is the use of a cyclic buffer. It's theoretically possible that current_settings wraps around between lines X and Y. That would cause a data race between A and Y.
@BartoszMilewski wraping around is extremely unlikely because update settings is done like every couple of seconds, so that would be some stealth memory visibility model(I mean it would be the worst cache coherency CPU model ever) in which it would be a problem.
Just to be sure: are you are commenting on the settings content, or just the index of the circular array of settings.
@BartoszMilewski BTW i have atomic<bool> is_initialized in the settings, so for example public method bool isBlocked() will always return false if settings havent been filled at least once.
@NoSenseEtAl: Lines A and Y set the contents. Lines B and X provide necessary synchronization and ensure that contents operations are synchronized too. What's important is that all readers must first read cur_settings and then the contents. If you like to think in terms of fences/barriers, B and X provide the fencing.
|
0

The general answer is no. If you are careful and you use only functions which have a memory_order parameter and pass them the right value for it depending on what you are doing, then it may be yes.

(And as other have pointed out, your code has problems. For instance, returning by value an atomic<> type doesn't make sense for me.)

Comments

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.