Need help with communication between multiple threads

Sean Kelly sean at f4.ca
Tue Feb 20 17:49:31 PST 2007


Chad J wrote:
> kris wrote:
>>
>> Basicially, you need to protect the value from contention between two 
>> threads. There are a number of ways to do this:
>>
>> 1) using native D facilities via the synchronized keyword: expose a 
>> getter and setter method, and have them both synch on the same 
>> object/lock. This is a fairly heavyweight resolution, but it would work.
>>
> 
> So would something like this do the trick?
> 
> class Mutex
> {
>     uint pointlessVariable;
> }
> Mutex mutex;
> 
> uint m_value = 42; // The thing to be protected.
> uint value() // getter
> {
>     synchronized(mutex)
>         return m_value;
> }
> uint value(uint newbie) // setter
> {
>     synchronized(mutex)
>         m_value = newbie;
> 
>     return newbie;
> }
> 
> Or am I supposed to do something else like put m_value inside the mutex 
> class?

You could use synchronized with no arguments and everything will work 
file.  The default behavior for free functions is to synchronize on a 
hidden global object.  Alternately:

     Object valueLock = new Object;

     uint m_value = 42; // The thing to be protected.

     uint value() // getter
     {
         synchronized(valueLock)
             return m_value;
     }

     uint value(uint newbie) // setter
     {
         synchronized(valueLock)
             m_value = newbie;
         return newbie;
     }

This works if you want to synch only specific functions with respect to 
one anohther.

>> 3) use CPU-specific instructions to ensure value access is atomic. 
>> This is what Sean has exposed in the Atomic module within Tango. It is 
>> a lightweight and low-overhead solution, and works by locking the bus 
>> for the duration of the read/write access.
> 
> This sounds cool, but I don't quite understand how to use the Atomic 
> module - what is msync and which value of it do I pick to make things 
> work?  I made a post about this in the Tango forum incase it's more 
> appropriate to discuss there.

The Tango forums are probably more appropriate, but I can give a quick 
summary here (I'm on my way out the door as I write this). 
tango.core.Atomic does essentially two things: it ensures that any 
operation it performs is atomic, and it provides methods to control 
memory ordering regarding such operations.  The latter issue is somewhat 
complicated, but suffice to say that msync.seq is the safest option and 
should be used in most situations.  So for the above:

     uint m_value = 42;

     uint value() // getter
     {
         return atomicLoad!(msync.seq)( m_value );
     }

     uint value(uint newbie) // setter
     {
         atomicStore!(msync.seq)( m_value, newbie );
         return newbie;
     }

For data which will always be modified atomically, a wrapper struct is 
also provided:

     Atomic!(uint) m_value;

     // Atomic really needs a ctor, but this should work
     // for "fast" construction.
     m_value.store!(msync.raw)( 42 );

     uint value() // getter
     {
         return m_value.load!(msync.seq);
     }

     uint value(uint newbie) // setter
     {
         m_value.store!(msync.seq)( newbie );
         return newbie;
     }

Please note that Atomic currently only supports x86, but if there's a 
demand for it then I may add support for other architectures.  If this 
happens, it will probably under Posix (and not Win32), since I'm not 
entirely sure about out-of-the-box assembler support with DMD/Win32.

> When I was porting Phobos to work on ARM-WinCE, it was very helpful to 
> be able to discard a module without breaking other parts of the lib, 
> namely in the case of that module requiring a broken language feature or 
> inline assembly (inline asm is not currently available with 
> arm-wince-pe-gdc, and I don't feel like learning ARM asm yet).  That 
> said, Atomic looks like it will be very broken on ARM in its current 
> state.  I also benchmarked synchronized reads vs atomic reads and yeah, 
> synchronized was much slower (I picked "whatever makes it compile" 
> values for msync).  So I'll probably implement a version using only 
> synchronization and a version that uses Atomic instead whenever possible.

See above :-)  Atomic won't work on ARM without additional code.

By the way, it may also eventually be necessary to add a hardware 
instruction for ordering load operations on x86, since I'm becoming 
convinced that load reordering is actually allowed by the IA-32 spec 
(and it may actually be done on some AMD CPUs).  I've been resisting 
this until now because it will slow down synchronized loads 
substantially for what may be only a small portion of the x86 hardware 
in production.  So if you (or anyone) decides to use Atomic as-is and 
see weird behavior with atomicLoad using msync.acq or msync.hlb, please 
let me know.


Sean


More information about the Digitalmars-d-learn mailing list