weak_ptr, shared_ptr, thread safety. Вопрос по блокировкам.
От: bratka  
Дата: 27.03.06 20:39
Оценка:
Сначала из документации:

shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);

// some time later

if(int * r = q.get())
{
// use *r
}

Imagine that after the if, but immediately before r is used, another thread executes the statement p.reset(). Now r is a dangling pointer.

The solution to this problem is to create a temporary shared_ptr from q:

shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);

// some time later

if(shared_ptr<int> r = q.lock())
{
// use *r
}

Now r holds a reference to the object that was pointed by q. Even if p.reset() is executed in another thread, the object will stay alive until r goes out of scope or is reset. By obtaining a shared_ptr to the object, we have effectively locked it against destruction.

---

Теперь собственно сам вопрос, возможно ли вместо копирования указателя, "стандартными средствами" boost заблокировать нить пытающуюся удалить объект на который существует слабая ссылка weak_ptr.

В чем собственно проблема:
Нельзя создавать shared_ptr из weak_ptr в потоке А, зная что в другом потоке (Б) объект (вернее его shared_ptr) может быть удален. Владельцем указателя является поток Б. Именно он определяет время, как указателя так соответственно и инстанса. "Нельзя" не потому что я доку неправильно прочитал, а такова постановка задачи.
То-есть если в потоке Б, было принято решение указатель удалить, то поток А должен получить фигу в виде weak_ptr::get() == NULL, если поток А в данный момент еще (или уже) использует указатель, то поток Б обязан ждать, хоть до морковкиного заговения, но указатель не удалять.
Использование
if(int * r = q.get())
{
// use *r
}
приведет к нарушению защиты памяти, и инвалидации указателя.
Использование
if(shared_ptr<int> r = q.lock())
{
// use *r
}
приведет к неблокируемому удалению указателя в потоке Б.

Я вижу 2 варианта:
1. Сделать блокирующий фасад над shared_ptr/weak_ptr.
2. искорёжить weak_ptr::get() и деструкотр shared_ptr.

1-й вариант конечно "правильный", 2-й вариант безобразный и must die.

Вопрос к многоуважаемому All, есть ли другие варианты ?

Учитывая, что shared_ptr уже thread safe, наворачивать фассад с дополнительным мъютексом видится мне как удвоение времени операции доступа к данным. Что в конечном итоге скажется (и очень) на общей производительности системы.
Re: weak_ptr, shared_ptr, thread safety. Вопрос по блокировк
От: bratka  
Дата: 28.03.06 07:05
Оценка:
topic up.
Re: weak_ptr, shared_ptr, thread safety. Вопрос по блокировк
От: Аноним  
Дата: 28.03.06 07:38
Оценка:
...
B>Использование
B>if(shared_ptr<int> r = q.lock())
B>{
B> // use *r
B>}
B>приведет к неблокируемому удалению указателя в потоке Б.

Это интересно почему? Фактически когда q.lock() вернет указатель на еще живой объект, кол-во ссылок ++.
Re: weak_ptr, shared_ptr, thread safety. Вопрос по блокировк
От: Аноним  
Дата: 28.03.06 08:01
Оценка:
B>Я вижу 2 варианта:
B>1. Сделать блокирующий фасад над shared_ptr/weak_ptr.
B>2. искорёжить weak_ptr::get() и деструкотр shared_ptr.

B>1-й вариант конечно "правильный", 2-й вариант безобразный и must die.


B>Вопрос к многоуважаемому All, есть ли другие варианты ?


B>Учитывая, что shared_ptr уже thread safe, наворачивать фассад с дополнительным мъютексом видится мне как удвоение времени операции доступа к данным. Что в конечном итоге скажется (и очень) на общей производительности системы.


А почему дополнительный mutex сильно скажется на производительности? У тебя что, приложение только и занимается тем, что достаёт указатель из именно этого weak_ptr (не обязательно ведь "улучшать" все weak_ptr, только тот, где это надо)? Может это можно делать пореже?

Ну, видимо, не подходят тебе shared_/weak_ptr.
Так ты храни указатель не на сам объект, а на того кто может его отдать (либо не отдать), а он уж пусть разбирается.
Re[2]: weak_ptr, shared_ptr, thread safety. Вопрос по блокир
От: bratka  
Дата: 28.03.06 12:52
Оценка:
Здравствуйте, Аноним, Вы писали:

А>...

B>>Использование
B>>if(shared_ptr<int> r = q.lock())
B>>{
B>> // use *r
B>>}
B>>приведет к неблокируемому удалению указателя в потоке Б.

А>Это интересно почему? Фактически когда q.lock() вернет указатель на еще живой объект, кол-во ссылок ++.


Теперь удаляем shared_ptr в потоке Б (кол-во ссылок --), объект остается только в потоке А. Поменяли владельца. Именно этого делать нельзя.
Re[2]: weak_ptr, shared_ptr, thread safety. Вопрос по блокир
От: bratka  
Дата: 28.03.06 13:07
Оценка:
Здравствуйте, Аноним, Вы писали:

B>>Я вижу 2 варианта:

B>>1. Сделать блокирующий фасад над shared_ptr/weak_ptr.
B>>2. искорёжить weak_ptr::get() и деструкотр shared_ptr.

B>>1-й вариант конечно "правильный", 2-й вариант безобразный и must die.


B>>Вопрос к многоуважаемому All, есть ли другие варианты ?


B>>Учитывая, что shared_ptr уже thread safe, наворачивать фассад с дополнительным мъютексом видится мне как удвоение времени операции доступа к данным. Что в конечном итоге скажется (и очень) на общей производительности системы.


А>А почему дополнительный mutex сильно скажется на производительности? У тебя что, приложение только и занимается тем, что достаёт указатель из именно этого weak_ptr (не обязательно ведь "улучшать" все weak_ptr, только тот, где это надо)? Может это можно делать пореже?

В данном потоке именно это и происходит: в бесконечном цикле, до тех пор пока возможно получить указатель на объект, получать его и проводить некую маленькую работу.

А>Ну, видимо, не подходят тебе shared_/weak_ptr.

weak_ptr/shared_ptr как раз очень подходят, жаль что нет механизма блокирования, и сохранения владельца. То-есть операции swap/reset — порочны в данной ситуации.
А>Так ты храни указатель не на сам объект, а на того кто может его отдать (либо не отдать), а он уж пусть разбирается.
shared_ptr<shared_ptr<T>> ??
Re[3]: weak_ptr, shared_ptr, thread safety. Вопрос по блокир
От: Аноним  
Дата: 28.03.06 16:01
Оценка:
B>>>Я вижу 2 варианта:
B>>>1. Сделать блокирующий фасад над shared_ptr/weak_ptr.
B>>>2. искорёжить weak_ptr::get() и деструкотр shared_ptr.

B>>>1-й вариант конечно "правильный", 2-й вариант безобразный и must die.


B>>>Вопрос к многоуважаемому All, есть ли другие варианты ?


B>>>Учитывая, что shared_ptr уже thread safe, наворачивать фассад с дополнительным мъютексом видится мне как удвоение времени операции доступа к данным. Что в конечном итоге скажется (и очень) на общей производительности системы.


А>>А почему дополнительный mutex сильно скажется на производительности? У тебя что, приложение только и занимается тем, что достаёт указатель из именно этого weak_ptr (не обязательно ведь "улучшать" все weak_ptr, только тот, где это надо)? Может это можно делать пореже?

B>В данном потоке именно это и происходит: в бесконечном цикле, до тех пор пока возможно получить указатель на объект, получать его и проводить некую маленькую работу.

Ну, указатель можно получать один раз а маленькую работу проводить 10 раз (это если он делает это непрерывно). Или забрать себе указатель и не отдавать до тех пор пока не поднимут какой-нибудь флажок.
А если он спит на event'е перед получением указателя то +1 mutex это уже не так много.

А>>Ну, видимо, не подходят тебе shared_/weak_ptr.

B>weak_ptr/shared_ptr как раз очень подходят, жаль что нет механизма блокирования, и сохранения владельца. То-есть операции swap/reset — порочны в данной ситуации.
А>>Так ты храни указатель не на сам объект, а на того кто может его отдать (либо не отдать), а он уж пусть разбирается.
B>shared_ptr<shared_ptr<T>> ??

Нет. Просто сделать корректную обёртку над shared_ptr на все случаи жизни — довольно сложно. А тебе ведь надо 2 операции —
1) получить объект если он есть (+возможно, вернуть)
2) убить объект дождавшись момента, когда им никто не пользуется

У этих двух потоков наверняка есть какие-то общие данные, вот и добавь к ним эти два метода и реализуй их так, как ты хочешь — их ведь всего 2-3.
Re[4]: weak_ptr, shared_ptr, thread safety. Вопрос по блокир
От: bratka  
Дата: 28.03.06 16:52
Оценка:
Здравствуйте, Аноним, Вы писали:

B>>>>Я вижу 2 варианта:

B>>>>1. Сделать блокирующий фасад над shared_ptr/weak_ptr.
B>>>>2. искорёжить weak_ptr::get() и деструкотр shared_ptr.

B>>>>1-й вариант конечно "правильный", 2-й вариант безобразный и must die.


B>>>>Вопрос к многоуважаемому All, есть ли другие варианты ?


B>>>>Учитывая, что shared_ptr уже thread safe, наворачивать фассад с дополнительным мъютексом видится мне как удвоение времени операции доступа к данным. Что в конечном итоге скажется (и очень) на общей производительности системы.


А>>>А почему дополнительный mutex сильно скажется на производительности? У тебя что, приложение только и занимается тем, что достаёт указатель из именно этого weak_ptr (не обязательно ведь "улучшать" все weak_ptr, только тот, где это надо)? Может это можно делать пореже?

B>>В данном потоке именно это и происходит: в бесконечном цикле, до тех пор пока возможно получить указатель на объект, получать его и проводить некую маленькую работу.

А>Ну, указатель можно получать один раз а маленькую работу проводить 10 раз (это если он делает это непрерывно). Или забрать себе указатель и не отдавать до тех пор пока не поднимут какой-нибудь флажок.

А>А если он спит на event'е перед получением указателя то +1 mutex это уже не так много.

А>>>Ну, видимо, не подходят тебе shared_/weak_ptr.

B>>weak_ptr/shared_ptr как раз очень подходят, жаль что нет механизма блокирования, и сохранения владельца. То-есть операции swap/reset — порочны в данной ситуации.
А>>>Так ты храни указатель не на сам объект, а на того кто может его отдать (либо не отдать), а он уж пусть разбирается.
B>>shared_ptr<shared_ptr<T>> ??

А>Нет. Просто сделать корректную обёртку над shared_ptr на все случаи жизни — довольно сложно. А тебе ведь надо 2 операции -

А>1) получить объект если он есть (+возможно, вернуть)
----
А>2) убить объект дождавшись момента, когда им никто не пользуется
----


А>У этих двух потоков наверняка есть какие-то общие данные, вот и добавь к ним эти два метода и реализуй их так, как ты хочешь — их ведь всего 2-3.



//thread А
while(1)
{
if(mytype* ptr__=weak_ptr_obj.get())
{
sem_wait(ptr__->semaphore);
// can do the things with ptr__;
}
}

//thread B
// nash_shared_ptr attribute of someclass
~someclass(){
while(nash_shared_ptr.use_count()); //псевдо-решение. беда ... даже биде можно сказать. неблокируемый цикл, а в это время в потоке А объект используется и висит на семафоре — типичный програмный дедлок.
}

тупейшее решение выглядит так:

//thread А
while(1)
{
if(mytype* ptr__=weak_ptr_obj.get())
{
if(pthread_mutex_trylock(ptr__->ptrlock)) // неважно занят или мертв errno не проверяем
break;
sem_wait(ptr__->semaphore);
// can do the things with ptr__;
pthread_mutex_unlock(ptr__->ptrlock);
}
}

//thread B:
~someclass(){
mytype* tmptpr=nash_shared_ptr.get();
sem_post(nash_shared_ptr.get()->semaphore); // очень скользко — смотрим ниже
sem_post(nash_shared_ptr.get()->semaphore); // для надежности
pthread_mutex_lock(tmpptr->ptr_lock); // очень надеемся, что мы заблокировали мьютекс сами, а не попали в очередной дедлок ...
pthread_mutex_destroy(tmpptr->ptr_lock);
}
// умираем , и nash_shared_ptr оходит в мир иной вместе с прощальной песней деструктора

То-есть картина удручающая.
грубая и тупая сила. никакой элегантности.
И никто не может гарантировать, что в последствии будет везде работать на ура. То-есть если одна итерация цикла в потоке А займет меньше 10мс то есть очень высокий шанс, что деструктор someclass в потоке Б так и останется висеть до ишачей пасхи.

Похоже у меня проблема где-то на генетическом уровне
Re[5]: weak_ptr, shared_ptr, thread safety. Вопрос по блокир
От: bratka  
Дата: 28.03.06 16:59
Оценка:
B>//thread B:
B>~someclass(){
B> mytype* tmptpr=nash_shared_ptr.get();
B> sem_post(nash_shared_ptr.get()->semaphore); // очень скользко — смотрим ниже
B> sem_post(nash_shared_ptr.get()->semaphore); // для надежности
B> pthread_mutex_lock(tmpptr->ptr_lock); // очень надеемся, что мы заблокировали мьютекс сами, а не попали в очередной дедлок ...
// ошибку допустил, исправляю:
pthread_mutex_lock(tmpptr->ptr_lock); // unlock before destroy.
B> pthread_mutex_destroy(tmpptr->ptr_lock);
B>}
B>// умираем , и nash_shared_ptr оходит в мир иной вместе с прощальной песней деструктора


B>Похоже у меня проблема где-то на генетическом уровне

суть проблемы правда от этого не изменилась.
Re[6]: weak_ptr, shared_ptr, thread safety. Вопрос по блокир
От: bratka  
Дата: 28.03.06 17:00
Оценка:
Здравствуйте, bratka, Вы писали:



B>>//thread B:

B>>~someclass(){
B>> mytype* tmptpr=nash_shared_ptr.get();
B>> sem_post(nash_shared_ptr.get()->semaphore); // очень скользко — смотрим ниже
B>> sem_post(nash_shared_ptr.get()->semaphore); // для надежности
B>> pthread_mutex_lock(tmpptr->ptr_lock); // очень надеемся, что мы заблокировали мьютекс сами, а не попали в очередной дедлок ...
B>// ошибку допустил, исправляю:
/// B>pthread_mutex_lock(tmpptr->ptr_lock); // unlock before destroy. --- copy-paste not works without correction
pthread_mutex_unlock(tmpptr->ptr_lock); // !!!!!!!!!!!!! DNA error of the coder.
B>> pthread_mutex_destroy(tmpptr->ptr_lock);
B>>}
B>>// умираем , и nash_shared_ptr оходит в мир иной вместе с прощальной песней деструктора


B>>Похоже у меня проблема где-то на генетическом уровне

B>суть проблемы правда от этого не изменилась.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.