Саморазлочивающий объект shared_ptr
От: кубик  
Дата: 12.07.22 07:12
Оценка:
Привет,

Провожу рефакторинг.

У меня есть функция t_object get_object ()
t_object это boost::shared_ptr

Все работает, но всем еще надо вызывають read-write локи и ничего не перепутать с разлоками.
Это всё муторно и черевато.
Я хочу написать get_object так , как угодно изменив и добавив
что б object лочила как надо и возвращала.
а когда возвращаемый shared_ptr пропадал и поля видимости и разрушался, что б он еще и разлочивал объект правильно.
С++ старый (VS 2008) и есть boost.
Я могу написать с макросами и BOOST_SCOPE_EXIT но это некрасиво.
Re: Саморазлочивающий объект shared_ptr
От: Chorkov Россия  
Дата: 12.07.22 07:48
Оценка:
Здравствуйте, кубик, Вы писали:

К>Привет,


К>Провожу рефакторинг.


К>У меня есть функция t_object get_object ()

К>t_object это boost::shared_ptr

К>Все работает, но всем еще надо вызывають read-write локи и ничего не перепутать с разлоками.

К>Это всё муторно и черевато.
К>Я хочу написать get_object так , как угодно изменив и добавив
К>что б object лочила как надо и возвращала.
К>а когда возвращаемый shared_ptr пропадал и поля видимости и разрушался, что б он еще и разлочивал объект правильно.
К>С++ старый (VS 2008) и есть boost.
К>Я могу написать с макросами и BOOST_SCOPE_EXIT но это некрасиво.

Правильно ли я понял ? :
У вас есть класс Foo, у которого есть функции lock/unlock.
typedef boost::shared_ptr<Foo> t_object;

Есть переменная:
t_object m_obj;


Вы хотите чтобы функция get_object() возвращала тот-же объект, указатель на который хранится в m_obj, но предваритено вызвав lock(), и автоматически
вызвать unlock(), когда указатель, возвращенный get_object, перестанет использоваться?


Или мутекс у вас отделен от Foo, и нужно только контролировать время жизни выданного указателя, для вызова unlock?
Re[2]: Саморазлочивающий объект shared_ptr
От: кубик  
Дата: 12.07.22 08:00
Оценка:
Здравствуйте,


C>Вы хотите чтобы функция get_object() возвращала тот-же объект, указатель на который хранится в m_obj, но предваритено вызвав lock(), и автоматически

C>вызвать unlock(), когда указатель, возвращенный get_object, перестанет использоваться?

Да, вот правильно.
Re[3]: Саморазлочивающий объект shared_ptr
От: Chorkov Россия  
Дата: 12.07.22 08:38
Оценка: 19 (2)
Здравствуйте, кубик, Вы писали:

К>Здравствуйте,



C>>Вы хотите чтобы функция get_object() возвращала тот-же объект, указатель на который хранится в m_obj, но предваритено вызвав lock(), и автоматически

C>>вызвать unlock(), когда указатель, возвращенный get_object, перестанет использоваться?

К>Да, вот правильно.


Тогда нужно воспользоваться специальным конструктором std::shared_ptr
( он 8-й в списке https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr )

Вообще-то он предназначен для создания указания на члены класса, хранимого под умным указателем. Но для "пометки" указателей возвращенных функцией он тоже сгодится.


template<typename T>
struct lock_content_guard
{
    std::shared_ptr<T> ptr;
    lock_content_guard(std::shared_ptr<T> p) : ptr(p)
    {
        if(ptr)
            ptr->lock();
    }
    ~lock_content_guard()
    {
        if(ptr)
            ptr->unlock();
    }
};

template<typename T>
std::shared_ptr<T> lock_content(std::shared_ptr<T> ptr)
{
    auto p_guard = std::make_shared< lock_content_guard<T> >(ptr);
    return std::shared_ptr<T>( p_guard, ptr.get() );
}


t_object get_object()
{
    return lock_content(m_obj);
}


см. полный пример: https://godbolt.org/z/nEWc3KrYq

У указателя boost::shared_ptr, тоже должен быть такой конструктор.
Re[3]: Саморазлочивающий объект shared_ptr
От: B0FEE664  
Дата: 12.07.22 08:47
Оценка: 35 (4) +1
Здравствуйте, кубик, Вы писали:

C>>Вы хотите чтобы функция get_object() возвращала тот-же объект, указатель на который хранится в m_obj, но предваритено вызвав lock(), и автоматически

C>>вызвать unlock(), когда указатель, возвращенный get_object, перестанет использоваться?
К>Да, вот правильно.

Так можно сделать, завернув shared_ptr в ещё один смарт-указатель, но практика показывает, что удобнее и безопаснее вместо функции get_object() написать функцию transform(Fn functor), которая в качестве параметра принимает функтор и исполняет его с данными которые лочатся до вызова функтора и разлочиваются после окончания его исполнения:

