debug mode performance patch
François Dumont
francois.cppdevs@free.fr
Tue Nov 16 21:16:00 GMT 2010
Hi
Here is a new proposition with the deadlock issue fixed. I use
auto_ptr to acquire it even if it is deprecated because unique_ptr is
only available in gnu++0x mode and the library is not built in this
mode, is it fine ? I also created a test to show the problem before the
fix. In fact I even had to add a sleep between the 2 locks in debug.cc
to reproduce the deadlock temporarily, it shows that the problem existed
and that it has been removed now.
Just in case you agree to apply the patch there are new files in
it, especially the safe_sequence.tcc one. Has it to be added in some
files ? I ask because to run tests I had to manually create a link to
this file in x86_64-unknown-linux-gnu/libstdc++-v3/include/debug. Will
it be generated automatically once commited ?
François
On 11/11/2010 10:06 PM, François Dumont wrote:
> Ok I will integrate a test showing the deadlock issue with current
> patch based on your code and then will fix the patch.
>
> Thanks for the code !
>
> On 11/10/2010 11:02 PM, Jonathan Wakely wrote:
>> On 10 November 2010 21:55, François Dumont wrote:
>>> Well, this is not something my change introduce. For the moment the
>>> debug
>>> mode is using a single mutex for all synchronizations. To keep binary
>>> compatibility I haven't been able to add a mutex instance to the safe
>>> sequence so I introduce a small mutex pool to limit contention.
>> Sorry if my review sounded negative, I think the general idea is very
>> good. But with a single mutex there is no chance for deadlock, it
>> just means only one swap can take place at a time.
>>
>> By introducing multiple mutexes without some checking you make it easy
>> to deadlock. Slow and correct is better than fast and deadlock ;-)
>>
>>>> There are two problems with _M_swap in your patch:
>>>>
>>>> 1) if two objects are swapped whih share the same mutex then it will
>>>> try to lock the same mutex twice. You should be able to reproduce
>>>> this like so (untested):
>>>>
>>>> __gnu_debug::vector<int> v[17];
>>>> swap(v[0], v[16]);
>>>>
>>>> If I'm not mistaken, v[0] and v[16] should get the same mutex from
>>>> get_mutex and so should lock it twice. That's bad, even in a
>>>> single-threaded program.
>>>>
>>> Yes, this is why I asked if mutex are guarantied to be reentrant
>>> even if I
>>> agree that even if it is so it is still bad practice.
>> When your mail arrived I had just finished some examples to
>> demonstrate the problems, maybe these should go in the testsuite.
>>
>> This deadlocks for me:
>>
>> #include<mutex>
>> #include<iostream>
>>
>> std::mutex& get_mutex(void* p)
>> {
>> const uintptr_t mask = 0xf;
>> static std::mutex mutexes[ mask + 1 ];
>> uintptr_t index = (reinterpret_cast<uintptr_t>(p) /
>> sizeof(void*))& mask;
>> std::cerr<< index<< std::endl;
>> return mutexes[ index ];
>> }
>>
>> struct A { void* p; };
>>
>> void swap(A& a, A& b)
>> {
>> std::lock_guard<std::mutex> l1(get_mutex(&a));
>> std::lock_guard<std::mutex> l2(get_mutex(&b));
>> }
>>
>> int main()
>> {
>> A a[17];
>> swap(a[0], a[16]); // distinct objects, but "share" a mutex
>> }
>>
>>>> 2) consider four sequences, A, B, C and D. They happen to have
>>>> addresses such that:
>>>>
>>>> &A.get_mutex() ==&C.get_mutex()
>>>> and
>>>> &B.get_mutex() ==&D.get_mutex()
>>>>
>>>> now, in my multithreaded program I do this concurrently:
>>>> swap(A, B); swap(D, C);
>>>> I'm not accessing any single object in more than one thread, but it
>>>> will deadlock.
>>>>
>>> Indeed
>> e.g this deadlocks for me:
>>
>> // same definition of A, get_mutex and swap:
>> #include<thread>
>>
>> A a[32];
>>
>> void f(int i, int j) { for (int k=0; k<10; ++k) swap(a[i], a[j]); }
>>
>> int main()
>> {
>> // swap distinct objects in two threads
>> std::thread t1(f, 0, 1);
>> std::thread t2(f, 17, 16);
>> t1.join();
>> t2.join();
>> }
>>
>>
>>>> The solution is to ensure that you don't lock the same mutex twice and
>>>> you always lock in a consistent order
>>>>
>> e.g. this fixes both deadlocks
>>
>> void swap(A& a, A& b)
>> {
>> std::mutex& m1 = get_mutex(&a);
>> std::mutex& m2 = get_mutex(&b);
>> if (&m1< &m2)
>> std::lock_guard<std::mutex> l(m1);
>> else
>> {
>> std::lock_guard<std::mutex> l1(&m1< &m2 ? m1 : m2);
>> std::lock_guard<std::mutex> l2(&m1< &m2 ? m2 : m1);
>> }
>> }
>>
>>> Ok, so except fixing this lock issue you seem to agree on the
>>> modified debug
>>> mode lock model, great. I will fix this issue and submit a new
>>> proposition.
>> Great - thanks for working on this!
>>
>> Jonathan
>
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: performance.patch
URL: <http://gcc.gnu.org/pipermail/libstdc++/attachments/20101116/66d5afc5/attachment.ksh>
More information about the Libstdc++
mailing list