Skip to main content
gamedev.net
🔒 Locked

Multithreading question

Started by KulSeran Mar 18, 2009 at 12:18 AM 4 replies 1.6k views
Original Post
KulSeran
KulSeran
Here's the rundown. I'm wondering if there is a better way to do this: Pseudo flow for my C++ code:

[L] - Lock
 - Unlock

Thread A:
volitile bool _request_done = false;
[L] Add Request ( data, &_request_done ) 
while ( 1 )
  do things
  if ( _request_done ) [L] Pop Request and final status  process outputs

Thread Z:
[L] Find Request 
Process Request
[L] Update Status 
if ( status == DONE ) (*request._callbackFlag) = true;



The idea behind it being that Thread A is doing a lot of things, and doesn't care when "Request" gets done, just that someday it might get done. Thread Z just processes requests as they come in, doing partial work on each item in the priority queue till they are done. Is it safe to just "synchronize" this with a standard bool as a flag "i'm done!" or is there a better way to "callback" to thread A that it can work with the data now? background for those who care: Thread A-Y are threads that might need data. Thread Z, prioritizes asynchronous file reads from the disk in an attempt to maintain datarate and deadline times for different requests. For example, the system may want to load a 200Mb data file, but there is a movie playing now that has a hard datarate requirement inorder to not stutter. On a normal read call the OS doesn't know how important the data is, so the movie may suffer instead of the data just waiting to finish till the movie is over. Edit: Hell, couldn't I even just wait on the status to change to something valid, and proceede from there? I guess my big fear is that without the synchronization primitive, the CPU A may never get an updated copy of the memory edited by Thread Z for that flag. [Edited by - KulSeran on March 18, 2009 12:44:32 AM]
Hodgman
Hodgman
Quote:
I guess my big fear is that without the synchronization primitive, the CPU A may never get an updated copy of the memory edited by Thread Z for that flag.
Volatile will ensure that the CPU will constantly read the value of your flag from memory, so you will get an updated variable - HOWEVER, your flag may be written to memory BEFORE your outputs are, causing bad, bad things to happen when you try to read the outputs from the other thread.

On many compilers, "volitile bool" will not be thread safe (Linux kernel programmers do not accept any contributions containing the word volatile - because it is a likely indicator of buggy/broken code).

Volatile stops the compiler from performing optimisations on that boolean at compile-time, but an out-of-order CPU (like a pentium) can still optimise it at runtime.

The main problem that these CPUs will cause, is that the flag can be set to true BEFORE the work actually has been completed.


Instead of a volatile variable, you need an atomic variable. Atomic doesn't exist in C++ yet (have to wait for C++0x), so you have to make your own using assembly or the Win32 InterlockedExchange family of functions.

[edit] see http://www.ddj.com/hpc-high-performance-computing/212701484
and http://herbsutter.wordpress.com/
KulSeran
KulSeran
Yeah. That is what I feared. I'm just trying to find a way to not have to lock in order to poll the status of the data.
KulSeran
KulSeran
Ok. So after thinking this through, and looking at other parts of the code I want to write multithreaded.
I understand the art of the "foreground-background buffer" trick, where you lock all threads on the "end of frame" and then swap buffers.
But I guess what i really need is a good document on how to make a fast message system to send data between threads ( in theory I don't want to be locking, check for messages, unlock. as I'm fairly sure that will sap all my CPU time right there.

(edit. reading your edit Hodgman)

I can kinda assume a "lock free queue" is what I need, but how can I insure that by the time my message is on the queue, all the data I need is actually written out to memory?
Hodgman
Hodgman
Quote:
Original post by KulSeran
Yeah. That is what I feared. I'm just trying to find a way to not have to lock in order to poll the status of the data.
The solution I gave you doesn't require locking. You just replace your volatile variable with an atomic variable and you're done. The atomic variable uses a memory barrier to ensure that your data is written to memory before the value of the flag. There is no locking involved (well, technically the CPUs cache might have to do locking, but you don't have to code any locks).

The hard part is that Atomic variables don't exist in C++ yet, so you have to make your own using memory barriers (e.g. InterlockedExchange on Win32)

Quote:
I can kinda assume a "lock free queue" is what I need, but how can I insure that by the time my message is on the queue, all the data I need is actually written out to memory?
Yep a lock-free queue is what you need for fast communication between threads.
As mentioned above, atomic variables (which are based on memory barrier instructions) ensure that memory is written in the correct order.
This is a very complicated data structure though, so it would be best to use one written by an expert ;)
As a Microsoft guy once said "lock free coding is the kind of thing where you think you've got it right, and then 12 months later you realize you didn't understand it at all".

[edit]
Instead of trying to write you own, you can use the lock-free linked-list provided by the Win32 API:
http://msdn.microsoft.com/en-us/library/ms684121(VS.85).aspx

If you want to try and make your own atomic variable class, then use the Interlocked functions from the Win32 API:
http://msdn.microsoft.com/en-us/library/ms684122(VS.85).aspx
KulSeran
KulSeran
Thanks!
I think I'll end up wrapping something around the Win32 InterlockedCompareExchange functions. I want this to run on several different platforms, and will hopefully be able to just replace that one intrinsic. I'd been reading up on different asm ops, and figured I needed one op to atomically swap out a flag, and a different op to force the processor to flush everything out to main memory. I didn't realize windows had instrinsics in VS2005+ that covered some of this with less assembly.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.