Need help with communication between multiple threads

Chad J gamerChad at _spamIsBad_gmail.com
Tue Feb 20 18:56:40 PST 2007


Sean Kelly wrote:
> 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

Cool thanks for the info and this handy low-overhead threading tool.

I didn't have any problems with msync.acq or msync.hlb so far, but when 
loading using msync.seq I get a Win32 Exception.  I created a ticket 
about this.


More information about the Digitalmars-d-learn mailing list