Recently I came across a specific issue while working with ACE_Atomic_Op on ACE build “5.4.6″.
The problem is that although ACE_Atomic_Op gives an access to the underlying synchronization object but still it is deprecated and causes deadlock.
ACE_Atomic_Op_Ex must be used instead.
Most probably it is specific to my configuration with Red Hat 3.2.3 (kernel 2.4.21) with ACE build 5.4.6.
Here is the full story which explains the usage of ACE_Atomic_Op.
Suppose you have an integer counter, which is shared among multiple threads. For example, such counter can be used to count incoming messages.
In your application different threads are increasing the value of your shared counter. Such functionality is provided by ACE with ACE_Atomic_Op template.
typedef ACE_Atomic_Op<ace_thread_mutex , int> MyCounter;
MyCounter counter(0); //original value
++counter;
std::cout < < counter.value() << std::endl; // output
Everything works fine. You can use different type of synchronization objects by specifying a template parameter for ACE_Atomic_op.
Now, here comes another requirement. You should reset the value of the counter and return the last value.
The following approach is wrong:
int iResult = counter.value();
counter = 0;
return iResult;
Because in multi threaded application there is a race condition when two threads are simultaneously increasing and resetting value at the same time. Step by step: the resetting thread will first get the last value, then another thread increases the counter and then resetting thread resets the counter. The explained race condition scenario results in losing several values, so in practice such application would tend to underestimate the true value.
Such a classical race condition can be solved if you have access to the underlying synchronization object. You can explicitly lock the access to the counter in first thread, then you get last value and reset it in the same thread. After you release the lock, another thread can increase the counter. In such scenario race condition is avoided and counter values are not lost.
So, to implement a better solution we would write something like this:
int GetAndReset(){
int result;
{
ACE_Guard<ace_thread_mutex> g(counter.mutex());
result = counter.value_i();
counter = 0;
}
return result;
}
For increasing the counter other threads can still use “++counter” since inside this operation ACE is automatically tries to obtain the underlying lock.
The problem is that I have a deadlock so I looked at the ACE_Atomic_Op header:
* NOTE: This member function is deprecated and so may go away in * the future. If you need access to the underlying mutex, consider * using the ACE_Atomic_Op_Ex template instead. */ ACE_LOCK &mutex (void);
Fortunately with ACE_Atomic_Op_Ex everything works fine, plus you also get a nice feature. It is possible to share one mutex for several counters so that all
will use one underlying synchronization which is also exposed to the application code.