template<typename T>
class SafeData
{
   public:
      template<class Fn>
      auto Transform(Fn&& op)
      {
         std::lock_guard<std::mutex> lock( mtx );
         return op(data);
      }
   private:
      std::mutex  mtx;
      T           data;
};


пример:

struct User
{
  std::string name;
};

....

SafeData<User> some_user;

....

void SetUserName(const std::string& strName)
{
  some_user.Transform( [strName](User& user){ user.name = strName; } );  
}


std::string GetUserName()
{
  return some_user.Transform( [](User& user){ return user.name; } );  
}



Это близкая к реальному коду иллюстрация идеи.
И каждый день — без права на ошибку...
Re[4]: Саморазлочивающий объект shared_ptr
От: кубик  
Дата: 12.07.22 09:02
Оценка:
Здравствуйте, Chorkov, Вы писали:

C>Тогда нужно воспользоваться специальным конструктором std::shared_ptr

C>( он 8-й в списке https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr )

Ойейей, это какое то новое С++ , а 8й коннструктор вообще из C++20.
Нельзя ли по другому как угодно изменив get_object и его параметры?
Re[5]: Саморазлочивающий объект shared_ptr
От: Chorkov Россия  
Дата: 12.07.22 09:32
Оценка: 9 (1)
Здравствуйте, кубик, Вы писали:

К>Здравствуйте, Chorkov, Вы писали:


C>>Тогда нужно воспользоваться специальным конструктором std::shared_ptr

C>>( он 8-й в списке https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr )

К>Ойейей, это какое то новое С++ , а 8й коннструктор вообще из C++20.

К>Нельзя ли по другому как угодно изменив get_object и его параметры?

Там два 8-х конструктора. Для до c++20 и после.
(В с++20 заменили const shared_ptr<Y>& на shared_ptr<Y>&& )
Этот конструктор был сразу.

В boost::shared_ptr он тоже был со второй версии shared_ptr:
https://www.boost.org/doc/libs/1_35_0/libs/smart_ptr/shared_ptr.htm#constructors
template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p); // never throws
Re[4]: Саморазлочивающий объект shared_ptr
От: B0FEE664  
Дата: 12.07.22 12:17
Оценка: +1
Здравствуйте, Chorkov, Вы писали:

C>см. полный пример: https://godbolt.org/z/nEWc3KrYq


Предположим, что у нас есть две нитки и два объекта:

t_object m_obj1 = std::make_shared<Foo>();
t_object m_obj2 = std::make_shared<Foo>();

t_object get_object1()
{
    return lock_content(m_obj1);
}

t_object get_object2()
{
    return lock_content(m_obj2);
}


выполняем в первой нитке код:

// thread 1
...
    auto i1 = get_object1();
...
    auto i2 = get_object2();
...


выполняем во второй нитке код:

// thread 2
...
    auto o2 = get_object2();
...
    auto o1 = get_object1();
...


Получаем deadlock. В лучшем случае — сразу, в худшем — иногда.
И каждый день — без права на ошибку...
Re[4]: Саморазлочивающий объект shared_ptr
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 13.07.22 09:27
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Это близкая к реальному коду иллюстрация идеи.

Только надо помнить про блокировку чтобы внутри лямбды дедлок не устроить.
Sic luceat lux!
Re[5]: Саморазлочивающий объект shared_ptr
От: B0FEE664  
Дата: 13.07.22 12:23
Оценка:
Здравствуйте, Kernan, Вы писали:

BFE>>Это близкая к реальному коду иллюстрация идеи.

K>Только надо помнить про блокировку чтобы внутри лямбды дедлок не устроить.

Да, разумеется.
И каждый день — без права на ошибку...
Re[3]: Саморазлочивающий объект shared_ptr
От: AleksandrN Россия  
Дата: 13.07.22 20:28
Оценка: 6 (1)
Здравствуйте, кубик, Вы писали:

К>Здравствуйте,



C>>Вы хотите чтобы функция get_object() возвращала тот-же объект, указатель на который хранится в m_obj, но предваритено вызвав lock(), и автоматически

C>>вызвать unlock(), когда указатель, возвращенный get_object, перестанет использоваться?

К>Да, вот правильно.


Что делается с этим shared_ptr там, откуда вызывается get_object()?
Думаю, лучше убрать вообще get_object() и добавить в класс, в котором определена переменная m_obj, функцию, которая будет выполнять требуемые действия и вызывать lock()/unlock() для объекта.
Re[4]: Саморазлочивающий объект shared_ptr
От: sergii.p  
Дата: 14.07.22 08:53
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>удобнее и безопаснее вместо функции get_object() написать функцию transform(Fn functor)


VS 2008, там лямбд нет. А без лямбд этот подход практически неприменим
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.