i've been using std::mutex lately , looking pattern/design guide on deleting object has std::mutex member. problem arises when object public function uses mutex(monitor function, critical section, etc) , when object deleted, might have threads still waiting on mutex. std::mutex have undefined behavior deleted while being locked other thread, , problem arises.
i want know acceptable pattern use in case. or if considered bad coding style, way avoid design, ie) not delete object mutex methods still being waited on.
example:
//a public method uses mutex. iasyncaction^ xinputbase::flushtask() { return create_async([this](){ _monitormutex.lock(); if (_readytoflush && !_nomoreflush) { //should flush once, block additional flush signals. _nomoreflush = true; _monitormutex.unlock(); //actually flush concurrency::task<void> uitask = concurrency::create_task(windows::applicationmodel::core::coreapplication::mainview->corewindow->dispatcher->runasync(windows::ui::core::coredispatcherpriority::normal, ref new windows::ui::core::dispatchedhandler([=]() { onflush(); }))); } else { _needtoflush = true; _monitormutex.unlock(); } }); }
tried solutions: destructor waiting mutex unlock after waiting threads processed. i've implemented setting flag outside of mutex, threads in method either exiting or waiting on mutex. i've locked std::mutex last time @ destructor. given std::mutex fair, destructor should unlocked last after other waiting threads processed, destroy object.
this doesn't work because std::mutex not guarantee fairness in platforms.
sort of working solution: implement object ref class or other reference counted object(smart pointers). implement concurrent method lamda holds strong reference object. once other objects holding object deleted , lamdas mutexes processed, object deleted automatically.
i don't method poses restriction on rest of code. winrt, method not give control thread deletes object, result, ui thread error can occur.
you cannot guard lifetime of object data within object.
you can guard object external mutex.
so start this:
template<class t> struct locked_view { template<class f> auto operator->*(f&& f) const -> std::result_of_t< f(t const&) > { auto l = lock(); return std::forward<f>(f)(*t); } locked_view(locked_view const&) = default; locked_view& operator=(locked_view const&) = default; locked_view()=default; explicit operator bool() const { return m&&t; } locked_view( std::mutex* min, t* tin ):m(min), t(tin) {} private: std::unique_lock<std::mutex> lock() const { return std::unique_lock<std::mutex>(*m); } std::mutex* m; t* t; }; template<class t> struct locked { locked()=default; locked(locked&& o): t( o.move_from() ) {} locked(locked const& o): t( o.copy_from() ) {} locked& operator=(locked&& o) { auto tin = o.move_from(); assign_to(std::move(tin)); return *this; } locked& operator=(locked const& o) { auto tin = o.copy_from(); assign_to(std::move(tin)); return *this; } template<class u, std::enable_if_t<!std::is_same<std::decay_t<u>, locked>{}, int> =0 > locked( u&& u ): t( std::forward<u>(u) ) {} // stars of show: locked_view<t const> read() const { return {&m, std::addressof(t)}; } locked_view<t> write() { return {&m, std::addressof(t)}; } t move_from() { return write()->*[](t& tin){return std::move(tin);}; } t copy_from() const { return read()->*[](t const& tin){return tin;}; } template<class u> void assign_to( u&& u ) { write()->*[&](t& t){ t = std::forward<u>(u); }; } private: mutable std::mutex m; t t; };
use looks like:
locked<int> my_int = 7; my_int.read()->*[](int x){ std::cout << x << '\n'; };
next, stuff std::unique_ptr<yourclass>
it. permit people delete it.
finally, share std::shared_ptr<>
it.
so
template<class t> using shared_locked = std::shared_ptr< locked< t > >; template<class t> using shared_locked_ptr = shared_locked< std::unique_ptr<t> >;
is type.
to use, this:
shared_locked_ptr<widget> w; w->read()->*[&]( auto& ptr ) { if (ptr) ptr->do_something(); };
where check lifetime within locked block.
to thread-safe delete object:
w->write()->*[&]( auto& ptr ) { ptr = {}; };
the lock around pointer widget, not around widget. pointer pointer widget shared.
code not tested, similar design has worked before.
Comments
Post a Comment