• Advertisement
Sign in to follow this  

Thread communication without mutexs

This topic is 743 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I currently have some very simple threading in my app. The main thread just loops and to stop my app freezing up I moved the work load to a separate thread. My second thread just does a lot of maths stuff to create the data for a texture, it does say 10 iterations (I am doing this https://en.wikipedia.org/wiki/Mandelbrot_set). Then finishes. The main thread then takes the data, creates a texture and then starts a thread to do another bunch of iterations. The process keeps repeating.

 

I don't access any shared data except two boolean values which I use for communication. I don't use any mutexs as it's just the two flags that are shared. I tried to work out if they were atomic or not and I don't think they are, I saw how to check the assembly and if they generate more than 1 line (which they do when used) then they are not atomic.

 

So how do I make them safe?

 

Currently they are in a struct with all the other data I need to pass over (I don't have an alternative way to pass data since I can only pass a single void*). I think it's the fact that they are in a struct that causes multiple lines of assembly to be generated. I can guarantee only one thread will write and one thread will read. For example, main thread is the only one that will ever write to the quit flag and the worker thread will only read it. Is that acceptable to do? the flags will out live the thread. I don't want to use mutexs since these flags are getting checked very often and this is already a very intensive app.

 

A few more details:

This is an Android app using c++ NDK.

I can't use <atomic> types since I can't use c++11

I am using pthreads.

 

Thanks.

Share this post


Link to post
Share on other sites
Advertisement

I can't use <atomic> types since I can't use c++11

 

Why not? NDK r10e provides toolchains with gcc 4.9 and clang 3.6. Recent enough for c++11

Share this post


Link to post
Share on other sites

The "quit" and "done" variables not being behind synchronization controls is just fine, so long as they are separate locations in memory (don't be clever and try to make them parts of a bitfield). Do declare them as 'volatile', however, to make sure that the threads actually read the relevant part of memory each time they access those variables.

 

I'm assuming the worker thread is provided a location to write its results (or provided a location to write a pointer to the results), and the main thread picks it up from there. So long as the main thread doesn't attempt to read that until the "done" flag is set (by the worker), that should be fine. But this is still data being shared; you're just protecting it with a protocol, instead of a locking structure.

 

Here once lay some out-of-date hogwash. Ignore it.

Edited by Wyrframe

Share this post


Link to post
Share on other sites

 

I can't use <atomic> types since I can't use c++11

 

Why not? NDK r10e provides toolchains with gcc 4.9 and clang 3.6. Recent enough for c++11

 

Good question. I set my project to use it a while ago and I couldn't get it working for reasons I cannot recall but I just did it now and there were a few complaints but it works fine now. I should have tried this earlier.

 

I guess I get my atomic types now :).

Share this post


Link to post
Share on other sites


This is an Android app using c++ NDK.

I can't use types since I can't use c++11

I am using pthreads.

I am not sure of android, but if it does implement socket and ports, you can use those.  On windows, when socket blocks (wheather read or listen or write) the thread utilizes 0 cpu usage. You can of course use non blocking and blocking sockets combined - how it would suite you.

 

In your case one solution might be for example a non blocking read from main thread + blocking write in worker + blocking read in worker + non blocking write in main thread (managed in main)

Share this post


Link to post
Share on other sites

I don't use any mutexs as it's just the two flags that are shared. I tried to work out if they were atomic or not and I don't think they are, I saw how to check the assembly and if they generate more than 1 line (which they do when used) then they are not atomic.

Even a single asm instruction may be non-atomic... That is not a good indicator. There's also memory reordering issues to worry about -- if you do some work and then set "done = true", thr compiler might optimize your code so that it sets "done = true" AND THEN does the work. That would obviously be very bad.

Start by just using a mutex, and storing the bool as an integer. Sure it's wasteful, but probably safe (most CPUs can perform atomic writes to ints, but not bools... But this isnt necessary true in all cases... You have to read CPU documentation here). Mutexes happen to also issue memory fences that guarantee the correct ordering of operations.

Then replace that crappy mutex approach with a std::atomic. That will automatically choose the correct underlying type for your system (e.g. storing your bool inside an int), and will also issue memory fences that guarantee the correct ordering of operations.

And if that's not possible, you'd have to read your CPU and compiler documentation to figure out how to generate compile-time and run-time memory barriers yourself, and atomic memory exchanges.

Share this post


Link to post
Share on other sites

 


Do declare them as 'volatile', however, to make sure that the threads actually read the relevant part of memory each time they access those variables.

 

See below, volatile is not the same in c++ as it is in java.  Ryan_001's post is accurate.

 

http://stackoverflow.com/questions/4557979/when-to-use-volatile-with-multi-threading

 

 

My education in C++ did not, indeed, include pointing out where the standard stops and the compiler's interpretation of it begins, when it comes to the volatile keyword. I had understood that all read/write to a volatile location must be sequenced as in the source, and that reads must be freshened from the provided access path (such as reading memory dereferenced via pointer), never from optimized-to-register cached access. So, forget essentially everything I've said in that last post.

 


[...] the main thread could see the 'done' but the worker thread's data could still be in that core's cache and never made it to shared memory, causing the main thread to read gibberish.

That sounds more like a hardware cache management problem, not something a user of the architecture should be required to be aware of... but I'll defer to your experience.

Share this post


Link to post
Share on other sites

I had understood that all read/write to a volatile location must be sequenced as in the source, and that reads must be freshened from the provided access path (such as reading memory dereferenced via pointer),


True in some languages. Just not C or C++.

volatile is useful for signal handlers but not threads .

Volatile requires that all reads and writes have side effects but doesn't add memory barriers. Volatile accesses cannot be optimized away but the compiler has a lot of other "as-if" optimizations out can legally apply regarding memory ordering.

Also recall that the machine kiang can reorder memory accesses in some cases, and volatile also did not protect against those.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement