I just discovered synchronized_value from reading the 1.54.0 beta documentation. On Wed, Feb 20, 2013 at 7:02 PM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Do you see something completely wrong with this addition?
No idea yet but I'm willing to test it in production code. See below for minor details.
Has some of you had a need for this? Could you share the context?
Yes, I think so. I have some cases (which I'm still working on but will be published as OSS soon) where I do something like that: struct ThingInfo { Id<ObjectInfo> id; std::string name; URI location; }; struct ThingContent { std::vector< Id<Foo> > foo_id_list; std::vector< Id<Bar> > bar_id_list; }; // this type MUST have thread-safe interface class Thing { public: explicit Thing( ThingInfo info ); ThingInfo info() const { boost::lock_guardboost::mutex lg( m_info_mutex ); return m_info; } ThingContent content() const { boost::lock_guardboost::mutex lg( m_content_mutex ); return m_content; } // + several modifying functions that use the work queue like that: void add( std::shared<Bar> bar ) { m_work_queue.push( [this, bar]{ m_bars.emplace_back( bar ); { boost::lock_guardboost::mutex lg( m_content_mutex ); m_content.bar_id_list.emplace_back( bar->id() ); } }); } void rename( std::string new_name ) { m_work_queue.push( [this, new_name]{ boost::lock_guardboost::mutex lg( m_content_mutex ); // SILENT ERROR!!! m_info.name = new_name; }); } private: WorkQueue m_work_queue; // tasks executed later std::vector< std::shared<Foo> > m_foos; // manipulated only by code in the work queue std::vector< std::shared<Bar> > m_bars; // idem ThingInfo m_info; // should be manipulated only with m_info_mutex locked ThingContent m_content; // should be manipulated only with m_content_mutex locked boost::mutex m_info_mutex; // protect m_info boost::mutex m_content_mutex; // protect m_content }; This is not real code but just how it looks like in my work-in-progress classes in my OSS project.
From reading the documentation, using synchronized_value I would then change the Thing implementation to:
// this type MUST have thread-safe interface class Thing { public: explicit Thing( ThingInfo info ); ThingInfo info() const { return *m_info; } ThingContent content() const { *return m_content; } // + several modifying functions that use the work queue like that: void add( std::shared<Bar> bar ) { m_work_queue.push( [this, bar]{ m_bars.emplace_back( bar ); m_content.synchronize()->bar_id_list.emplace_back( bar->id() ); }); } void rename( std::string new_name ) { m_work_queue.push( [this, new_name]{ m_info.synchronize()->name = new_name; // OK: CAN'T USE THE WRONG MUTEX!! }); } private: WorkQueue m_work_queue; // tasks executed later std::vector< std::shared<Foo> > m_foos; // manipulated only by code in the work queue std::vector< std::shared<Bar> > m_bars; // idem boost::sychronized_value<ThingInfo> m_info; boost::sychronized_value<ThingContent> m_content; }; This version is to me: - more explicit on reading; - shorter; - avoid some mistakes like the one pointed in the comments - which might be hard to spot; The only thing that bother me is that synchronized_value is a long name and the "_value" part is, in my opinion, too much. Also, maybe using the pointer-semantic operators is not the best idea. I guess that if std::optional does, then it's ok. Once Boost 1.54.0 is released I will have the opportunity to try it by refactoring my code. I'll report if I find issues. Joel Lamotte